From 04c0fa58fbf453b586a759715dafee98102a8f58 Mon Sep 17 00:00:00 2001 From: Onyeka Obi Date: Sat, 6 Jun 2026 22:19:24 -0700 Subject: [PATCH 01/78] hir-ty: walk container exprs for unused_must_use ExprValidator::check_unused_must_use previously matched only Expr::Call and Expr::MethodCall directly on the expression of a Statement::Expr with semicolon. This left several stmt-with-semi cases unwarned: blocks whose tail is a #[must_use] call, unsafe-blocks, if/else arms, match arms, and const blocks. Refactors check_unused_must_use to walk into Expr::Block { tail }, Expr::Unsafe { tail }, Expr::If { then_branch, else_branch }, Expr::Match { arms }, and Expr::Const(inner) before applying the existing leaf check (callee or method #[must_use] attribute, or #[must_use] ADT return type). The diagnostic stays at the leaf call so the span points to the actual must_use producer rather than the wrapping block. Adds ide-diagnostics tests covering each new container case. Follow-up to rust-lang/rust-analyzer#22239. Signed-off-by: Onyeka Obi --- .../crates/hir-ty/src/diagnostics/expr.rs | 55 +++++-- .../src/handlers/unused_must_use.rs | 135 ++++++++++++++++++ 2 files changed, 180 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 760ebd27e0573..ee75023c4a0d1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -333,17 +333,20 @@ impl<'db> ExprValidator<'db> { let pattern_arena = Arena::new(); let cx = MatchCheckCtx::new(self.owner.module(self.db()), &self.infcx, self.env); for stmt in &**statements { - let diag = match *stmt { + match *stmt { Statement::Expr { expr: stmt_expr, has_semi: true } if self.validate_lints => { - self.check_unused_must_use(stmt_expr) + let mut diags = Vec::new(); + self.check_unused_must_use(stmt_expr, &mut diags); + self.diagnostics.extend(diags); } Statement::Let { pat, initializer, else_branch: None, .. } => { - self.check_non_exhaustive_let(&cx, &pattern_arena, pat, initializer) + if let Some(diag) = + self.check_non_exhaustive_let(&cx, &pattern_arena, pat, initializer) + { + self.diagnostics.push(diag); + } } - _ => None, - }; - if let Some(diag) = diag { - self.diagnostics.push(diag); + _ => {} } } } @@ -415,7 +418,37 @@ impl<'db> ExprValidator<'db> { pattern } - fn check_unused_must_use(&self, expr: ExprId) -> Option> { + fn check_unused_must_use( + &self, + mut expr: ExprId, + acc: &mut Vec>, + ) { + // Walk through container expressions so that the value-producing leaf is + // checked even when wrapped in a block, `unsafe { .. }`, `if`/`match`, or + // a `const { .. }` block. Single-tail chains are followed by reassigning + // `expr`; branching containers (`if`/`match`) recurse on each arm. + loop { + match &self.body[expr] { + Expr::Block { tail: Some(tail), .. } + | Expr::Unsafe { tail: Some(tail), .. } + | Expr::Const(tail) => expr = *tail, + Expr::If { then_branch, else_branch, .. } => { + self.check_unused_must_use(*then_branch, acc); + if let Some(else_branch) = else_branch { + self.check_unused_must_use(*else_branch, acc); + } + return; + } + Expr::Match { arms, .. } => { + for arm in arms.iter() { + self.check_unused_must_use(arm.expr, acc); + } + return; + } + _ => break, + } + } + let fn_def = match &self.body[expr] { Expr::Call { callee, .. } => { let callee_ty = self.infer.expr_ty(*callee); @@ -430,7 +463,7 @@ impl<'db> ExprValidator<'db> { Expr::MethodCall { .. } => { self.infer.method_resolution(expr).map(|(func, _)| func.into()) } - _ => return None, + _ => None, }; let ty_def = self.infer.type_of_expr_with_adjust(expr).and_then(|ty| match ty.kind() { TyKind::Adt(adt, _) => Some(adt.def_id().into()), @@ -440,7 +473,9 @@ impl<'db> ExprValidator<'db> { AttrFlags::must_use_message(self.db(), owner?) .map(|message| BodyValidationDiagnostic::UnusedMustUse { expr, message }) }; - must_use_diag(fn_def).or_else(|| must_use_diag(ty_def)) + if let Some(diag) = must_use_diag(fn_def).or_else(|| must_use_diag(ty_def)) { + acc.push(diag); + } } fn check_for_trailing_return(&mut self, body_expr: ExprId, body: &Body) { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_must_use.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_must_use.rs index e8d0717c91c2f..2173dc9c0addb 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_must_use.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_must_use.rs @@ -126,6 +126,141 @@ fn main() { x = produces(); let _ = x; } +"#, + ); + } + + #[test] + fn block_tail_expression_in_stmt_position() { + check_diagnostics( + r#" +#[must_use] +fn produces() -> i32 { 0 } +fn main() { + { + produces() + //^^^^^^^^^^ warn: unused return value that must be used + }; +} +"#, + ); + } + + #[test] + fn unsafe_block_tail_expression_in_stmt_position() { + check_diagnostics( + r#" +#[must_use] +unsafe fn produces() -> i32 { 0 } +fn main() { + unsafe { + produces() + //^^^^^^^^^^ warn: unused return value that must be used + }; +} +"#, + ); + } + + #[test] + fn nested_block_tail_expression() { + check_diagnostics( + r#" +#[must_use] +fn produces() -> i32 { 0 } +fn main() { + { + { + produces() + //^^^^^^^^^^ warn: unused return value that must be used + } + }; +} +"#, + ); + } + + #[test] + fn no_warning_when_block_tail_is_bound() { + check_diagnostics( + r#" +#[must_use] +fn produces() -> i32 { 0 } +fn main() { + let _x = { + produces() + }; +} +"#, + ); + } + + #[test] + fn if_branches_in_stmt_position() { + check_diagnostics( + r#" +#[must_use] +fn produces() -> i32 { 0 } +fn main() { + if true { + produces() + //^^^^^^^^^^ warn: unused return value that must be used + } else { + produces() + //^^^^^^^^^^ warn: unused return value that must be used + }; +} +"#, + ); + } + + #[test] + fn match_arms_in_stmt_position() { + check_diagnostics( + r#" +#[must_use] +fn produces() -> i32 { 0 } +fn main() { + match 0 { + 0 => produces(), + //^^^^^^^^^^ warn: unused return value that must be used + _ => produces(), + //^^^^^^^^^^ warn: unused return value that must be used + }; +} +"#, + ); + } + + #[test] + fn const_block_in_stmt_position() { + check_diagnostics( + r#" +#[must_use] +const fn produces() -> i32 { 0 } +fn main() { + const { + produces() + //^^^^^^^^^^ warn: unused return value that must be used + }; +} +"#, + ); + } + + #[test] + fn must_use_type_through_block() { + check_diagnostics( + r#" +#[must_use] +struct Important; +fn produces() -> Important { Important } +fn main() { + { + produces() + //^^^^^^^^^^ warn: unused return value that must be used + }; +} "#, ); } From d96d6877b25addb14c9d8e96adb76e3652cee2ff Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 30 Jun 2026 23:37:40 +0300 Subject: [PATCH 02/78] Support Cargo 1.97.0 lockfile path setting The `resolver.lockfile_path` setting was stabilized, so we can omit the `-Zlockfile-path` argument. --- .../project-model/src/build_dependencies.rs | 8 +++++++- .../project-model/src/cargo_config_file.rs | 18 ++++++++++++++++-- .../project-model/src/cargo_workspace.rs | 5 ++++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index 352f41a82636a..c8a0e1b9989de 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -474,13 +474,19 @@ impl WorkspaceBuildScripts { cmd.arg("--lockfile-path"); cmd.arg(lockfile_copy.path.as_str()); } - LockfileUsage::WithEnvVar => { + LockfileUsage::WithEnvVarUnstable => { cmd.arg("-Zlockfile-path"); cmd.env( "CARGO_RESOLVER_LOCKFILE_PATH", lockfile_copy.path.as_os_str(), ); } + LockfileUsage::WithEnvVar => { + cmd.env( + "CARGO_RESOLVER_LOCKFILE_PATH", + lockfile_copy.path.as_os_str(), + ); + } } } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs index ae36deb71f02a..97526dbf8e9f8 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs @@ -141,7 +141,9 @@ pub(crate) struct LockfileCopy { pub(crate) enum LockfileUsage { /// Rust [1.82.0, 1.95.0). `cargo --lockfile-path ` WithFlag, - /// Rust >= 1.95.0. `CARGO_RESOLVER_LOCKFILE_PATH= cargo ` + /// Rust [1.95.0, 1.97.0). `CARGO_RESOLVER_LOCKFILE_PATH= cargo -Zlockfile-path ` + WithEnvVarUnstable, + /// Rust >= 1.97.0. `CARGO_RESOLVER_LOCKFILE_PATH= cargo ` WithEnvVar, } @@ -158,7 +160,7 @@ pub(crate) fn make_lockfile_copy( build: semver::BuildMetadata::EMPTY, }; - const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV: semver::Version = + const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV_UNSTABLE: semver::Version = semver::Version { major: 1, minor: 95, @@ -167,8 +169,20 @@ pub(crate) fn make_lockfile_copy( build: semver::BuildMetadata::EMPTY, }; + const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV: semver::Version = + semver::Version { + major: 1, + minor: 97, + patch: 0, + pre: semver::Prerelease::EMPTY, + build: semver::BuildMetadata::EMPTY, + }; + let usage = if *toolchain_version >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV { LockfileUsage::WithEnvVar + } else if *toolchain_version >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV_UNSTABLE + { + LockfileUsage::WithEnvVarUnstable } else if *toolchain_version >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_FLAG { LockfileUsage::WithFlag } else { diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index 5d8273832bb23..fb9047a30f9d2 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -763,10 +763,13 @@ impl FetchMetadata { other_options.push("--lockfile-path".to_owned()); other_options.push(lockfile_copy.path.to_string()); } - LockfileUsage::WithEnvVar => { + LockfileUsage::WithEnvVarUnstable => { other_options.push("-Zlockfile-path".to_owned()); command.env("CARGO_RESOLVER_LOCKFILE_PATH", lockfile_copy.path.as_os_str()); } + LockfileUsage::WithEnvVar => { + command.env("CARGO_RESOLVER_LOCKFILE_PATH", lockfile_copy.path.as_os_str()); + } } using_lockfile_copy = true; } From c3add6ee70ab8dce70a13f7844a5ab8796bf6abb Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 28 Jun 2026 15:36:40 +0200 Subject: [PATCH 03/78] misc improvements - clean-up `MacroArgResult` docs - remove needless import - move `impl_internable!(ModPath)` to `mod_path.rs` - combine the impl blocks of HirFileId AFAICT there's no semantic separation between them, and the first impl block is placed before the definition itself. --- .../rust-analyzer/crates/hir-expand/src/db.rs | 4 +- .../crates/hir-expand/src/lib.rs | 158 +++++++++--------- .../crates/hir-expand/src/mod_path.rs | 2 + 3 files changed, 82 insertions(+), 82 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index 90e4e0086e035..ffa64fe01bcbf 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -21,8 +21,10 @@ use crate::{ span_map::{ExpansionSpanMap, RealSpanMap, SpanMap}, tt, }; -/// This is just to ensure the types of smart_macro_arg and macro_arg are the same + +/// This is just to ensure the types of [`macro_arg_considering_derives`] and [`macro_arg`] are the same. type MacroArgResult = (tt::TopSubtree, SyntaxFixupUndoInfo, Span); + /// Total limit on the number of tokens produced by any macro invocation. /// /// If an invocation produces more tokens than this limit, it will not be stored in the database and diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 0ba04bc395b40..a8933a424e29d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -33,7 +33,7 @@ use thin_vec::ThinVec; use triomphe::Arc; use core::fmt; -use std::{hash::Hash, ops}; +use std::ops; use base_db::Crate; use either::Either; @@ -52,7 +52,6 @@ use crate::{ include_input_to_file_id, }, db::ExpandDatabase, - mod_path::ModPath, proc_macro::{CustomProcMacroExpander, ProcMacroKind, ProcMacros}, span_map::{ExpansionSpanMap, SpanMap}, }; @@ -378,75 +377,6 @@ impl MacroCallKind { } } -impl HirFileId { - pub fn edition(self, db: &dyn ExpandDatabase) -> Edition { - match self { - HirFileId::FileId(file_id) => file_id.edition(db), - HirFileId::MacroFile(m) => m.loc(db).def.edition, - } - } - pub fn original_file(self, db: &dyn ExpandDatabase) -> EditionedFileId { - let mut file_id = self; - loop { - match file_id { - HirFileId::FileId(id) => break id, - HirFileId::MacroFile(macro_call_id) => { - file_id = macro_call_id.loc(db).kind.file_id() - } - } - } - } - - pub fn original_file_respecting_includes(mut self, db: &dyn ExpandDatabase) -> EditionedFileId { - loop { - match self { - HirFileId::FileId(id) => break id, - HirFileId::MacroFile(file) => { - let loc = file.loc(db); - if loc.def.is_include() - && let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind - && let Ok(it) = include_input_to_file_id(db, file, &eager.arg) - { - break it; - } - self = loc.kind.file_id(); - } - } - } - } - - pub fn original_call_node(self, db: &dyn ExpandDatabase) -> Option> { - let mut call = self.macro_file()?.loc(db).to_node(db); - loop { - match call.file_id { - HirFileId::FileId(file_id) => { - break Some(InRealFile { file_id, value: call.value }); - } - HirFileId::MacroFile(macro_call_id) => { - call = macro_call_id.loc(db).to_node(db); - } - } - } - } - - pub fn call_node(self, db: &dyn ExpandDatabase) -> Option> { - Some(self.macro_file()?.loc(db).to_node(db)) - } - - pub fn as_builtin_derive_attr_node( - &self, - db: &dyn ExpandDatabase, - ) -> Option> { - let macro_file = self.macro_file()?; - let loc = macro_file.loc(db); - let attr = match loc.def.kind { - MacroDefKind::BuiltInDerive(..) => loc.to_node(db), - _ => return None, - }; - Some(attr.with_value(ast::Attr::cast(attr.value.clone())?)) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum MacroKind { /// `macro_rules!` or Macros 2.0 macro. @@ -1059,8 +989,6 @@ impl ExpandTo { } } -intern::impl_internable!(ModPath); - /// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the /// reason why we use salsa at all. /// @@ -1107,6 +1035,17 @@ impl From for HirFileId { } } +impl PartialEq for HirFileId { + fn eq(&self, &other: &EditionedFileId) -> bool { + *self == HirFileId::from(other) + } +} +impl PartialEq for EditionedFileId { + fn eq(&self, &other: &HirFileId) -> bool { + other == HirFileId::from(*self) + } +} + impl HirFileId { #[inline] pub fn macro_file(self) -> Option { @@ -1138,15 +1077,72 @@ impl HirFileId { } } } -} -impl PartialEq for HirFileId { - fn eq(&self, &other: &EditionedFileId) -> bool { - *self == HirFileId::from(other) + pub fn edition(self, db: &dyn ExpandDatabase) -> Edition { + match self { + HirFileId::FileId(file_id) => file_id.edition(db), + HirFileId::MacroFile(m) => m.loc(db).def.edition, + } } -} -impl PartialEq for EditionedFileId { - fn eq(&self, &other: &HirFileId) -> bool { - other == HirFileId::from(*self) + + pub fn original_file(self, db: &dyn ExpandDatabase) -> EditionedFileId { + let mut file_id = self; + loop { + match file_id { + HirFileId::FileId(id) => break id, + HirFileId::MacroFile(macro_call_id) => { + file_id = macro_call_id.loc(db).kind.file_id() + } + } + } + } + + pub fn original_file_respecting_includes(mut self, db: &dyn ExpandDatabase) -> EditionedFileId { + loop { + match self { + HirFileId::FileId(id) => break id, + HirFileId::MacroFile(file) => { + let loc = file.loc(db); + if loc.def.is_include() + && let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind + && let Ok(it) = include_input_to_file_id(db, file, &eager.arg) + { + break it; + } + self = loc.kind.file_id(); + } + } + } + } + + pub fn original_call_node(self, db: &dyn ExpandDatabase) -> Option> { + let mut call = self.macro_file()?.loc(db).to_node(db); + loop { + match call.file_id { + HirFileId::FileId(file_id) => { + break Some(InRealFile { file_id, value: call.value }); + } + HirFileId::MacroFile(macro_call_id) => { + call = macro_call_id.loc(db).to_node(db); + } + } + } + } + + pub fn call_node(self, db: &dyn ExpandDatabase) -> Option> { + Some(self.macro_file()?.loc(db).to_node(db)) + } + + pub fn as_builtin_derive_attr_node( + &self, + db: &dyn ExpandDatabase, + ) -> Option> { + let macro_file = self.macro_file()?; + let loc = macro_file.loc(db); + let attr = match loc.def.kind { + MacroDefKind::BuiltInDerive(..) => loc.to_node(db), + _ => return None, + }; + Some(attr.with_value(ast::Attr::cast(attr.value.clone())?)) } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index 9142ad8b92119..b90bfac483b19 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -24,6 +24,8 @@ pub struct ModPath { segments: SmallVec<[Name; 1]>, } +intern::impl_internable!(ModPath); + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PathKind { Plain, From 41b216ec9ed878899aef79e909f23c2a2ec4c244 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 2 Jul 2026 20:02:57 +0200 Subject: [PATCH 04/78] turn queries into methods on `MacroCallId` --- .../crates/hir-def/src/attrs/docs.rs | 2 +- .../crates/hir-def/src/expr_store/expander.rs | 4 +- .../hir-def/src/macro_expansion_tests/mod.rs | 4 +- .../crates/hir-def/src/nameres/assoc.rs | 2 +- .../hir-def/src/nameres/tests/incremental.rs | 58 +-- .../hir-expand/src/builtin/derive_macro.rs | 2 +- .../rust-analyzer/crates/hir-expand/src/db.rs | 406 +----------------- .../crates/hir-expand/src/eager.rs | 8 +- .../crates/hir-expand/src/files.rs | 2 +- .../crates/hir-expand/src/lib.rs | 376 +++++++++++++++- .../crates/hir-expand/src/span_map.rs | 4 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- .../rust-analyzer/crates/hir/src/semantics.rs | 7 +- 13 files changed, 429 insertions(+), 448 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs index 0d01d54786a1f..cb91f08af3409 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs @@ -390,7 +390,7 @@ fn expand_doc_macro_call<'db>( .value?; expander.recursion_depth += 1; - let parse = expander.db.parse_macro_expansion(call_id).value.0.clone(); + let parse = call_id.parse_macro_expansion(expander.db).value.0.clone(); let expr = parse.cast::().map(|parse| parse.tree())?; expander.recursion_depth -= 1; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs index c79a1db8472ba..35cf1c9681a04 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs @@ -184,12 +184,12 @@ impl<'db> Expander<'db> { self.recursion_depth = u32::MAX; cov_mark::hit!(your_stack_belongs_to_me); return ExpandResult::only_err(ExpandError::new( - db.macro_arg_considering_derives(call_id, &call_id.lookup(db).kind).2, + call_id.macro_arg_considering_derives(db, &call_id.lookup(db).kind).2, ExpandErrorKind::RecursionOverflow, )); } - let res = db.parse_macro_expansion(call_id); + let res = call_id.parse_macro_expansion(db); let err = err.or_else(|| res.err.clone()); ExpandResult { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 1d6812ad56a54..88f41274e4e4b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -62,7 +62,7 @@ fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) .modules() .flat_map(|module| module.1.scope.all_macro_calls()) .filter_map(|macro_call| { - let errors = db.parse_macro_expansion_error(macro_call)?; + let errors = macro_call.parse_macro_expansion_error(&db)?; let errors = errors.err.as_ref()?.render_to_string(&db); let macro_loc = macro_call.loc(&db); let ast_id = match macro_loc.kind { @@ -125,7 +125,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream let ptr = InFile::new(source.file_id, AstPtr::new(¯o_call_node)); let macro_call_id = resolve_macro_call_id(&db, def_map, ast_id, ptr) .unwrap_or_else(|| panic!("unable to find semantic macro call {macro_call_node}")); - let expansion_result = db.parse_macro_expansion(macro_call_id); + let expansion_result = macro_call_id.parse_macro_expansion(&db); expansions.push((macro_call_node.clone(), expansion_result)); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs index 7b5b39cb08cbe..cc88d21fbfa5c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs @@ -356,7 +356,7 @@ impl<'db> AssocItemCollector<'db> { return; } - let (syntax, span_map) = &self.db.parse_macro_expansion(macro_call_id).value; + let (syntax, span_map) = ¯o_call_id.parse_macro_expansion(self.db).value; 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.db.ast_id_map(self.file_id)); let old_span_map = mem::replace(&mut self.span_map, SpanMap::ExpansionSpanMap(span_map)); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs index bb19456f46fb2..e7aa994c07ef7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs @@ -239,8 +239,8 @@ pub struct S {} "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion", - "macro_arg", + "MacroCallId::parse_macro_expansion_", + "MacroCallId::macro_arg_", ] "#]], expect![[r#" @@ -249,8 +249,8 @@ pub struct S {} "ast_id_map", "file_item_tree_query", "real_span_map", - "macro_arg", - "parse_macro_expansion", + "MacroCallId::macro_arg_", + "MacroCallId::parse_macro_expansion_", "ast_id_map", "file_item_tree_query", ] @@ -302,9 +302,9 @@ fn f() { foo } "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion", - "expand_proc_macro", - "macro_arg", + "MacroCallId::parse_macro_expansion_", + "MacroCallId::expand_proc_macro_", + "MacroCallId::macro_arg_", "proc_macro_span", ] "#]], @@ -314,9 +314,9 @@ fn f() { foo } "ast_id_map", "file_item_tree_query", "real_span_map", - "macro_arg", - "expand_proc_macro", - "parse_macro_expansion", + "MacroCallId::macro_arg_", + "MacroCallId::expand_proc_macro_", + "MacroCallId::parse_macro_expansion_", "ast_id_map", "file_item_tree_query", ] @@ -427,20 +427,20 @@ pub struct S {} "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion", - "macro_arg", + "MacroCallId::parse_macro_expansion_", + "MacroCallId::macro_arg_", "DeclarativeMacroExpander::expander_", "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion", - "macro_arg", + "MacroCallId::parse_macro_expansion_", + "MacroCallId::macro_arg_", "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion", - "expand_proc_macro", - "macro_arg", + "MacroCallId::parse_macro_expansion_", + "MacroCallId::expand_proc_macro_", + "MacroCallId::macro_arg_", "proc_macro_span", ] "#]], @@ -450,10 +450,10 @@ pub struct S {} "ast_id_map", "file_item_tree_query", "real_span_map", - "macro_arg", + "MacroCallId::macro_arg_", "DeclarativeMacroExpander::expander_", - "macro_arg", - "macro_arg", + "MacroCallId::macro_arg_", + "MacroCallId::macro_arg_", ] "#]], ); @@ -538,16 +538,16 @@ m!(Z); "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion", - "macro_arg", + "MacroCallId::parse_macro_expansion_", + "MacroCallId::macro_arg_", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion", - "macro_arg", + "MacroCallId::parse_macro_expansion_", + "MacroCallId::macro_arg_", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion", - "macro_arg", + "MacroCallId::parse_macro_expansion_", + "MacroCallId::macro_arg_", ] "#]], ); @@ -575,9 +575,9 @@ m!(Z); "ast_id_map", "file_item_tree_query", "real_span_map", - "macro_arg", - "macro_arg", - "macro_arg", + "MacroCallId::macro_arg_", + "MacroCallId::macro_arg_", + "MacroCallId::macro_arg_", ] "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index c4da558fdab24..86ee3f153bdc9 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -391,7 +391,7 @@ fn to_adt_syntax( tt: &tt::TopSubtree, call_site: Span, ) -> Result<(ast::Adt, span::SpanMap), ExpandError> { - let (parsed, tm) = crate::db::token_tree_to_syntax_node(db, tt, crate::ExpandTo::Items); + let (parsed, tm) = crate::token_tree_to_syntax_node(db, tt, crate::ExpandTo::Items); let macro_items = ast::MacroItems::cast(parsed.syntax_node()) .ok_or_else(|| ExpandError::other(call_site, "invalid item definition"))?; let item = diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index ffa64fe01bcbf..c8a8be4311e09 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -1,17 +1,13 @@ //! Defines database & queries for macro expansion. use base_db::{Crate, SourceDatabase}; -use mbe::MatchedArmIndex; use span::{AstIdMap, Span}; -use std::borrow::Cow; -use syntax::{AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, ast}; +use syntax::{AstNode, Parse, SyntaxNode, SyntaxToken, ast}; use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree}; -use triomphe::Arc; use crate::{ - AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, - EagerExpander, EditionedFileId, ExpandError, ExpandResult, ExpandTo, FileRange, HirFileId, - MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, + AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander, + EditionedFileId, FileRange, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, builtin::pseudo_derive_attr_expansion, cfg_process::attr_macro_input_to_token_tree, declarative::DeclarativeMacroExpander, @@ -22,17 +18,6 @@ use crate::{ tt, }; -/// This is just to ensure the types of [`macro_arg_considering_derives`] and [`macro_arg`] are the same. -type MacroArgResult = (tt::TopSubtree, SyntaxFixupUndoInfo, Span); - -/// Total limit on the number of tokens produced by any macro invocation. -/// -/// If an invocation produces more tokens than this limit, it will not be stored in the database and -/// an error will be emitted. -/// -/// Actual max for `analysis-stats .` at some point: 30672. -const TOKEN_LIMIT: usize = 2_097_152; - #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum TokenExpander<'db> { /// Old-style `macro_rules` or the new macros 2.0 @@ -62,13 +47,6 @@ pub trait ExpandDatabase: SourceDatabase { #[salsa::transparent] fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode; - /// Implementation for the macro case. - #[salsa::transparent] - fn parse_macro_expansion( - &self, - macro_file: MacroCallId, - ) -> &ExpandResult<(Parse, ExpansionSpanMap)>; - #[salsa::transparent] #[salsa::invoke(SpanMap::new)] fn span_map(&self, file_id: HirFileId) -> SpanMap<'_>; @@ -80,21 +58,6 @@ pub trait ExpandDatabase: SourceDatabase { #[salsa::transparent] fn real_span_map(&self, file_id: EditionedFileId) -> &RealSpanMap; - /// Lowers syntactic macro call to a token tree representation. That's a firewall - /// query, only typing in the macro call itself changes the returned - /// subtree. - #[deprecated = "calling this is incorrect, call `macro_arg_considering_derives` instead"] - #[salsa::invoke(macro_arg)] - #[salsa::transparent] - fn macro_arg(&self, id: MacroCallId) -> &MacroArgResult; - - #[salsa::transparent] - fn macro_arg_considering_derives<'db>( - &'db self, - id: MacroCallId, - kind: &MacroCallKind, - ) -> &'db MacroArgResult; - /// Fetches the expander for this macro. #[salsa::transparent] #[salsa::invoke(TokenExpander::macro_expander)] @@ -108,13 +71,6 @@ pub trait ExpandDatabase: SourceDatabase { def_crate: Crate, id: AstId, ) -> &DeclarativeMacroExpander; - - #[salsa::invoke(parse_macro_expansion_error)] - #[salsa::transparent] - fn parse_macro_expansion_error( - &self, - macro_call: MacroCallId, - ) -> Option>>; } fn resolve_span(db: &dyn ExpandDatabase, Span { range, anchor, ctx: _ }: Span) -> FileRange { @@ -135,7 +91,7 @@ pub fn expand_speculative( token_to_map: SyntaxToken, ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> { let loc = actual_macro_call.loc(db); - let (_, _, span) = *db.macro_arg_considering_derives(actual_macro_call, &loc.kind); + let (_, _, span) = *actual_macro_call.macro_arg_considering_derives(db, &loc.kind); let span_map = RealSpanMap::absolute(span.anchor.file_id); let span_map = SpanMap::RealSpanMap(&span_map); @@ -243,7 +199,7 @@ pub fn expand_speculative( // Otherwise the expand query will fetch the non speculative attribute args and pass those instead. let mut speculative_expansion = match loc.def.kind { MacroDefKind::ProcMacro(ast, expander, _) => { - let span = proc_macro_span(db, ast); + let span = crate::proc_macro_span(db, ast); tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); tt.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(span)); expander.expand( @@ -273,13 +229,14 @@ pub fn expand_speculative( it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) } MacroDefKind::BuiltInAttr(_, it) => it.expand(db, actual_macro_call, &tt, span), - MacroDefKind::UnimplementedBuiltIn(_) => expand_unimplemented_builtin_macro(span), + MacroDefKind::UnimplementedBuiltIn(_) => crate::expand_unimplemented_builtin_macro(span), }; let expand_to = loc.expand_to(); fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info); - let (node, rev_tmap) = token_tree_to_syntax_node(db, &speculative_expansion.value, expand_to); + let (node, rev_tmap) = + crate::token_tree_to_syntax_node(db, &speculative_expansion.value, expand_to); let syntax_node = node.syntax_node(); let token = rev_tmap @@ -298,13 +255,6 @@ pub fn expand_speculative( Some((node.syntax_node(), token)) } -fn expand_unimplemented_builtin_macro(span: Span) -> ExpandResult { - ExpandResult::new( - tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), - ExpandError::other(span, "this built-in macro is not implemented"), - ) -} - #[salsa::tracked(lru = 1024, returns(ref))] fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> AstIdMap { AstIdMap::from_source(&db.parse_or_expand(file_id)) @@ -316,38 +266,11 @@ fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode { match file_id { HirFileId::FileId(file_id) => file_id.parse(db).syntax_node(), HirFileId::MacroFile(macro_file) => { - db.parse_macro_expansion(macro_file).value.0.syntax_node() + macro_file.parse_macro_expansion(db).value.0.syntax_node() } } } -// FIXME: We should verify that the parsed node is one of the many macro node variants we expect -// instead of having it be untyped -#[salsa_macros::tracked(returns(ref), lru = 512)] -fn parse_macro_expansion( - db: &dyn ExpandDatabase, - macro_file: MacroCallId, -) -> ExpandResult<(Parse, ExpansionSpanMap)> { - let _p = tracing::info_span!("parse_macro_expansion").entered(); - let loc = macro_file.loc(db); - let expand_to = loc.expand_to(); - let mbe::ValueResult { value: (tt, matched_arm), err } = macro_expand(db, macro_file, loc); - - let (parse, mut rev_token_map) = token_tree_to_syntax_node(db, &tt, expand_to); - rev_token_map.matched_arm = matched_arm; - - ExpandResult { value: (parse, rev_token_map), err } -} - -fn parse_macro_expansion_error( - db: &dyn ExpandDatabase, - macro_call_id: MacroCallId, -) -> Option>> { - let e: ExpandResult> = - db.parse_macro_expansion(macro_call_id).as_ref().map(|it| Arc::from(it.0.errors())); - if e.value.is_empty() && e.err.is_none() { None } else { Some(e) } -} - pub(crate) fn parse_with_map( db: &dyn ExpandDatabase, file_id: HirFileId, @@ -357,146 +280,12 @@ pub(crate) fn parse_with_map( (file_id.parse(db).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(file_id))) } HirFileId::MacroFile(macro_file) => { - let (parse, map) = &db.parse_macro_expansion(macro_file).value; + let (parse, map) = ¯o_file.parse_macro_expansion(db).value; (parse.clone(), SpanMap::ExpansionSpanMap(map)) } } } -/// This resolves the [MacroCallId] to check if it is a derive macro if so get the [macro_arg] for the derive. -/// Other wise return the [macro_arg] for the macro_call_id. -/// -/// This is not connected to the database so it does not cache the result. However, the inner [macro_arg] query is -#[allow(deprecated)] // we are macro_arg_considering_derives -fn macro_arg_considering_derives<'db>( - db: &'db dyn ExpandDatabase, - id: MacroCallId, - kind: &MacroCallKind, -) -> &'db MacroArgResult { - match kind { - // Get the macro arg for the derive macro - MacroCallKind::Derive { derive_macro_id, .. } => db.macro_arg(*derive_macro_id), - // Normal macro arg - _ => db.macro_arg(id), - } -} - -#[salsa_macros::tracked(returns(ref))] -fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { - let loc = id.loc(db); - - if let MacroCallLoc { - def: MacroDefId { kind: MacroDefKind::BuiltInEager(..), .. }, - kind: MacroCallKind::FnLike { eager: Some(eager), .. }, - .. - } = &loc - { - return (eager.arg.clone(), SyntaxFixupUndoInfo::NONE, eager.span); - } - - let (parse, map) = parse_with_map(db, loc.kind.file_id()); - let root = parse.syntax_node(); - - let (is_derive, censor_item_tree_attr_ids, item_node, span) = match &loc.kind { - MacroCallKind::FnLike { ast_id, .. } => { - let node = &ast_id.to_ptr(db).to_node(&root); - let path_range = node - .path() - .map_or_else(|| node.syntax().text_range(), |path| path.syntax().text_range()); - let span = map.span_for_range(path_range); - - let dummy_tt = |kind| { - ( - tt::TopSubtree::from_token_trees( - tt::Delimiter { open: span, close: span, kind }, - tt::TokenTreesView::empty(), - ), - SyntaxFixupUndoInfo::default(), - span, - ) - }; - - let Some(tt) = node.token_tree() else { - return dummy_tt(tt::DelimiterKind::Invisible); - }; - let first = tt.left_delimiter_token().map(|it| it.kind()).unwrap_or(T!['(']); - let last = tt.right_delimiter_token().map(|it| it.kind()).unwrap_or(T![.]); - - let mismatched_delimiters = !matches!( - (first, last), - (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) - ); - if mismatched_delimiters { - // Don't expand malformed (unbalanced) macro invocations. This is - // less than ideal, but trying to expand unbalanced macro calls - // sometimes produces pathological, deeply nested code which breaks - // all kinds of things. - // - // So instead, we'll return an empty subtree here - cov_mark::hit!(issue9358_bad_macro_stack_overflow); - - let kind = match first { - _ if loc.def.is_proc_macro() => tt::DelimiterKind::Invisible, - T!['('] => tt::DelimiterKind::Parenthesis, - T!['['] => tt::DelimiterKind::Bracket, - T!['{'] => tt::DelimiterKind::Brace, - _ => tt::DelimiterKind::Invisible, - }; - return dummy_tt(kind); - } - - let mut tt = syntax_bridge::syntax_node_to_token_tree( - tt.syntax(), - map, - span, - if loc.def.is_proc_macro() { - DocCommentDesugarMode::ProcMacro - } else { - DocCommentDesugarMode::Mbe - }, - ); - if loc.def.is_proc_macro() { - // proc macros expect their inputs without parentheses, MBEs expect it with them included - tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); - } - return (tt, SyntaxFixupUndoInfo::NONE, span); - } - // MacroCallKind::Derive should not be here. As we are getting the argument for the derive macro - MacroCallKind::Derive { .. } => { - unreachable!("`ExpandDatabase::macro_arg` called with `MacroCallKind::Derive`") - } - MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { - let node = ast_id.to_ptr(db).to_node(&root); - let (_, attr) = attr_ids.invoc_attr().find_attr_range_with_source(db, loc.krate, &node); - let range = attr - .path() - .map(|path| path.syntax().text_range()) - .unwrap_or_else(|| attr.syntax().text_range()); - let span = map.span_for_range(range); - - let is_derive = matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive()); - (is_derive, &**attr_ids, node, span) - } - }; - - let (mut tt, undo_info) = attr_macro_input_to_token_tree( - db, - item_node.syntax(), - map, - span, - is_derive, - censor_item_tree_attr_ids, - loc.krate, - ); - - if loc.def.is_proc_macro() { - // proc macros expect their inputs without parentheses, MBEs expect it with them included - tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); - } - - (tt, undo_info, span) -} - impl<'db> TokenExpander<'db> { fn macro_expander(db: &'db dyn ExpandDatabase, id: MacroDefId) -> TokenExpander<'db> { match id.kind { @@ -512,178 +301,3 @@ impl<'db> TokenExpander<'db> { } } } - -fn macro_expand<'db>( - db: &'db dyn ExpandDatabase, - macro_call_id: MacroCallId, - loc: &MacroCallLoc, -) -> ExpandResult<(Cow<'db, tt::TopSubtree>, MatchedArmIndex)> { - let _p = tracing::info_span!("macro_expand").entered(); - - let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind { - MacroDefKind::ProcMacro(..) => { - return expand_proc_macro(db, macro_call_id) - .as_ref() - .map(|it| (Cow::Borrowed(it), None)); - } - _ => { - let (macro_arg, undo_info, span) = - db.macro_arg_considering_derives(macro_call_id, &loc.kind); - let span = *span; - - let arg = macro_arg; - let res = match loc.def.kind { - MacroDefKind::Declarative(id, _) => { - db.decl_macro_expander(loc.def.krate, id).expand(db, arg, macro_call_id, span) - } - MacroDefKind::BuiltIn(_, it) => { - it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None) - } - MacroDefKind::BuiltInDerive(_, it) => { - it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None) - } - MacroDefKind::UnimplementedBuiltIn(_) => { - expand_unimplemented_builtin_macro(span).zip_val(None) - } - MacroDefKind::BuiltInEager(_, it) => { - // This might look a bit odd, but we do not expand the inputs to eager macros here. - // Eager macros inputs are expanded, well, eagerly when we collect the macro calls. - // That kind of expansion uses the ast id map of an eager macros input though which goes through - // the HirFileId machinery. As eager macro inputs are assigned a macro file id that query - // will end up going through here again, whereas we want to just want to inspect the raw input. - // As such we just return the input subtree here. - let eager = match &loc.kind { - MacroCallKind::FnLike { eager: None, .. } => { - return ExpandResult::ok(Cow::Borrowed(macro_arg)).zip_val(None); - } - MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager), - _ => None, - }; - - let mut res = it.expand(db, macro_call_id, arg, span).map_err(Into::into); - - if let Some(EagerCallInfo { error, .. }) = eager { - // FIXME: We should report both errors! - res.err = error.clone().or(res.err); - } - res.zip_val(None) - } - MacroDefKind::BuiltInAttr(_, it) => { - let mut res = it.expand(db, macro_call_id, arg, span); - fixup::reverse_fixups(&mut res.value, undo_info); - res.zip_val(None) - } - MacroDefKind::ProcMacro(_, _, _) => unreachable!(), - }; - (res, span) - } - }; - - // Skip checking token tree limit for include! macro call - if !loc.def.is_include() { - // Set a hard limit for the expanded tt - if let Err(value) = check_tt_count(&tt) { - return value - .map(|()| Cow::Owned(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)))) - .zip_val(matched_arm); - } - } - - ExpandResult { value: (Cow::Owned(tt), matched_arm), err } -} - -/// Retrieves the span to be used for a proc-macro expansions spans. -/// This is a firewall query as it requires parsing the file, which we don't want proc-macros to -/// directly depend on as that would cause to frequent invalidations, mainly because of the -/// 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 ExpandDatabase, ast: AstId) -> Span { - #[salsa::tracked] - fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId, _: ()) -> Span { - let root = db.parse_or_expand(ast.file_id); - let ast_id_map = db.ast_id_map(ast.file_id); - let span_map = db.span_map(ast.file_id); - - let node = ast_id_map.get(ast.value).to_node(&root); - let range = ast::HasName::name(&node) - .map_or_else(|| node.syntax().text_range(), |name| name.syntax().text_range()); - span_map.span_for_range(range) - } - proc_macro_span(db, ast, ()) -} - -/// Special case of [`macro_expand`] for procedural macros. We can't LRU -/// proc macros, since they are not deterministic in general, and -/// non-determinism breaks salsa in a very, very, very bad way. -/// @edwin0cheng heroically debugged this once! See #4315 for details -#[salsa_macros::tracked(returns(ref))] -fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult { - let loc = id.loc(db); - let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(id, &loc.kind); - - let (ast, expander) = match loc.def.kind { - MacroDefKind::ProcMacro(ast, expander, _) => (ast, expander), - _ => unreachable!(), - }; - - let attr_arg = match &loc.kind { - MacroCallKind::Attr { attr_args: Some(attr_args), .. } => Some(&**attr_args), - _ => None, - }; - - let ExpandResult { value: mut tt, err } = { - let span = proc_macro_span(db, ast); - expander.expand( - db, - loc.def.krate, - loc.krate, - macro_arg, - attr_arg, - span_with_def_site_ctxt(db, span, id.into(), loc.def.edition), - span_with_call_site_ctxt(db, span, id.into(), loc.def.edition), - span_with_mixed_site_ctxt(db, span, id.into(), loc.def.edition), - ) - }; - - // Set a hard limit for the expanded tt - if let Err(value) = check_tt_count(&tt) { - return value.map(|()| tt::TopSubtree::empty(tt::DelimSpan::from_single(*span))); - } - - fixup::reverse_fixups(&mut tt, undo_info); - - ExpandResult { value: tt, err } -} - -pub(crate) fn token_tree_to_syntax_node( - db: &dyn ExpandDatabase, - tt: &tt::TopSubtree, - expand_to: ExpandTo, -) -> (Parse, ExpansionSpanMap) { - let entry_point = match expand_to { - ExpandTo::Statements => syntax_bridge::TopEntryPoint::MacroStmts, - ExpandTo::Items => syntax_bridge::TopEntryPoint::MacroItems, - ExpandTo::Pattern => syntax_bridge::TopEntryPoint::Pattern, - ExpandTo::Type => syntax_bridge::TopEntryPoint::Type, - ExpandTo::Expr => syntax_bridge::TopEntryPoint::Expr, - }; - syntax_bridge::token_tree_to_syntax_node(tt, entry_point, &mut |ctx| ctx.edition(db)) -} - -fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> { - let tt = tt.top_subtree(); - let count = tt.count(); - if count <= TOKEN_LIMIT { - Ok(()) - } else { - Err(ExpandResult { - value: (), - err: Some(ExpandError::other( - tt.delimiter.open, - format!( - "macro invocation exceeds token limit: produced {count} tokens, limit is {TOKEN_LIMIT}", - ), - )), - }) - } -} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs index f8a560834adb3..30e9624b2b714 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -62,9 +62,9 @@ pub fn expand_eager_macro_input( }; let arg_id = MacroCallId::new(db, loc); #[allow(deprecated)] // builtin eager macros are never derives - let (_, _, span) = db.macro_arg(arg_id); + let (_, _, span) = arg_id.macro_arg(db); let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } = - db.parse_macro_expansion(arg_id); + arg_id.parse_macro_expansion(db); let mut arg_map = ExpansionSpanMap::empty(); @@ -136,7 +136,7 @@ fn lazy_expand<'db>( ); eager_callback(ast_id.map(|ast_id| (AstPtr::new(macro_call), ast_id)), id); - db.parse_macro_expansion(id) + id.parse_macro_expansion(db) .as_ref() .map(|parse| (InFile::new(id.into(), parse.0.clone()), &parse.1)) } @@ -226,7 +226,7 @@ fn eager_macro_recur( call_id, ); let ExpandResult { value: (parse, map), err: err2 } = - db.parse_macro_expansion(call_id); + call_id.parse_macro_expansion(db); map.iter().for_each(|(o, span)| expanded_map.push(o + offset, span)); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs index a4c206156da34..5f03e1b6b3f12 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs @@ -213,7 +213,7 @@ impl FileIdToSyntax for EditionedFileId { } impl FileIdToSyntax for MacroCallId { fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode { - db.parse_macro_expansion(self).value.0.syntax_node() + self.parse_macro_expansion(db).value.0.syntax_node() } } impl FileIdToSyntax for HirFileId { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index a8933a424e29d..73647d0ece781 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -33,17 +33,19 @@ use thin_vec::ThinVec; use triomphe::Arc; use core::fmt; -use std::ops; +use std::{borrow::Cow, ops}; use base_db::Crate; use either::Either; +use mbe::MatchedArmIndex; use span::{ Edition, ErasedFileAstId, FileAstId, NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER, Span, SyntaxContext, }; use syntax::{ - SyntaxNode, SyntaxToken, TextRange, TextSize, + Parse, SyntaxError, SyntaxNode, SyntaxToken, T, TextRange, TextSize, ast::{self, AstNode}, }; +use syntax_bridge::DocCommentDesugarMode; use crate::{ attrs::AttrId, @@ -51,7 +53,10 @@ use crate::{ BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander, include_input_to_file_id, }, + cfg_process::attr_macro_input_to_token_tree, db::ExpandDatabase, + fixup::SyntaxFixupUndoInfo, + hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt}, proc_macro::{CustomProcMacroExpander, ProcMacroKind, ProcMacros}, span_map::{ExpansionSpanMap, SpanMap}, }; @@ -66,6 +71,18 @@ pub use mbe::{DeclarativeMacro, MacroCallStyle, MacroCallStyles, ValueResult}; pub use tt; +/// This is just to ensure the types of [`MacroCallId::macro_arg_considering_derives`] +/// and [`MacroCallId::macro_arg`] are the same. +type MacroArgResult = (tt::TopSubtree, SyntaxFixupUndoInfo, Span); + +/// Total limit on the number of tokens produced by any macro invocation. +/// +/// If an invocation produces more tokens than this limit, it will not be stored in the database and +/// an error will be emitted. +/// +/// Actual max for `analysis-stats .` at some point: 30672. +const TOKEN_LIMIT: usize = 2_097_152; + #[macro_export] macro_rules! impl_intern_lookup { ($db:ident, $id:ident, $loc:ident) => { @@ -467,6 +484,357 @@ impl MacroCallId { } } +#[salsa::tracked] +impl MacroCallId { + /// Implementation of [`crate::db::parse_or_expand`] for the macro case. + // FIXME: We should verify that the parsed node is one of the many macro node variants we expect + // instead of having it be untyped + #[salsa::tracked(returns(ref), lru = 512)] + pub fn parse_macro_expansion( + self, + db: &dyn ExpandDatabase, + ) -> ExpandResult<(Parse, ExpansionSpanMap)> { + let _p = tracing::info_span!("parse_macro_expansion").entered(); + let loc = self.loc(db); + let expand_to = loc.expand_to(); + let mbe::ValueResult { value: (tt, matched_arm), err } = self.macro_expand(db, loc); + + let (parse, mut rev_token_map) = token_tree_to_syntax_node(db, &tt, expand_to); + rev_token_map.matched_arm = matched_arm; + + ExpandResult { value: (parse, rev_token_map), err } + } + + pub fn parse_macro_expansion_error( + self, + db: &dyn ExpandDatabase, + ) -> Option>> { + let e: ExpandResult> = + self.parse_macro_expansion(db).as_ref().map(|it| Arc::from(it.0.errors())); + if e.value.is_empty() && e.err.is_none() { None } else { Some(e) } + } + + /// This resolves the [MacroCallId] to check if it is a derive macro if so get the [macro_arg] for the derive. + /// Other wise return the [macro_arg] for the macro_call_id. + /// + /// This is not connected to the database so it does not cache the result. However, the inner [macro_arg] query is + /// + /// [macro_arg]: Self::macro_arg + #[allow(deprecated)] // we are macro_arg_considering_derives + pub fn macro_arg_considering_derives<'db>( + self, + db: &'db dyn ExpandDatabase, + kind: &MacroCallKind, + ) -> &'db MacroArgResult { + match kind { + // Get the macro arg for the derive macro + MacroCallKind::Derive { derive_macro_id, .. } => derive_macro_id.macro_arg(db), + // Normal macro arg + _ => self.macro_arg(db), + } + } + + /// Lowers syntactic macro call to a token tree representation. That's a firewall + /// query, only typing in the macro call itself changes the returned + /// subtree. + #[salsa::tracked(returns(ref))] + fn macro_arg(self, db: &dyn ExpandDatabase) -> MacroArgResult { + let loc = self.loc(db); + + if let MacroCallLoc { + def: MacroDefId { kind: MacroDefKind::BuiltInEager(..), .. }, + kind: MacroCallKind::FnLike { eager: Some(eager), .. }, + .. + } = &loc + { + return (eager.arg.clone(), SyntaxFixupUndoInfo::NONE, eager.span); + } + + let (parse, map) = crate::db::parse_with_map(db, loc.kind.file_id()); + let root = parse.syntax_node(); + + let (is_derive, censor_item_tree_attr_ids, item_node, span) = match &loc.kind { + MacroCallKind::FnLike { ast_id, .. } => { + let node = &ast_id.to_ptr(db).to_node(&root); + let path_range = node + .path() + .map_or_else(|| node.syntax().text_range(), |path| path.syntax().text_range()); + let span = map.span_for_range(path_range); + + let dummy_tt = |kind| { + ( + tt::TopSubtree::from_token_trees( + tt::Delimiter { open: span, close: span, kind }, + tt::TokenTreesView::empty(), + ), + SyntaxFixupUndoInfo::default(), + span, + ) + }; + + let Some(tt) = node.token_tree() else { + return dummy_tt(tt::DelimiterKind::Invisible); + }; + let first = tt.left_delimiter_token().map(|it| it.kind()).unwrap_or(T!['(']); + let last = tt.right_delimiter_token().map(|it| it.kind()).unwrap_or(T![.]); + + let mismatched_delimiters = !matches!( + (first, last), + (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) + ); + if mismatched_delimiters { + // Don't expand malformed (unbalanced) macro invocations. This is + // less than ideal, but trying to expand unbalanced macro calls + // sometimes produces pathological, deeply nested code which breaks + // all kinds of things. + // + // So instead, we'll return an empty subtree here + cov_mark::hit!(issue9358_bad_macro_stack_overflow); + + let kind = match first { + _ if loc.def.is_proc_macro() => tt::DelimiterKind::Invisible, + T!['('] => tt::DelimiterKind::Parenthesis, + T!['['] => tt::DelimiterKind::Bracket, + T!['{'] => tt::DelimiterKind::Brace, + _ => tt::DelimiterKind::Invisible, + }; + return dummy_tt(kind); + } + + let mut tt = syntax_bridge::syntax_node_to_token_tree( + tt.syntax(), + map, + span, + if loc.def.is_proc_macro() { + DocCommentDesugarMode::ProcMacro + } else { + DocCommentDesugarMode::Mbe + }, + ); + if loc.def.is_proc_macro() { + // proc macros expect their inputs without parentheses, MBEs expect it with them included + tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); + } + return (tt, SyntaxFixupUndoInfo::NONE, span); + } + // MacroCallKind::Derive should not be here. As we are getting the argument for the derive macro + MacroCallKind::Derive { .. } => { + unreachable!("`ExpandDatabase::macro_arg` called with `MacroCallKind::Derive`") + } + MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { + let node = ast_id.to_ptr(db).to_node(&root); + let (_, attr) = + attr_ids.invoc_attr().find_attr_range_with_source(db, loc.krate, &node); + let range = attr + .path() + .map(|path| path.syntax().text_range()) + .unwrap_or_else(|| attr.syntax().text_range()); + let span = map.span_for_range(range); + + let is_derive = matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive()); + (is_derive, &**attr_ids, node, span) + } + }; + + let (mut tt, undo_info) = attr_macro_input_to_token_tree( + db, + item_node.syntax(), + map, + span, + is_derive, + censor_item_tree_attr_ids, + loc.krate, + ); + + if loc.def.is_proc_macro() { + // proc macros expect their inputs without parentheses, MBEs expect it with them included + tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); + } + + (tt, undo_info, span) + } + + fn macro_expand<'db>( + self, + db: &'db dyn ExpandDatabase, + loc: &MacroCallLoc, + ) -> ExpandResult<(Cow<'db, tt::TopSubtree>, MatchedArmIndex)> { + let _p = tracing::info_span!("macro_expand").entered(); + + let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind { + MacroDefKind::ProcMacro(..) => { + return self.expand_proc_macro(db).as_ref().map(|it| (Cow::Borrowed(it), None)); + } + _ => { + let (macro_arg, undo_info, span) = + self.macro_arg_considering_derives(db, &loc.kind); + let span = *span; + + let arg = macro_arg; + let res = match loc.def.kind { + MacroDefKind::Declarative(id, _) => { + db.decl_macro_expander(loc.def.krate, id).expand(db, arg, self, span) + } + MacroDefKind::BuiltIn(_, it) => { + it.expand(db, self, arg, span).map_err(Into::into).zip_val(None) + } + MacroDefKind::BuiltInDerive(_, it) => { + it.expand(db, self, arg, span).map_err(Into::into).zip_val(None) + } + MacroDefKind::UnimplementedBuiltIn(_) => { + expand_unimplemented_builtin_macro(span).zip_val(None) + } + MacroDefKind::BuiltInEager(_, it) => { + // This might look a bit odd, but we do not expand the inputs to eager macros here. + // Eager macros inputs are expanded, well, eagerly when we collect the macro calls. + // That kind of expansion uses the ast id map of an eager macros input though which goes through + // the HirFileId machinery. As eager macro inputs are assigned a macro file id that query + // will end up going through here again, whereas we want to just want to inspect the raw input. + // As such we just return the input subtree here. + let eager = match &loc.kind { + MacroCallKind::FnLike { eager: None, .. } => { + return ExpandResult::ok(Cow::Borrowed(macro_arg)).zip_val(None); + } + MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager), + _ => None, + }; + + let mut res = it.expand(db, self, arg, span).map_err(Into::into); + + if let Some(EagerCallInfo { error, .. }) = eager { + // FIXME: We should report both errors! + res.err = error.clone().or(res.err); + } + res.zip_val(None) + } + MacroDefKind::BuiltInAttr(_, it) => { + let mut res = it.expand(db, self, arg, span); + fixup::reverse_fixups(&mut res.value, undo_info); + res.zip_val(None) + } + MacroDefKind::ProcMacro(_, _, _) => unreachable!(), + }; + (res, span) + } + }; + + // Skip checking token tree limit for include! macro call + if !loc.def.is_include() { + // Set a hard limit for the expanded tt + if let Err(value) = check_tt_count(&tt) { + return value + .map(|()| Cow::Owned(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)))) + .zip_val(matched_arm); + } + } + + ExpandResult { value: (Cow::Owned(tt), matched_arm), err } + } + + /// Special case of [`Self::macro_expand`] for procedural macros. We can't LRU + /// proc macros, since they are not deterministic in general, and + /// non-determinism breaks salsa in a very, very, very bad way. + /// @edwin0cheng heroically debugged this once! See #4315 for details + #[salsa::tracked(returns(ref))] + fn expand_proc_macro(self, db: &dyn ExpandDatabase) -> ExpandResult { + let loc = self.loc(db); + let (macro_arg, undo_info, span) = self.macro_arg_considering_derives(db, &loc.kind); + + let (ast, expander) = match loc.def.kind { + MacroDefKind::ProcMacro(ast, expander, _) => (ast, expander), + _ => unreachable!(), + }; + + let attr_arg = match &loc.kind { + MacroCallKind::Attr { attr_args: Some(attr_args), .. } => Some(&**attr_args), + _ => None, + }; + + let ExpandResult { value: mut tt, err } = { + let span = proc_macro_span(db, ast); + expander.expand( + db, + loc.def.krate, + loc.krate, + macro_arg, + attr_arg, + span_with_def_site_ctxt(db, span, self.into(), loc.def.edition), + span_with_call_site_ctxt(db, span, self.into(), loc.def.edition), + span_with_mixed_site_ctxt(db, span, self.into(), loc.def.edition), + ) + }; + + // Set a hard limit for the expanded tt + if let Err(value) = check_tt_count(&tt) { + return value.map(|()| tt::TopSubtree::empty(tt::DelimSpan::from_single(*span))); + } + + fixup::reverse_fixups(&mut tt, undo_info); + + ExpandResult { value: tt, err } + } +} + +fn expand_unimplemented_builtin_macro(span: Span) -> ExpandResult { + ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other(span, "this built-in macro is not implemented"), + ) +} + +/// Retrieves the span to be used for a proc-macro expansions spans. +/// This is a firewall query as it requires parsing the file, which we don't want proc-macros to +/// directly depend on as that would cause to frequent invalidations, mainly because of the +/// 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 ExpandDatabase, ast: AstId) -> Span { + #[salsa::tracked] + fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId, _: ()) -> Span { + let root = db.parse_or_expand(ast.file_id); + let ast_id_map = db.ast_id_map(ast.file_id); + let span_map = db.span_map(ast.file_id); + + let node = ast_id_map.get(ast.value).to_node(&root); + let range = ast::HasName::name(&node) + .map_or_else(|| node.syntax().text_range(), |name| name.syntax().text_range()); + span_map.span_for_range(range) + } + proc_macro_span(db, ast, ()) +} + +pub(crate) fn token_tree_to_syntax_node( + db: &dyn ExpandDatabase, + tt: &tt::TopSubtree, + expand_to: ExpandTo, +) -> (Parse, ExpansionSpanMap) { + let entry_point = match expand_to { + ExpandTo::Statements => syntax_bridge::TopEntryPoint::MacroStmts, + ExpandTo::Items => syntax_bridge::TopEntryPoint::MacroItems, + ExpandTo::Pattern => syntax_bridge::TopEntryPoint::Pattern, + ExpandTo::Type => syntax_bridge::TopEntryPoint::Type, + ExpandTo::Expr => syntax_bridge::TopEntryPoint::Expr, + }; + syntax_bridge::token_tree_to_syntax_node(tt, entry_point, &mut |ctx| ctx.edition(db)) +} + +fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> { + let tt = tt.top_subtree(); + let count = tt.count(); + if count <= TOKEN_LIMIT { + Ok(()) + } else { + Err(ExpandResult { + value: (), + err: Some(ExpandError::other( + tt.delimiter.open, + format!( + "macro invocation exceeds token limit: produced {count} tokens, limit is {TOKEN_LIMIT}", + ), + )), + }) + } +} + impl MacroDefId { pub fn make_call( self, @@ -862,7 +1230,7 @@ impl<'db> ExpansionInfo<'db> { let arg_tt = loc.kind.arg(db); let arg_map = db.span_map(arg_tt.file_id); - let (parse, exp_map) = &db.parse_macro_expansion(macro_file).value; + let (parse, exp_map) = ¯o_file.parse_macro_expansion(db).value; let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() }; ExpansionInfo { expanded, loc, arg: arg_tt, exp_map, arg_map } @@ -1073,7 +1441,7 @@ impl HirFileId { HirFileId::FileId(_) => SyntaxContext::root(edition), HirFileId::MacroFile(m) => { let kind = &m.loc(db).kind; - db.macro_arg_considering_derives(m, kind).2.ctx + m.macro_arg_considering_derives(db, kind).2.ctx } } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs index 6a94df8b5a554..d19eead4a80d4 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs @@ -41,7 +41,7 @@ impl<'db> SpanMap<'db> { match file_id { HirFileId::FileId(file_id) => SpanMap::RealSpanMap(db.real_span_map(file_id)), HirFileId::MacroFile(m) => { - SpanMap::ExpansionSpanMap(&db.parse_macro_expansion(m).value.1) + SpanMap::ExpansionSpanMap(&m.parse_macro_expansion(db).value.1) } } } @@ -115,5 +115,5 @@ pub(crate) fn expansion_span_map( db: &dyn ExpandDatabase, file_id: MacroCallId, ) -> &ExpansionSpanMap { - &db.parse_macro_expansion(file_id).value.1 + &file_id.parse_macro_expansion(db).value.1 } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 8d21b351e42cd..ceac3f0876110 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1159,7 +1159,7 @@ fn macro_call_diagnostics<'db>( macro_call_id: MacroCallId, acc: &mut Vec>, ) { - let Some(e) = db.parse_macro_expansion_error(macro_call_id) else { + let Some(e) = macro_call_id.parse_macro_expansion_error(db) else { return; }; let ValueResult { value: parse_errors, err } = e; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index dcbd6c37af52d..8fc08abef17df 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -564,7 +564,7 @@ impl<'db> SemanticsImpl<'db> { } pub fn expand(&self, file_id: MacroCallId) -> ExpandResult { - let res = self.db.parse_macro_expansion(file_id).as_ref().map(|it| it.0.syntax_node()); + let res = file_id.parse_macro_expansion(self.db).as_ref().map(|it| it.0.syntax_node()); self.cache(res.value.clone(), file_id.into()); res } @@ -661,7 +661,7 @@ impl<'db> SemanticsImpl<'db> { .into_iter() .map(|call| { let file_id = call?.left()?; - let ExpandResult { value, err } = self.db.parse_macro_expansion(file_id); + let ExpandResult { value, err } = file_id.parse_macro_expansion(self.db); let root_node = value.0.syntax_node(); self.cache(root_node.clone(), file_id.into()); Some(ExpandResult { value: root_node, err: err.clone() }) @@ -1979,8 +1979,7 @@ impl<'db> SemanticsImpl<'db> { } pub fn resolve_macro_call_arm(&self, macro_call: &ast::MacroCall) -> Option { - let file_id = self.to_def(macro_call)?; - self.db.parse_macro_expansion(file_id).value.1.matched_arm + self.to_def(macro_call)?.parse_macro_expansion(self.db).value.1.matched_arm } pub fn get_unsafe_ops(&self, def: ExpressionStoreOwner) -> FxHashSet { From 6645218f395bd532cd3c3565bff05a0ef7ef6958 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 2 Jul 2026 21:51:04 +0200 Subject: [PATCH 05/78] turn queries into methods on `HirFileId` --- .../crates/hir-def/src/attrs/docs.rs | 4 +- .../crates/hir-def/src/expr_store/expander.rs | 4 +- .../crates/hir-def/src/item_tree.rs | 2 +- .../crates/hir-def/src/item_tree/lower.rs | 2 +- .../hir-def/src/macro_expansion_tests/mod.rs | 6 +- .../crates/hir-def/src/nameres/assoc.rs | 6 +- .../hir-def/src/nameres/tests/incremental.rs | 68 +++++++++---------- .../crates/hir-def/src/signatures.rs | 2 +- .../rust-analyzer/crates/hir-def/src/src.rs | 4 +- .../rust-analyzer/crates/hir-expand/src/db.rs | 45 ++---------- .../crates/hir-expand/src/declarative.rs | 3 +- .../crates/hir-expand/src/eager.rs | 2 +- .../crates/hir-expand/src/files.rs | 10 +-- .../crates/hir-expand/src/lib.rs | 49 +++++++++++-- .../crates/hir-expand/src/span_map.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/tests.rs | 14 ++-- .../crates/hir-ty/src/tests/incremental.rs | 28 ++++---- .../crates/hir/src/diagnostics.rs | 4 +- .../crates/hir/src/has_source.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 8 +-- .../rust-analyzer/crates/hir/src/semantics.rs | 8 +-- .../hir/src/semantics/child_by_source.rs | 2 +- .../crates/hir/src/source_analyzer.rs | 2 +- .../src/handlers/incorrect_case.rs | 4 +- .../src/handlers/missing_fields.rs | 6 +- .../src/handlers/missing_unsafe.rs | 3 +- .../src/handlers/mutability_errors.rs | 3 +- .../src/handlers/no_such_field.rs | 4 +- .../src/handlers/remove_trailing_return.rs | 4 +- .../src/handlers/remove_unnecessary_else.rs | 4 +- .../replace_filter_map_next_with_find_map.rs | 4 +- .../trait_impl_redundant_assoc_item.rs | 4 +- .../src/handlers/type_mismatch.rs | 12 ++-- .../src/handlers/typed_hole.rs | 3 +- .../src/handlers/unresolved_field.rs | 6 +- .../src/handlers/unresolved_method.rs | 6 +- .../src/handlers/unresolved_module.rs | 3 +- .../crates/load-cargo/src/lib.rs | 4 +- .../rust-analyzer/src/cli/analysis_stats.rs | 10 +-- 39 files changed, 175 insertions(+), 182 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs index cb91f08af3409..69eac7b3c97fb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs @@ -401,7 +401,7 @@ fn expand_doc_macro_call<'db>( let new_source_ctx = DocExprSourceCtx { resolver: source_ctx.resolver.clone(), file_id: expansion_file_id, - ast_id_map: expander.db.ast_id_map(expansion_file_id), + ast_id_map: expansion_file_id.ast_id_map(expander.db), span_map: expander.db.span_map(expansion_file_id), }; Some((expr, new_source_ctx)) @@ -456,7 +456,7 @@ fn extend_with_attrs<'a, 'db>( DocExprSourceCtx { resolver, file_id, - ast_id_map: db.ast_id_map(file_id), + ast_id_map: file_id.ast_id_map(db), span_map: db.span_map(file_id), }, ) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs index 35cf1c9681a04..fbe7bab457a94 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs @@ -47,7 +47,7 @@ impl<'db> Expander<'db> { recursion_depth: 0, recursion_limit, span_map: db.span_map(current_file_id), - ast_id_map: db.ast_id_map(current_file_id), + ast_id_map: current_file_id.ast_id_map(db), } } @@ -201,7 +201,7 @@ impl<'db> Expander<'db> { let old_span_map = std::mem::replace(&mut self.span_map, SpanMap::ExpansionSpanMap(&res.value.1)); let prev_ast_id_map = - mem::replace(&mut self.ast_id_map, db.ast_id_map(self.current_file_id)); + mem::replace(&mut self.ast_id_map, self.current_file_id.ast_id_map(db)); let mark = Mark { file_id: old_file_id, span_map: old_span_map, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index 4b50161c04cd0..ffd89e41ff1d9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -146,7 +146,7 @@ fn file_item_tree_query( let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered(); let ctx = lower::Ctx::new(db, file_id, krate); - let syntax = db.parse_or_expand(file_id); + let syntax = file_id.parse_or_expand(db); let mut item_tree = match_ast! { match syntax { ast::SourceFile(file) => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index 91c2e60fd7c4c..b0d9d960d81f7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -40,7 +40,7 @@ impl<'db> Ctx<'db> { Self { db, tree: ItemTree::default(), - source_ast_id_map: db.ast_id_map(file), + source_ast_id_map: file.ast_id_map(db), file, cfg_options: OnceCell::new(), span_map: OnceCell::new(), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 88f41274e4e4b..c53a7321e83b4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -75,7 +75,7 @@ fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) ast_id.file_id.file_id().expect("macros inside macros are not supported"); let ast = editioned_file_id.parse(&db).syntax_node(); - let ast_id_map = db.ast_id_map(ast_id.file_id); + let ast_id_map = ast_id.file_id.ast_id_map(&db); let node = ast_id_map.get_erased(ast_id.value).to_node(&ast); Some((node.text_range(), errors)) }) @@ -120,7 +120,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream let mut expansions = Vec::new(); for macro_call_node in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) { - let ast_id = db.ast_id_map(source.file_id).ast_id(¯o_call_node); + let ast_id = source.file_id.ast_id_map(&db).ast_id(¯o_call_node); let ast_id = InFile::new(source.file_id, ast_id); let ptr = InFile::new(source.file_id, AstPtr::new(¯o_call_node)); let macro_call_id = resolve_macro_call_id(&db, def_map, ast_id, ptr) @@ -476,7 +476,7 @@ m!(g); .collect(); for macro_call_node in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) { - let ast_id = db.ast_id_map(source.file_id).ast_id(¯o_call_node); + let ast_id = source.file_id.ast_id_map(&db).ast_id(¯o_call_node); let ast_id = InFile::new(source.file_id, ast_id); let ptr = InFile::new(source.file_id, AstPtr::new(¯o_call_node)); let macro_call_id = resolve_macro_call_id(&db, def_map, ast_id, ptr) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs index cc88d21fbfa5c..8bac83c3699ba 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs @@ -51,7 +51,7 @@ impl TraitItems { tr: TraitId, ) -> (TraitItems, DefDiagnostics) { let ItemLoc { container: module_id, id: ast_id } = *tr.lookup(db); - let ast_id_map = db.ast_id_map(ast_id.file_id); + let ast_id_map = ast_id.file_id.ast_id_map(db); let source = ast_id.with_value(ast_id_map.get(ast_id.value)).to_node(db); if source.eq_token().is_some() { // FIXME(trait-alias) probably needs special handling here @@ -162,7 +162,7 @@ impl<'db> AssocItemCollector<'db> { module_id, def_map, local_def_map, - ast_id_map: db.ast_id_map(file_id), + ast_id_map: file_id.ast_id_map(db), span_map: db.span_map(file_id), cfg_options: module_id.krate(db).cfg_options(db), file_id, @@ -358,7 +358,7 @@ impl<'db> AssocItemCollector<'db> { let (syntax, span_map) = ¯o_call_id.parse_macro_expansion(self.db).value; 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.db.ast_id_map(self.file_id)); + 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; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs index e7aa994c07ef7..8e339fa973195 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs @@ -166,15 +166,15 @@ fn no() {} [ "crate_local_def_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "EnumVariants::of_", @@ -183,7 +183,7 @@ fn no() {} expect![[r#" [ "parse", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", "real_span_map", "EnumVariants::of_", @@ -224,21 +224,21 @@ pub struct S {} [ "crate_local_def_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "DeclarativeMacroExpander::expander_", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "macro_def_shim", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "MacroCallId::parse_macro_expansion_", "MacroCallId::macro_arg_", ] @@ -246,12 +246,12 @@ pub struct S {} expect![[r#" [ "parse", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", "real_span_map", "MacroCallId::macro_arg_", "MacroCallId::parse_macro_expansion_", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", ] "#]], @@ -282,26 +282,26 @@ fn f() { foo } [ "crate_local_def_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "crate_local_def_map", "ProcMacros::get_for_crate_", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "macro_def_shim", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "MacroCallId::parse_macro_expansion_", "MacroCallId::expand_proc_macro_", "MacroCallId::macro_arg_", @@ -311,13 +311,13 @@ fn f() { foo } expect![[r#" [ "parse", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", "real_span_map", "MacroCallId::macro_arg_", "MacroCallId::expand_proc_macro_", "MacroCallId::parse_macro_expansion_", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", ] "#]], @@ -406,38 +406,38 @@ pub struct S {} [ "crate_local_def_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "crate_local_def_map", "ProcMacros::get_for_crate_", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "DeclarativeMacroExpander::expander_", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "macro_def_shim", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "MacroCallId::parse_macro_expansion_", "MacroCallId::macro_arg_", "DeclarativeMacroExpander::expander_", "macro_def_shim", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "MacroCallId::parse_macro_expansion_", "MacroCallId::macro_arg_", "macro_def_shim", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "MacroCallId::parse_macro_expansion_", "MacroCallId::expand_proc_macro_", "MacroCallId::macro_arg_", @@ -447,7 +447,7 @@ pub struct S {} expect![[r#" [ "parse", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", "real_span_map", "MacroCallId::macro_arg_", @@ -523,29 +523,29 @@ m!(Z); [ "crate_local_def_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "DeclarativeMacroExpander::expander_", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "macro_def_shim", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "MacroCallId::parse_macro_expansion_", "MacroCallId::macro_arg_", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "MacroCallId::parse_macro_expansion_", "MacroCallId::macro_arg_", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "MacroCallId::parse_macro_expansion_", "MacroCallId::macro_arg_", ] @@ -572,7 +572,7 @@ m!(Z); expect![[r#" [ "parse", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", "real_span_map", "MacroCallId::macro_arg_", @@ -610,7 +610,7 @@ pub type Ty = (); expect![[r#" [ "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", ] @@ -630,7 +630,7 @@ pub type Ty = (); expect![[r#" [ "parse", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", "real_span_map", ] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index e9307a125502a..5a4f78cfbc86b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -1068,7 +1068,7 @@ impl EnumVariants { ) -> (EnumVariants, ThinVec) { let loc = e.lookup(db); let source = loc.source(db); - let ast_id_map = db.ast_id_map(source.file_id); + let ast_id_map = source.file_id.ast_id_map(db); let mut diagnostics = ThinVec::new(); let cfg_options = loc.container.krate(db).cfg_options(db); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs index e33fd95908b9f..6d6003079818e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs @@ -14,7 +14,7 @@ pub trait HasSource { type Value: AstNode; fn source(&self, db: &dyn DefDatabase) -> InFile { let InFile { file_id, value } = self.ast_ptr(db); - InFile::new(file_id, value.to_node(&db.parse_or_expand(file_id))) + InFile::new(file_id, value.to_node(&file_id.parse_or_expand(db))) } fn ast_ptr(&self, db: &dyn DefDatabase) -> InFile>; } @@ -26,7 +26,7 @@ where type Value = T::Ast; fn ast_ptr(&self, db: &dyn DefDatabase) -> InFile> { let id = self.ast_id(); - let ast_id_map = db.ast_id_map(id.file_id); + let ast_id_map = id.file_id.ast_id_map(db); InFile::new(id.file_id, ast_id_map.get(id.value)) } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index c8a8be4311e09..69c8ffe6095d2 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -1,8 +1,8 @@ //! Defines database & queries for macro expansion. use base_db::{Crate, SourceDatabase}; -use span::{AstIdMap, Span}; -use syntax::{AstNode, Parse, SyntaxNode, SyntaxToken, ast}; +use span::Span; +use syntax::{AstNode, SyntaxNode, SyntaxToken, ast}; use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree}; use crate::{ @@ -37,16 +37,9 @@ pub enum TokenExpander<'db> { #[query_group::query_group] pub trait ExpandDatabase: SourceDatabase { - #[salsa::invoke(ast_id_map)] - #[salsa::transparent] - fn ast_id_map(&self, file_id: HirFileId) -> &AstIdMap; - #[salsa::transparent] fn resolve_span(&self, span: Span) -> FileRange; - #[salsa::transparent] - fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode; - #[salsa::transparent] #[salsa::invoke(SpanMap::new)] fn span_map(&self, file_id: HirFileId) -> SpanMap<'_>; @@ -54,6 +47,7 @@ pub trait ExpandDatabase: SourceDatabase { #[salsa::transparent] #[salsa::invoke(crate::span_map::expansion_span_map)] fn expansion_span_map(&self, file_id: MacroCallId) -> &ExpansionSpanMap; + #[salsa::invoke(crate::span_map::real_span_map)] #[salsa::transparent] fn real_span_map(&self, file_id: EditionedFileId) -> &RealSpanMap; @@ -76,7 +70,7 @@ pub trait ExpandDatabase: SourceDatabase { fn resolve_span(db: &dyn ExpandDatabase, Span { range, anchor, ctx: _ }: Span) -> FileRange { let file_id = EditionedFileId::from_span_file_id(db, anchor.file_id); let anchor_offset = - db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start(); + HirFileId::from(file_id).ast_id_map(db).get_erased(anchor.ast_id).text_range().start(); FileRange { file_id, range: range + anchor_offset } } @@ -255,37 +249,6 @@ pub fn expand_speculative( Some((node.syntax_node(), token)) } -#[salsa::tracked(lru = 1024, returns(ref))] -fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> AstIdMap { - AstIdMap::from_source(&db.parse_or_expand(file_id)) -} - -/// Main public API -- parses a hir file, not caring whether it's a real -/// file or a macro expansion. -fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode { - match file_id { - HirFileId::FileId(file_id) => file_id.parse(db).syntax_node(), - HirFileId::MacroFile(macro_file) => { - macro_file.parse_macro_expansion(db).value.0.syntax_node() - } - } -} - -pub(crate) fn parse_with_map( - db: &dyn ExpandDatabase, - file_id: HirFileId, -) -> (Parse, SpanMap<'_>) { - match file_id { - HirFileId::FileId(file_id) => { - (file_id.parse(db).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(file_id))) - } - HirFileId::MacroFile(macro_file) => { - let (parse, map) = ¯o_file.parse_macro_expansion(db).value; - (parse.clone(), SpanMap::ExpansionSpanMap(map)) - } - } -} - impl<'db> TokenExpander<'db> { fn macro_expander(db: &'db dyn ExpandDatabase, id: MacroDefId) -> TokenExpander<'db> { match id.kind { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index 107014164e78d..5ba30f12562ac 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -87,7 +87,8 @@ impl DeclarativeMacroExpander { def_crate: Crate, id: AstId, ) -> DeclarativeMacroExpander { - let (root, map) = crate::db::parse_with_map(db, id.file_id); + let (root, map) = id.file_id.parse_with_map(db); + let root = root.syntax_node(); let transparency = |node: ast::AnyHasAttrs| { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs index 30e9624b2b714..16053840b768a 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -206,7 +206,7 @@ fn eager_macro_recur( continue; } }; - let ast_id = db.ast_id_map(curr.file_id).ast_id(&call); + let ast_id = curr.file_id.ast_id_map(db).ast_id(&call); let ExpandResult { value, err } = match def.kind { MacroDefKind::BuiltInEager(..) => { let ExpandResult { value, err } = expand_eager_macro_input( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs index 5f03e1b6b3f12..a5e24338807c8 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs @@ -94,16 +94,16 @@ pub type AstId = crate::InFile>; impl AstId { pub fn to_node(&self, db: &dyn ExpandDatabase) -> N { - self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)) + self.to_ptr(db).to_node(&self.file_id.parse_or_expand(db)) } pub fn to_range(&self, db: &dyn ExpandDatabase) -> TextRange { self.to_ptr(db).text_range() } pub fn to_in_file_node(&self, db: &dyn ExpandDatabase) -> crate::InFile { - crate::InFile::new(self.file_id, self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))) + crate::InFile::new(self.file_id, self.to_ptr(db).to_node(&self.file_id.parse_or_expand(db))) } pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> AstPtr { - db.ast_id_map(self.file_id).get(self.value) + self.file_id.ast_id_map(db).get(self.value) } pub fn erase(&self) -> ErasedAstId { crate::InFile::new(self.file_id, self.value.erase()) @@ -124,7 +124,7 @@ impl ErasedAstId { self.to_ptr(db).text_range() } pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> SyntaxNodePtr { - db.ast_id_map(self.file_id).get_erased(self.value) + self.file_id.ast_id_map(db).get_erased(self.value) } } @@ -218,7 +218,7 @@ impl FileIdToSyntax for MacroCallId { } impl FileIdToSyntax for HirFileId { fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode { - db.parse_or_expand(self) + self.parse_or_expand(db) } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 73647d0ece781..719a38edf441a 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -39,7 +39,8 @@ use base_db::Crate; use either::Either; use mbe::MatchedArmIndex; use span::{ - Edition, ErasedFileAstId, FileAstId, NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER, Span, SyntaxContext, + AstIdMap, Edition, ErasedFileAstId, FileAstId, NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER, Span, + SyntaxContext, }; use syntax::{ Parse, SyntaxError, SyntaxNode, SyntaxToken, T, TextRange, TextSize, @@ -486,7 +487,7 @@ impl MacroCallId { #[salsa::tracked] impl MacroCallId { - /// Implementation of [`crate::db::parse_or_expand`] for the macro case. + /// Implementation of [`HirFileId::parse_or_expand`] for the macro case. // FIXME: We should verify that the parsed node is one of the many macro node variants we expect // instead of having it be untyped #[salsa::tracked(returns(ref), lru = 512)] @@ -550,7 +551,7 @@ impl MacroCallId { return (eager.arg.clone(), SyntaxFixupUndoInfo::NONE, eager.span); } - let (parse, map) = crate::db::parse_with_map(db, loc.kind.file_id()); + let (parse, map) = loc.kind.file_id().parse_with_map(db); let root = parse.syntax_node(); let (is_derive, censor_item_tree_attr_ids, item_node, span) = match &loc.kind { @@ -790,8 +791,8 @@ fn expand_unimplemented_builtin_macro(span: Span) -> ExpandResult) -> Span { #[salsa::tracked] fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId, _: ()) -> Span { - let root = db.parse_or_expand(ast.file_id); - let ast_id_map = db.ast_id_map(ast.file_id); + let root = ast.file_id.parse_or_expand(db); + let ast_id_map = ast.file_id.ast_id_map(db); let span_map = db.span_map(ast.file_id); let node = ast_id_map.get(ast.value).to_node(&root); @@ -854,10 +855,10 @@ impl MacroDefId { | MacroDefKind::BuiltInDerive(id, _) | MacroDefKind::BuiltInEager(id, _) | MacroDefKind::UnimplementedBuiltIn(id) => { - id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range()) + id.with_value(id.file_id.ast_id_map(db).get(id.value).text_range()) } MacroDefKind::ProcMacro(id, _, _) => { - id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range()) + id.with_value(id.file_id.ast_id_map(db).get(id.value).text_range()) } } } @@ -1513,4 +1514,38 @@ impl HirFileId { }; Some(attr.with_value(ast::Attr::cast(attr.value.clone())?)) } + + /// Main public API -- parses a hir file, not caring whether it's a real + /// file or a macro expansion. + pub fn parse_or_expand(self, db: &dyn ExpandDatabase) -> SyntaxNode { + match self { + HirFileId::FileId(file_id) => file_id.parse(db).syntax_node(), + HirFileId::MacroFile(macro_file) => { + macro_file.parse_macro_expansion(db).value.0.syntax_node() + } + } + } + + pub(crate) fn parse_with_map( + self, + db: &dyn ExpandDatabase, + ) -> (Parse, SpanMap<'_>) { + match self { + HirFileId::FileId(file_id) => { + (file_id.parse(db).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(file_id))) + } + HirFileId::MacroFile(macro_file) => { + let (parse, map) = ¯o_file.parse_macro_expansion(db).value; + (parse.clone(), SpanMap::ExpansionSpanMap(map)) + } + } + } +} + +#[salsa::tracked] +impl HirFileId { + #[salsa::tracked(lru = 1024, returns(ref))] + pub fn ast_id_map(self, db: &dyn ExpandDatabase) -> AstIdMap { + AstIdMap::from_source(&self.parse_or_expand(db)) + } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs index d19eead4a80d4..1b588a8425161 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs @@ -54,7 +54,7 @@ pub(crate) fn real_span_map( ) -> RealSpanMap { use syntax::ast::HasModuleItem; let mut pairs = vec![(syntax::TextSize::new(0), span::ROOT_ERASED_FILE_AST_ID)]; - let ast_id_map = db.ast_id_map(editioned_file_id.into()); + let ast_id_map = HirFileId::from(editioned_file_id).ast_id_map(db); let tree = editioned_file_id.parse(db).tree(); // This is an incrementality layer. Basically we can't use absolute ranges for our spans as that diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index 9ffe38e74969e..febd2a833a6a7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -25,7 +25,7 @@ use hir_def::{ src::HasSource, type_ref::TypeRefId, }; -use hir_expand::{FileRange, InFile, db::ExpandDatabase}; +use hir_expand::{FileRange, InFile}; use itertools::Itertools; use rustc_hash::FxHashMap; use stdx::format_to; @@ -278,7 +278,7 @@ fn expr_node( ) -> Option> { Some(match body_source_map.expr_syntax(expr) { Ok(sp) => { - let root = db.parse_or_expand(sp.file_id); + let root = sp.file_id.parse_or_expand(db); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) } Err(SyntheticSyntax) => return None, @@ -292,7 +292,7 @@ fn pat_node( ) -> Option> { Some(match body_source_map.pat_syntax(pat) { Ok(sp) => { - let root = db.parse_or_expand(sp.file_id); + let root = sp.file_id.parse_or_expand(db); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) } Err(SyntheticSyntax) => return None, @@ -306,7 +306,7 @@ fn type_node( ) -> Option> { Some(match body_source_map.type_syntax(type_ref) { Ok(sp) => { - let root = db.parse_or_expand(sp.file_id); + let root = sp.file_id.parse_or_expand(db); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) } Err(SyntheticSyntax) => return None, @@ -349,7 +349,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { if let Some((binding_id, syntax_ptr)) = self_param { let ty = &inference_result.type_of_binding[binding_id]; if let Some(syntax_ptr) = syntax_ptr { - let root = db.parse_or_expand(syntax_ptr.file_id); + let root = syntax_ptr.file_id.parse_or_expand(&db); let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone()); types.push((node, ty.as_ref())); } @@ -361,7 +361,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { } let node = match source_map.pat_syntax(pat) { Ok(sp) => { - let root = db.parse_or_expand(sp.file_id); + let root = sp.file_id.parse_or_expand(&db); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) } Err(SyntheticSyntax) => continue, @@ -375,7 +375,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { for (expr, ty) in inference_result.type_of_expr.iter() { let node = match source_map.expr_syntax(expr) { Ok(sp) => { - let root = db.parse_or_expand(sp.file_id); + let root = sp.file_id.parse_or_expand(&db); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) } Err(SyntheticSyntax) => continue, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 4c26b191208ca..3432d47b2f54c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -34,7 +34,7 @@ fn foo() -> i32 { "source_root_crates", "crate_local_def_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "InferenceResult::for_body_", @@ -78,7 +78,7 @@ fn foo() -> i32 { expect_test::expect![[r#" [ "parse", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", "real_span_map", "AttrFlags::query_", @@ -123,7 +123,7 @@ fn baz() -> i32 { "source_root_crates", "crate_local_def_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "InferenceResult::for_body_", @@ -194,7 +194,7 @@ fn baz() -> i32 { expect_test::expect![[r#" [ "parse", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", "real_span_map", "AttrFlags::query_", @@ -247,7 +247,7 @@ $0", "source_root_crates", "crate_local_def_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "TraitImpls::for_crate_", @@ -284,7 +284,7 @@ pub struct NewStruct { expect_test::expect![[r#" [ "parse", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", "real_span_map", "crate_local_def_map", @@ -322,7 +322,7 @@ $0", "source_root_crates", "crate_local_def_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "TraitImpls::for_crate_", @@ -360,7 +360,7 @@ pub enum SomeEnum { expect_test::expect![[r#" [ "parse", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", "real_span_map", "crate_local_def_map", @@ -398,7 +398,7 @@ $0", "source_root_crates", "crate_local_def_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "TraitImpls::for_crate_", @@ -433,7 +433,7 @@ fn bar() -> f32 { expect_test::expect![[r#" [ "parse", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", "real_span_map", "crate_local_def_map", @@ -475,7 +475,7 @@ $0", "source_root_crates", "crate_local_def_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "TraitImpls::for_crate_", @@ -518,7 +518,7 @@ impl SomeStruct { expect_test::expect![[r#" [ "parse", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", "real_span_map", "crate_local_def_map", @@ -576,7 +576,7 @@ fn main() { "source_root_crates", "crate_local_def_map", "file_item_tree_query", - "ast_id_map", + "HirFileId::ast_id_map_", "parse", "real_span_map", "TraitItems::query_with_diagnostics_", @@ -671,7 +671,7 @@ fn main() { expect_test::expect![[r#" [ "parse", - "ast_id_map", + "HirFileId::ast_id_map_", "file_item_tree_query", "real_span_map", "crate_local_def_map", diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index d99ed37d7eb21..33a46a3e175a0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -993,7 +993,7 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::PathDiagnostic { node, diag } => { let source = expr_or_pat_syntax(*node)?; - let syntax = source.value.to_node(&db.parse_or_expand(source.file_id)); + let syntax = source.value.to_node(&source.file_id.parse_or_expand(db)); let path = match_ast! { match (syntax.syntax()) { ast::RecordExpr(it) => it.path()?, @@ -1296,7 +1296,7 @@ impl<'db> AnyDiagnostic<'db> { Some(match diag { TyLoweringDiagnostic::PathDiagnostic { source, diag } => { let source = Self::type_syntax(*source, source_map)?; - let syntax = source.value.to_node(&db.parse_or_expand(source.file_id)); + let syntax = source.value.to_node(&source.file_id.parse_or_expand(db)); let ast::Type::PathType(syntax) = syntax else { return None }; Self::path_diagnostic(diag, source.with_value(syntax.path()?))? } diff --git a/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs index 9bff8bda3a96d..9248aaec02097 100644 --- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs @@ -297,7 +297,7 @@ impl HasSource for Param<'_> { let (_, source_map) = ExpressionStore::with_source_map(db, owner.expression_store_owner(db)); let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?; - let root = db.parse_or_expand(file_id); + let root = file_id.parse_or_expand(db); match value.to_node(&root) { Either::Left(ast::Expr::ClosureExpr(it)) => it .param_list()? diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index ceac3f0876110..a78ce2a2ab105 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -813,7 +813,7 @@ impl Module { expr_store_diagnostics(db, acc, source_map); let (variants, diagnostics) = e.id.enum_variants_with_diagnostics(db); let file = e.id.lookup(db).id.file_id; - let ast_id_map = db.ast_id_map(file); + let ast_id_map = file.ast_id_map(db); for diag in diagnostics { acc.push( InactiveCode { @@ -883,7 +883,7 @@ impl Module { .iter() .for_each(|&(_ast, call_id)| macro_call_diagnostics(db, call_id, acc)); - let ast_id_map = db.ast_id_map(file_id); + let ast_id_map = file_id.ast_id_map(db); for diag in impl_id.impl_items_with_diagnostics(db).1.iter() { emit_def_diagnostic(db, acc, diag, edition, loc.container.krate(db)); @@ -1170,7 +1170,7 @@ fn macro_call_diagnostics<'db>( let RenderedExpandError { message, error, kind } = err.render_to_string(db); if Some(err.span().anchor.file_id) == file_id.file_id().map(|it| it.span_file_id(db)) { range.value = err.span().range - + db.ast_id_map(file_id).get_erased(err.span().anchor.ast_id).text_range().start(); + + file_id.ast_id_map(db).get_erased(err.span().anchor.ast_id).text_range().start(); } acc.push(MacroError { range, message, error, kind }.into()); } @@ -1260,7 +1260,7 @@ fn emit_def_diagnostic_<'db>( } DefDiagnosticKind::UnconfiguredCode { ast_id, cfg, opts } => { - let ast_id_map = db.ast_id_map(ast_id.file_id); + let ast_id_map = ast_id.file_id.ast_id_map(db); let ptr = ast_id_map.get_erased(ast_id.value); acc.push( InactiveCode { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 8fc08abef17df..c696ebcf18fb5 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -550,7 +550,7 @@ impl<'db> SemanticsImpl<'db> { } pub fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode { - let node = self.db.parse_or_expand(file_id); + let node = file_id.parse_or_expand(self.db); self.cache(node.clone(), file_id); node } @@ -690,7 +690,7 @@ impl<'db> SemanticsImpl<'db> { pub fn derive_helpers_in_scope(&self, adt: &ast::Adt) -> Option> { let sa = self.analyze_no_infer(adt.syntax())?; - let id = self.db.ast_id_map(sa.file_id).ast_id(adt); + let id = sa.file_id.ast_id_map(self.db).ast_id(adt); let result = sa .resolver .def_map() @@ -713,7 +713,7 @@ impl<'db> SemanticsImpl<'db> { })?; let attr_name = attr.path().and_then(|it| it.as_single_name_ref())?.as_name(); let sa = self.analyze_no_infer(adt.syntax())?; - let id = self.db.ast_id_map(sa.file_id).ast_id(&adt); + let id = sa.file_id.ast_id_map(self.db).ast_id(&adt); let res: Vec<_> = sa .resolver .def_map() @@ -1498,7 +1498,7 @@ impl<'db> SemanticsImpl<'db> { self.analyze_impl(InFile::new(expansion, &parent), None, false) })? .resolver; - let id = db.ast_id_map(expansion).ast_id(&adt); + let id = expansion.ast_id_map(db).ast_id(&adt); let helpers = resolver .def_map() .derive_helpers_in_scope(InFile::new(expansion, id))?; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index 97c5a451ab6b8..d9c98c920ee15 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -200,7 +200,7 @@ impl ChildBySource for EnumId { return; } - let ast_id_map = db.ast_id_map(loc.id.file_id); + let ast_id_map = loc.id.file_id.ast_id_map(db); self.enum_variants(db).variants.values().for_each(|&(variant, _)| { res[keys::ENUM_VARIANT].insert(ast_id_map.get(variant.lookup(db).id.value), variant); diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index fc03f82135df5..59d029b1e27c8 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -1366,7 +1366,7 @@ impl<'db> SourceAnalyzer<'db> { }, ); if let Some(adt) = adt { - let ast_id = db.ast_id_map(self.file_id).ast_id(&adt); + let ast_id = self.file_id.ast_id_map(db).ast_id(&adt); if let Some(helpers) = self .resolver .def_map() diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index bda3f9bf0ad04..888f55cea777e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -1,4 +1,4 @@ -use hir::{CaseType, InFile, db::ExpandDatabase}; +use hir::{CaseType, InFile}; use ide_db::{assists::Assist, defs::NameClass, rename::RenameDefinition}; use syntax::AstNode; @@ -37,7 +37,7 @@ pub(crate) fn incorrect_case( } fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::IncorrectCase) -> Option> { - let root = ctx.sema.db.parse_or_expand(d.file); + let root = d.file.parse_or_expand(ctx.sema.db); let name_node = d.ident.to_node(&root); let def = NameClass::classify(&ctx.sema, &name_node)?.defined()?; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index fe71707f078e7..2030be436887a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -1,8 +1,6 @@ use either::Either; use hir::{ - AssocItem, FindPathConfig, HasVisibility, HirDisplay, InFile, Type, - db::{ExpandDatabase, HirDatabase}, - sym, + AssocItem, FindPathConfig, HasVisibility, HirDisplay, InFile, Type, db::HirDatabase, sym, }; use ide_db::{ FxHashMap, @@ -65,7 +63,7 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingFields) -> Option, d: &hir::MissingUnsafe) -> Option, d: &hir::NeedMut) -> Option { - let root = ctx.sema.db.parse_or_expand(d.span.file_id); + 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() diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs index 3dd6744b05bdb..f6fe83177fba9 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -1,5 +1,5 @@ use either::Either; -use hir::{HasSource, HirDisplay, Semantics, VariantId, db::ExpandDatabase}; +use hir::{HasSource, HirDisplay, Semantics, VariantId}; use ide_db::text_edit::TextEdit; use ide_db::{EditionedFileId, RootDatabase, source_change::SourceChange}; use syntax::{ @@ -32,7 +32,7 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_, '_>, d: &hir::NoSuchFie fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::NoSuchField) -> Option> { // FIXME: quickfix for pattern - let root = ctx.sema.db.parse_or_expand(d.field.file_id); + let root = d.field.file_id.parse_or_expand(ctx.sema.db); match &d.field.value.to_node(&root) { Either::Left(node) => { if let Some(private_field) = d.private { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs index b5a47e508e14d..7e431f3ff4a86 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs @@ -1,4 +1,4 @@ -use hir::{FileRange, db::ExpandDatabase, diagnostics::RemoveTrailingReturn}; +use hir::{FileRange, diagnostics::RemoveTrailingReturn}; use ide_db::text_edit::TextEdit; use ide_db::{assists::Assist, source_change::SourceChange}; use syntax::{AstNode, ast}; @@ -37,7 +37,7 @@ pub(crate) fn remove_trailing_return( } fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &RemoveTrailingReturn) -> Option> { - let root = ctx.sema.db.parse_or_expand(d.return_expr.file_id); + let root = d.return_expr.file_id.parse_or_expand(ctx.sema.db); let return_expr = d.return_expr.value.to_node(&root); let stmt = return_expr.syntax().parent().and_then(ast::ExprStmt::cast); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs index aa7b57e2928f8..337b05e21f71a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs @@ -1,4 +1,4 @@ -use hir::{db::ExpandDatabase, diagnostics::RemoveUnnecessaryElse}; +use hir::diagnostics::RemoveUnnecessaryElse; use ide_db::text_edit::TextEdit; use ide_db::{assists::Assist, source_change::SourceChange}; use itertools::Itertools; @@ -41,7 +41,7 @@ pub(crate) fn remove_unnecessary_else( } fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &RemoveUnnecessaryElse) -> Option> { - let root = ctx.sema.db.parse_or_expand(d.if_expr.file_id); + let root = d.if_expr.file_id.parse_or_expand(ctx.sema.db); let if_expr = d.if_expr.value.to_node(&root); let if_expr = ctx.sema.original_ast_node(if_expr)?; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs index f974c55023135..e67d86b8a212a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs @@ -1,4 +1,4 @@ -use hir::{InFile, db::ExpandDatabase}; +use hir::InFile; use ide_db::source_change::SourceChange; use ide_db::text_edit::TextEdit; use syntax::{ @@ -29,7 +29,7 @@ fn fixes( ctx: &DiagnosticsContext<'_, '_>, d: &hir::ReplaceFilterMapNextWithFindMap, ) -> Option> { - let root = ctx.sema.db.parse_or_expand(d.file); + let root = d.file.parse_or_expand(ctx.sema.db); let next_expr = d.next_expr.to_node(&root); let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs index ee972f2d1dc69..9374688d0ec6f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs @@ -1,4 +1,4 @@ -use hir::{HasSource, HirDisplay, db::ExpandDatabase}; +use hir::{HasSource, HirDisplay}; use ide_db::text_edit::TextRange; use ide_db::{ assists::{Assist, AssistId}, @@ -82,7 +82,7 @@ fn quickfix_for_redundant_assoc_item( let file_id = d.file_id.file_id()?; let add_assoc_item_def = |builder: &mut SourceChangeBuilder| -> Option<()> { let db = ctx.sema.db; - let root = db.parse_or_expand(d.file_id); + let root = d.file_id.parse_or_expand(db); // don't modify trait def in outer crate let impl_def = d.impl_.to_node(&root); let current_crate = ctx.sema.scope(impl_def.syntax())?.krate(); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index da6fc20c3e65f..8950850d4a814 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -1,5 +1,5 @@ use either::Either; -use hir::{CallableKind, ClosureStyle, HirDisplay, InFile, db::ExpandDatabase}; +use hir::{CallableKind, ClosureStyle, HirDisplay, InFile}; use ide_db::{ famous_defs::FamousDefs, source_change::{SourceChange, SourceChangeBuilder}, @@ -151,7 +151,7 @@ fn add_missing_ok_or_some( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let root = ctx.db().parse_or_expand(expr_ptr.file_id); + let root = expr_ptr.file_id.parse_or_expand(ctx.db()); let expr = expr_ptr.value.to_node(&root); let hir::FileRange { file_id, range: expr_range } = ctx.sema.original_range_opt(expr.syntax())?; @@ -246,7 +246,7 @@ fn remove_unnecessary_wrapper( acc: &mut Vec, ) -> Option<()> { let db = ctx.db(); - let root = db.parse_or_expand(expr_ptr.file_id); + let root = expr_ptr.file_id.parse_or_expand(db); let expr = expr_ptr.value.to_node(&root); // FIXME: support inside MacroCall? let expr = ctx.sema.original_ast_node(expr)?; @@ -327,7 +327,7 @@ fn remove_semicolon( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let root = ctx.db().parse_or_expand(expr_ptr.file_id); + let root = expr_ptr.file_id.parse_or_expand(ctx.db()); let expr = expr_ptr.value.to_node(&root); if !d.actual.is_unit() { return None; @@ -365,7 +365,7 @@ fn str_ref_to_owned( return None; } - let root = ctx.db().parse_or_expand(expr_ptr.file_id); + let root = expr_ptr.file_id.parse_or_expand(ctx.db()); let expr = expr_ptr.value.to_node(&root); let hir::FileRange { file_id, range } = ctx.sema.original_range_opt(expr.syntax())?; @@ -391,7 +391,7 @@ fn add_await( return None; } - let root = ctx.db().parse_or_expand(expr_ptr.file_id); + let root = expr_ptr.file_id.parse_or_expand(ctx.db()); let expr = expr_ptr.value.to_node(&root); let hir::FileRange { file_id, range } = ctx.sema.original_range_opt(expr.syntax())?; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs index e000d6388ae69..4c59482961a0b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -2,7 +2,6 @@ use std::ops::Not; use hir::{ ClosureStyle, FindPathConfig, HirDisplay, - db::ExpandDatabase, term_search::{TermSearchConfig, TermSearchCtx, term_search}, }; use ide_db::text_edit::TextEdit; @@ -46,7 +45,7 @@ pub(crate) fn typed_hole<'db>( fn fixes<'db>(ctx: &DiagnosticsContext<'_, 'db>, d: &hir::TypedHole<'db>) -> Option> { let db = ctx.sema.db; - let root = db.parse_or_expand(d.expr.file_id); + let root = d.expr.file_id.parse_or_expand(db); let (original_range, _) = d.expr.as_ref().map(|it| it.to_node(&root)).syntax().original_file_range_opt(db)?; let scope = ctx.sema.scope(d.expr.value.to_node(&root).syntax())?; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs index 78e13677cfb1d..682a8130a8822 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -1,5 +1,5 @@ use either::Either; -use hir::{Adt, FileRange, HasSource, HirDisplay, InFile, Struct, Union, db::ExpandDatabase}; +use hir::{Adt, FileRange, HasSource, HirDisplay, InFile, Struct, Union}; use ide_db::text_edit::TextEdit; use ide_db::{ assists::{Assist, AssistId}, @@ -64,7 +64,7 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedField<'_>) -> Opti // FIXME: Add Snippet Support fn field_fix(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedField<'_>) -> Option { // Get the FileRange of the invalid field access - let root = ctx.sema.db.parse_or_expand(d.expr.file_id); + let root = d.expr.file_id.parse_or_expand(ctx.sema.db); let expr = d.expr.value.to_node(&root).left()?; let error_range = ctx.sema.original_range_opt(expr.syntax())?; @@ -266,7 +266,7 @@ fn method_fix( ctx: &DiagnosticsContext<'_, '_>, expr_ptr: &InFile>>, ) -> Option { - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); + let root = expr_ptr.file_id.parse_or_expand(ctx.sema.db); let expr = expr_ptr.value.to_node(&root); let FileRange { range, file_id } = ctx.sema.original_range_opt(expr.syntax())?; Some(Assist { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs index 01929a5144719..853e9d9f5692d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -1,4 +1,4 @@ -use hir::{FileRange, HirDisplay, InFile, db::ExpandDatabase}; +use hir::{FileRange, HirDisplay, InFile}; use ide_db::text_edit::TextEdit; use ide_db::{ assists::{Assist, AssistId}, @@ -82,7 +82,7 @@ fn field_fix( return None; } let expr_ptr = &d.expr; - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); + let root = expr_ptr.file_id.parse_or_expand(ctx.sema.db); let expr = expr_ptr.value.to_node(&root); let (file_id, range) = match expr.left()? { ast::Expr::MethodCallExpr(mcall) => { @@ -118,7 +118,7 @@ fn assoc_func_fix( let db = ctx.sema.db; let expr_ptr = &d.expr; - let root = db.parse_or_expand(expr_ptr.file_id); + let root = expr_ptr.file_id.parse_or_expand(db); let expr: ast::Expr = expr_ptr.value.to_node(&root).left()?; let call = ast::MethodCallExpr::cast(expr.syntax().clone())?; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs index 1e0e9105d88b5..d5f2697bd2188 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs @@ -1,4 +1,3 @@ -use hir::db::ExpandDatabase; use ide_db::{assists::Assist, base_db::AnchoredPathBuf, source_change::FileSystemEdit}; use itertools::Itertools; use syntax::AstNode; @@ -33,7 +32,7 @@ pub(crate) fn unresolved_module( } fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedModule) -> Option> { - let root = ctx.sema.db.parse_or_expand(d.decl.file_id); + let root = d.decl.file_id.parse_or_expand(ctx.sema.db); let unresolved_module = d.decl.value.to_node(&root); Some( d.candidates diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 96c95ec277cc6..4a97a5f05e9b8 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -705,8 +705,8 @@ impl ProcMacroExpander for Expander { let call_site_ast_id = macro_call_loc.kind.erased_ast_id(); if let Some(editioned_file_id) = call_site_file.file_id() { - let range = db - .ast_id_map(editioned_file_id.into()) + let range = hir_expand::HirFileId::from(editioned_file_id) + .ast_id_map(db) .get_erased(call_site_ast_id) .text_range(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 1a036c3b99195..f53512e5ec54c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -13,7 +13,7 @@ use cfg::{CfgAtom, CfgDiff}; use hir::{ Adt, AssocItem, Crate, DefWithBody, FindPathConfig, GenericDef, HasCrate, HasSource, HirDisplay, ModuleDef, Name, Variant, crate_lang_items, - db::{DefDatabase, ExpandDatabase, HirDatabase}, + db::{DefDatabase, HirDatabase}, }; use hir_def::{ DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, SyntheticSyntax, @@ -1503,7 +1503,7 @@ fn location_csv_expr(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, expr_id: Ok(s) => s, Err(SyntheticSyntax) => return "synthetic,,".to_owned(), }; - let root = db.parse_or_expand(src.file_id); + let root = src.file_id.parse_or_expand(db); let node = src.map(|e| e.to_node(&root).syntax().clone()); let original_range = node.as_ref().original_file_range_rooted(db); let path = vfs.file_path(original_range.file_id.file_id(db)); @@ -1519,7 +1519,7 @@ fn location_csv_pat(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, pat_id: Pa Ok(s) => s, Err(SyntheticSyntax) => return "synthetic,,".to_owned(), }; - let root = db.parse_or_expand(src.file_id); + let root = src.file_id.parse_or_expand(db); let node = src.map(|e| e.to_node(&root).syntax().clone()); let original_range = node.as_ref().original_file_range_rooted(db); let path = vfs.file_path(original_range.file_id.file_id(db)); @@ -1538,7 +1538,7 @@ fn expr_syntax_range<'a>( ) -> Option<(&'a VfsPath, LineCol, LineCol)> { let src = sm.expr_syntax(expr_id); if let Ok(src) = src { - let root = db.parse_or_expand(src.file_id); + let root = src.file_id.parse_or_expand(db); let node = src.map(|e| e.to_node(&root).syntax().clone()); let original_range = node.as_ref().original_file_range_rooted(db); let path = vfs.file_path(original_range.file_id.file_id(db)); @@ -1559,7 +1559,7 @@ fn pat_syntax_range<'a>( ) -> Option<(&'a VfsPath, LineCol, LineCol)> { let src = sm.pat_syntax(pat_id); if let Ok(src) = src { - let root = db.parse_or_expand(src.file_id); + let root = src.file_id.parse_or_expand(db); let node = src.map(|e| e.to_node(&root).syntax().clone()); let original_range = node.as_ref().original_file_range_rooted(db); let path = vfs.file_path(original_range.file_id.file_id(db)); From 9566dad034ad555792db41ce6e29b98ef646f1cd Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Tue, 30 Jun 2026 20:24:19 +0000 Subject: [PATCH 06/78] reproduction of miscompilation Signed-off-by: Xiangfei Ding --- .../async-drop/async-drop-array-step.rs | 94 +++++++++++++++++++ .../async-drop-array-step.run.stdout | 4 + 2 files changed, 98 insertions(+) create mode 100644 tests/ui/async-await/async-drop/async-drop-array-step.rs create mode 100644 tests/ui/async-await/async-drop/async-drop-array-step.run.stdout diff --git a/tests/ui/async-await/async-drop/async-drop-array-step.rs b/tests/ui/async-await/async-drop/async-drop-array-step.rs new file mode 100644 index 0000000000000..9bb26ccde6c10 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-array-step.rs @@ -0,0 +1,94 @@ +//@ run-pass +//@ check-run-results +//@ edition: 2021 + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::future::{async_drop_in_place, AsyncDrop, Future}; +use std::mem::ManuallyDrop; +use std::pin::{pin, Pin}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{mpsc, Arc}; +use std::task::{Context, Poll, Wake, Waker}; + +static DROP_COUNT: AtomicUsize = AtomicUsize::new(0); + +struct YieldOnce(bool); + +impl Future for YieldOnce { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + if !self.0 { + self.0 = true; + cx.waker().wake_by_ref(); + Poll::Pending + } else { + Poll::Ready(()) + } + } +} + +struct Item(usize); + +impl Drop for Item { + fn drop(&mut self) {} +} + +impl AsyncDrop for Item { + async fn drop(self: Pin<&mut Self>) { + let count = DROP_COUNT.fetch_add(1, Ordering::Relaxed); + if count >= 8 { + panic!("Infinite loop detected: array drop index reset across yield points!"); + } + YieldOnce(false).await; + println!("Dropping {}", self.0); + } +} + +fn block_on(fut_unpin: F) -> F::Output { + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} + +async fn run() { + let _arr: [Item; 4] = [Item(0), Item(1), Item(2), Item(3)]; +} + +fn main() { + block_on(run()); +} diff --git a/tests/ui/async-await/async-drop/async-drop-array-step.run.stdout b/tests/ui/async-await/async-drop/async-drop-array-step.run.stdout new file mode 100644 index 0000000000000..50423d4dc19d4 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-array-step.run.stdout @@ -0,0 +1,4 @@ +Dropping 0 +Dropping 1 +Dropping 2 +Dropping 3 From f0f0a3dab61ca187e92292ba2946b0abba1bb25d Mon Sep 17 00:00:00 2001 From: Till Adam Date: Fri, 19 Jun 2026 13:27:30 +0200 Subject: [PATCH 07/78] internal: log when cache priming completes on startup Add a simple info log statement when cache priming finishes on startup. Disclosure: Anthropic Claude 3.5 Flash assisted with this change. --- src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index fef0d097314c0..5823eb283e322 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -408,6 +408,9 @@ impl GlobalState { .iter() .for_each(|flycheck| flycheck.restart_workspace(None)); } + if !cancelled { + tracing::info!("workspace loaded and indexed"); + } if let Some((message, fraction, title)) = last_report.take() { self.report_progress( title, From f3402e531e1a22a04299e0d6f6c113626803f438 Mon Sep 17 00:00:00 2001 From: Till Adam Date: Sun, 5 Jul 2026 10:51:52 +0200 Subject: [PATCH 08/78] internal: address cache priming review feedback AI-assisted-by: OpenAI Codex --- .../crates/rust-analyzer/src/main_loop.rs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 5823eb283e322..b4727360e5375 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -397,19 +397,19 @@ impl GlobalState { if cancelled { self.prime_caches_queue .request_op("restart after cancellation".to_owned(), ()); - } else if self.config.check_on_save(None) - && self.config.flycheck_workspace(None) - && !self.fetch_build_data_queue.op_requested() - { - // Priming finished; now run the deferred initial workspace flycheck - // (kept off the critical path so `cargo check` doesn't contend with - // cache priming for CPU). - self.flycheck - .iter() - .for_each(|flycheck| flycheck.restart_workspace(None)); - } - if !cancelled { - tracing::info!("workspace loaded and indexed"); + } else { + if self.config.check_on_save(None) + && self.config.flycheck_workspace(None) + && !self.fetch_build_data_queue.op_requested() + { + // Priming finished; now run the deferred initial workspace flycheck + // (kept off the critical path so `cargo check` doesn't contend with + // cache priming for CPU). + self.flycheck + .iter() + .for_each(|flycheck| flycheck.restart_workspace(None)); + } + tracing::info!("cache priming completed successfully"); } if let Some((message, fraction, title)) = last_report.take() { self.report_progress( From 401dc215a7bd0fef533325446b642fbd88ec2d53 Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Wed, 1 Jul 2026 05:44:55 +0000 Subject: [PATCH 09/78] fix remapping in indexing and self-assignments Storage is still required even if the local is moved out and immediately moved in again. Signed-off-by: Xiangfei Ding --- .../src/impls/storage_liveness.rs | 26 +- .../rustc_mir_transform/src/coroutine/mod.rs | 47 ++- ...drop.array-{closure#0}.ElaborateDrops.diff | 220 +++++++++++ ...drop.array-{closure#0}.StateTransform.diff | 273 ++++++++++++++ ...ray-{closure#0}.coroutine_drop_async.0.mir | 154 ++++++++ ...losure#0}.[AsyncInt;2].StateTransform.diff | 357 ++++++++++++++++++ ...rate_drops-{closure#0}.ElaborateDrops.diff | 24 +- ...rate_drops-{closure#0}.StateTransform.diff | 50 +-- tests/mir-opt/coroutine/async_drop.rs | 9 + .../async-drop/async-drop-initial.rs | 2 +- 10 files changed, 1121 insertions(+), 41 deletions(-) create mode 100644 tests/mir-opt/coroutine/async_drop.array-{closure#0}.ElaborateDrops.diff create mode 100644 tests/mir-opt/coroutine/async_drop.array-{closure#0}.StateTransform.diff create mode 100644 tests/mir-opt/coroutine/async_drop.array-{closure#0}.coroutine_drop_async.0.mir create mode 100644 tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.[AsyncInt;2].StateTransform.diff diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 8b6a92071a7d7..5a4bf1a68dd5d 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -155,7 +155,6 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { match &stmt.kind { StatementKind::StorageDead(l) => state.kill(*l), - // If a place is assigned to in a statement, it needs storage for that statement. StatementKind::Assign((place, _)) => { state.gen_(place.local); } @@ -180,12 +179,35 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { fn apply_primary_statement_effect( &self, state: &mut Self::Domain, - _: &Statement<'tcx>, + stmt: &Statement<'tcx>, loc: Location, ) { // If we move from a place then it only stops needing storage *after* // that statement. self.check_for_move(state, loc); + + match &stmt.kind { + // If a place is assigned to in a statement, it needs storage after that statement. + // Even if the place was moved from in the rvalue (e.g. `x = x + 1` or `x = f(move x)`), + // the assignment restores a valid value into the place. + StatementKind::Assign((place, _)) => { + state.gen_(place.local); + } + StatementKind::SetDiscriminant { place, .. } => { + state.gen_(place.local); + } + + StatementKind::StorageDead(_) + | StatementKind::AscribeUserType(..) + | StatementKind::PlaceMention(..) + | StatementKind::Coverage(..) + | StatementKind::FakeRead(..) + | StatementKind::ConstEvalCounter + | StatementKind::Nop + | StatementKind::Intrinsic(..) + | StatementKind::BackwardIncompatibleDropHint { .. } + | StatementKind::StorageLive(..) => {} + } } fn apply_early_terminator_effect( diff --git a/compiler/rustc_mir_transform/src/coroutine/mod.rs b/compiler/rustc_mir_transform/src/coroutine/mod.rs index 53283680c0966..a64d23b0b939f 100644 --- a/compiler/rustc_mir_transform/src/coroutine/mod.rs +++ b/compiler/rustc_mir_transform/src/coroutine/mod.rs @@ -79,6 +79,7 @@ use rustc_span::def_id::DefId; use tracing::{debug, instrument}; use crate::deref_separator::deref_finder; +use crate::patch::MirPatch; use crate::{abort_unwinding_calls, pass_manager as pm, simplify}; pub(super) struct StateTransform; @@ -199,6 +200,8 @@ struct TransformVisitor<'tcx> { old_yield_ty: Ty<'tcx>, old_ret_ty: Ty<'tcx>, + + patch: Option>, } impl<'tcx> TransformVisitor<'tcx> { @@ -408,11 +411,51 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { } #[tracing::instrument(level = "trace", skip(self), ret)] - fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, _location: Location) { + fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, location: Location) { // Replace an Local in the remap with a coroutine struct access if let Some(&Some((ty, variant_index, idx))) = self.remap.get(place.local) { replace_base(place, self.make_field(variant_index, idx, ty), self.tcx); } + if let Some(new_projection) = self.process_projection(&place.projection, location) { + place.projection = self.tcx.mk_place_elems(&new_projection); + } + } + + fn process_projection_elem( + &mut self, + elem: PlaceElem<'tcx>, + location: Location, + ) -> Option> { + match elem { + PlaceElem::Index(local) => { + if let Some(&Some((ty, variant, idx))) = self.remap.get(local) { + // `PlaceElem::Index` only accepts a `Local`, not an arbitrary `Place`. + // If the local in indexing was saved across a yield point and remapped to a + // coroutine struct field, we cannot inline the struct field access into + // the index projection. + // For example, an local storing the counter to track which element to drop in + // an array is one such case. + // + // Instead, we inject an assignment before this location to restore the + // saved local from the coroutine struct (`local = copy $projection`), + // and leave the `PlaceElem::Index(local)` projection unchanged. + let field = self.make_field(variant, idx, ty); + self.patch.as_mut().unwrap().add_assign( + location, + Place::from(local), + Rvalue::Use(Operand::Copy(field), WithRetag::No), + ); + } + None + } + PlaceElem::Field(..) + | PlaceElem::OpaqueCast(..) + | PlaceElem::UnwrapUnsafeBinder(..) + | PlaceElem::Deref + | PlaceElem::ConstantIndex { .. } + | PlaceElem::Subslice { .. } + | PlaceElem::Downcast(..) => None, + } } #[tracing::instrument(level = "trace", skip(self, stmt), ret)] @@ -1096,6 +1139,7 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { new_ret_local, old_ret_ty, old_yield_ty, + patch: Some(MirPatch::new(body)), }; transform.visit_body(body); @@ -1116,6 +1160,7 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { Some(Statement::new(source_info, assign)) }), ); + transform.patch.take().unwrap().apply(body); // Remove the context argument within generator bodies. if matches!(coroutine_kind, CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) { diff --git a/tests/mir-opt/coroutine/async_drop.array-{closure#0}.ElaborateDrops.diff b/tests/mir-opt/coroutine/async_drop.array-{closure#0}.ElaborateDrops.diff new file mode 100644 index 0000000000000..bad684407ae57 --- /dev/null +++ b/tests/mir-opt/coroutine/async_drop.array-{closure#0}.ElaborateDrops.diff @@ -0,0 +1,220 @@ +- // MIR for `array::{closure#0}` before ElaborateDrops ++ // MIR for `array::{closure#0}` after ElaborateDrops + + fn array::{closure#0}(_1: {async fn body of array()}, _2: std::future::ResumeTy) -> () + yields () + { + debug _task_context => _2; + let mut _0: (); + let _3: [AsyncInt; 2]; + let mut _4: AsyncInt; + let mut _5: AsyncInt; ++ let mut _6: impl std::future::Future; ++ let mut _7: std::future::ResumeTy; ++ let mut _8: std::task::Poll<()>; ++ let mut _9: isize; ++ let mut _10: std::pin::Pin<&mut impl std::future::Future>; ++ let mut _11: &mut std::task::Context<'_>; ++ let mut _12: std::future::ResumeTy; ++ let mut _13: &mut impl std::future::Future; ++ let mut _14: std::future::ResumeTy; ++ let mut _15: std::task::Poll<()>; ++ let mut _16: isize; ++ let mut _17: std::pin::Pin<&mut impl std::future::Future>; ++ let mut _18: &mut std::task::Context<'_>; ++ let mut _19: std::future::ResumeTy; ++ let mut _20: &mut impl std::future::Future; ++ let mut _21: std::pin::Pin<&mut [AsyncInt; 2]>; ++ let mut _22: &mut [AsyncInt; 2]; + scope 1 { + debug array => _3; + } + + bb0: { + StorageLive(_3); + StorageLive(_4); + _4 = AsyncInt(const 1_i32); + StorageLive(_5); + _5 = AsyncInt(const 2_i32); + _3 = [move _4, move _5]; +- drop(_5) -> [return: bb1, unwind: bb9, drop: bb5]; ++ goto -> bb1; + } + + bb1: { + StorageDead(_5); +- drop(_4) -> [return: bb2, unwind: bb10, drop: bb6]; ++ goto -> bb2; + } + + bb2: { + StorageDead(_4); + _0 = const (); +- drop(_3) -> [return: bb3, unwind: bb11, drop: bb7]; ++ goto -> bb34; + } + + bb3: { + StorageDead(_3); +- drop(_1) -> [return: bb4, drop: bb8, unwind continue]; ++ drop(_1) -> [return: bb4, unwind: bb12]; + } + + bb4: { + return; + } + + bb5: { + StorageDead(_5); +- drop(_4) -> [return: bb6, unwind: bb13]; ++ goto -> bb6; + } + + bb6: { + StorageDead(_4); + goto -> bb7; + } + + bb7: { + StorageDead(_3); +- drop(_1) -> [return: bb8, unwind continue]; ++ goto -> bb8; + } + + bb8: { + coroutine_drop; + } + + bb9 (cleanup): { + StorageDead(_5); +- drop(_4) -> [return: bb10, unwind terminate(cleanup)]; ++ goto -> bb10; + } + + bb10 (cleanup): { + StorageDead(_4); + goto -> bb11; + } + + bb11 (cleanup): { + StorageDead(_3); + drop(_1) -> [return: bb12, unwind terminate(cleanup)]; + } + + bb12 (cleanup): { + resume; + } + + bb13 (cleanup): { + StorageDead(_4); + StorageDead(_3); +- drop(_1) -> [return: bb12, unwind terminate(cleanup)]; ++ goto -> bb12; ++ } ++ ++ bb14: { ++ StorageDead(_6); ++ goto -> bb3; ++ } ++ ++ bb15: { ++ StorageDead(_6); ++ goto -> bb7; ++ } ++ ++ bb16 (cleanup): { ++ StorageDead(_6); ++ goto -> bb11; ++ } ++ ++ bb17: { ++ assert(const false, "`async fn` resumed after async drop") -> [success: bb17, unwind: bb16]; ++ } ++ ++ bb18: { ++ _2 = move _7; ++ StorageDead(_7); ++ goto -> bb17; ++ } ++ ++ bb19: { ++ _2 = move _7; ++ StorageDead(_7); ++ goto -> bb25; ++ } ++ ++ bb20: { ++ StorageLive(_7); ++ _7 = yield(const ()) -> [resume: bb18, drop: bb19]; ++ } ++ ++ bb21: { ++ unreachable; ++ } ++ ++ bb22: { ++ _9 = discriminant(_8); ++ switchInt(move _9) -> [0: bb15, 1: bb20, otherwise: bb21]; ++ } ++ ++ bb23: { ++ _8 = as Future>::poll(move _10, move _11) -> [return: bb22, unwind: bb16]; ++ } ++ ++ bb24: { ++ _12 = move _2; ++ _11 = std::future::get_context::<'_, '_>(move _12) -> [return: bb23, unwind: bb16]; ++ } ++ ++ bb25: { ++ _13 = &mut _6; ++ _10 = Pin::<&mut impl Future>::new_unchecked(move _13) -> [return: bb24, unwind: bb16]; ++ } ++ ++ bb26: { ++ _2 = move _14; ++ StorageDead(_14); ++ goto -> bb32; ++ } ++ ++ bb27: { ++ _2 = move _14; ++ StorageDead(_14); ++ goto -> bb25; ++ } ++ ++ bb28: { ++ StorageLive(_14); ++ _14 = yield(const ()) -> [resume: bb26, drop: bb27]; ++ } ++ ++ bb29: { ++ _16 = discriminant(_15); ++ switchInt(move _16) -> [0: bb14, 1: bb28, otherwise: bb21]; ++ } ++ ++ bb30: { ++ _15 = as Future>::poll(move _17, move _18) -> [return: bb29, unwind: bb16]; ++ } ++ ++ bb31: { ++ _19 = move _2; ++ _18 = std::future::get_context::<'_, '_>(move _19) -> [return: bb30, unwind: bb16]; ++ } ++ ++ bb32: { ++ _20 = &mut _6; ++ _17 = Pin::<&mut impl Future>::new_unchecked(move _20) -> [return: bb31, unwind: bb16]; ++ } ++ ++ bb33: { ++ StorageLive(_6); ++ _6 = async_drop_in_place::<[AsyncInt; 2]>(copy (_21.0: &mut [AsyncInt; 2])) -> [return: bb32, unwind: bb16]; ++ } ++ ++ bb34: { ++ _22 = &mut _3; ++ _21 = Pin::<&mut [AsyncInt; 2]>::new_unchecked(move _22) -> [return: bb33, unwind: bb11]; + } + } + diff --git a/tests/mir-opt/coroutine/async_drop.array-{closure#0}.StateTransform.diff b/tests/mir-opt/coroutine/async_drop.array-{closure#0}.StateTransform.diff new file mode 100644 index 0000000000000..5b4e617251396 --- /dev/null +++ b/tests/mir-opt/coroutine/async_drop.array-{closure#0}.StateTransform.diff @@ -0,0 +1,273 @@ +- // MIR for `array::{closure#0}` before StateTransform ++ // MIR for `array::{closure#0}` after StateTransform + +- fn array::{closure#0}(_1: {async fn body of array()}, _2: std::future::ResumeTy) -> () +- yields () +- { +- debug _task_context => _2; +- let mut _0: (); ++ fn array::{closure#0}(_1: Pin<&mut {async fn body of array()}>, _2: &mut Context<'_>) -> Poll<()> { ++ coroutine layout { ++ field _s0: (); ++ field _s1: [AsyncInt; 2]; ++ field _s2: impl Future; ++ variant_fields = { ++ Unresumed(0): [], ++ Returned (1): [], ++ Panicked (2): [], ++ Suspend0 (3): [_s1, _s2], ++ Suspend1 (4): [_s0, _s1, _s2], ++ } ++ storage_conflicts = BitMatrix(3x3) {(_s0, _s0), (_s0, _s1), (_s0, _s2), (_s1, _s0), (_s1, _s1), (_s1, _s2), (_s2, _s0), (_s2, _s1), (_s2, _s2)} ++ } ++ debug _task_context => _26; ++ coroutine debug array => _s1; ++ let mut _0: std::task::Poll<()>; + let _3: [AsyncInt; 2]; + let mut _4: AsyncInt; + let mut _5: AsyncInt; + let mut _6: impl std::future::Future; + let mut _7: std::future::ResumeTy; + let mut _8: std::task::Poll<()>; + let mut _9: isize; + let mut _10: std::pin::Pin<&mut impl std::future::Future>; + let mut _11: &mut std::task::Context<'_>; + let mut _12: std::future::ResumeTy; + let mut _13: &mut impl std::future::Future; + let mut _14: std::future::ResumeTy; + let mut _15: std::task::Poll<()>; + let mut _16: isize; + let mut _17: std::pin::Pin<&mut impl std::future::Future>; + let mut _18: &mut std::task::Context<'_>; + let mut _19: std::future::ResumeTy; + let mut _20: &mut impl std::future::Future; + let mut _21: std::pin::Pin<&mut [AsyncInt; 2]>; + let mut _22: &mut [AsyncInt; 2]; ++ let mut _23: (); ++ let mut _24: u32; ++ let mut _25: &mut {async fn body of array()}; ++ let mut _26: std::future::ResumeTy; ++ let mut _27: std::ptr::NonNull>; + scope 1 { +- debug array => _3; ++ debug array => (((*_25) as variant#4).1: [AsyncInt; 2]); + } + + bb0: { +- StorageLive(_3); +- StorageLive(_4); +- _4 = AsyncInt(const 1_i32); +- StorageLive(_5); +- _5 = AsyncInt(const 2_i32); +- _3 = [move _4, move _5]; +- goto -> bb1; ++ _27 = move _2 as std::ptr::NonNull> (Transmute); ++ _26 = std::future::ResumeTy(move _27); ++ _25 = copy (_1.0: &mut {async fn body of array()}); ++ _24 = discriminant((*_25)); ++ switchInt(move _24) -> [0: bb26, 1: bb25, 2: bb24, 3: bb22, 4: bb23, otherwise: bb11]; + } + + bb1: { + StorageDead(_5); + goto -> bb2; + } + + bb2: { + StorageDead(_4); +- _0 = const (); +- goto -> bb29; ++ (((*_25) as variant#4).0: ()) = const (); ++ goto -> bb19; + } + + bb3: { +- StorageDead(_3); +- drop(_1) -> [return: bb4, unwind: bb8]; ++ nop; ++ goto -> bb20; + } + + bb4: { ++ _0 = Poll::<()>::Ready(move (((*_25) as variant#4).0: ())); ++ discriminant((*_25)) = 1; + return; + } + +- bb5: { +- StorageDead(_3); ++ bb5 (cleanup): { ++ nop; + goto -> bb6; + } + +- bb6: { +- coroutine_drop; ++ bb6 (cleanup): { ++ goto -> bb21; + } + +- bb7 (cleanup): { +- StorageDead(_3); +- drop(_1) -> [return: bb8, unwind terminate(cleanup)]; ++ bb7: { ++ nop; ++ goto -> bb3; + } + + bb8 (cleanup): { +- resume; ++ nop; ++ goto -> bb5; + } + + bb9: { +- StorageDead(_6); +- goto -> bb3; ++ assert(const false, "`async fn` resumed after async drop") -> [success: bb9, unwind: bb8]; + } + + bb10: { +- StorageDead(_6); +- goto -> bb5; ++ _26 = move _7; ++ StorageDead(_7); ++ goto -> bb9; + } + +- bb11 (cleanup): { +- StorageDead(_6); +- goto -> bb7; ++ bb11: { ++ unreachable; + } + + bb12: { +- assert(const false, "`async fn` resumed after async drop") -> [success: bb12, unwind: bb11]; ++ _26 = move _14; ++ StorageDead(_14); ++ goto -> bb17; + } + + bb13: { +- _2 = move _7; +- StorageDead(_7); +- goto -> bb12; ++ StorageLive(_14); ++ _0 = Poll::<()>::Pending; ++ StorageDead(_14); ++ discriminant((*_25)) = 4; ++ return; + } + + bb14: { +- _2 = move _7; +- StorageDead(_7); +- goto -> bb20; ++ _16 = discriminant(_15); ++ switchInt(move _16) -> [0: bb7, 1: bb13, otherwise: bb11]; + } + + bb15: { +- StorageLive(_7); +- _7 = yield(const ()) -> [resume: bb13, drop: bb14]; ++ _15 = as Future>::poll(move _17, move _18) -> [return: bb14, unwind: bb8]; + } + + bb16: { +- unreachable; ++ _19 = move _26; ++ _18 = copy (_19.0: std::ptr::NonNull>) as &mut std::task::Context<'_> (Transmute); ++ goto -> bb15; + } + + bb17: { +- _9 = discriminant(_8); +- switchInt(move _9) -> [0: bb10, 1: bb15, otherwise: bb16]; ++ _20 = &mut (((*_25) as variant#4).2: impl std::future::Future); ++ _17 = Pin::<&mut impl Future>::new_unchecked(move _20) -> [return: bb16, unwind: bb8]; + } + + bb18: { +- _8 = as Future>::poll(move _10, move _11) -> [return: bb17, unwind: bb11]; ++ nop; ++ (((*_25) as variant#4).2: impl std::future::Future) = async_drop_in_place::<[AsyncInt; 2]>(copy (_21.0: &mut [AsyncInt; 2])) -> [return: bb17, unwind: bb8]; + } + + bb19: { +- _12 = move _2; +- _11 = std::future::get_context::<'_, '_>(move _12) -> [return: bb18, unwind: bb11]; ++ _22 = &mut (((*_25) as variant#4).1: [AsyncInt; 2]); ++ _21 = Pin::<&mut [AsyncInt; 2]>::new_unchecked(move _22) -> [return: bb18, unwind: bb5]; + } + + bb20: { +- _13 = &mut _6; +- _10 = Pin::<&mut impl Future>::new_unchecked(move _13) -> [return: bb19, unwind: bb11]; ++ goto -> bb4; + } + +- bb21: { +- _2 = move _14; +- StorageDead(_14); +- goto -> bb27; ++ bb21 (cleanup): { ++ discriminant((*_25)) = 2; ++ resume; + } + + bb22: { +- _2 = move _14; +- StorageDead(_14); +- goto -> bb20; ++ StorageLive(_7); ++ _7 = move _26; ++ goto -> bb10; + } + + bb23: { + StorageLive(_14); +- _14 = yield(const ()) -> [resume: bb21, drop: bb22]; ++ _14 = move _26; ++ goto -> bb12; + } + + bb24: { +- _16 = discriminant(_15); +- switchInt(move _16) -> [0: bb9, 1: bb23, otherwise: bb16]; ++ assert(const false, "`async fn` resumed after panicking") -> [success: bb24, unwind continue]; + } + + bb25: { +- _15 = as Future>::poll(move _17, move _18) -> [return: bb24, unwind: bb11]; ++ assert(const false, "`async fn` resumed after completion") -> [success: bb25, unwind continue]; + } + + bb26: { +- _19 = move _2; +- _18 = std::future::get_context::<'_, '_>(move _19) -> [return: bb25, unwind: bb11]; +- } +- +- bb27: { +- _20 = &mut _6; +- _17 = Pin::<&mut impl Future>::new_unchecked(move _20) -> [return: bb26, unwind: bb11]; +- } +- +- bb28: { +- StorageLive(_6); +- _6 = async_drop_in_place::<[AsyncInt; 2]>(copy (_21.0: &mut [AsyncInt; 2])) -> [return: bb27, unwind: bb11]; +- } +- +- bb29: { +- _22 = &mut _3; +- _21 = Pin::<&mut [AsyncInt; 2]>::new_unchecked(move _22) -> [return: bb28, unwind: bb7]; ++ nop; ++ StorageLive(_4); ++ _4 = AsyncInt(const 1_i32); ++ StorageLive(_5); ++ _5 = AsyncInt(const 2_i32); ++ (((*_25) as variant#4).1: [AsyncInt; 2]) = [move _4, move _5]; ++ goto -> bb1; + } + } + diff --git a/tests/mir-opt/coroutine/async_drop.array-{closure#0}.coroutine_drop_async.0.mir b/tests/mir-opt/coroutine/async_drop.array-{closure#0}.coroutine_drop_async.0.mir new file mode 100644 index 0000000000000..9fc1ebbe39d14 --- /dev/null +++ b/tests/mir-opt/coroutine/async_drop.array-{closure#0}.coroutine_drop_async.0.mir @@ -0,0 +1,154 @@ +// MIR for `array::{closure#0}` 0 coroutine_drop_async + +fn array::{closure#0}(_1: Pin<&mut {async fn body of array()}>, _2: &mut Context<'_>) -> Poll<()> { + debug _task_context => _26; + let mut _0: std::task::Poll<()>; + let _3: [AsyncInt; 2]; + let mut _4: AsyncInt; + let mut _5: AsyncInt; + let mut _6: impl std::future::Future; + let mut _7: std::future::ResumeTy; + let mut _8: std::task::Poll<()>; + let mut _9: isize; + let mut _10: std::pin::Pin<&mut impl std::future::Future>; + let mut _11: &mut std::task::Context<'_>; + let mut _12: std::future::ResumeTy; + let mut _13: &mut impl std::future::Future; + let mut _14: std::future::ResumeTy; + let mut _15: std::task::Poll<()>; + let mut _16: isize; + let mut _17: std::pin::Pin<&mut impl std::future::Future>; + let mut _18: &mut std::task::Context<'_>; + let mut _19: std::future::ResumeTy; + let mut _20: &mut impl std::future::Future; + let mut _21: std::pin::Pin<&mut [AsyncInt; 2]>; + let mut _22: &mut [AsyncInt; 2]; + let mut _23: (); + let mut _24: u32; + let mut _25: &mut {async fn body of array()}; + let mut _26: std::future::ResumeTy; + let mut _27: std::ptr::NonNull>; + scope 1 { + debug array => (((*_25) as variant#4).1: [AsyncInt; 2]); + } + + bb0: { + _27 = move _2 as std::ptr::NonNull> (Transmute); + _26 = std::future::ResumeTy(move _27); + _25 = copy (_1.0: &mut {async fn body of array()}); + _24 = discriminant((*_25)); + switchInt(move _24) -> [0: bb16, 2: bb21, 3: bb19, 4: bb20, otherwise: bb22]; + } + + bb1: { + nop; + goto -> bb2; + } + + bb2: { + _0 = Poll::<()>::Ready(const ()); + return; + } + + bb3 (cleanup): { + nop; + goto -> bb4; + } + + bb4 (cleanup): { + goto -> bb18; + } + + bb5: { + nop; + goto -> bb1; + } + + bb6 (cleanup): { + nop; + goto -> bb3; + } + + bb7: { + _26 = move _7; + StorageDead(_7); + goto -> bb13; + } + + bb8: { + StorageLive(_7); + _0 = Poll::<()>::Pending; + StorageDead(_7); + discriminant((*_25)) = 3; + return; + } + + bb9: { + unreachable; + } + + bb10: { + _9 = discriminant(_8); + switchInt(move _9) -> [0: bb5, 1: bb8, otherwise: bb9]; + } + + bb11: { + _8 = as Future>::poll(move _10, move _11) -> [return: bb10, unwind: bb6]; + } + + bb12: { + _12 = move _26; + _11 = copy (_12.0: std::ptr::NonNull>) as &mut std::task::Context<'_> (Transmute); + goto -> bb11; + } + + bb13: { + _13 = &mut (((*_25) as variant#4).2: impl std::future::Future); + _10 = Pin::<&mut impl Future>::new_unchecked(move _13) -> [return: bb12, unwind: bb6]; + } + + bb14: { + _26 = move _14; + StorageDead(_14); + goto -> bb13; + } + + bb15: { + _0 = Poll::<()>::Ready(const ()); + return; + } + + bb16: { + goto -> bb17; + } + + bb17: { + goto -> bb15; + } + + bb18 (cleanup): { + discriminant((*_25)) = 2; + resume; + } + + bb19: { + StorageLive(_7); + _7 = move _26; + goto -> bb7; + } + + bb20: { + StorageLive(_14); + _14 = move _26; + goto -> bb14; + } + + bb21: { + assert(const false, "`async fn` resumed after panicking") -> [success: bb21, unwind continue]; + } + + bb22: { + _0 = Poll::<()>::Ready(const ()); + return; + } +} diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.[AsyncInt;2].StateTransform.diff b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.[AsyncInt;2].StateTransform.diff new file mode 100644 index 0000000000000..7718554895afd --- /dev/null +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.[AsyncInt;2].StateTransform.diff @@ -0,0 +1,357 @@ +- // MIR for `std::future::async_drop_in_place::{closure#0}` before StateTransform ++ // MIR for `std::future::async_drop_in_place::{closure#0}` after StateTransform + +- fn async_drop_in_place::{closure#0}(_1: {async fn body of async_drop_in_place<[AsyncInt; 2]>()}, _2: std::future::ResumeTy) -> () +- yields () +- { +- let mut _0: (); ++ fn async_drop_in_place::{closure#0}(_1: Pin<&mut {async fn body of async_drop_in_place<[AsyncInt; 2]>()}>, _2: &mut Context<'_>) -> Poll<()> { ++ coroutine layout { ++ field _s0: *mut [AsyncInt]; ++ field _s1: usize; ++ field _s2: usize; ++ field _s3: impl Future; ++ field _s4: impl Future; ++ variant_fields = { ++ Unresumed(0): [], ++ Returned (1): [], ++ Panicked (2): [], ++ Suspend0 (3): [_s0, _s1, _s2, _s3], ++ Suspend1 (4): [_s0, _s1, _s2, _s4], ++ Suspend2 (5): [_s0, _s1, _s2, _s4], ++ } ++ storage_conflicts = BitMatrix(5x5) {(_s0, _s0), (_s0, _s1), (_s0, _s2), (_s0, _s3), (_s0, _s4), (_s1, _s0), (_s1, _s1), (_s1, _s2), (_s1, _s3), (_s1, _s4), (_s2, _s0), (_s2, _s1), (_s2, _s2), (_s2, _s3), (_s2, _s4), (_s3, _s0), (_s3, _s1), (_s3, _s2), (_s3, _s3), (_s4, _s0), (_s4, _s1), (_s4, _s2), (_s4, _s4)} ++ } ++ let mut _0: std::task::Poll<()>; + let mut _3: &mut [AsyncInt; 2]; + let mut _4: *mut [AsyncInt; 2]; + let mut _5: *mut [AsyncInt]; + let mut _6: usize; + let mut _7: usize; + let mut _8: *mut AsyncInt; + let mut _9: bool; + let mut _10: *mut AsyncInt; + let mut _11: bool; + let mut _12: impl std::future::Future; + let mut _13: std::future::ResumeTy; + let mut _14: std::task::Poll<()>; + let mut _15: isize; + let mut _16: std::pin::Pin<&mut impl std::future::Future>; + let mut _17: &mut std::task::Context<'_>; + let mut _18: std::future::ResumeTy; + let mut _19: &mut impl std::future::Future; + let mut _20: std::pin::Pin<&mut AsyncInt>; + let mut _21: &mut AsyncInt; + let mut _22: *mut AsyncInt; + let mut _23: bool; + let mut _24: impl std::future::Future; + let mut _25: std::future::ResumeTy; + let mut _26: std::task::Poll<()>; + let mut _27: isize; + let mut _28: std::pin::Pin<&mut impl std::future::Future>; + let mut _29: &mut std::task::Context<'_>; + let mut _30: std::future::ResumeTy; + let mut _31: &mut impl std::future::Future; + let mut _32: std::future::ResumeTy; + let mut _33: std::task::Poll<()>; + let mut _34: isize; + let mut _35: std::pin::Pin<&mut impl std::future::Future>; + let mut _36: &mut std::task::Context<'_>; + let mut _37: std::future::ResumeTy; + let mut _38: &mut impl std::future::Future; + let mut _39: std::pin::Pin<&mut AsyncInt>; + let mut _40: &mut AsyncInt; ++ let mut _41: (); ++ let mut _42: u32; ++ let mut _43: &mut {async fn body of std::future::async_drop_in_place<[AsyncInt; 2]>()}; ++ let mut _44: *mut [AsyncInt]; ++ let mut _45: *mut [AsyncInt]; ++ let _46: std::future::ResumeTy; ++ let mut _47: std::ptr::NonNull>; + + bb0: { +- _3 = move (_1.0: &mut [AsyncInt; 2]); +- _4 = &raw mut (*_3); +- _5 = move _4 as *mut [AsyncInt] (PointerCoercion(Unsize, Implicit)); +- _6 = PtrMetadata(copy _5); +- _7 = const 0_usize; +- goto -> bb20; ++ _47 = move _2 as std::ptr::NonNull> (Transmute); ++ _46 = std::future::ResumeTy(move _47); ++ _43 = copy (_1.0: &mut {async fn body of std::future::async_drop_in_place<[AsyncInt; 2]>()}); ++ _42 = discriminant((*_43)); ++ switchInt(move _42) -> [0: bb28, 1: bb27, 2: bb26, 3: bb23, 4: bb24, 5: bb25, otherwise: bb8]; + } + + bb1: { ++ _0 = Poll::<()>::Ready(move _41); ++ discriminant((*_43)) = 1; + return; + } + + bb2 (cleanup): { +- resume; ++ goto -> bb22; + } + + bb3 (cleanup): { +- _8 = &raw mut (*_5)[_7]; +- _7 = Add(move _7, const 1_usize); ++ _7 = no_retag copy (((*_43) as variant#5).2: usize); ++ _44 = no_retag copy (((*_43) as variant#5).0: *mut [AsyncInt]); ++ _8 = &raw mut (*_44)[_7]; ++ (((*_43) as variant#5).2: usize) = Add(move (((*_43) as variant#5).2: usize), const 1_usize); + drop((*_8)) -> [return: bb4, unwind terminate(cleanup)]; + } + + bb4 (cleanup): { +- _9 = Eq(copy _7, copy _6); ++ _9 = Eq(copy (((*_43) as variant#5).2: usize), copy (((*_43) as variant#5).1: usize)); + switchInt(move _9) -> [0: bb3, otherwise: bb2]; + } + +- bb5: { +- _10 = &raw mut (*_5)[_7]; +- _7 = Add(move _7, const 1_usize); +- _21 = &mut (*_10); +- _20 = Pin::<&mut AsyncInt>::new_unchecked(move _21) -> [return: bb18, unwind: bb4]; ++ bb5 (cleanup): { ++ nop; ++ goto -> bb4; + } + + bb6: { +- _11 = Eq(copy _7, copy _6); +- switchInt(move _11) -> [0: bb5, otherwise: bb1]; ++ assert(const false, "`async fn` resumed after async drop") -> [success: bb6, unwind: bb5]; + } + + bb7: { +- StorageDead(_12); ++ _46 = move _13; ++ StorageDead(_13); + goto -> bb6; + } + +- bb8 (cleanup): { +- StorageDead(_12); +- goto -> bb4; ++ bb8: { ++ unreachable; + } + + bb9: { +- assert(const false, "`async fn` resumed after async drop") -> [success: bb9, unwind: bb8]; ++ _7 = no_retag copy (((*_43) as variant#5).2: usize); ++ _45 = no_retag copy (((*_43) as variant#5).0: *mut [AsyncInt]); ++ _22 = &raw mut (*_45)[_7]; ++ (((*_43) as variant#5).2: usize) = Add(move (((*_43) as variant#5).2: usize), const 1_usize); ++ _40 = &mut (*_22); ++ _39 = Pin::<&mut AsyncInt>::new_unchecked(move _40) -> [return: bb21, unwind: bb4]; + } + + bb10: { +- _2 = move _13; +- StorageDead(_13); +- goto -> bb9; ++ _23 = Eq(copy (((*_43) as variant#5).2: usize), copy (((*_43) as variant#5).1: usize)); ++ switchInt(move _23) -> [0: bb9, otherwise: bb1]; + } + + bb11: { +- _2 = move _13; +- StorageDead(_13); +- goto -> bb17; ++ nop; ++ goto -> bb10; + } + +- bb12: { +- StorageLive(_13); +- _13 = yield(const ()) -> [resume: bb10, drop: bb11]; ++ bb12 (cleanup): { ++ nop; ++ goto -> bb4; + } + + bb13: { +- unreachable; ++ assert(const false, "`async fn` resumed after async drop") -> [success: bb13, unwind: bb12]; + } + + bb14: { +- _15 = discriminant(_14); +- switchInt(move _15) -> [0: bb7, 1: bb12, otherwise: bb13]; ++ _46 = move _25; ++ StorageDead(_25); ++ goto -> bb13; + } + + bb15: { +- _14 = as Future>::poll(move _16, move _17) -> [return: bb14, unwind: bb8]; ++ _46 = move _32; ++ StorageDead(_32); ++ goto -> bb20; + } + + bb16: { +- _18 = move _2; +- _17 = std::future::get_context::<'_, '_>(move _18) -> [return: bb15, unwind: bb8]; ++ StorageLive(_32); ++ _0 = Poll::<()>::Pending; ++ StorageDead(_32); ++ discriminant((*_43)) = 5; ++ return; + } + + bb17: { +- _19 = &mut _12; +- _16 = Pin::<&mut impl Future>::new_unchecked(move _19) -> [return: bb16, unwind: bb8]; ++ _34 = discriminant(_33); ++ switchInt(move _34) -> [0: bb11, 1: bb16, otherwise: bb8]; + } + + bb18: { +- StorageLive(_12); +- _12 = async_drop_in_place::(copy (_20.0: &mut AsyncInt)) -> [return: bb17, unwind: bb8]; ++ _33 = as Future>::poll(move _35, move _36) -> [return: bb17, unwind: bb12]; + } + + bb19: { +- _22 = &raw mut (*_5)[_7]; +- _7 = Add(move _7, const 1_usize); +- _40 = &mut (*_22); +- _39 = Pin::<&mut AsyncInt>::new_unchecked(move _40) -> [return: bb39, unwind: bb4]; ++ _37 = move _46; ++ _36 = copy (_37.0: std::ptr::NonNull>) as &mut std::task::Context<'_> (Transmute); ++ goto -> bb18; + } + + bb20: { +- _23 = Eq(copy _7, copy _6); +- switchInt(move _23) -> [0: bb19, otherwise: bb1]; ++ _38 = &mut (((*_43) as variant#5).3: impl std::future::Future); ++ _35 = Pin::<&mut impl Future>::new_unchecked(move _38) -> [return: bb19, unwind: bb12]; + } + + bb21: { +- StorageDead(_24); +- goto -> bb20; ++ nop; ++ (((*_43) as variant#5).3: impl std::future::Future) = async_drop_in_place::(copy (_39.0: &mut AsyncInt)) -> [return: bb20, unwind: bb12]; + } + +- bb22: { +- StorageDead(_24); +- goto -> bb6; ++ bb22 (cleanup): { ++ discriminant((*_43)) = 2; ++ resume; + } + +- bb23 (cleanup): { +- StorageDead(_24); +- goto -> bb4; ++ bb23: { ++ StorageLive(_13); ++ _13 = move _46; ++ goto -> bb7; + } + + bb24: { +- assert(const false, "`async fn` resumed after async drop") -> [success: bb24, unwind: bb23]; ++ StorageLive(_25); ++ _25 = move _46; ++ goto -> bb14; + } + + bb25: { +- _2 = move _25; +- StorageDead(_25); +- goto -> bb24; ++ StorageLive(_32); ++ _32 = move _46; ++ goto -> bb15; + } + + bb26: { +- _2 = move _25; +- StorageDead(_25); +- goto -> bb31; ++ assert(const false, "`async fn` resumed after panicking") -> [success: bb26, unwind continue]; + } + + bb27: { +- StorageLive(_25); +- _25 = yield(const ()) -> [resume: bb25, drop: bb26]; ++ _0 = Poll::<()>::Ready(const ()); ++ return; + } + + bb28: { +- _27 = discriminant(_26); +- switchInt(move _27) -> [0: bb22, 1: bb27, otherwise: bb13]; +- } +- +- bb29: { +- _26 = as Future>::poll(move _28, move _29) -> [return: bb28, unwind: bb23]; +- } +- +- bb30: { +- _30 = move _2; +- _29 = std::future::get_context::<'_, '_>(move _30) -> [return: bb29, unwind: bb23]; +- } +- +- bb31: { +- _31 = &mut _24; +- _28 = Pin::<&mut impl Future>::new_unchecked(move _31) -> [return: bb30, unwind: bb23]; +- } +- +- bb32: { +- _2 = move _32; +- StorageDead(_32); +- goto -> bb38; +- } +- +- bb33: { +- _2 = move _32; +- StorageDead(_32); +- goto -> bb31; +- } +- +- bb34: { +- StorageLive(_32); +- _32 = yield(const ()) -> [resume: bb32, drop: bb33]; +- } +- +- bb35: { +- _34 = discriminant(_33); +- switchInt(move _34) -> [0: bb21, 1: bb34, otherwise: bb13]; +- } +- +- bb36: { +- _33 = as Future>::poll(move _35, move _36) -> [return: bb35, unwind: bb23]; +- } +- +- bb37: { +- _37 = move _2; +- _36 = std::future::get_context::<'_, '_>(move _37) -> [return: bb36, unwind: bb23]; +- } +- +- bb38: { +- _38 = &mut _24; +- _35 = Pin::<&mut impl Future>::new_unchecked(move _38) -> [return: bb37, unwind: bb23]; +- } +- +- bb39: { +- StorageLive(_24); +- _24 = async_drop_in_place::(copy (_39.0: &mut AsyncInt)) -> [return: bb38, unwind: bb23]; ++ _3 = move ((*_43).0: &mut [AsyncInt; 2]); ++ _4 = &raw mut (*_3); ++ (((*_43) as variant#5).0: *mut [AsyncInt]) = move _4 as *mut [AsyncInt] (PointerCoercion(Unsize, Implicit)); ++ (((*_43) as variant#5).1: usize) = PtrMetadata(copy (((*_43) as variant#5).0: *mut [AsyncInt])); ++ (((*_43) as variant#5).2: usize) = const 0_usize; ++ goto -> bb10; + } + } + diff --git a/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.ElaborateDrops.diff b/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.ElaborateDrops.diff index dd65409f0db13..4e0cbbfc4357c 100644 --- a/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.ElaborateDrops.diff +++ b/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.ElaborateDrops.diff @@ -33,8 +33,8 @@ + let mut _39: &mut std::task::Context<'_>; + let mut _40: std::future::ResumeTy; + let mut _41: &mut impl std::future::Future; -+ let mut _42: std::pin::Pin<&mut {async closure@$DIR/async_drop.rs:78:27: 78:35}>; -+ let mut _43: &mut {async closure@$DIR/async_drop.rs:78:27: 78:35}; ++ let mut _42: std::pin::Pin<&mut {async closure@$DIR/async_drop.rs:86:27: 86:35}>; ++ let mut _43: &mut {async closure@$DIR/async_drop.rs:86:27: 86:35}; + let mut _44: impl std::future::Future; + let mut _45: std::future::ResumeTy; + let mut _46: std::task::Poll<()>; @@ -50,8 +50,8 @@ + let mut _56: &mut std::task::Context<'_>; + let mut _57: std::future::ResumeTy; + let mut _58: &mut impl std::future::Future; -+ let mut _59: std::pin::Pin<&mut {closure@$DIR/async_drop.rs:70:25: 70:27}>; -+ let mut _60: &mut {closure@$DIR/async_drop.rs:70:25: 70:27}; ++ let mut _59: std::pin::Pin<&mut {closure@$DIR/async_drop.rs:78:25: 78:27}>; ++ let mut _60: &mut {closure@$DIR/async_drop.rs:78:25: 78:27}; + let mut _61: impl std::future::Future; + let mut _62: std::future::ResumeTy; + let mut _63: std::task::Poll<()>; @@ -200,13 +200,13 @@ let _23: AsyncInt; scope 10 { debug foo => _23; - let _24: {closure@$DIR/async_drop.rs:70:25: 70:27}; + let _24: {closure@$DIR/async_drop.rs:78:25: 78:27}; scope 11 { debug async_closure => _24; let _25: AsyncInt; scope 12 { debug foo => _25; - let _26: {async closure@$DIR/async_drop.rs:78:27: 78:35}; + let _26: {async closure@$DIR/async_drop.rs:86:27: 86:35}; scope 13 { debug async_coroutine => _26; } @@ -321,11 +321,11 @@ StorageLive(_23); _23 = AsyncInt(const 14_i32); StorageLive(_24); - _24 = {closure@$DIR/async_drop.rs:70:25: 70:27} { foo: move _23 }; + _24 = {closure@$DIR/async_drop.rs:78:25: 78:27} { foo: move _23 }; StorageLive(_25); _25 = AsyncInt(const 15_i32); StorageLive(_26); - _26 = {closure@$DIR/async_drop.rs:78:27: 78:35} { foo: move _25 }; + _26 = {closure@$DIR/async_drop.rs:86:27: 86:35} { foo: move _25 }; _0 = const (); - drop(_26) -> [return: bb10, unwind: bb44, drop: bb23]; + goto -> bb103; @@ -838,12 +838,12 @@ + + bb102: { + StorageLive(_27); -+ _27 = async_drop_in_place::<{async closure@$DIR/async_drop.rs:78:27: 78:35}>(copy (_42.0: &mut {async closure@$DIR/async_drop.rs:78:27: 78:35})) -> [return: bb101, unwind: bb85]; ++ _27 = async_drop_in_place::<{async closure@$DIR/async_drop.rs:86:27: 86:35}>(copy (_42.0: &mut {async closure@$DIR/async_drop.rs:86:27: 86:35})) -> [return: bb101, unwind: bb85]; + } + + bb103: { + _43 = &mut _26; -+ _42 = Pin::<&mut {async closure@$DIR/async_drop.rs:78:27: 78:35}>::new_unchecked(move _43) -> [return: bb102, unwind: bb44]; ++ _42 = Pin::<&mut {async closure@$DIR/async_drop.rs:86:27: 86:35}>::new_unchecked(move _43) -> [return: bb102, unwind: bb44]; + } + + bb104: { @@ -939,12 +939,12 @@ + + bb122: { + StorageLive(_44); -+ _44 = async_drop_in_place::<{closure@$DIR/async_drop.rs:70:25: 70:27}>(copy (_59.0: &mut {closure@$DIR/async_drop.rs:70:25: 70:27})) -> [return: bb121, unwind: bb106]; ++ _44 = async_drop_in_place::<{closure@$DIR/async_drop.rs:78:25: 78:27}>(copy (_59.0: &mut {closure@$DIR/async_drop.rs:78:25: 78:27})) -> [return: bb121, unwind: bb106]; + } + + bb123: { + _60 = &mut _24; -+ _59 = Pin::<&mut {closure@$DIR/async_drop.rs:70:25: 70:27}>::new_unchecked(move _60) -> [return: bb122, unwind: bb46]; ++ _59 = Pin::<&mut {closure@$DIR/async_drop.rs:78:25: 78:27}>::new_unchecked(move _60) -> [return: bb122, unwind: bb46]; + } + + bb124: { diff --git a/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.StateTransform.diff b/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.StateTransform.diff index 5fb3dba08ad87..34ea0eeaa07a2 100644 --- a/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.StateTransform.diff +++ b/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.StateTransform.diff @@ -17,8 +17,8 @@ + field _s6: AsyncEnum; + field _s7: AsyncInt; + field _s8: AsyncReference<'_>; -+ field _s9: {closure@$DIR/async_drop.rs:70:25: 70:27}; -+ field _s10: {async closure@$DIR/async_drop.rs:78:27: 78:35}; ++ field _s9: {closure@$DIR/async_drop.rs:78:25: 78:27}; ++ field _s10: {async closure@$DIR/async_drop.rs:86:27: 86:35}; + field _s11: impl Future; + field _s12: impl Future; + field _s13: impl Future; @@ -83,8 +83,8 @@ let mut _39: &mut std::task::Context<'_>; let mut _40: std::future::ResumeTy; let mut _41: &mut impl std::future::Future; - let mut _42: std::pin::Pin<&mut {async closure@$DIR/async_drop.rs:78:27: 78:35}>; - let mut _43: &mut {async closure@$DIR/async_drop.rs:78:27: 78:35}; + let mut _42: std::pin::Pin<&mut {async closure@$DIR/async_drop.rs:86:27: 86:35}>; + let mut _43: &mut {async closure@$DIR/async_drop.rs:86:27: 86:35}; let mut _44: impl std::future::Future; let mut _45: std::future::ResumeTy; let mut _46: std::task::Poll<()>; @@ -100,8 +100,8 @@ let mut _56: &mut std::task::Context<'_>; let mut _57: std::future::ResumeTy; let mut _58: &mut impl std::future::Future; - let mut _59: std::pin::Pin<&mut {closure@$DIR/async_drop.rs:70:25: 70:27}>; - let mut _60: &mut {closure@$DIR/async_drop.rs:70:25: 70:27}; + let mut _59: std::pin::Pin<&mut {closure@$DIR/async_drop.rs:78:25: 78:27}>; + let mut _60: &mut {closure@$DIR/async_drop.rs:78:25: 78:27}; let mut _61: impl std::future::Future; let mut _62: std::future::ResumeTy; let mut _63: std::task::Poll<()>; @@ -271,18 +271,18 @@ scope 10 { debug foo => _23; + coroutine debug async_closure => _s9; - let _24: {closure@$DIR/async_drop.rs:70:25: 70:27}; + let _24: {closure@$DIR/async_drop.rs:78:25: 78:27}; scope 11 { - debug async_closure => _24; -+ debug async_closure => (((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:70:25: 70:27}); ++ debug async_closure => (((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:78:25: 78:27}); let _25: AsyncInt; scope 12 { debug foo => _25; + coroutine debug async_coroutine => _s10; - let _26: {async closure@$DIR/async_drop.rs:78:27: 78:35}; + let _26: {async closure@$DIR/async_drop.rs:86:27: 86:35}; scope 13 { - debug async_coroutine => _26; -+ debug async_coroutine => (((*_182) as variant#4).10: {async closure@$DIR/async_drop.rs:78:27: 78:35}); ++ debug async_coroutine => (((*_182) as variant#4).10: {async closure@$DIR/async_drop.rs:86:27: 86:35}); } } } @@ -404,17 +404,17 @@ StorageLive(_23); _23 = AsyncInt(const 14_i32); - StorageLive(_24); -- _24 = {closure@$DIR/async_drop.rs:70:25: 70:27} { foo: move _23 }; +- _24 = {closure@$DIR/async_drop.rs:78:25: 78:27} { foo: move _23 }; + nop; -+ (((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:70:25: 70:27}) = {closure@$DIR/async_drop.rs:70:25: 70:27} { foo: move _23 }; ++ (((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:78:25: 78:27}) = {closure@$DIR/async_drop.rs:78:25: 78:27} { foo: move _23 }; StorageLive(_25); _25 = AsyncInt(const 15_i32); - StorageLive(_26); -- _26 = {closure@$DIR/async_drop.rs:78:27: 78:35} { foo: move _25 }; +- _26 = {closure@$DIR/async_drop.rs:86:27: 86:35} { foo: move _25 }; - _0 = const (); - goto -> bb72; + nop; -+ (((*_182) as variant#4).10: {async closure@$DIR/async_drop.rs:78:27: 78:35}) = {closure@$DIR/async_drop.rs:78:27: 78:35} { foo: move _25 }; ++ (((*_182) as variant#4).10: {async closure@$DIR/async_drop.rs:86:27: 86:35}) = {closure@$DIR/async_drop.rs:86:27: 86:35} { foo: move _25 }; + (((*_182) as variant#20).0: ()) = const (); + goto -> bb51; } @@ -517,7 +517,7 @@ + bb24 (cleanup): { StorageDead(_25); - goto -> bb25; -+ drop((((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:70:25: 70:27})) -> [return: bb25, unwind terminate(cleanup)]; ++ drop((((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:78:25: 78:27})) -> [return: bb25, unwind terminate(cleanup)]; } - bb25: { @@ -720,14 +720,14 @@ - drop(_1) -> [return: bb51, unwind terminate(cleanup)]; + bb50: { + nop; -+ (((*_182) as variant#4).11: impl std::future::Future) = async_drop_in_place::<{async closure@$DIR/async_drop.rs:78:27: 78:35}>(copy (_42.0: &mut {async closure@$DIR/async_drop.rs:78:27: 78:35})) -> [return: bb49, unwind: bb40]; ++ (((*_182) as variant#4).11: impl std::future::Future) = async_drop_in_place::<{async closure@$DIR/async_drop.rs:86:27: 86:35}>(copy (_42.0: &mut {async closure@$DIR/async_drop.rs:86:27: 86:35})) -> [return: bb49, unwind: bb40]; } - bb51 (cleanup): { - resume; + bb51: { -+ _43 = &mut (((*_182) as variant#4).10: {async closure@$DIR/async_drop.rs:78:27: 78:35}); -+ _42 = Pin::<&mut {async closure@$DIR/async_drop.rs:78:27: 78:35}>::new_unchecked(move _43) -> [return: bb50, unwind: bb23]; ++ _43 = &mut (((*_182) as variant#4).10: {async closure@$DIR/async_drop.rs:86:27: 86:35}); ++ _42 = Pin::<&mut {async closure@$DIR/async_drop.rs:86:27: 86:35}>::new_unchecked(move _43) -> [return: bb50, unwind: bb23]; } bb52: { @@ -811,14 +811,14 @@ - _33 = move _2; - _32 = std::future::get_context::<'_, '_>(move _33) -> [return: bb61, unwind: bb54]; + nop; -+ (((*_182) as variant#6).10: impl std::future::Future) = async_drop_in_place::<{closure@$DIR/async_drop.rs:70:25: 70:27}>(copy (_59.0: &mut {closure@$DIR/async_drop.rs:70:25: 70:27})) -> [return: bb61, unwind: bb53]; ++ (((*_182) as variant#6).10: impl std::future::Future) = async_drop_in_place::<{closure@$DIR/async_drop.rs:78:25: 78:27}>(copy (_59.0: &mut {closure@$DIR/async_drop.rs:78:25: 78:27})) -> [return: bb61, unwind: bb53]; } bb63: { - _34 = &mut _27; - _31 = Pin::<&mut impl Future>::new_unchecked(move _34) -> [return: bb62, unwind: bb54]; -+ _60 = &mut (((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:70:25: 70:27}); -+ _59 = Pin::<&mut {closure@$DIR/async_drop.rs:70:25: 70:27}>::new_unchecked(move _60) -> [return: bb62, unwind: bb25]; ++ _60 = &mut (((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:78:25: 78:27}); ++ _59 = Pin::<&mut {closure@$DIR/async_drop.rs:78:25: 78:27}>::new_unchecked(move _60) -> [return: bb62, unwind: bb25]; } bb64: { @@ -879,13 +879,13 @@ bb71: { - StorageLive(_27); -- _27 = async_drop_in_place::<{async closure@$DIR/async_drop.rs:78:27: 78:35}>(copy (_42.0: &mut {async closure@$DIR/async_drop.rs:78:27: 78:35})) -> [return: bb70, unwind: bb54]; +- _27 = async_drop_in_place::<{async closure@$DIR/async_drop.rs:86:27: 86:35}>(copy (_42.0: &mut {async closure@$DIR/async_drop.rs:86:27: 86:35})) -> [return: bb70, unwind: bb54]; + _70 = as Future>::poll(move _72, move _73) -> [return: bb70, unwind: bb65]; } bb72: { - _43 = &mut _26; -- _42 = Pin::<&mut {async closure@$DIR/async_drop.rs:78:27: 78:35}>::new_unchecked(move _43) -> [return: bb71, unwind: bb36]; +- _42 = Pin::<&mut {async closure@$DIR/async_drop.rs:86:27: 86:35}>::new_unchecked(move _43) -> [return: bb71, unwind: bb36]; + _74 = move _183; + _73 = copy (_74.0: std::ptr::NonNull>) as &mut std::task::Context<'_> (Transmute); + goto -> bb71; @@ -1027,7 +1027,7 @@ bb91: { - StorageLive(_44); -- _44 = async_drop_in_place::<{closure@$DIR/async_drop.rs:70:25: 70:27}>(copy (_59.0: &mut {closure@$DIR/async_drop.rs:70:25: 70:27})) -> [return: bb90, unwind: bb75]; +- _44 = async_drop_in_place::<{closure@$DIR/async_drop.rs:78:25: 78:27}>(copy (_59.0: &mut {closure@$DIR/async_drop.rs:78:25: 78:27})) -> [return: bb90, unwind: bb75]; + _183 = move _96; + StorageDead(_96); + goto -> bb90; @@ -1035,7 +1035,7 @@ bb92: { - _60 = &mut _24; -- _59 = Pin::<&mut {closure@$DIR/async_drop.rs:70:25: 70:27}>::new_unchecked(move _60) -> [return: bb91, unwind: bb38]; +- _59 = Pin::<&mut {closure@$DIR/async_drop.rs:78:25: 78:27}>::new_unchecked(move _60) -> [return: bb91, unwind: bb38]; + _183 = move _103; + StorageDead(_103); + goto -> bb97; diff --git a/tests/mir-opt/coroutine/async_drop.rs b/tests/mir-opt/coroutine/async_drop.rs index 506baac1dabd8..dfba9e14708db 100644 --- a/tests/mir-opt/coroutine/async_drop.rs +++ b/tests/mir-opt/coroutine/async_drop.rs @@ -51,6 +51,14 @@ async fn double() { let async_int_again = AsyncInt(0); } +// EMIT_MIR async_drop.array-{closure#0}.ElaborateDrops.diff +// EMIT_MIR async_drop.array-{closure#0}.StateTransform.diff +// EMIT_MIR async_drop.array-{closure#0}.coroutine_drop_async.0.mir +// EMIT_MIR core.future-async_drop-async_drop_in_place-{closure#0}.[AsyncInt;2].StateTransform.diff +async fn array() { + let array = [AsyncInt(1), AsyncInt(2)]; +} + // EMIT_MIR async_drop.elaborate_drops-{closure#0}.ElaborateDrops.diff // EMIT_MIR async_drop.elaborate_drops-{closure#0}.StateTransform.diff async fn elaborate_drops() { @@ -90,6 +98,7 @@ fn main() { let i = 13; let fut = pin!(async { + array().await; elaborate_drops().await; test_async_drop(Int(0)).await; diff --git a/tests/ui/async-await/async-drop/async-drop-initial.rs b/tests/ui/async-await/async-drop/async-drop-initial.rs index 7da87a78701b5..873256890f6a7 100644 --- a/tests/ui/async-await/async-drop/async-drop-initial.rs +++ b/tests/ui/async-await/async-drop/async-drop-initial.rs @@ -54,7 +54,7 @@ fn main() { let fut = pin!(async { test_async_drop(Int(0), 16).await; test_async_drop(AsyncInt(0), 32).await; - test_async_drop([AsyncInt(1), AsyncInt(2)], 104).await; + test_async_drop([AsyncInt(1), AsyncInt(2)], 112).await; test_async_drop((AsyncInt(3), AsyncInt(4)), 120).await; test_async_drop(5, 16).await; let j = 42; From 74c99d9c96ae53e4a2d584ad97e9407feabaf964 Mon Sep 17 00:00:00 2001 From: 0xEgao Date: Wed, 24 Jun 2026 00:17:04 +0530 Subject: [PATCH 10/78] Document NonNull layout guarantees --- library/core/src/num/nonzero.rs | 4 ++-- library/core/src/ptr/non_null.rs | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 5863fd57e71e6..b6532f6064330 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -82,7 +82,7 @@ impl_zeroable_primitive!( /// /// `NonZero` is guaranteed to have the same layout and bit validity as `T` /// with the exception that the all-zero bit pattern is invalid. -/// `Option>` is guaranteed to be compatible with `T`, including in +/// `Option>` is guaranteed to be ABI-compatible with `T`, including in /// FFI. /// /// Thanks to the [null pointer optimization], `NonZero` and @@ -525,7 +525,7 @@ macro_rules! nonzero_integer { /// #[doc = concat!("`", stringify!($Ty), "` is guaranteed to have the same layout and bit validity as `", stringify!($Int), "`")] /// with the exception that `0` is not a valid instance. - #[doc = concat!("`Option<", stringify!($Ty), ">` is guaranteed to be compatible with `", stringify!($Int), "`,")] + #[doc = concat!("`Option<", stringify!($Ty), ">` is guaranteed to be ABI-compatible with `", stringify!($Int), "`,")] /// including in FFI. /// /// Thanks to the [null pointer optimization], diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index bafc37469b32f..b30c9f8196ded 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -47,7 +47,12 @@ use crate::{fmt, hash, intrinsics, mem, ptr}; /// it is your responsibility to ensure that `as_mut` is never called, and `as_ptr` /// is never used for mutation. /// -/// # Representation +/// # Layout +/// +/// `NonNull` is guaranteed to have the same layout and bit validity as `*mut T` +/// with the exception that a null pointer is invalid. +/// `Option>` is guaranteed to be ABI-compatible with `*mut T`, including in +/// FFI. /// /// Thanks to the [null pointer optimization], /// `NonNull` and `Option>` From 0b521ee9626c71e92a773f2f7a7c7200703058c5 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 6 Jul 2026 09:57:46 +0100 Subject: [PATCH 11/78] internal: Remove dead struct field NavigationTarget::docs isn't actually used anywhere, so remove it. After 05c0f88c77233154ca592d59c90ec7da81ad63da I thought I'd take a look at any other obviously unused fields and found this. This change has negligible performance impact in my testing, but it's less code. AI disclosure: Written with help by Claude and Fable 5. --- .../crates/ide/src/goto_definition.rs | 1 - .../crates/ide/src/navigation_target.rs | 34 ++++--------------- .../rust-analyzer/crates/ide/src/runnables.rs | 1 - 3 files changed, 7 insertions(+), 29 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 032a431586a33..b6d1689437ff1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -367,7 +367,6 @@ fn try_lookup_include_path( kind: None, container_name: None, description: None, - docs: None, }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 24031429db88f..fd6c8263f9d8c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -12,7 +12,7 @@ use ide_db::{ FileId, FileRange, RootDatabase, SymbolKind, base_db::{CrateOrigin, LangCrateOrigin, all_crates}, defs::{Definition, find_std_module}, - documentation::{Documentation, HasDocs}, + documentation::HasDocs, famous_defs::FamousDefs, ra_fixture::UpmapFromRaFixture, }; @@ -50,7 +50,6 @@ pub struct NavigationTarget { pub kind: Option, pub container_name: Option, pub description: Option, - pub docs: Option>, /// In addition to a `name` field, a `NavigationTarget` may also be aliased /// In such cases we want a `NavigationTarget` to be accessible by its alias pub alias: Option, @@ -69,7 +68,7 @@ impl fmt::Debug for NavigationTarget { f.field("file_id", &self.file_id).field("full_range", &self.full_range); opt!(focus_range); f.field("name", &self.name); - opt!(kind container_name description docs); + opt!(kind container_name description); f.finish() } } @@ -106,7 +105,6 @@ impl UpmapFromRaFixture for NavigationTarget { virtual_file_id, real_file_id, )?, - docs: self.docs.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?, alias: self.alias.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?, }) } @@ -156,7 +154,6 @@ impl NavigationTarget { full_range, SymbolKind::Module, ); - res.docs = module.docs(db).map(Documentation::into_owned); res.description = Some( module.display(db, module.krate(db).to_display_target(db)).to_string(), ); @@ -233,7 +230,6 @@ impl NavigationTarget { focus_range, container_name: None, description: None, - docs: None, alias: None, } } @@ -294,7 +290,6 @@ impl<'db> TryToNav for FileSymbol<'db> { } hir::ModuleDef::BuiltinType(_) => None, }, - docs: None, } }), ) @@ -458,7 +453,6 @@ where D::KIND, ) .map(|mut res| { - res.docs = self.docs(db).map(Documentation::into_owned); res.description = Some(self.display(db, self.krate(db).to_display_target(db)).to_string()); res.container_name = self.container_name(db); @@ -545,7 +539,6 @@ impl TryToNav for hir::ExternCrateDecl { SymbolKind::CrateRoot, ); - res.docs = self.docs(db).map(Documentation::into_owned); res.description = Some(self.display(db, krate.to_display_target(db)).to_string()); res.container_name = container_name(db, *self); res @@ -567,7 +560,6 @@ impl TryToNav for hir::Field { FieldSource::Named(it) => { NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field).map( |mut res| { - res.docs = self.docs(db).map(Documentation::into_owned); res.description = Some(self.display(db, krate.to_display_target(db)).to_string()); res @@ -601,17 +593,11 @@ impl TryToNav for hir::Macro { Either::Left(it) => it, Either::Right(it) => it, }; - Some( - NavigationTarget::from_named( - db, - src.as_ref().with_value(name_owner), - self.kind(db).into(), - ) - .map(|mut res| { - res.docs = self.docs(db).map(Documentation::into_owned); - res - }), - ) + Some(NavigationTarget::from_named( + db, + src.as_ref().with_value(name_owner), + self.kind(db).into(), + )) } } @@ -683,7 +669,6 @@ impl ToNav for LocalSource { focus_range, container_name: None, description: None, - docs: None, } }, ) @@ -715,7 +700,6 @@ impl TryToNav for hir::Label { focus_range, container_name: None, description: None, - docs: None, }, )) } @@ -755,7 +739,6 @@ impl TryToNav for hir::TypeParam { focus_range, container_name: None, description: None, - docs: None, }, )) } @@ -789,7 +772,6 @@ impl TryToNav for hir::LifetimeParam { focus_range, container_name: None, description: None, - docs: None, }, )) } @@ -822,7 +804,6 @@ impl TryToNav for hir::ConstParam { focus_range, container_name: None, description: None, - docs: None, }, )) } @@ -847,7 +828,6 @@ impl TryToNav for hir::InlineAsmOperand { focus_range, container_name: None, description: None, - docs: None, }, )) } diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 60750608a5b49..ce4e5cf97fb24 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -545,7 +545,6 @@ fn module_def_doctest(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Op .call_site(); nav.focus_range = None; nav.description = None; - nav.docs = None; nav.kind = None; let res = Runnable { use_name_in_title: false, From b587ba0402e0760029e9e52704ae09fb9a45ad64 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 6 Jul 2026 15:48:20 +0200 Subject: [PATCH 12/78] Add do_not_recommend test --- .../auxiliary/foreign_blanket_impl.rs | 10 ++++ .../method_call.current.stderr | 57 +++++++++++++++++++ .../do_not_recommend/method_call.next.stderr | 45 +++++++++++++++ .../do_not_recommend/method_call.rs | 38 +++++++++++++ 4 files changed, 150 insertions(+) create mode 100644 tests/ui/diagnostic_namespace/do_not_recommend/auxiliary/foreign_blanket_impl.rs create mode 100644 tests/ui/diagnostic_namespace/do_not_recommend/method_call.current.stderr create mode 100644 tests/ui/diagnostic_namespace/do_not_recommend/method_call.next.stderr create mode 100644 tests/ui/diagnostic_namespace/do_not_recommend/method_call.rs diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/auxiliary/foreign_blanket_impl.rs b/tests/ui/diagnostic_namespace/do_not_recommend/auxiliary/foreign_blanket_impl.rs new file mode 100644 index 0000000000000..b3490d743b9bd --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/auxiliary/foreign_blanket_impl.rs @@ -0,0 +1,10 @@ +pub struct ForeignType(pub T); + +pub trait DoNotMentionThis {} + +#[diagnostic::do_not_recommend] +impl Clone for ForeignType { + fn clone(&self) -> Self { + todo!() + } +} diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/method_call.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/method_call.current.stderr new file mode 100644 index 0000000000000..0fd90fae5e6fd --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/method_call.current.stderr @@ -0,0 +1,57 @@ +error[E0277]: the trait bound `ForeignType: Clone` is not satisfied + --> $DIR/method_call.rs:21:30 + | +LL | let _ = Clone::clone(&f); + | ^^ the trait `Clone` is not implemented for `ForeignType` + +error[E0599]: the method `clone` exists for struct `ForeignType`, but its trait bounds were not satisfied + --> $DIR/method_call.rs:24:19 + | +LL | let _ = f.clone(); + | ^^^^^ method cannot be called on `ForeignType` due to unsatisfied trait bounds + | + ::: $DIR/auxiliary/foreign_blanket_impl.rs:1:1 + | +LL | pub struct ForeignType(pub T); + | ------------------------- doesn't satisfy `ForeignType: Clone` + | + = note: the following trait bounds were not satisfied: + `u8: DoNotMentionThis` + which is required by `ForeignType: Clone` + +error[E0277]: the trait bound `LocalType: Clone` is not satisfied + --> $DIR/method_call.rs:31:30 + | +LL | let _ = Clone::clone(&l); + | ^^ the trait `Clone` is not implemented for `LocalType` + | +help: consider annotating `LocalType` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | pub struct LocalType(pub T); + | + +error[E0599]: the method `clone` exists for struct `LocalType`, but its trait bounds were not satisfied + --> $DIR/method_call.rs:34:19 + | +LL | pub struct LocalType(pub T); + | ----------------------- method `clone` not found for this struct because it doesn't satisfy `LocalType: Clone` +... +LL | let _ = l.clone(); + | ^^^^^ method cannot be called on `LocalType` due to unsatisfied trait bounds + | +note: trait bound `u8: DoNotMentionThis` was not satisfied + --> $DIR/method_call.rs:12:9 + | +LL | impl Clone for LocalType { + | ^^^^^^^^^^^^^^^^ ----- ------------ + | | + | unsatisfied trait bound introduced here + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `clone`, perhaps you need to implement it: + candidate #1: `Clone` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/method_call.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/method_call.next.stderr new file mode 100644 index 0000000000000..1bd7267b6ee99 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/method_call.next.stderr @@ -0,0 +1,45 @@ +error[E0277]: the trait bound `ForeignType: Clone` is not satisfied + --> $DIR/method_call.rs:21:30 + | +LL | let _ = Clone::clone(&f); + | ------------ ^^ the trait `Clone` is not implemented for `ForeignType` + | | + | required by a bound introduced by this call + +error[E0599]: no method named `clone` found for struct `ForeignType` in the current scope + --> $DIR/method_call.rs:24:19 + | +LL | let _ = f.clone(); + | ^^^^^ method not found in `ForeignType` + +error[E0277]: the trait bound `LocalType: Clone` is not satisfied + --> $DIR/method_call.rs:31:30 + | +LL | let _ = Clone::clone(&l); + | ------------ ^^ the trait `Clone` is not implemented for `LocalType` + | | + | required by a bound introduced by this call + | +help: consider annotating `LocalType` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | pub struct LocalType(pub T); + | + +error[E0599]: no method named `clone` found for struct `LocalType` in the current scope + --> $DIR/method_call.rs:34:19 + | +LL | pub struct LocalType(pub T); + | ----------------------- method `clone` not found for this struct +... +LL | let _ = l.clone(); + | ^^^^^ method not found in `LocalType` + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `clone`, perhaps you need to implement it: + candidate #1: `Clone` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/method_call.rs b/tests/ui/diagnostic_namespace/do_not_recommend/method_call.rs new file mode 100644 index 0000000000000..2b10cacd30227 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/method_call.rs @@ -0,0 +1,38 @@ +//@aux-build:foreign_blanket_impl.rs +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +extern crate foreign_blanket_impl; +use foreign_blanket_impl::{DoNotMentionThis, ForeignType}; + +pub struct LocalType(pub T); + +#[diagnostic::do_not_recommend] +impl Clone for LocalType { + fn clone(&self) -> Self { + todo!() + } +} + +fn main() { + { + let f = ForeignType(42_u8); + let _ = Clone::clone(&f); + //~^ERROR the trait bound `ForeignType: Clone` is not satisfied [E0277] + + let _ = f.clone(); + //[next]~^ERROR no method named `clone` found for struct `ForeignType` in the current scope [E0599] + //[current]~^^ERROR the method `clone` exists for struct `ForeignType`, but its trait bounds were not satisfied [E0599] + } + + { + let l = LocalType(42_u8); + let _ = Clone::clone(&l); + //~^ERROR the trait bound `LocalType: Clone` is not satisfied [E0277] + + let _ = l.clone(); + //[next]~^ERROR no method named `clone` found for struct `LocalType` in the current scope [E0599] + //[current]~^^ERROR the method `clone` exists for struct `LocalType`, but its trait bounds were not satisfied [E0599] + } +} From 8b2330ef601e326468430035c3088afb4ab5b720 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 4 Jul 2026 09:35:22 +0200 Subject: [PATCH 13/78] misc improvements - move span map creation out of the closure, as it's an expensive operation --- .../rust-analyzer/crates/hir-def/src/find_path.rs | 10 ++++++---- .../crates/hir-def/src/macro_expansion_tests/mod.rs | 3 ++- src/tools/rust-analyzer/crates/hir-expand/src/lib.rs | 5 ++--- .../rust-analyzer/crates/hir-expand/src/span_map.rs | 6 ++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 6aaf8a674eee9..945deb8b525a7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -641,6 +641,8 @@ fn find_local_import_locations( #[cfg(test)] mod tests { + use std::cell::LazyCell; + use expect_test::{Expect, expect}; use hir_expand::db::ExpandDatabase; use itertools::Itertools; @@ -672,10 +674,10 @@ mod tests { syntax::SourceFile::parse(&format!("use {path};"), span::Edition::CURRENT); let ast_path = parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap(); - let mod_path = ModPath::from_src(&db, ast_path, &mut |range| { - db.span_map(pos.file_id.into()).span_for_range(range).ctx - }) - .unwrap(); + let span_map = LazyCell::new(|| db.span_map(pos.file_id.into())); + let mod_path = + ModPath::from_src(&db, ast_path, &mut |range| span_map.span_for_range(range).ctx) + .unwrap(); let (def_map, local_def_map) = module.local_def_map(&db); let resolved = def_map diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index c53a7321e83b4..226628665262c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -465,11 +465,12 @@ m!(g); ModuleSource::SourceFile(it) => it, ModuleSource::Module(_) | ModuleSource::BlockExpr(_) => panic!(), }; + let span_map = db.real_span_map(file_id); let no_downmap_spans: Vec<_> = source_file .syntax() .descendants() .map(|node| { - let mut span = db.real_span_map(file_id).span_for_range(node.text_range()); + let mut span = span_map.span_for_range(node.text_range()); span.anchor.ast_id = NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER; span }) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 719a38edf441a..c30f386a34907 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -28,7 +28,6 @@ mod fixup; mod prettify_macro_expansion_; use salsa::plumbing::{AsId, FromId}; -use stdx::TupleExt; use thin_vec::ThinVec; use triomphe::Arc; @@ -1216,8 +1215,8 @@ impl<'db> ExpansionInfo<'db> { self.arg.file_id, arg_map .ranges_with_span_exact(span) - .filter(|(range, _)| range.intersect(arg_range).is_some()) - .map(TupleExt::head) + .map(|(range, _)| range) + .filter(|range| range.intersect(arg_range).is_some()) .collect(), ) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs index 1b588a8425161..0880d5f8feef3 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs @@ -30,7 +30,7 @@ impl<'db> SpanMap<'db> { // FIXME: Is it correct for us to only take the span at the start? This feels somewhat // wrong. The context will be right, but the range could be considered wrong. See // https://github.com/rust-lang/rust/issues/23480, we probably want to fetch the span at - // the start and end, then merge them like rustc does in `Span::to + // the start and end, then merge them like rustc does in `Span::to` Self::ExpansionSpanMap(span_map) => span_map.span_at(range.start()), Self::RealSpanMap(span_map) => span_map.span_for_range(range), } @@ -40,9 +40,7 @@ impl<'db> SpanMap<'db> { pub(crate) fn new(db: &'db dyn ExpandDatabase, file_id: HirFileId) -> SpanMap<'db> { match file_id { HirFileId::FileId(file_id) => SpanMap::RealSpanMap(db.real_span_map(file_id)), - HirFileId::MacroFile(m) => { - SpanMap::ExpansionSpanMap(&m.parse_macro_expansion(db).value.1) - } + HirFileId::MacroFile(m) => SpanMap::ExpansionSpanMap(db.expansion_span_map(m)), } } } From 2e58a752b1f63cfe74bce84f448ca00802102adc Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 4 Jul 2026 09:35:22 +0200 Subject: [PATCH 14/78] adjust existing uses of `*span_map` queries --- .../rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index e12cdef495a8a..9bea6b9daccbc 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -22,7 +22,6 @@ use crate::{ db::ExpandDatabase, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt}, name, - span_map::SpanMap, tt::{self, DelimSpan, TtElement, TtIter}, }; @@ -826,11 +825,10 @@ fn include_expand( ); } }; - let span_map = db.real_span_map(editioned_file_id); // FIXME: Parse errors ExpandResult::ok(syntax_node_to_token_tree( &editioned_file_id.parse(db).syntax_node(), - SpanMap::RealSpanMap(span_map), + db.span_map(editioned_file_id.into()), span, syntax_bridge::DocCommentDesugarMode::ProcMacro, )) From 2987fc9c1710af8ab924095e89f034bb13e638ae Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 6 Jul 2026 15:02:42 +0200 Subject: [PATCH 15/78] migrate `ExpandDatabase::*span_map` queries --- .../crates/hir-def/src/attrs/docs.rs | 4 ++-- .../crates/hir-def/src/expr_store/expander.rs | 2 +- .../crates/hir-def/src/find_path.rs | 3 +-- .../crates/hir-def/src/item_tree/lower.rs | 2 +- .../hir-def/src/macro_expansion_tests/mod.rs | 10 ++++----- .../crates/hir-def/src/nameres/assoc.rs | 2 +- .../crates/hir-def/src/signatures.rs | 2 +- .../rust-analyzer/crates/hir-def/src/src.rs | 2 +- .../crates/hir-def/src/visibility.rs | 2 +- .../crates/hir-expand/src/builtin/fn_macro.rs | 2 +- .../rust-analyzer/crates/hir-expand/src/db.rs | 14 +------------ .../crates/hir-expand/src/files.rs | 20 +++++++++--------- .../crates/hir-expand/src/lib.rs | 11 +++++----- .../crates/hir-expand/src/span_map.rs | 21 +++++++++++-------- .../rust-analyzer/crates/hir/src/semantics.rs | 5 +++-- .../crates/hir/src/source_analyzer.rs | 2 +- .../ide-assists/src/handlers/inline_call.rs | 10 +++------ .../ide-assists/src/handlers/inline_macro.rs | 3 +-- .../src/handlers/replace_if_let_with_match.rs | 3 +-- .../crates/ide-assists/src/utils.rs | 4 ++-- .../src/completions/item_list/trait_impl.rs | 6 +++--- .../crates/ide-db/src/path_transform.rs | 2 +- .../crates/ide-diagnostics/src/lib.rs | 7 ++----- .../crates/ide/src/expand_macro.rs | 5 ++--- .../crates/ide/src/hover/render.rs | 5 ++--- 25 files changed, 65 insertions(+), 84 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs index 69eac7b3c97fb..1e0fa0ce9d454 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs @@ -402,7 +402,7 @@ fn expand_doc_macro_call<'db>( resolver: source_ctx.resolver.clone(), file_id: expansion_file_id, ast_id_map: expansion_file_id.ast_id_map(expander.db), - span_map: expander.db.span_map(expansion_file_id), + span_map: expansion_file_id.span_map(expander.db), }; Some((expr, new_source_ctx)) } @@ -457,7 +457,7 @@ fn extend_with_attrs<'a, 'db>( resolver, file_id, ast_id_map: file_id.ast_id_map(db), - span_map: db.span_map(file_id), + span_map: file_id.span_map(db), }, ) }); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs index fbe7bab457a94..368406275d644 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs @@ -46,7 +46,7 @@ impl<'db> Expander<'db> { current_file_id, recursion_depth: 0, recursion_limit, - span_map: db.span_map(current_file_id), + span_map: current_file_id.span_map(db), ast_id_map: current_file_id.ast_id_map(db), } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 945deb8b525a7..40f74d280261c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -644,7 +644,6 @@ mod tests { use std::cell::LazyCell; use expect_test::{Expect, expect}; - use hir_expand::db::ExpandDatabase; use itertools::Itertools; use span::Edition; use stdx::format_to; @@ -674,7 +673,7 @@ mod tests { syntax::SourceFile::parse(&format!("use {path};"), span::Edition::CURRENT); let ast_path = parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap(); - let span_map = LazyCell::new(|| db.span_map(pos.file_id.into())); + let span_map = LazyCell::new(|| hir_expand::HirFileId::from(pos.file_id).span_map(&db)); let mod_path = ModPath::from_src(&db, ast_path, &mut |range| span_map.span_for_range(range).ctx) .unwrap(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index b0d9d960d81f7..43769eda54b89 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -56,7 +56,7 @@ impl<'db> Ctx<'db> { } pub(super) fn span_map(&self) -> SpanMap<'_> { - *self.span_map.get_or_init(|| self.db.span_map(self.file)) + *self.span_map.get_or_init(|| self.file.span_map(self.db)) } pub(super) fn lower_module_items(mut self, item_owner: &dyn HasModuleItem) -> ItemTree { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 226628665262c..5cca220967fe9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -18,7 +18,7 @@ use std::{any::TypeId, iter, ops::Range, sync}; use expect_test::Expect; use hir_expand::{ - AstId, ExpansionInfo, InFile, MacroCallId, MacroCallKind, MacroKind, + AstId, ExpansionInfo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroKind, builtin::quote::quote, db::ExpandDatabase, proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind}, @@ -215,7 +215,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream } let pp = pretty_print_macro_expansion( src.value, - db.span_map(src.file_id), + src.file_id.span_map(&db), show_spans, show_ctxt, ); @@ -230,7 +230,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream if let Some(macro_file) = src.file_id.macro_file() { let pp = pretty_print_macro_expansion( src.value.syntax().clone(), - db.span_map(macro_file.into()), + HirFileId::from(macro_file).span_map(&db), false, false, ); @@ -245,7 +245,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream { let pp = pretty_print_macro_expansion( src.value.syntax().clone(), - db.span_map(macro_file.into()), + HirFileId::from(macro_file).span_map(&db), false, false, ); @@ -465,7 +465,7 @@ m!(g); ModuleSource::SourceFile(it) => it, ModuleSource::Module(_) | ModuleSource::BlockExpr(_) => panic!(), }; - let span_map = db.real_span_map(file_id); + let span_map = HirFileId::from(file_id).span_map(&db); let no_downmap_spans: Vec<_> = source_file .syntax() .descendants() diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs index 8bac83c3699ba..5bec8515d26da 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs @@ -163,7 +163,7 @@ impl<'db> AssocItemCollector<'db> { def_map, local_def_map, ast_id_map: file_id.ast_id_map(db), - span_map: db.span_map(file_id), + span_map: file_id.span_map(db), cfg_options: module_id.krate(db).cfg_options(db), file_id, container, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 5a4f78cfbc86b..3105a4a413108 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -990,7 +990,7 @@ fn lower_fields( let mut col = ExprCollector::new(db, module, fields.file_id); let override_visibility = override_visibility.map(|vis| { LazyCell::new(|| { - let span_map = db.span_map(fields.file_id); + let span_map = fields.file_id.span_map(db); visibility_from_ast(db, vis, &mut |range| span_map.span_for_range(range).ctx) }) }); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs index 6d6003079818e..4fb2e7dacced7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs @@ -53,7 +53,7 @@ fn use_tree_source_map(db: &dyn DefDatabase, use_ast_id: AstId) -> Are let ast_use_tree = ast.use_tree().expect("missing `use_tree`"); let mut span_map = None; crate::item_tree::lower_use_tree(db, ast_use_tree, &mut |range| { - span_map.get_or_insert_with(|| db.span_map(use_ast_id.file_id)).span_for_range(range).ctx + span_map.get_or_insert_with(|| use_ast_id.file_id.span_map(db)).span_for_range(range).ctx }) .expect("failed to lower use tree") .1 diff --git a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs index 81a61ec20f17e..143e2c44445a4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs @@ -306,7 +306,7 @@ pub fn visibility_from_ast( ) -> Visibility { let mut span_map = None; let raw_vis = crate::item_tree::visibility_from_ast(db, ast_vis.value, &mut |range| { - span_map.get_or_insert_with(|| db.span_map(ast_vis.file_id)).span_for_range(range).ctx + span_map.get_or_insert_with(|| ast_vis.file_id.span_map(db)).span_for_range(range).ctx }); match raw_vis { RawVisibility::PubSelf(explicitness) => { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index 9bea6b9daccbc..68c9fc4a37bc7 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -828,7 +828,7 @@ fn include_expand( // FIXME: Parse errors ExpandResult::ok(syntax_node_to_token_tree( &editioned_file_id.parse(db).syntax_node(), - db.span_map(editioned_file_id.into()), + crate::HirFileId::from(editioned_file_id).span_map(db), span, syntax_bridge::DocCommentDesugarMode::ProcMacro, )) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index 69c8ffe6095d2..b5f5785a540e0 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -14,7 +14,7 @@ use crate::{ fixup::{self, SyntaxFixupUndoInfo}, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt}, proc_macro::CustomProcMacroExpander, - span_map::{ExpansionSpanMap, RealSpanMap, SpanMap}, + span_map::{RealSpanMap, SpanMap}, tt, }; @@ -40,18 +40,6 @@ pub trait ExpandDatabase: SourceDatabase { #[salsa::transparent] fn resolve_span(&self, span: Span) -> FileRange; - #[salsa::transparent] - #[salsa::invoke(SpanMap::new)] - fn span_map(&self, file_id: HirFileId) -> SpanMap<'_>; - - #[salsa::transparent] - #[salsa::invoke(crate::span_map::expansion_span_map)] - fn expansion_span_map(&self, file_id: MacroCallId) -> &ExpansionSpanMap; - - #[salsa::invoke(crate::span_map::real_span_map)] - #[salsa::transparent] - fn real_span_map(&self, file_id: EditionedFileId) -> &RealSpanMap; - /// Fetches the expander for this macro. #[salsa::transparent] #[salsa::invoke(TokenExpander::macro_expander)] diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs index a5e24338807c8..a6ae901973103 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs @@ -342,7 +342,7 @@ impl> InFile { let FileRange { file_id: editioned_file_id, range } = map_node_range_up_rooted( db, - db.expansion_span_map(file_id), + file_id.expansion_span_map(db), self.value.borrow().text_range(), )?; @@ -385,7 +385,7 @@ impl InFile { HirFileId::MacroFile(mac_file) => { let (range, ctxt) = span_for_offset( db, - db.expansion_span_map(mac_file), + mac_file.expansion_span_map(db), self.value.text_range().start(), ); @@ -411,7 +411,7 @@ impl InFile { HirFileId::MacroFile(mac_file) => { let (range, ctxt) = span_for_offset( db, - db.expansion_span_map(mac_file), + mac_file.expansion_span_map(db), self.value.text_range().start(), ); @@ -425,7 +425,7 @@ impl InFile { impl InMacroFile { pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> (FileRange, SyntaxContext) { - span_for_offset(db, db.expansion_span_map(self.file_id), self.value) + span_for_offset(db, self.file_id.expansion_span_map(db), self.value) } } @@ -439,7 +439,7 @@ impl InFile { (FileRange { file_id, range: self.value }, SyntaxContext::root(file_id.edition(db))) } HirFileId::MacroFile(mac_file) => { - match map_node_range_up(db, db.expansion_span_map(mac_file), self.value) { + match map_node_range_up(db, mac_file.expansion_span_map(db), self.value) { Some(it) => it, None => { let loc = mac_file.loc(db); @@ -457,7 +457,7 @@ impl InFile { match self.file_id { HirFileId::FileId(file_id) => FileRange { file_id, range: self.value }, HirFileId::MacroFile(mac_file) => { - match map_node_range_up_rooted(db, db.expansion_span_map(mac_file), self.value) { + match map_node_range_up_rooted(db, mac_file.expansion_span_map(db), self.value) { Some(it) => it, _ => { let loc = mac_file.loc(db); @@ -475,7 +475,7 @@ impl InFile { match self.file_id { HirFileId::FileId(file_id) => FileRange { file_id, range: self.value }, HirFileId::MacroFile(mac_file) => { - match map_node_range_up_rooted(db, db.expansion_span_map(mac_file), self.value) { + match map_node_range_up_rooted(db, mac_file.expansion_span_map(db), self.value) { Some(it) => it, _ => { let loc = mac_file.loc(db); @@ -496,7 +496,7 @@ impl InFile { SyntaxContext::root(file_id.edition(db)), )), HirFileId::MacroFile(mac_file) => { - map_node_range_up(db, db.expansion_span_map(mac_file), self.value) + map_node_range_up(db, mac_file.expansion_span_map(db), self.value) } } } @@ -508,7 +508,7 @@ impl InFile { match self.file_id { HirFileId::FileId(file_id) => Some(FileRange { file_id, range: self.value }), HirFileId::MacroFile(mac_file) => { - map_node_range_up_rooted(db, db.expansion_span_map(mac_file), self.value) + map_node_range_up_rooted(db, mac_file.expansion_span_map(db), self.value) } } } @@ -530,7 +530,7 @@ impl InFile { let FileRange { file_id: editioned_file_id, range } = map_node_range_up_rooted( db, - db.expansion_span_map(file_id), + file_id.expansion_span_map(db), self.value.syntax().text_range(), )?; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index c30f386a34907..c0b0a614563d6 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -792,7 +792,7 @@ fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId) -> Span { fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId, _: ()) -> Span { let root = ast.file_id.parse_or_expand(db); let ast_id_map = ast.file_id.ast_id_map(db); - let span_map = db.span_map(ast.file_id); + let span_map = ast.file_id.span_map(db); let node = ast_id_map.get(ast.value).to_node(&root); let range = ast::HasName::name(&node) @@ -1228,7 +1228,7 @@ impl<'db> ExpansionInfo<'db> { let loc = macro_file.loc(db); let arg_tt = loc.kind.arg(db); - let arg_map = db.span_map(arg_tt.file_id); + let arg_map = arg_tt.file_id.span_map(db); let (parse, exp_map) = ¯o_file.parse_macro_expansion(db).value; let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() }; @@ -1530,9 +1530,10 @@ impl HirFileId { db: &dyn ExpandDatabase, ) -> (Parse, SpanMap<'_>) { match self { - HirFileId::FileId(file_id) => { - (file_id.parse(db).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(file_id))) - } + HirFileId::FileId(file_id) => ( + file_id.parse(db).to_syntax(), + SpanMap::RealSpanMap(crate::span_map::real_span_map(db, file_id)), + ), HirFileId::MacroFile(macro_file) => { let (parse, map) = ¯o_file.parse_macro_expansion(db).value; (parse.clone(), SpanMap::ExpansionSpanMap(map)) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs index 0880d5f8feef3..8f2b74091f865 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs @@ -35,16 +35,20 @@ impl<'db> SpanMap<'db> { Self::RealSpanMap(span_map) => span_map.span_for_range(range), } } +} +impl HirFileId { #[inline] - pub(crate) fn new(db: &'db dyn ExpandDatabase, file_id: HirFileId) -> SpanMap<'db> { - match file_id { - HirFileId::FileId(file_id) => SpanMap::RealSpanMap(db.real_span_map(file_id)), - HirFileId::MacroFile(m) => SpanMap::ExpansionSpanMap(db.expansion_span_map(m)), + pub fn span_map<'db>(self, db: &'db dyn ExpandDatabase) -> SpanMap<'db> { + match self { + HirFileId::FileId(file_id) => SpanMap::RealSpanMap(real_span_map(db, file_id)), + HirFileId::MacroFile(m) => SpanMap::ExpansionSpanMap(m.expansion_span_map(db)), } } } +/// This is an implementation detail of [`HirFileId::span_map`]. Outside this crate, use +/// `HirFileId::from(file_id).span_map(db)` instead of `real_span_map(db, file_id)`. #[salsa_macros::tracked(returns(ref))] pub(crate) fn real_span_map( db: &dyn ExpandDatabase, @@ -109,9 +113,8 @@ pub(crate) fn real_span_map( ) } -pub(crate) fn expansion_span_map( - db: &dyn ExpandDatabase, - file_id: MacroCallId, -) -> &ExpansionSpanMap { - &file_id.parse_macro_expansion(db).value.1 +impl MacroCallId { + pub fn expansion_span_map(self, db: &dyn ExpandDatabase) -> &ExpansionSpanMap { + &self.parse_macro_expansion(db).value.1 + } } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index c696ebcf18fb5..02cec440e19d1 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1002,7 +1002,8 @@ impl<'db> SemanticsImpl<'db> { else { return tok.into(); }; - let span = self.db.real_span_map(tok.file_id).span_for_range(tok.value.text_range()); + let span = + HirFileId::from(tok.file_id).span_map(self.db).span_for_range(tok.value.text_range()); let Some(InMacroFile { file_id, value: mut mapped_tokens }) = self.with_ctx(|ctx| { Some( ctx.cache @@ -1254,7 +1255,7 @@ impl<'db> SemanticsImpl<'db> { let _p = tracing::info_span!("descend_into_macros_impl").entered(); let db = self.db; - let span = db.span_map(file_id).span_for_range(token.text_range()); + let span = file_id.span_map(db).span_for_range(token.text_range()); // Process the expansion of a call, pushing all tokens with our span in the expansion back onto our stack let process_expansion_for_token = diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 59d029b1e27c8..9e4d825ab2291 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -2108,7 +2108,7 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H let Some(macro_file) = name.file_id.macro_file() else { return HygieneId::ROOT; }; - let span_map = db.expansion_span_map(macro_file); + let span_map = macro_file.expansion_span_map(db); let ctx = span_map.span_at(name.value.text_range().start()).ctx; HygieneId::new(ctx.opaque_and_semiopaque(db)) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index a14b30db42896..438966a1d67dd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -1,11 +1,7 @@ use std::collections::BTreeSet; use either::Either; -use hir::{ - FileRange, PathResolution, Semantics, TypeInfo, - db::{ExpandDatabase, HirDatabase}, - sym, -}; +use hir::{FileRange, PathResolution, Semantics, TypeInfo, db::HirDatabase, sym}; use ide_db::{ EditionedFileId, FxHashMap, RootDatabase, base_db::Crate, @@ -352,7 +348,7 @@ fn inline( let file_id = sema.hir_file_for(fn_body.syntax()); let body_to_clone = if let Some(macro_file) = file_id.macro_file() { cov_mark::hit!(inline_call_defined_in_macro); - let span_map = sema.db.expansion_span_map(macro_file); + let span_map = macro_file.expansion_span_map(sema.db); let body_prettified = prettify_macro_expansion(sema.db, fn_body.syntax().clone(), span_map, *krate); if let Some(body) = ast::BlockExpr::cast(body_prettified) { body } else { fn_body.clone() } @@ -496,7 +492,7 @@ fn inline( let param_ty = param_ty.clone().map(|param_ty| { let file_id = sema.hir_file_for(param_ty.syntax()); if let Some(macro_file) = file_id.macro_file() { - let span_map = sema.db.expansion_span_map(macro_file); + let span_map = macro_file.expansion_span_map(sema.db); let param_ty_prettified = prettify_macro_expansion( sema.db, param_ty.syntax().clone(), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs index f4ac75d2290dd..5a185637df4ef 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs @@ -1,4 +1,3 @@ -use hir::db::ExpandDatabase; use ide_db::syntax_helpers::prettify_macro_expansion; use syntax::ast::{self, AstNode, edit::IndentLevel}; @@ -58,7 +57,7 @@ pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Op text_range, |builder| { let expanded = ctx.sema.parse_or_expand(macro_call.into()); - let span_map = ctx.sema.db.expansion_span_map(macro_call); + let span_map = macro_call.expansion_span_map(ctx.sema.db); // Don't call `prettify_macro_expansion()` outside the actual assist action; it does some heavy rowan tree manipulation, // which can be very costly for big macros when it is done *even without the assist being invoked*. let expanded = prettify_macro_expansion(ctx.db(), expanded, span_map, target_crate_id); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 36ee300ca9acf..9a61163e87e71 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -1,4 +1,3 @@ -use hir::db::ExpandDatabase; use itertools::Itertools; use std::iter::successors; @@ -512,7 +511,7 @@ fn pretty_pat_inside_macro( let pretty_node = hir::prettify_macro_expansion( db, pat, - db.expansion_span_map(file_id), + file_id.expansion_span_map(db), scope.module().krate(db).into(), ); ast::Pat::cast(pretty_node) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 2c4fb5f405a77..344beb32ae0e9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -5,7 +5,7 @@ use std::slice; pub(crate) use gen_trait_fn_body::gen_trait_fn_body; use hir::{ HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution, Semantics, - db::{ExpandDatabase, HirDatabase}, + db::HirDatabase, }; use ide_db::{ RootDatabase, @@ -241,7 +241,7 @@ pub fn add_trait_assoc_items_to_impl( .map(|InFile { file_id, value: original_item }| { let mut cloned_item = { if let Some(macro_file) = file_id.macro_file() { - let span_map = sema.db.expansion_span_map(macro_file); + let span_map = macro_file.expansion_span_map(sema.db); let item_prettified = prettify_macro_expansion( sema.db, original_item.syntax().clone(), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index c165a32082dfc..aca774710cb24 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -31,7 +31,7 @@ //! } //! ``` -use hir::{MacroCallId, Name, db::ExpandDatabase}; +use hir::{MacroCallId, Name}; use ide_db::text_edit::TextEdit; use ide_db::{ SymbolKind, documentation::HasDocs, path_transform::PathTransform, @@ -494,7 +494,7 @@ fn make_const_compl_syntax( macro_file: Option, ) -> SmolStr { let const_ = if let Some(macro_file) = macro_file { - let span_map = ctx.db.expansion_span_map(macro_file); + let span_map = macro_file.expansion_span_map(ctx.db); prettify_macro_expansion(ctx.db, const_.syntax().clone(), span_map, ctx.krate.into()) } else { const_.syntax().clone() @@ -522,7 +522,7 @@ fn function_declaration( macro_file: Option, ) -> String { let node = if let Some(macro_file) = macro_file { - let span_map = ctx.db.expansion_span_map(macro_file); + let span_map = macro_file.expansion_span_map(ctx.db); prettify_macro_expansion(ctx.db, node.syntax().clone(), span_map, ctx.krate.into()) } else { node.syntax().clone() diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 661f0cff8e135..7cf8dff48bf59 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -151,7 +151,7 @@ impl<'a> PathTransform<'a> { prettify_macro_expansion( db, node, - db.expansion_span_map(file_id), + file_id.expansion_span_map(db), self.target_scope.module().krate(db).into(), ) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index e26e3dbc1d8ac..8d8ed9acaa0fc 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -117,10 +117,7 @@ mod tests; use std::sync::LazyLock; -use hir::{ - Crate, DisplayTarget, InFile, MacroCallIdExt, Semantics, db::ExpandDatabase, - diagnostics::AnyDiagnostic, -}; +use hir::{Crate, DisplayTarget, InFile, MacroCallIdExt, Semantics, diagnostics::AnyDiagnostic}; use ide_db::{ FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap, assists::{Assist, AssistId, AssistResolveStrategy, ExprFillDefaultMode}, @@ -618,7 +615,7 @@ fn handle_diag_from_macros( node: &InFile, ) -> bool { let Some(macro_file) = node.file_id.macro_file() else { return true }; - let span_map = sema.db.expansion_span_map(macro_file); + let span_map = macro_file.expansion_span_map(sema.db); let mut spans = span_map.spans_for_range(node.text_range()); if spans.any(|span| { span.ctx.outer_expn(sema.db).is_some_and(|expansion| { diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index c2b3a3d8d6893..6c34b97a39a37 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -1,4 +1,3 @@ -use hir::db::ExpandDatabase; use hir::{ExpandResult, InFile, Semantics}; use ide_db::{ FileId, RootDatabase, base_db::Crate, helpers::pick_best_token, @@ -67,7 +66,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< .count(); let ExpandResult { err, value: expansion } = expansions.get(idx)?.clone()?; let expansion_file_id = sema.hir_file_for(&expansion).macro_file()?; - let expansion_span_map = db.expansion_span_map(expansion_file_id); + let expansion_span_map = expansion_file_id.expansion_span_map(db); let mut expansion = format( db, SyntaxKind::MACRO_ITEMS, @@ -159,7 +158,7 @@ fn expand_macro_recur( } let file_id = sema.hir_file_for(&expanded).macro_file().expect("expansion must produce a macro file"); - let expansion_span_map = sema.db.expansion_span_map(file_id); + let expansion_span_map = file_id.expansion_span_map(sema.db); result_span_map.merge( TextRange::at(offset_in_original_node, macro_call.syntax().text_range().len()), expanded.text_range().len(), diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index da4f185d75641..fe94e169ed9a9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -6,7 +6,6 @@ use hir::{ Adt, AsAssocItem, AsExternAssocItem, CaptureKind, DisplayTarget, DropGlue, DynCompatibilityViolation, HasCrate, HasSource, HirDisplay, Layout, LayoutError, MethodViolationCode, Name, Semantics, Symbol, Trait, Type, TypeInfo, Variant, - db::ExpandDatabase, }; use ide_db::{ RootDatabase, @@ -543,7 +542,7 @@ pub(super) fn definition( let source = it.source(db)?; let mut body = source.value.body()?.syntax().clone(); if let Some(macro_file) = source.file_id.macro_file() { - let span_map = db.expansion_span_map(macro_file); + let span_map = macro_file.expansion_span_map(db); body = prettify_macro_expansion(db, body, span_map, it.krate(db).into()); } if env::var_os("RA_DEV").is_some() { @@ -575,7 +574,7 @@ pub(super) fn definition( let source = it.source(db)?; let mut body = source.value.body()?.syntax().clone(); if let Some(macro_file) = source.file_id.macro_file() { - let span_map = db.expansion_span_map(macro_file); + let span_map = macro_file.expansion_span_map(db); body = prettify_macro_expansion(db, body, span_map, it.krate(db).into()); } if env::var_os("RA_DEV").is_some() { From ee3380024b084d120a96307cd6ee3d49845b7462 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 7 Jul 2026 18:37:44 +0300 Subject: [PATCH 16/78] Fix proc macros `TokenStream::from_str()` for doc comments We should strip `///` (or `//!` or `/**` or `/*!`), which is 3 characters, not 2. --- .../crates/proc-macro-srv/src/tests/mod.rs | 8 +- .../crates/proc-macro-srv/src/token_stream.rs | 1528 +++++++++-------- 2 files changed, 771 insertions(+), 765 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index a8ddb216f0e41..2cb6817fb9167 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -84,7 +84,7 @@ pub struct Foo { GROUP [] 1 1 1 IDENT 1 doc PUNCT 1 = [alone] - LITER 1 Str / The domain where this federated instance is running + LITER 1 Str The domain where this federated instance is running PUNCT 1 # [joint] GROUP [] 1 1 1 IDENT 1 helper @@ -120,7 +120,7 @@ pub struct Foo { GROUP [] 1 1 1 IDENT 1 doc PUNCT 1 = [alone] - LITER 1 Str / The domain where this federated instance is running + LITER 1 Str The domain where this federated instance is running PUNCT 1 # [joint] GROUP [] 1 1 1 IDENT 1 helper @@ -156,7 +156,7 @@ pub struct Foo { GROUP [] 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 IDENT 42:Root[0000, 0]@0..0#0 doc PUNCT 42:Root[0000, 0]@0..0#0 = [alone] - LITER 42:Root[0000, 0]@75..130#0 Str / The domain where this federated instance is running + LITER 42:Root[0000, 0]@75..130#0 Str The domain where this federated instance is running PUNCT 42:Root[0000, 0]@135..136#0 # [joint] GROUP [] 42:Root[0000, 0]@136..137#0 42:Root[0000, 0]@157..158#0 42:Root[0000, 0]@136..158#0 IDENT 42:Root[0000, 0]@137..143#0 helper @@ -192,7 +192,7 @@ pub struct Foo { GROUP [] 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 IDENT 42:Root[0000, 0]@0..0#0 doc PUNCT 42:Root[0000, 0]@0..0#0 = [alone] - LITER 42:Root[0000, 0]@75..130#0 Str / The domain where this federated instance is running + LITER 42:Root[0000, 0]@75..130#0 Str The domain where this federated instance is running PUNCT 42:Root[0000, 0]@135..136#0 # [joint] GROUP [] 42:Root[0000, 0]@136..137#0 42:Root[0000, 0]@157..158#0 42:Root[0000, 0]@136..158#0 IDENT 42:Root[0000, 0]@137..143#0 helper diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/token_stream.rs index 2358f6963c79e..5201bb6aeb86b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/token_stream.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/token_stream.rs @@ -1,761 +1,767 @@ -//! The proc-macro server token stream implementation. - -use core::fmt; -use std::{mem, sync::Arc}; - -use intern::Symbol; -use rustc_lexer::{DocStyle, LiteralKind}; -use rustc_proc_macro::Delimiter; - -use crate::bridge::{DelimSpan, Group, Ident, LitKind, Literal, Punct, TokenTree}; - -/// Trait for allowing tests to parse tokenstreams with dynamic span ranges -pub(crate) trait SpanLike { - fn derive_ranged(&self, range: std::ops::Range) -> Self; -} - -#[derive(Clone)] -pub struct TokenStream(pub(crate) Arc>>); - -impl Default for TokenStream { - fn default() -> Self { - Self(Default::default()) - } -} - -impl TokenStream { - pub fn new(tts: Vec>) -> TokenStream { - TokenStream(Arc::new(tts)) - } - - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn iter(&self) -> TokenStreamIter<'_, S> { - TokenStreamIter::new(self) - } - - pub fn as_single_group(&self) -> Option<&Group> { - match &**self.0 { - [TokenTree::Group(group)] => Some(group), - _ => None, - } - } - - pub(crate) fn from_str(s: &str, span: S) -> Result - where - S: SpanLike + Copy, - { - let mut groups = Vec::new(); - groups.push((rustc_proc_macro::Delimiter::None, 0..0, vec![])); - let mut offset = 0; - let mut tokens = rustc_lexer::tokenize(s, rustc_lexer::FrontmatterAllowed::No).peekable(); - while let Some(token) = tokens.next() { - let range = offset..offset + token.len as usize; - offset += token.len as usize; - - let mut is_joint = || { - tokens.peek().is_some_and(|token| { - matches!( - token.kind, - rustc_lexer::TokenKind::RawLifetime - | rustc_lexer::TokenKind::GuardedStrPrefix - | rustc_lexer::TokenKind::Lifetime { .. } - | rustc_lexer::TokenKind::Semi - | rustc_lexer::TokenKind::Comma - | rustc_lexer::TokenKind::Dot - | rustc_lexer::TokenKind::OpenParen - | rustc_lexer::TokenKind::CloseParen - | rustc_lexer::TokenKind::OpenBrace - | rustc_lexer::TokenKind::CloseBrace - | rustc_lexer::TokenKind::OpenBracket - | rustc_lexer::TokenKind::CloseBracket - | rustc_lexer::TokenKind::At - | rustc_lexer::TokenKind::Pound - | rustc_lexer::TokenKind::Tilde - | rustc_lexer::TokenKind::Question - | rustc_lexer::TokenKind::Colon - | rustc_lexer::TokenKind::Dollar - | rustc_lexer::TokenKind::Eq - | rustc_lexer::TokenKind::Bang - | rustc_lexer::TokenKind::Lt - | rustc_lexer::TokenKind::Gt - | rustc_lexer::TokenKind::Minus - | rustc_lexer::TokenKind::And - | rustc_lexer::TokenKind::Or - | rustc_lexer::TokenKind::Plus - | rustc_lexer::TokenKind::Star - | rustc_lexer::TokenKind::Slash - | rustc_lexer::TokenKind::Percent - | rustc_lexer::TokenKind::Caret - ) - }) - }; - - let Some((open_delim, _, tokenstream)) = groups.last_mut() else { - return Err("Unbalanced delimiters".to_owned()); - }; - match token.kind { - rustc_lexer::TokenKind::OpenParen => { - groups.push((rustc_proc_macro::Delimiter::Parenthesis, range, vec![])) - } - rustc_lexer::TokenKind::CloseParen if *open_delim != Delimiter::Parenthesis => { - return if *open_delim == Delimiter::None { - Err("Unexpected ')'".to_owned()) - } else { - Err("Expected ')'".to_owned()) - }; - } - rustc_lexer::TokenKind::CloseParen => { - let (delimiter, open_range, stream) = groups.pop().unwrap(); - groups.last_mut().ok_or_else(|| "Unbalanced delimiters".to_owned())?.2.push( - TokenTree::Group(Group { - delimiter, - stream: if stream.is_empty() { - None - } else { - Some(TokenStream::new(stream)) - }, - span: DelimSpan { - entire: span.derive_ranged(open_range.start..range.end), - open: span.derive_ranged(open_range), - close: span.derive_ranged(range), - }, - }), - ); - } - rustc_lexer::TokenKind::OpenBrace => { - groups.push((rustc_proc_macro::Delimiter::Brace, range, vec![])) - } - rustc_lexer::TokenKind::CloseBrace if *open_delim != Delimiter::Brace => { - return if *open_delim == Delimiter::None { - Err("Unexpected '}'".to_owned()) - } else { - Err("Expected '}'".to_owned()) - }; - } - rustc_lexer::TokenKind::CloseBrace => { - let (delimiter, open_range, stream) = groups.pop().unwrap(); - groups.last_mut().ok_or_else(|| "Unbalanced delimiters".to_owned())?.2.push( - TokenTree::Group(Group { - delimiter, - stream: if stream.is_empty() { - None - } else { - Some(TokenStream::new(stream)) - }, - span: DelimSpan { - entire: span.derive_ranged(open_range.start..range.end), - open: span.derive_ranged(open_range), - close: span.derive_ranged(range), - }, - }), - ); - } - rustc_lexer::TokenKind::OpenBracket => { - groups.push((rustc_proc_macro::Delimiter::Bracket, range, vec![])) - } - rustc_lexer::TokenKind::CloseBracket if *open_delim != Delimiter::Bracket => { - return if *open_delim == Delimiter::None { - Err("Unexpected ']'".to_owned()) - } else { - Err("Expected ']'".to_owned()) - }; - } - rustc_lexer::TokenKind::CloseBracket => { - let (delimiter, open_range, stream) = groups.pop().unwrap(); - groups.last_mut().ok_or_else(|| "Unbalanced delimiters".to_owned())?.2.push( - TokenTree::Group(Group { - delimiter, - stream: if stream.is_empty() { - None - } else { - Some(TokenStream::new(stream)) - }, - span: DelimSpan { - entire: span.derive_ranged(open_range.start..range.end), - open: span.derive_ranged(open_range), - close: span.derive_ranged(range), - }, - }), - ); - } - rustc_lexer::TokenKind::LineComment { doc_style: None } - | rustc_lexer::TokenKind::BlockComment { doc_style: None, terminated: _ } => { - continue; - } - rustc_lexer::TokenKind::LineComment { doc_style: Some(doc_style) } => { - let text = &s[range.start + 2..range.end]; - tokenstream.push(TokenTree::Punct(Punct { ch: b'#', joint: false, span })); - if doc_style == DocStyle::Inner { - tokenstream.push(TokenTree::Punct(Punct { ch: b'!', joint: false, span })); - } - tokenstream.push(TokenTree::Group(Group { - delimiter: Delimiter::Bracket, - stream: Some(TokenStream::new(vec![ - TokenTree::Ident(Ident { - sym: Symbol::intern("doc"), - is_raw: false, - span, - }), - TokenTree::Punct(Punct { ch: b'=', joint: false, span }), - TokenTree::Literal(Literal { - kind: LitKind::Str, - symbol: Symbol::intern(&text.escape_debug().to_string()), - suffix: None, - span: span.derive_ranged(range), - }), - ])), - span: DelimSpan { open: span, close: span, entire: span }, - })); - } - rustc_lexer::TokenKind::BlockComment { doc_style: Some(doc_style), terminated } => { - let text = - &s[range.start + 2..if terminated { range.end - 2 } else { range.end }]; - tokenstream.push(TokenTree::Punct(Punct { ch: b'#', joint: false, span })); - if doc_style == DocStyle::Inner { - tokenstream.push(TokenTree::Punct(Punct { ch: b'!', joint: false, span })); - } - tokenstream.push(TokenTree::Group(Group { - delimiter: Delimiter::Bracket, - stream: Some(TokenStream::new(vec![ - TokenTree::Ident(Ident { - sym: Symbol::intern("doc"), - is_raw: false, - span, - }), - TokenTree::Punct(Punct { ch: b'=', joint: false, span }), - TokenTree::Literal(Literal { - kind: LitKind::Str, - symbol: Symbol::intern(&text.escape_debug().to_string()), - suffix: None, - span: span.derive_ranged(range), - }), - ])), - span: DelimSpan { open: span, close: span, entire: span }, - })); - } - rustc_lexer::TokenKind::Whitespace => continue, - rustc_lexer::TokenKind::Frontmatter { .. } => unreachable!(), - rustc_lexer::TokenKind::Unknown => { - return Err(format!("Unknown token: `{}`", &s[range])); - } - rustc_lexer::TokenKind::UnknownPrefix => { - return Err(format!("Unknown prefix: `{}`", &s[range])); - } - rustc_lexer::TokenKind::UnknownPrefixLifetime => { - return Err(format!("Unknown lifetime prefix: `{}`", &s[range])); - } - // FIXME: Error on edition >= 2024 ... I dont think the proc-macro server can fetch editions currently - // and whose edition is this? - rustc_lexer::TokenKind::GuardedStrPrefix => { - tokenstream.push(TokenTree::Punct(Punct { - ch: s.as_bytes()[range.start], - joint: true, - span: span.derive_ranged(range.start..range.start + 1), - })); - tokenstream.push(TokenTree::Punct(Punct { - ch: s.as_bytes()[range.start + 1], - joint: is_joint(), - span: span.derive_ranged(range.start + 1..range.end), - })) - } - rustc_lexer::TokenKind::Ident => tokenstream.push(TokenTree::Ident(Ident { - sym: Symbol::intern(&s[range.clone()]), - is_raw: false, - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::InvalidIdent => { - return Err(format!("Invalid identifier: `{}`", &s[range])); - } - rustc_lexer::TokenKind::RawIdent => { - let range = range.start + 2..range.end; - tokenstream.push(TokenTree::Ident(Ident { - sym: Symbol::intern(&s[range.clone()]), - is_raw: true, - span: span.derive_ranged(range), - })) - } - rustc_lexer::TokenKind::Literal { kind, suffix_start } => { - tokenstream.push(TokenTree::Literal(literal_from_lexer( - &s[range.clone()], - span.derive_ranged(range), - kind, - suffix_start, - ))) - } - rustc_lexer::TokenKind::RawLifetime => { - let range = range.start + 1 + 2..range.end; - tokenstream.push(TokenTree::Punct(Punct { - ch: b'\'', - joint: true, - span: span.derive_ranged(range.start..range.start + 1), - })); - tokenstream.push(TokenTree::Ident(Ident { - sym: Symbol::intern(&s[range.clone()]), - is_raw: true, - span: span.derive_ranged(range), - })) - } - rustc_lexer::TokenKind::Lifetime { starts_with_number } => { - if starts_with_number { - return Err("Lifetime cannot start with a number".to_owned()); - } - let range = range.start + 1..range.end; - tokenstream.push(TokenTree::Punct(Punct { - ch: b'\'', - joint: true, - span: span.derive_ranged(range.start..range.start + 1), - })); - tokenstream.push(TokenTree::Ident(Ident { - sym: Symbol::intern(&s[range.clone()]), - is_raw: false, - span: span.derive_ranged(range), - })) - } - rustc_lexer::TokenKind::Semi => tokenstream.push(TokenTree::Punct(Punct { - ch: b';', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Comma => tokenstream.push(TokenTree::Punct(Punct { - ch: b',', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Dot => tokenstream.push(TokenTree::Punct(Punct { - ch: b'.', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::At => tokenstream.push(TokenTree::Punct(Punct { - ch: b'@', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Pound => tokenstream.push(TokenTree::Punct(Punct { - ch: b'#', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Tilde => tokenstream.push(TokenTree::Punct(Punct { - ch: b'~', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Question => tokenstream.push(TokenTree::Punct(Punct { - ch: b'?', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Colon => tokenstream.push(TokenTree::Punct(Punct { - ch: b':', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Dollar => tokenstream.push(TokenTree::Punct(Punct { - ch: b'$', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Eq => tokenstream.push(TokenTree::Punct(Punct { - ch: b'=', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Bang => tokenstream.push(TokenTree::Punct(Punct { - ch: b'!', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Lt => tokenstream.push(TokenTree::Punct(Punct { - ch: b'<', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Gt => tokenstream.push(TokenTree::Punct(Punct { - ch: b'>', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Minus => tokenstream.push(TokenTree::Punct(Punct { - ch: b'-', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::And => tokenstream.push(TokenTree::Punct(Punct { - ch: b'&', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Or => tokenstream.push(TokenTree::Punct(Punct { - ch: b'|', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Plus => tokenstream.push(TokenTree::Punct(Punct { - ch: b'+', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Star => tokenstream.push(TokenTree::Punct(Punct { - ch: b'*', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Slash => tokenstream.push(TokenTree::Punct(Punct { - ch: b'/', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Caret => tokenstream.push(TokenTree::Punct(Punct { - ch: b'^', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Percent => tokenstream.push(TokenTree::Punct(Punct { - ch: b'%', - joint: is_joint(), - span: span.derive_ranged(range), - })), - rustc_lexer::TokenKind::Eof => break, - } - } - if let Some((Delimiter::None, _, tokentrees)) = groups.pop() - && groups.is_empty() - { - Ok(TokenStream::new(tokentrees)) - } else { - Err("Mismatched token groups".to_owned()) - } - } -} - -impl fmt::Display for TokenStream { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut emit_whitespace = false; - for tt in self.0.iter() { - display_token_tree(tt, &mut emit_whitespace, f)?; - } - Ok(()) - } -} - -fn display_token_tree( - tt: &TokenTree, - emit_whitespace: &mut bool, - f: &mut std::fmt::Formatter<'_>, -) -> std::fmt::Result { - if mem::take(emit_whitespace) { - write!(f, " ")?; - } - match tt { - TokenTree::Group(Group { delimiter, stream, span: _ }) => { - write!( - f, - "{}", - match delimiter { - rustc_proc_macro::Delimiter::Parenthesis => "(", - rustc_proc_macro::Delimiter::Brace => "{", - rustc_proc_macro::Delimiter::Bracket => "[", - rustc_proc_macro::Delimiter::None => "", - } - )?; - if let Some(stream) = stream { - write!(f, "{stream}")?; - } - write!( - f, - "{}", - match delimiter { - rustc_proc_macro::Delimiter::Parenthesis => ")", - rustc_proc_macro::Delimiter::Brace => "}", - rustc_proc_macro::Delimiter::Bracket => "]", - rustc_proc_macro::Delimiter::None => "", - } - )?; - } - TokenTree::Punct(Punct { ch, joint, span: _ }) => { - *emit_whitespace = !*joint; - write!(f, "{}", *ch as char)?; - } - TokenTree::Ident(Ident { sym, is_raw, span: _ }) => { - if *is_raw { - write!(f, "r#")?; - } - write!(f, "{sym}")?; - *emit_whitespace = true; - } - TokenTree::Literal(lit) => { - display_fmt_literal(lit, f)?; - let joint = match lit.kind { - LitKind::Str - | LitKind::StrRaw(_) - | LitKind::ByteStr - | LitKind::ByteStrRaw(_) - | LitKind::CStr - | LitKind::CStrRaw(_) => true, - _ => false, - }; - *emit_whitespace = !joint; - } - } - Ok(()) -} - -pub fn literal_to_string(literal: &Literal) -> String { - let mut buf = String::new(); - display_fmt_literal(literal, &mut buf).unwrap(); - buf -} - -fn display_fmt_literal(literal: &Literal, f: &mut impl std::fmt::Write) -> fmt::Result { - match literal.kind { - LitKind::Byte => write!(f, "b'{}'", literal.symbol), - LitKind::Char => write!(f, "'{}'", literal.symbol), - LitKind::Integer | LitKind::Float | LitKind::ErrWithGuar => { - write!(f, "{}", literal.symbol) - } - LitKind::Str => write!(f, "\"{}\"", literal.symbol), - LitKind::ByteStr => write!(f, "b\"{}\"", literal.symbol), - LitKind::CStr => write!(f, "c\"{}\"", literal.symbol), - LitKind::StrRaw(num_of_hashes) => { - let num_of_hashes = num_of_hashes as usize; - write!( - f, - r#"r{0:# { - let num_of_hashes = num_of_hashes as usize; - write!( - f, - r#"br{0:# { - let num_of_hashes = num_of_hashes as usize; - write!( - f, - r#"cr{0:# fmt::Debug for TokenStream { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - debug_token_stream(self, 0, f) - } -} - -fn debug_token_stream( - ts: &TokenStream, - depth: usize, - f: &mut std::fmt::Formatter<'_>, -) -> std::fmt::Result { - for tt in ts.0.iter() { - debug_token_tree(tt, depth, f)?; - } - Ok(()) -} - -fn debug_token_tree( - tt: &TokenTree, - depth: usize, - f: &mut std::fmt::Formatter<'_>, -) -> std::fmt::Result { - write!(f, "{:indent$}", "", indent = depth * 2)?; - match tt { - TokenTree::Group(Group { delimiter, stream, span }) => { - writeln!( - f, - "GROUP {}{} {:#?} {:#?} {:#?}", - match delimiter { - rustc_proc_macro::Delimiter::Parenthesis => "(", - rustc_proc_macro::Delimiter::Brace => "{", - rustc_proc_macro::Delimiter::Bracket => "[", - rustc_proc_macro::Delimiter::None => "$", - }, - match delimiter { - rustc_proc_macro::Delimiter::Parenthesis => ")", - rustc_proc_macro::Delimiter::Brace => "}", - rustc_proc_macro::Delimiter::Bracket => "]", - rustc_proc_macro::Delimiter::None => "$", - }, - span.open, - span.close, - span.entire, - )?; - if let Some(stream) = stream { - debug_token_stream(stream, depth + 1, f)?; - } - return Ok(()); - } - TokenTree::Punct(Punct { ch, joint, span }) => write!( - f, - "PUNCT {span:#?} {} {}", - *ch as char, - if *joint { "[joint]" } else { "[alone]" } - )?, - TokenTree::Ident(Ident { sym, is_raw, span }) => { - write!(f, "IDENT {span:#?} ")?; - if *is_raw { - write!(f, "r#")?; - } - write!(f, "{sym}")?; - } - TokenTree::Literal(Literal { kind, symbol, suffix, span }) => write!( - f, - "LITER {span:#?} {kind:?} {symbol}{}", - match suffix { - Some(suffix) => suffix.clone(), - None => Symbol::intern(""), - } - )?, - } - writeln!(f) -} - -impl TokenStream { - /// Push `tt` onto the end of the stream, possibly gluing it to the last - /// token. Uses `make_mut` to maximize efficiency. - pub(crate) fn push_tree(&mut self, tt: TokenTree) { - let vec_mut = Arc::make_mut(&mut self.0); - vec_mut.push(tt); - } - - /// Push `stream` onto the end of the stream, possibly gluing the first - /// token tree to the last token. (No other token trees will be glued.) - /// Uses `make_mut` to maximize efficiency. - pub(crate) fn push_stream(&mut self, stream: TokenStream) { - let vec_mut = Arc::make_mut(&mut self.0); - - let stream_iter = stream.0.iter().cloned(); - - vec_mut.extend(stream_iter); - } -} - -impl FromIterator> for TokenStream { - fn from_iter>>(iter: I) -> Self { - TokenStream::new(iter.into_iter().collect::>>()) - } -} - -#[derive(Clone)] -pub struct TokenStreamIter<'t, S> { - stream: &'t TokenStream, - index: usize, -} - -impl<'t, S> TokenStreamIter<'t, S> { - fn new(stream: &'t TokenStream) -> Self { - TokenStreamIter { stream, index: 0 } - } -} - -impl<'t, S> Iterator for TokenStreamIter<'t, S> { - type Item = &'t TokenTree; - - fn next(&mut self) -> Option<&'t TokenTree> { - self.stream.0.get(self.index).map(|tree| { - self.index += 1; - tree - }) - } -} - -pub(super) fn literal_from_lexer( - s: &str, - span: Span, - kind: rustc_lexer::LiteralKind, - suffix_start: u32, -) -> Literal { - let (kind, start_offset, end_offset) = match kind { - LiteralKind::Int { .. } => (LitKind::Integer, 0, 0), - LiteralKind::Float { .. } => (LitKind::Float, 0, 0), - LiteralKind::Char { terminated } => (LitKind::Char, 1, terminated as usize), - LiteralKind::Byte { terminated } => (LitKind::Byte, 2, terminated as usize), - LiteralKind::Str { terminated } => (LitKind::Str, 1, terminated as usize), - LiteralKind::ByteStr { terminated } => (LitKind::ByteStr, 2, terminated as usize), - LiteralKind::CStr { terminated } => (LitKind::CStr, 2, terminated as usize), - LiteralKind::RawStr { n_hashes } => ( - LitKind::StrRaw(n_hashes.unwrap_or_default()), - 2 + n_hashes.unwrap_or_default() as usize, - 1 + n_hashes.unwrap_or_default() as usize, - ), - LiteralKind::RawByteStr { n_hashes } => ( - LitKind::ByteStrRaw(n_hashes.unwrap_or_default()), - 3 + n_hashes.unwrap_or_default() as usize, - 1 + n_hashes.unwrap_or_default() as usize, - ), - LiteralKind::RawCStr { n_hashes } => ( - LitKind::CStrRaw(n_hashes.unwrap_or_default()), - 3 + n_hashes.unwrap_or_default() as usize, - 1 + n_hashes.unwrap_or_default() as usize, - ), - }; - - let (lit, suffix) = s.split_at(suffix_start as usize); - let lit = &lit[start_offset..lit.len() - end_offset]; - let suffix = match suffix { - "" | "_" => None, - suffix => Some(Symbol::intern(suffix)), - }; - - Literal { kind, symbol: Symbol::intern(lit), suffix, span } -} - -impl SpanLike for crate::SpanId { - fn derive_ranged(&self, _: std::ops::Range) -> Self { - *self - } -} - -impl SpanLike for () { - fn derive_ranged(&self, _: std::ops::Range) -> Self { - *self - } -} - -impl SpanLike for crate::Span { - fn derive_ranged(&self, range: std::ops::Range) -> Self { - crate::Span { - range: span::TextRange::new( - span::TextSize::new(range.start as u32), - span::TextSize::new(range.end as u32), - ), - anchor: self.anchor, - ctx: self.ctx, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn ts_to_string() { - let token_stream = - TokenStream::from_str("{} () [] <> ;/., \"gfhdgfuiofghd\" 0f32 r#\"dff\"# 'r#lt", ()) - .unwrap(); - assert_eq!(token_stream.to_string(), "{}()[]<> ;/., \"gfhdgfuiofghd\"0f32 r#\"dff\"#'r#lt"); - } -} +//! The proc-macro server token stream implementation. + +use core::fmt; +use std::{mem, sync::Arc}; + +use intern::Symbol; +use rustc_lexer::{DocStyle, LiteralKind}; +use rustc_proc_macro::Delimiter; + +use crate::bridge::{DelimSpan, Group, Ident, LitKind, Literal, Punct, TokenTree}; + +/// Trait for allowing tests to parse tokenstreams with dynamic span ranges +pub(crate) trait SpanLike { + fn derive_ranged(&self, range: std::ops::Range) -> Self; +} + +#[derive(Clone)] +pub struct TokenStream(pub(crate) Arc>>); + +impl Default for TokenStream { + fn default() -> Self { + Self(Default::default()) + } +} + +impl TokenStream { + pub fn new(tts: Vec>) -> TokenStream { + TokenStream(Arc::new(tts)) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn iter(&self) -> TokenStreamIter<'_, S> { + TokenStreamIter::new(self) + } + + pub fn as_single_group(&self) -> Option<&Group> { + match &**self.0 { + [TokenTree::Group(group)] => Some(group), + _ => None, + } + } + + pub(crate) fn from_str(s: &str, span: S) -> Result + where + S: SpanLike + Copy, + { + let mut groups = Vec::new(); + groups.push((rustc_proc_macro::Delimiter::None, 0..0, vec![])); + let mut offset = 0; + let mut tokens = rustc_lexer::tokenize(s, rustc_lexer::FrontmatterAllowed::No).peekable(); + while let Some(token) = tokens.next() { + let range = offset..offset + token.len as usize; + offset += token.len as usize; + + let mut is_joint = || { + tokens.peek().is_some_and(|token| { + matches!( + token.kind, + rustc_lexer::TokenKind::RawLifetime + | rustc_lexer::TokenKind::GuardedStrPrefix + | rustc_lexer::TokenKind::Lifetime { .. } + | rustc_lexer::TokenKind::Semi + | rustc_lexer::TokenKind::Comma + | rustc_lexer::TokenKind::Dot + | rustc_lexer::TokenKind::OpenParen + | rustc_lexer::TokenKind::CloseParen + | rustc_lexer::TokenKind::OpenBrace + | rustc_lexer::TokenKind::CloseBrace + | rustc_lexer::TokenKind::OpenBracket + | rustc_lexer::TokenKind::CloseBracket + | rustc_lexer::TokenKind::At + | rustc_lexer::TokenKind::Pound + | rustc_lexer::TokenKind::Tilde + | rustc_lexer::TokenKind::Question + | rustc_lexer::TokenKind::Colon + | rustc_lexer::TokenKind::Dollar + | rustc_lexer::TokenKind::Eq + | rustc_lexer::TokenKind::Bang + | rustc_lexer::TokenKind::Lt + | rustc_lexer::TokenKind::Gt + | rustc_lexer::TokenKind::Minus + | rustc_lexer::TokenKind::And + | rustc_lexer::TokenKind::Or + | rustc_lexer::TokenKind::Plus + | rustc_lexer::TokenKind::Star + | rustc_lexer::TokenKind::Slash + | rustc_lexer::TokenKind::Percent + | rustc_lexer::TokenKind::Caret + ) + }) + }; + + let Some((open_delim, _, tokenstream)) = groups.last_mut() else { + return Err("Unbalanced delimiters".to_owned()); + }; + match token.kind { + rustc_lexer::TokenKind::OpenParen => { + groups.push((rustc_proc_macro::Delimiter::Parenthesis, range, vec![])) + } + rustc_lexer::TokenKind::CloseParen if *open_delim != Delimiter::Parenthesis => { + return if *open_delim == Delimiter::None { + Err("Unexpected ')'".to_owned()) + } else { + Err("Expected ')'".to_owned()) + }; + } + rustc_lexer::TokenKind::CloseParen => { + let (delimiter, open_range, stream) = groups.pop().unwrap(); + groups.last_mut().ok_or_else(|| "Unbalanced delimiters".to_owned())?.2.push( + TokenTree::Group(Group { + delimiter, + stream: if stream.is_empty() { + None + } else { + Some(TokenStream::new(stream)) + }, + span: DelimSpan { + entire: span.derive_ranged(open_range.start..range.end), + open: span.derive_ranged(open_range), + close: span.derive_ranged(range), + }, + }), + ); + } + rustc_lexer::TokenKind::OpenBrace => { + groups.push((rustc_proc_macro::Delimiter::Brace, range, vec![])) + } + rustc_lexer::TokenKind::CloseBrace if *open_delim != Delimiter::Brace => { + return if *open_delim == Delimiter::None { + Err("Unexpected '}'".to_owned()) + } else { + Err("Expected '}'".to_owned()) + }; + } + rustc_lexer::TokenKind::CloseBrace => { + let (delimiter, open_range, stream) = groups.pop().unwrap(); + groups.last_mut().ok_or_else(|| "Unbalanced delimiters".to_owned())?.2.push( + TokenTree::Group(Group { + delimiter, + stream: if stream.is_empty() { + None + } else { + Some(TokenStream::new(stream)) + }, + span: DelimSpan { + entire: span.derive_ranged(open_range.start..range.end), + open: span.derive_ranged(open_range), + close: span.derive_ranged(range), + }, + }), + ); + } + rustc_lexer::TokenKind::OpenBracket => { + groups.push((rustc_proc_macro::Delimiter::Bracket, range, vec![])) + } + rustc_lexer::TokenKind::CloseBracket if *open_delim != Delimiter::Bracket => { + return if *open_delim == Delimiter::None { + Err("Unexpected ']'".to_owned()) + } else { + Err("Expected ']'".to_owned()) + }; + } + rustc_lexer::TokenKind::CloseBracket => { + let (delimiter, open_range, stream) = groups.pop().unwrap(); + groups.last_mut().ok_or_else(|| "Unbalanced delimiters".to_owned())?.2.push( + TokenTree::Group(Group { + delimiter, + stream: if stream.is_empty() { + None + } else { + Some(TokenStream::new(stream)) + }, + span: DelimSpan { + entire: span.derive_ranged(open_range.start..range.end), + open: span.derive_ranged(open_range), + close: span.derive_ranged(range), + }, + }), + ); + } + rustc_lexer::TokenKind::LineComment { doc_style: None } + | rustc_lexer::TokenKind::BlockComment { doc_style: None, terminated: _ } => { + continue; + } + rustc_lexer::TokenKind::LineComment { doc_style: Some(doc_style) } => { + let text = &s[range.start + 3..range.end]; + tokenstream.push(TokenTree::Punct(Punct { ch: b'#', joint: false, span })); + if doc_style == DocStyle::Inner { + tokenstream.push(TokenTree::Punct(Punct { ch: b'!', joint: false, span })); + } + tokenstream.push(TokenTree::Group(Group { + delimiter: Delimiter::Bracket, + stream: Some(TokenStream::new(vec![ + TokenTree::Ident(Ident { + sym: Symbol::intern("doc"), + is_raw: false, + span, + }), + TokenTree::Punct(Punct { ch: b'=', joint: false, span }), + TokenTree::Literal(Literal { + kind: LitKind::Str, + symbol: Symbol::intern(&text.escape_debug().to_string()), + suffix: None, + span: span.derive_ranged(range), + }), + ])), + span: DelimSpan { open: span, close: span, entire: span }, + })); + } + rustc_lexer::TokenKind::BlockComment { doc_style: Some(doc_style), terminated } => { + let text = + &s[range.start + 3..if terminated { range.end - 2 } else { range.end }]; + tokenstream.push(TokenTree::Punct(Punct { ch: b'#', joint: false, span })); + if doc_style == DocStyle::Inner { + tokenstream.push(TokenTree::Punct(Punct { ch: b'!', joint: false, span })); + } + tokenstream.push(TokenTree::Group(Group { + delimiter: Delimiter::Bracket, + stream: Some(TokenStream::new(vec![ + TokenTree::Ident(Ident { + sym: Symbol::intern("doc"), + is_raw: false, + span, + }), + TokenTree::Punct(Punct { ch: b'=', joint: false, span }), + TokenTree::Literal(Literal { + kind: LitKind::Str, + symbol: Symbol::intern(&text.escape_debug().to_string()), + suffix: None, + span: span.derive_ranged(range), + }), + ])), + span: DelimSpan { open: span, close: span, entire: span }, + })); + } + rustc_lexer::TokenKind::Whitespace => continue, + rustc_lexer::TokenKind::Frontmatter { .. } => unreachable!(), + rustc_lexer::TokenKind::Unknown => { + return Err(format!("Unknown token: `{}`", &s[range])); + } + rustc_lexer::TokenKind::UnknownPrefix => { + return Err(format!("Unknown prefix: `{}`", &s[range])); + } + rustc_lexer::TokenKind::UnknownPrefixLifetime => { + return Err(format!("Unknown lifetime prefix: `{}`", &s[range])); + } + // FIXME: Error on edition >= 2024 ... I dont think the proc-macro server can fetch editions currently + // and whose edition is this? + rustc_lexer::TokenKind::GuardedStrPrefix => { + tokenstream.push(TokenTree::Punct(Punct { + ch: s.as_bytes()[range.start], + joint: true, + span: span.derive_ranged(range.start..range.start + 1), + })); + tokenstream.push(TokenTree::Punct(Punct { + ch: s.as_bytes()[range.start + 1], + joint: is_joint(), + span: span.derive_ranged(range.start + 1..range.end), + })) + } + rustc_lexer::TokenKind::Ident => tokenstream.push(TokenTree::Ident(Ident { + sym: Symbol::intern(&s[range.clone()]), + is_raw: false, + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::InvalidIdent => { + return Err(format!("Invalid identifier: `{}`", &s[range])); + } + rustc_lexer::TokenKind::RawIdent => { + let range = range.start + 2..range.end; + tokenstream.push(TokenTree::Ident(Ident { + sym: Symbol::intern(&s[range.clone()]), + is_raw: true, + span: span.derive_ranged(range), + })) + } + rustc_lexer::TokenKind::Literal { kind, suffix_start } => { + tokenstream.push(TokenTree::Literal(literal_from_lexer( + &s[range.clone()], + span.derive_ranged(range), + kind, + suffix_start, + ))) + } + rustc_lexer::TokenKind::RawLifetime => { + let range = range.start + 1 + 2..range.end; + tokenstream.push(TokenTree::Punct(Punct { + ch: b'\'', + joint: true, + span: span.derive_ranged(range.start..range.start + 1), + })); + tokenstream.push(TokenTree::Ident(Ident { + sym: Symbol::intern(&s[range.clone()]), + is_raw: true, + span: span.derive_ranged(range), + })) + } + rustc_lexer::TokenKind::Lifetime { starts_with_number } => { + if starts_with_number { + return Err("Lifetime cannot start with a number".to_owned()); + } + let range = range.start + 1..range.end; + tokenstream.push(TokenTree::Punct(Punct { + ch: b'\'', + joint: true, + span: span.derive_ranged(range.start..range.start + 1), + })); + tokenstream.push(TokenTree::Ident(Ident { + sym: Symbol::intern(&s[range.clone()]), + is_raw: false, + span: span.derive_ranged(range), + })) + } + rustc_lexer::TokenKind::Semi => tokenstream.push(TokenTree::Punct(Punct { + ch: b';', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Comma => tokenstream.push(TokenTree::Punct(Punct { + ch: b',', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Dot => tokenstream.push(TokenTree::Punct(Punct { + ch: b'.', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::At => tokenstream.push(TokenTree::Punct(Punct { + ch: b'@', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Pound => tokenstream.push(TokenTree::Punct(Punct { + ch: b'#', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Tilde => tokenstream.push(TokenTree::Punct(Punct { + ch: b'~', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Question => tokenstream.push(TokenTree::Punct(Punct { + ch: b'?', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Colon => tokenstream.push(TokenTree::Punct(Punct { + ch: b':', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Dollar => tokenstream.push(TokenTree::Punct(Punct { + ch: b'$', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Eq => tokenstream.push(TokenTree::Punct(Punct { + ch: b'=', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Bang => tokenstream.push(TokenTree::Punct(Punct { + ch: b'!', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Lt => tokenstream.push(TokenTree::Punct(Punct { + ch: b'<', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Gt => tokenstream.push(TokenTree::Punct(Punct { + ch: b'>', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Minus => tokenstream.push(TokenTree::Punct(Punct { + ch: b'-', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::And => tokenstream.push(TokenTree::Punct(Punct { + ch: b'&', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Or => tokenstream.push(TokenTree::Punct(Punct { + ch: b'|', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Plus => tokenstream.push(TokenTree::Punct(Punct { + ch: b'+', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Star => tokenstream.push(TokenTree::Punct(Punct { + ch: b'*', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Slash => tokenstream.push(TokenTree::Punct(Punct { + ch: b'/', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Caret => tokenstream.push(TokenTree::Punct(Punct { + ch: b'^', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Percent => tokenstream.push(TokenTree::Punct(Punct { + ch: b'%', + joint: is_joint(), + span: span.derive_ranged(range), + })), + rustc_lexer::TokenKind::Eof => break, + } + } + if let Some((Delimiter::None, _, tokentrees)) = groups.pop() + && groups.is_empty() + { + Ok(TokenStream::new(tokentrees)) + } else { + Err("Mismatched token groups".to_owned()) + } + } +} + +impl fmt::Display for TokenStream { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut emit_whitespace = false; + for tt in self.0.iter() { + display_token_tree(tt, &mut emit_whitespace, f)?; + } + Ok(()) + } +} + +fn display_token_tree( + tt: &TokenTree, + emit_whitespace: &mut bool, + f: &mut std::fmt::Formatter<'_>, +) -> std::fmt::Result { + if mem::take(emit_whitespace) { + write!(f, " ")?; + } + match tt { + TokenTree::Group(Group { delimiter, stream, span: _ }) => { + write!( + f, + "{}", + match delimiter { + rustc_proc_macro::Delimiter::Parenthesis => "(", + rustc_proc_macro::Delimiter::Brace => "{", + rustc_proc_macro::Delimiter::Bracket => "[", + rustc_proc_macro::Delimiter::None => "", + } + )?; + if let Some(stream) = stream { + write!(f, "{stream}")?; + } + write!( + f, + "{}", + match delimiter { + rustc_proc_macro::Delimiter::Parenthesis => ")", + rustc_proc_macro::Delimiter::Brace => "}", + rustc_proc_macro::Delimiter::Bracket => "]", + rustc_proc_macro::Delimiter::None => "", + } + )?; + } + TokenTree::Punct(Punct { ch, joint, span: _ }) => { + *emit_whitespace = !*joint; + write!(f, "{}", *ch as char)?; + } + TokenTree::Ident(Ident { sym, is_raw, span: _ }) => { + if *is_raw { + write!(f, "r#")?; + } + write!(f, "{sym}")?; + *emit_whitespace = true; + } + TokenTree::Literal(lit) => { + display_fmt_literal(lit, f)?; + let joint = match lit.kind { + LitKind::Str + | LitKind::StrRaw(_) + | LitKind::ByteStr + | LitKind::ByteStrRaw(_) + | LitKind::CStr + | LitKind::CStrRaw(_) => true, + _ => false, + }; + *emit_whitespace = !joint; + } + } + Ok(()) +} + +pub fn literal_to_string(literal: &Literal) -> String { + let mut buf = String::new(); + display_fmt_literal(literal, &mut buf).unwrap(); + buf +} + +fn display_fmt_literal(literal: &Literal, f: &mut impl std::fmt::Write) -> fmt::Result { + match literal.kind { + LitKind::Byte => write!(f, "b'{}'", literal.symbol), + LitKind::Char => write!(f, "'{}'", literal.symbol), + LitKind::Integer | LitKind::Float | LitKind::ErrWithGuar => { + write!(f, "{}", literal.symbol) + } + LitKind::Str => write!(f, "\"{}\"", literal.symbol), + LitKind::ByteStr => write!(f, "b\"{}\"", literal.symbol), + LitKind::CStr => write!(f, "c\"{}\"", literal.symbol), + LitKind::StrRaw(num_of_hashes) => { + let num_of_hashes = num_of_hashes as usize; + write!( + f, + r#"r{0:# { + let num_of_hashes = num_of_hashes as usize; + write!( + f, + r#"br{0:# { + let num_of_hashes = num_of_hashes as usize; + write!( + f, + r#"cr{0:# fmt::Debug for TokenStream { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + debug_token_stream(self, 0, f) + } +} + +fn debug_token_stream( + ts: &TokenStream, + depth: usize, + f: &mut std::fmt::Formatter<'_>, +) -> std::fmt::Result { + for tt in ts.0.iter() { + debug_token_tree(tt, depth, f)?; + } + Ok(()) +} + +fn debug_token_tree( + tt: &TokenTree, + depth: usize, + f: &mut std::fmt::Formatter<'_>, +) -> std::fmt::Result { + write!(f, "{:indent$}", "", indent = depth * 2)?; + match tt { + TokenTree::Group(Group { delimiter, stream, span }) => { + writeln!( + f, + "GROUP {}{} {:#?} {:#?} {:#?}", + match delimiter { + rustc_proc_macro::Delimiter::Parenthesis => "(", + rustc_proc_macro::Delimiter::Brace => "{", + rustc_proc_macro::Delimiter::Bracket => "[", + rustc_proc_macro::Delimiter::None => "$", + }, + match delimiter { + rustc_proc_macro::Delimiter::Parenthesis => ")", + rustc_proc_macro::Delimiter::Brace => "}", + rustc_proc_macro::Delimiter::Bracket => "]", + rustc_proc_macro::Delimiter::None => "$", + }, + span.open, + span.close, + span.entire, + )?; + if let Some(stream) = stream { + debug_token_stream(stream, depth + 1, f)?; + } + return Ok(()); + } + TokenTree::Punct(Punct { ch, joint, span }) => write!( + f, + "PUNCT {span:#?} {} {}", + *ch as char, + if *joint { "[joint]" } else { "[alone]" } + )?, + TokenTree::Ident(Ident { sym, is_raw, span }) => { + write!(f, "IDENT {span:#?} ")?; + if *is_raw { + write!(f, "r#")?; + } + write!(f, "{sym}")?; + } + TokenTree::Literal(Literal { kind, symbol, suffix, span }) => write!( + f, + "LITER {span:#?} {kind:?} {symbol}{}", + match suffix { + Some(suffix) => suffix.clone(), + None => Symbol::intern(""), + } + )?, + } + writeln!(f) +} + +impl TokenStream { + /// Push `tt` onto the end of the stream, possibly gluing it to the last + /// token. Uses `make_mut` to maximize efficiency. + pub(crate) fn push_tree(&mut self, tt: TokenTree) { + let vec_mut = Arc::make_mut(&mut self.0); + vec_mut.push(tt); + } + + /// Push `stream` onto the end of the stream, possibly gluing the first + /// token tree to the last token. (No other token trees will be glued.) + /// Uses `make_mut` to maximize efficiency. + pub(crate) fn push_stream(&mut self, stream: TokenStream) { + let vec_mut = Arc::make_mut(&mut self.0); + + let stream_iter = stream.0.iter().cloned(); + + vec_mut.extend(stream_iter); + } +} + +impl FromIterator> for TokenStream { + fn from_iter>>(iter: I) -> Self { + TokenStream::new(iter.into_iter().collect::>>()) + } +} + +#[derive(Clone)] +pub struct TokenStreamIter<'t, S> { + stream: &'t TokenStream, + index: usize, +} + +impl<'t, S> TokenStreamIter<'t, S> { + fn new(stream: &'t TokenStream) -> Self { + TokenStreamIter { stream, index: 0 } + } +} + +impl<'t, S> Iterator for TokenStreamIter<'t, S> { + type Item = &'t TokenTree; + + fn next(&mut self) -> Option<&'t TokenTree> { + self.stream.0.get(self.index).map(|tree| { + self.index += 1; + tree + }) + } +} + +pub(super) fn literal_from_lexer( + s: &str, + span: Span, + kind: rustc_lexer::LiteralKind, + suffix_start: u32, +) -> Literal { + let (kind, start_offset, end_offset) = match kind { + LiteralKind::Int { .. } => (LitKind::Integer, 0, 0), + LiteralKind::Float { .. } => (LitKind::Float, 0, 0), + LiteralKind::Char { terminated } => (LitKind::Char, 1, terminated as usize), + LiteralKind::Byte { terminated } => (LitKind::Byte, 2, terminated as usize), + LiteralKind::Str { terminated } => (LitKind::Str, 1, terminated as usize), + LiteralKind::ByteStr { terminated } => (LitKind::ByteStr, 2, terminated as usize), + LiteralKind::CStr { terminated } => (LitKind::CStr, 2, terminated as usize), + LiteralKind::RawStr { n_hashes } => ( + LitKind::StrRaw(n_hashes.unwrap_or_default()), + 2 + n_hashes.unwrap_or_default() as usize, + 1 + n_hashes.unwrap_or_default() as usize, + ), + LiteralKind::RawByteStr { n_hashes } => ( + LitKind::ByteStrRaw(n_hashes.unwrap_or_default()), + 3 + n_hashes.unwrap_or_default() as usize, + 1 + n_hashes.unwrap_or_default() as usize, + ), + LiteralKind::RawCStr { n_hashes } => ( + LitKind::CStrRaw(n_hashes.unwrap_or_default()), + 3 + n_hashes.unwrap_or_default() as usize, + 1 + n_hashes.unwrap_or_default() as usize, + ), + }; + + let (lit, suffix) = s.split_at(suffix_start as usize); + let lit = &lit[start_offset..lit.len() - end_offset]; + let suffix = match suffix { + "" | "_" => None, + suffix => Some(Symbol::intern(suffix)), + }; + + Literal { kind, symbol: Symbol::intern(lit), suffix, span } +} + +impl SpanLike for crate::SpanId { + fn derive_ranged(&self, _: std::ops::Range) -> Self { + *self + } +} + +impl SpanLike for () { + fn derive_ranged(&self, _: std::ops::Range) -> Self { + *self + } +} + +impl SpanLike for crate::Span { + fn derive_ranged(&self, range: std::ops::Range) -> Self { + crate::Span { + range: span::TextRange::new( + span::TextSize::new(range.start as u32), + span::TextSize::new(range.end as u32), + ), + anchor: self.anchor, + ctx: self.ctx, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn ts_to_string() { + let token_stream = + TokenStream::from_str("{} () [] <> ;/., \"gfhdgfuiofghd\" 0f32 r#\"dff\"# 'r#lt", ()) + .unwrap(); + assert_eq!(token_stream.to_string(), "{}()[]<> ;/., \"gfhdgfuiofghd\"0f32 r#\"dff\"#'r#lt"); + } + + #[test] + fn doc_comment_from_str() { + let token_stream = TokenStream::from_str("/// foo", ()).unwrap(); + assert_eq!(token_stream.to_string(), r#"# [doc = " foo"]"#); + } +} From 325cfdc0420359af60d83d73a9bb83cdc622deb2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 28 Jun 2026 01:44:18 +0300 Subject: [PATCH 17/78] Change some things for `#[doc = macro!()]` expansion - Increase the recursion depth when handling the expansion and not when expanding, so that we actually respect the recursion limit. - Reuse the already-available span map on expansion. - Have only one context struct. --- .../crates/hir-def/src/attrs/docs.rs | 79 +++++++++---------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs index 1e0fa0ce9d454..ecf73e4555db7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs @@ -327,9 +327,6 @@ struct DocMacroExpander<'db> { krate: Crate, recursion_depth: usize, recursion_limit: usize, -} - -struct DocExprSourceCtx<'db> { resolver: Resolver<'db>, file_id: HirFileId, ast_id_map: &'db AstIdMap, @@ -338,12 +335,11 @@ struct DocExprSourceCtx<'db> { fn expand_doc_expr_via_macro_pipeline<'db>( expander: &mut DocMacroExpander<'db>, - source_ctx: &DocExprSourceCtx<'db>, expr: ast::Expr, ) -> Option { match expr { ast::Expr::ParenExpr(paren_expr) => { - expand_doc_expr_via_macro_pipeline(expander, source_ctx, paren_expr.expr()?) + expand_doc_expr_via_macro_pipeline(expander, paren_expr.expr()?) } ast::Expr::Literal(literal) => match literal.kind() { ast::LiteralKind::String(string) => string.value().ok().map(Into::into), @@ -351,9 +347,7 @@ fn expand_doc_expr_via_macro_pipeline<'db>( }, ast::Expr::MacroExpr(macro_expr) => { let macro_call = macro_expr.macro_call()?; - let (expr, new_source_ctx) = expand_doc_macro_call(expander, source_ctx, macro_call)?; - // After expansion, the expr lives in the expansion file; use its source context. - expand_doc_expr_via_macro_pipeline(expander, &new_source_ctx, expr) + expand_doc_macro_call(expander, macro_call) } _ => None, } @@ -361,19 +355,18 @@ fn expand_doc_expr_via_macro_pipeline<'db>( fn expand_doc_macro_call<'db>( expander: &mut DocMacroExpander<'db>, - source_ctx: &DocExprSourceCtx<'db>, macro_call: ast::MacroCall, -) -> Option<(ast::Expr, DocExprSourceCtx<'db>)> { +) -> Option { if expander.recursion_depth >= expander.recursion_limit { return None; } let path = macro_call.path()?; let mod_path = ModPath::from_src(expander.db, path, &mut |range| { - source_ctx.span_map.span_for_range(range).ctx + expander.span_map.span_for_range(range).ctx })?; - let call_site = source_ctx.span_map.span_for_range(macro_call.syntax().text_range()); - let ast_id = AstId::new(source_ctx.file_id, source_ctx.ast_id_map.ast_id(¯o_call)); + let call_site = expander.span_map.span_for_range(macro_call.syntax().text_range()); + let ast_id = AstId::new(expander.file_id, expander.ast_id_map.ast_id(¯o_call)); let call_id = macro_call_as_call_id( expander.db, ast_id, @@ -382,29 +375,35 @@ fn expand_doc_macro_call<'db>( ExpandTo::Expr, expander.krate, |path| { - source_ctx.resolver.resolve_path_as_macro_def(expander.db, path, Some(MacroSubNs::Bang)) + expander.resolver.resolve_path_as_macro_def(expander.db, path, Some(MacroSubNs::Bang)) }, &mut |_, _| (), ) .ok()? .value?; - expander.recursion_depth += 1; - let parse = call_id.parse_macro_expansion(expander.db).value.0.clone(); - let expr = parse.cast::().map(|parse| parse.tree())?; - expander.recursion_depth -= 1; + let (parse, span_map) = &call_id.parse_macro_expansion(expander.db).value; + let expr = parse.clone().cast::().map(|parse| parse.tree())?; // Build a new source context for the expansion file so that any further // recursive expansion (e.g. a user macro expanding to `concat!(...)`) // correctly resolves AstIds and spans in the expansion. let expansion_file_id: HirFileId = call_id.into(); - let new_source_ctx = DocExprSourceCtx { - resolver: source_ctx.resolver.clone(), - file_id: expansion_file_id, - ast_id_map: expansion_file_id.ast_id_map(expander.db), - span_map: expansion_file_id.span_map(expander.db), - }; - Some((expr, new_source_ctx)) + let old_file_id = std::mem::replace(&mut expander.file_id, expansion_file_id); + let old_span_map = + 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; + + 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; + + expansion } fn extend_with_attrs<'a, 'db>( @@ -420,7 +419,7 @@ fn extend_with_attrs<'a, 'db>( make_resolver: &dyn Fn() -> Resolver<'db>, ) { // Lazily initialised when we first encounter a `#[doc = macro!()]`. - let mut expander: Option<(DocMacroExpander<'db>, DocExprSourceCtx<'db>)> = None; + let mut expander: Option> = None; expand_cfg_attr_with_doc_comments::<_, Infallible>( AttrDocCommentIter::from_syntax_node(node).filter(|attr| match attr { @@ -442,27 +441,23 @@ fn extend_with_attrs<'a, 'db>( { result.extend_with_doc_attr(value, indent); } else { - let (exp, ctx) = expander.get_or_insert_with(|| { + 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; - ( - DocMacroExpander { - db, - krate, - recursion_depth: 0, - recursion_limit, - }, - DocExprSourceCtx { - resolver, - file_id, - ast_id_map: file_id.ast_id_map(db), - span_map: file_id.span_map(db), - }, - ) + DocMacroExpander { + db, + krate, + recursion_depth: 0, + recursion_limit, + resolver, + file_id, + ast_id_map: file_id.ast_id_map(db), + span_map: file_id.span_map(db), + } }); if let Some(expanded) = - expand_doc_expr_via_macro_pipeline(exp, ctx, value) + expand_doc_expr_via_macro_pipeline(exp, value) { result.extend_with_unmapped_doc_str(&expanded, indent); } From 8627859b6f0c503305f876426f234ebdffe8ac67 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 6 Jun 2026 23:35:42 +0800 Subject: [PATCH 18/78] internal: some string alloc perf --- .../ide-completion/src/completions/item_list/trait_impl.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index aca774710cb24..059ccae3b6567 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -511,7 +511,7 @@ fn make_const_compl_syntax( let len = end - start; let range = TextRange::new(0.into(), len); - let syntax = const_.text().slice(range).to_string(); + let syntax = const_.text().slice(range).to_smolstr(); format_smolstr!("{} =", syntax.trim_end()) } @@ -537,9 +537,10 @@ fn function_declaration( .map_or(end, |f| f.text_range().start()); let len = end - start; - let syntax = node.text().slice(..len).to_string(); + let mut syntax = node.text().slice(..len).to_string(); + syntax.truncate(syntax.trim_end().len()); - syntax.trim_end().to_owned() + syntax } #[cfg(test)] From b9735cf2c8073e10d2f40d300408d520a66109fd Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 6 Jun 2026 23:29:43 +0800 Subject: [PATCH 19/78] fix: pretty assoc const when trait in macro Example --- ```diff -typeT = $0 whereSelf:Sized; +type T = $0 where Self: Sized; ``` --- .../src/completions/item_list/trait_impl.rs | 74 +++++++++++++++---- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 059ccae3b6567..a705bca63fbc5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -265,6 +265,7 @@ fn get_transformed_assoc_item( ctx: &CompletionContext<'_, '_>, assoc_item: ast::AssocItem, impl_def: hir::Impl, + macro_file: Option, ) -> Option { let trait_ = impl_def.trait_(ctx.db)?; let source_scope = &ctx.sema.scope(assoc_item.syntax())?; @@ -278,7 +279,16 @@ fn get_transformed_assoc_item( let assoc_item = ast::AssocItem::cast(transform.apply(assoc_item.syntax()))?; let (editor, assoc_item) = SyntaxEditor::with_ast_node(&assoc_item); assoc_item.remove_attrs_and_docs(&editor); - ast::AssocItem::cast(editor.finish().new_root().clone()) + let transformed = editor.finish().new_root().clone(); + + let prettied = if let Some(macro_file) = macro_file { + let span_map = macro_file.expansion_span_map(ctx.db); + prettify_macro_expansion(ctx.db, transformed, span_map, ctx.krate.into()) + } else { + transformed + }; + + ast::AssocItem::cast(prettied) } /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. @@ -383,7 +393,9 @@ fn add_type_alias_impl( if let Some(source) = ctx.sema.source(type_alias) { let assoc_item = ast::AssocItem::TypeAlias(source.value); - if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { + if let Some(transformed_item) = + get_transformed_assoc_item(ctx, assoc_item, impl_def, source.file_id.macro_file()) + { let transformed_ty = match transformed_item { ast::AssocItem::TypeAlias(ty) => ty, _ => unreachable!(), @@ -458,14 +470,15 @@ fn add_const_impl( && let Some(source) = ctx.sema.source(const_) { let assoc_item = ast::AssocItem::Const(source.value); - if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { + if let Some(transformed_item) = + get_transformed_assoc_item(ctx, assoc_item, impl_def, source.file_id.macro_file()) + { let transformed_const = match transformed_item { ast::AssocItem::Const(const_) => const_, _ => unreachable!(), }; - let label = - make_const_compl_syntax(ctx, &transformed_const, source.file_id.macro_file()); + let label = make_const_compl_syntax(&transformed_const); let replacement = format!("{label} "); let mut item = @@ -488,17 +501,8 @@ fn add_const_impl( } } -fn make_const_compl_syntax( - ctx: &CompletionContext<'_, '_>, - const_: &ast::Const, - macro_file: Option, -) -> SmolStr { - let const_ = if let Some(macro_file) = macro_file { - let span_map = macro_file.expansion_span_map(ctx.db); - prettify_macro_expansion(ctx.db, const_.syntax().clone(), span_map, ctx.krate.into()) - } else { - const_.syntax().clone() - }; +fn make_const_compl_syntax(const_: &ast::Const) -> SmolStr { + let const_ = const_.syntax(); let start = const_.text_range().start(); let const_end = const_.text_range().end(); @@ -1365,6 +1369,44 @@ impl Foo for Test { $0 } } +"#, + ); + + check_edit( + "type T", + r#" +macro_rules! noop { + ($($item: item)*) => { + $($item)* + } +} + +noop! { + trait Foo { + type T where Self: Sized; + } +} + +impl Foo for () { + $0 +} +"#, + r#" +macro_rules! noop { + ($($item: item)*) => { + $($item)* + } +} + +noop! { + trait Foo { + type T where Self: Sized; + } +} + +impl Foo for () { + type T = $0 where Self: Sized; +} "#, ); } From c7e3f0fa929f9cbbf3605afbd5bdb9a9618fc120 Mon Sep 17 00:00:00 2001 From: edragain Date: Tue, 7 Jul 2026 15:54:08 +0000 Subject: [PATCH 20/78] fix: avoid panic in merge imports on trailing path separator --- .../crates/ide-assists/src/handlers/merge_imports.rs | 11 +++++++++++ .../crates/ide-db/src/imports/merge_imports.rs | 5 ++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs index c0356337057ba..2d3b1b05400c3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs @@ -824,6 +824,17 @@ mod top { } use top::{a::A, b::{B as D, B as C}}; +", + ); + } + + #[test] + fn test_merge_with_trailing_path_separator() { + check_assist_not_applicable( + merge_imports, + r" +use foo::bar; +use foo::$0; ", ); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs index 9b68c27ae3443..17fae61da6131 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs @@ -827,7 +827,10 @@ fn split_prefix( make.use_tree(self_path, None, use_tree.rename(), false) } } else { - let suffix_segments = path.segments().skip(prefix.segments().count()); + let suffix_segments: Vec<_> = path.segments().skip(prefix.segments().count()).collect(); + if suffix_segments.is_empty() { + return None; + } let suffix_path = make.path_from_segments(suffix_segments, false); make.use_tree( suffix_path, From 571d721ea9e6b97afc3580c8848a18e088dcc756 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 7 Jul 2026 16:41:37 +0200 Subject: [PATCH 21/78] misc improvements - Use `HirFileId::parse_with_map`. This avoids a duplicate call to `MacroCallId::parse_macro_expansion` in the macro case. --- src/tools/rust-analyzer/crates/hir-expand/src/lib.rs | 4 ++-- src/tools/rust-analyzer/crates/hir/src/semantics.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index c0b0a614563d6..d205b72e9a1f1 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -790,9 +790,9 @@ fn expand_unimplemented_builtin_macro(span: Span) -> ExpandResult) -> Span { #[salsa::tracked] fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId, _: ()) -> Span { - let root = ast.file_id.parse_or_expand(db); + let (parse, span_map) = ast.file_id.parse_with_map(db); + let root = parse.syntax_node(); let ast_id_map = ast.file_id.ast_id_map(db); - let span_map = ast.file_id.span_map(db); let node = ast_id_map.get(ast.value).to_node(&root); let range = ast::HasName::name(&node) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 02cec440e19d1..bd2c84bb55ff4 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1980,7 +1980,7 @@ impl<'db> SemanticsImpl<'db> { } pub fn resolve_macro_call_arm(&self, macro_call: &ast::MacroCall) -> Option { - self.to_def(macro_call)?.parse_macro_expansion(self.db).value.1.matched_arm + self.to_def(macro_call)?.expansion_span_map(self.db).matched_arm } pub fn get_unsafe_ops(&self, def: ExpressionStoreOwner) -> FxHashSet { From c657dd49cdec800232f746d77e5caa6e807edd79 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 7 Jul 2026 15:17:57 +0200 Subject: [PATCH 22/78] reduce uses of `hir_expand::db::expand_speculative` --- .../rust-analyzer/crates/hir/src/semantics.rs | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index bd2c84bb55ff4..76cf14227d275 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -739,12 +739,7 @@ impl<'db> SemanticsImpl<'db> { token_to_map: SyntaxToken, ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> { let macro_file = self.to_def(actual_macro_call)?; - hir_expand::db::expand_speculative( - self.db, - macro_file, - speculative_args.syntax(), - token_to_map, - ) + self.speculative_expand_raw(macro_file, speculative_args.syntax(), token_to_map) } pub fn speculative_expand_raw( @@ -766,12 +761,7 @@ impl<'db> SemanticsImpl<'db> { ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> { let macro_call = self.wrap_node_infile(actual_macro_call.clone()); let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(macro_call.as_ref()))?; - hir_expand::db::expand_speculative( - self.db, - macro_call_id, - speculative_args.syntax(), - token_to_map, - ) + self.speculative_expand_raw(macro_call_id, speculative_args.syntax(), token_to_map) } pub fn speculative_expand_derive_as_pseudo_attr_macro( @@ -789,12 +779,7 @@ impl<'db> SemanticsImpl<'db> { ) .map(|(_, it, _)| it) })?; - hir_expand::db::expand_speculative( - self.db, - macro_call_id, - speculative_args.syntax(), - token_to_map, - ) + self.speculative_expand_raw(macro_call_id, speculative_args.syntax(), token_to_map) } /// Checks if renaming `renamed` to `new_name` may introduce conflicts with other locals, From 3af3197ec3f7095c3495d3d7739e5b94e6c0febb Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 7 Jul 2026 16:14:54 +0200 Subject: [PATCH 23/78] internal: make `expand_speculative` a method on `MacroCallId` For symmetry with `MacroCallId::macro_expand` --- .../rust-analyzer/crates/hir-expand/src/db.rs | 189 +----------------- .../crates/hir-expand/src/lib.rs | 184 ++++++++++++++++- .../rust-analyzer/crates/hir/src/semantics.rs | 2 +- 3 files changed, 185 insertions(+), 190 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index b5f5785a540e0..5c9520d397988 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -2,20 +2,12 @@ use base_db::{Crate, SourceDatabase}; use span::Span; -use syntax::{AstNode, SyntaxNode, SyntaxToken, ast}; -use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree}; +use syntax::ast; use crate::{ AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander, - EditionedFileId, FileRange, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, - builtin::pseudo_derive_attr_expansion, - cfg_process::attr_macro_input_to_token_tree, - declarative::DeclarativeMacroExpander, - fixup::{self, SyntaxFixupUndoInfo}, - hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt}, - proc_macro::CustomProcMacroExpander, - span_map::{RealSpanMap, SpanMap}, - tt, + EditionedFileId, FileRange, HirFileId, MacroDefId, MacroDefKind, + declarative::DeclarativeMacroExpander, proc_macro::CustomProcMacroExpander, }; #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -62,181 +54,6 @@ fn resolve_span(db: &dyn ExpandDatabase, Span { range, anchor, ctx: _ }: Span) - FileRange { file_id, range: range + anchor_offset } } -/// This expands the given macro call, but with different arguments. This is -/// used for completion, where we want to see what 'would happen' if we insert a -/// token. The `token_to_map` mapped down into the expansion, with the mapped -/// token(s) returned with their priority. -pub fn expand_speculative( - db: &dyn ExpandDatabase, - actual_macro_call: MacroCallId, - speculative_args: &SyntaxNode, - token_to_map: SyntaxToken, -) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> { - let loc = actual_macro_call.loc(db); - let (_, _, span) = *actual_macro_call.macro_arg_considering_derives(db, &loc.kind); - - let span_map = RealSpanMap::absolute(span.anchor.file_id); - let span_map = SpanMap::RealSpanMap(&span_map); - - // Build the subtree and token mapping for the speculative args - let (mut tt, undo_info) = match &loc.kind { - MacroCallKind::FnLike { .. } => ( - syntax_bridge::syntax_node_to_token_tree( - speculative_args, - span_map, - span, - if loc.def.is_proc_macro() { - DocCommentDesugarMode::ProcMacro - } else { - DocCommentDesugarMode::Mbe - }, - ), - SyntaxFixupUndoInfo::NONE, - ), - MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => ( - syntax_bridge::syntax_node_to_token_tree( - speculative_args, - span_map, - span, - DocCommentDesugarMode::ProcMacro, - ), - SyntaxFixupUndoInfo::NONE, - ), - MacroCallKind::Derive { derive_macro_id, .. } => { - let MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } = - &derive_macro_id.loc(db).kind - else { - unreachable!("`derive_macro_id` should be `MacroCallKind::Attr`"); - }; - attr_macro_input_to_token_tree( - db, - speculative_args, - span_map, - span, - true, - attr_ids, - loc.krate, - ) - } - MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => attr_macro_input_to_token_tree( - db, - speculative_args, - span_map, - span, - false, - attr_ids, - loc.krate, - ), - }; - - let attr_arg = match &loc.kind { - MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => { - if loc.def.is_attribute_derive() { - // for pseudo-derive expansion we actually pass the attribute itself only - ast::Attr::cast(speculative_args.clone()) - .and_then(|attr| { - if let ast::Meta::TokenTreeMeta(meta) = attr.meta()? { - meta.token_tree() - } else { - None - } - }) - .map(|token_tree| { - let mut tree = syntax_node_to_token_tree( - token_tree.syntax(), - span_map, - span, - DocCommentDesugarMode::ProcMacro, - ); - tree.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); - tree.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(span)); - tree - }) - } else { - // Attributes may have an input token tree, build the subtree and map for this as well - // then try finding a token id for our token if it is inside this input subtree. - let item = ast::Item::cast(speculative_args.clone())?; - let (_, meta) = - attr_ids.invoc_attr().find_attr_range_with_source_opt(db, loc.krate, &item)?; - if let ast::Meta::TokenTreeMeta(meta) = meta - && let Some(tt) = meta.token_tree() - { - let mut attr_arg = syntax_bridge::syntax_node_to_token_tree( - tt.syntax(), - span_map, - span, - DocCommentDesugarMode::ProcMacro, - ); - attr_arg.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); - Some(attr_arg) - } else { - None - } - } - } - _ => None, - }; - - // Do the actual expansion, we need to directly expand the proc macro due to the attribute args - // Otherwise the expand query will fetch the non speculative attribute args and pass those instead. - let mut speculative_expansion = match loc.def.kind { - MacroDefKind::ProcMacro(ast, expander, _) => { - let span = crate::proc_macro_span(db, ast); - tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); - tt.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(span)); - expander.expand( - db, - loc.def.krate, - loc.krate, - &tt, - attr_arg.as_ref(), - span_with_def_site_ctxt(db, span, actual_macro_call.into(), loc.def.edition), - span_with_call_site_ctxt(db, span, actual_macro_call.into(), loc.def.edition), - span_with_mixed_site_ctxt(db, span, actual_macro_call.into(), loc.def.edition), - ) - } - MacroDefKind::BuiltInAttr(_, it) if it.is_derive() => { - pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span) - } - MacroDefKind::Declarative(it, _) => db - .decl_macro_expander(loc.krate, it) - .expand_unhygienic(db, &tt, loc.kind.call_style(), span), - MacroDefKind::BuiltIn(_, it) => { - it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) - } - MacroDefKind::BuiltInDerive(_, it) => { - it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) - } - MacroDefKind::BuiltInEager(_, it) => { - it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) - } - MacroDefKind::BuiltInAttr(_, it) => it.expand(db, actual_macro_call, &tt, span), - MacroDefKind::UnimplementedBuiltIn(_) => crate::expand_unimplemented_builtin_macro(span), - }; - - let expand_to = loc.expand_to(); - - fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info); - let (node, rev_tmap) = - crate::token_tree_to_syntax_node(db, &speculative_expansion.value, expand_to); - - let syntax_node = node.syntax_node(); - let token = rev_tmap - .ranges_with_span(span_map.span_for_range(token_to_map.text_range())) - .filter_map(|(range, ctx)| syntax_node.covering_element(range).into_token().zip(Some(ctx))) - .map(|(t, ctx)| { - // prefer tokens of the same kind and text, as well as non opaque marked ones - // Note the inversion of the score here, as we want to prefer the first token in case - // of all tokens having the same score - let ranking = ctx.is_opaque(db) as u8 - + 2 * (t.kind() != token_to_map.kind()) as u8 - + 4 * ((t.text() != token_to_map.text()) as u8); - (t, ranking) - }) - .collect(); - Some((node.syntax_node(), token)) -} - impl<'db> TokenExpander<'db> { fn macro_expander(db: &'db dyn ExpandDatabase, id: MacroDefId) -> TokenExpander<'db> { match id.kind { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index d205b72e9a1f1..75ede0c719af8 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -45,20 +45,20 @@ use syntax::{ Parse, SyntaxError, SyntaxNode, SyntaxToken, T, TextRange, TextSize, ast::{self, AstNode}, }; -use syntax_bridge::DocCommentDesugarMode; +use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree}; use crate::{ attrs::AttrId, builtin::{ BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander, - include_input_to_file_id, + include_input_to_file_id, pseudo_derive_attr_expansion, }, cfg_process::attr_macro_input_to_token_tree, db::ExpandDatabase, fixup::SyntaxFixupUndoInfo, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt}, proc_macro::{CustomProcMacroExpander, ProcMacroKind, ProcMacros}, - span_map::{ExpansionSpanMap, SpanMap}, + span_map::{ExpansionSpanMap, RealSpanMap, SpanMap}, }; pub use crate::{ @@ -775,6 +775,184 @@ impl MacroCallId { } } +impl MacroCallId { + /// This expands the given macro call, but with different arguments. This is + /// used for completion, where we want to see what 'would happen' if we insert a + /// token. The `token_to_map` mapped down into the expansion, with the mapped + /// token(s) returned with their priority. + pub fn expand_speculative( + self, + db: &dyn ExpandDatabase, + speculative_args: &SyntaxNode, + token_to_map: SyntaxToken, + ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> { + let loc = self.loc(db); + let (_, _, span) = *self.macro_arg_considering_derives(db, &loc.kind); + + let span_map = RealSpanMap::absolute(span.anchor.file_id); + let span_map = SpanMap::RealSpanMap(&span_map); + + // Build the subtree and token mapping for the speculative args + let (mut tt, undo_info) = match &loc.kind { + MacroCallKind::FnLike { .. } => ( + syntax_bridge::syntax_node_to_token_tree( + speculative_args, + span_map, + span, + if loc.def.is_proc_macro() { + DocCommentDesugarMode::ProcMacro + } else { + DocCommentDesugarMode::Mbe + }, + ), + SyntaxFixupUndoInfo::NONE, + ), + MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => ( + syntax_bridge::syntax_node_to_token_tree( + speculative_args, + span_map, + span, + DocCommentDesugarMode::ProcMacro, + ), + SyntaxFixupUndoInfo::NONE, + ), + MacroCallKind::Derive { derive_macro_id, .. } => { + let MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } = + &derive_macro_id.loc(db).kind + else { + unreachable!("`derive_macro_id` should be `MacroCallKind::Attr`"); + }; + attr_macro_input_to_token_tree( + db, + speculative_args, + span_map, + span, + true, + attr_ids, + loc.krate, + ) + } + MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => { + attr_macro_input_to_token_tree( + db, + speculative_args, + span_map, + span, + false, + attr_ids, + loc.krate, + ) + } + }; + + let attr_arg = match &loc.kind { + MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => { + if loc.def.is_attribute_derive() { + // for pseudo-derive expansion we actually pass the attribute itself only + ast::Attr::cast(speculative_args.clone()) + .and_then(|attr| { + if let ast::Meta::TokenTreeMeta(meta) = attr.meta()? { + meta.token_tree() + } else { + None + } + }) + .map(|token_tree| { + let mut tree = syntax_node_to_token_tree( + token_tree.syntax(), + span_map, + span, + DocCommentDesugarMode::ProcMacro, + ); + tree.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); + tree.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(span)); + tree + }) + } else { + // Attributes may have an input token tree, build the subtree and map for this as well + // then try finding a token id for our token if it is inside this input subtree. + let item = ast::Item::cast(speculative_args.clone())?; + let (_, meta) = attr_ids + .invoc_attr() + .find_attr_range_with_source_opt(db, loc.krate, &item)?; + if let ast::Meta::TokenTreeMeta(meta) = meta + && let Some(tt) = meta.token_tree() + { + let mut attr_arg = syntax_bridge::syntax_node_to_token_tree( + tt.syntax(), + span_map, + span, + DocCommentDesugarMode::ProcMacro, + ); + attr_arg.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); + Some(attr_arg) + } else { + None + } + } + } + _ => None, + }; + + // Do the actual expansion, we need to directly expand the proc macro due to the attribute args + // Otherwise the expand query will fetch the non speculative attribute args and pass those instead. + let mut speculative_expansion = match loc.def.kind { + MacroDefKind::ProcMacro(ast, expander, _) => { + let span = proc_macro_span(db, ast); + tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); + tt.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(span)); + expander.expand( + db, + loc.def.krate, + loc.krate, + &tt, + attr_arg.as_ref(), + span_with_def_site_ctxt(db, span, self.into(), loc.def.edition), + span_with_call_site_ctxt(db, span, self.into(), loc.def.edition), + span_with_mixed_site_ctxt(db, span, self.into(), loc.def.edition), + ) + } + MacroDefKind::BuiltInAttr(_, it) if it.is_derive() => { + pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span) + } + MacroDefKind::Declarative(it, _) => db + .decl_macro_expander(loc.krate, it) + .expand_unhygienic(db, &tt, loc.kind.call_style(), span), + MacroDefKind::BuiltIn(_, it) => it.expand(db, self, &tt, span).map_err(Into::into), + MacroDefKind::BuiltInDerive(_, it) => { + it.expand(db, self, &tt, span).map_err(Into::into) + } + MacroDefKind::BuiltInEager(_, it) => it.expand(db, self, &tt, span).map_err(Into::into), + MacroDefKind::BuiltInAttr(_, it) => it.expand(db, self, &tt, span), + MacroDefKind::UnimplementedBuiltIn(_) => expand_unimplemented_builtin_macro(span), + }; + + let expand_to = loc.expand_to(); + + fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info); + let (node, rev_tmap) = + token_tree_to_syntax_node(db, &speculative_expansion.value, expand_to); + + let syntax_node = node.syntax_node(); + let token = rev_tmap + .ranges_with_span(span_map.span_for_range(token_to_map.text_range())) + .filter_map(|(range, ctx)| { + syntax_node.covering_element(range).into_token().zip(Some(ctx)) + }) + .map(|(t, ctx)| { + // prefer tokens of the same kind and text, as well as non opaque marked ones + // Note the inversion of the score here, as we want to prefer the first token in case + // of all tokens having the same score + let ranking = ctx.is_opaque(db) as u8 + + 2 * (t.kind() != token_to_map.kind()) as u8 + + 4 * ((t.text() != token_to_map.text()) as u8); + (t, ranking) + }) + .collect(); + Some((node.syntax_node(), token)) + } +} + fn expand_unimplemented_builtin_macro(span: Span) -> ExpandResult { ExpandResult::new( tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 76cf14227d275..8023d239afd0a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -748,7 +748,7 @@ impl<'db> SemanticsImpl<'db> { speculative_args: &SyntaxNode, token_to_map: SyntaxToken, ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> { - hir_expand::db::expand_speculative(self.db, macro_file, speculative_args, token_to_map) + macro_file.expand_speculative(self.db, speculative_args, token_to_map) } /// Expand the macro call with a different item as the input, mapping the `token_to_map` down into the From 340b9b1c4725077530d7fa80b7885a27982064fe Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 7 Jul 2026 14:59:13 +0200 Subject: [PATCH 24/78] migrate `ExpandDatabase::resolve_span` --- .../rust-analyzer/crates/hir-expand/src/db.rs | 15 ++------------- .../rust-analyzer/crates/hir-expand/src/lib.rs | 18 ++++++++++++++---- .../rust-analyzer/crates/load-cargo/src/lib.rs | 6 +++--- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index 5c9520d397988..c0a73369a64f0 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -1,13 +1,12 @@ //! Defines database & queries for macro expansion. use base_db::{Crate, SourceDatabase}; -use span::Span; use syntax::ast; use crate::{ AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander, - EditionedFileId, FileRange, HirFileId, MacroDefId, MacroDefKind, - declarative::DeclarativeMacroExpander, proc_macro::CustomProcMacroExpander, + MacroDefId, MacroDefKind, declarative::DeclarativeMacroExpander, + proc_macro::CustomProcMacroExpander, }; #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -29,9 +28,6 @@ pub enum TokenExpander<'db> { #[query_group::query_group] pub trait ExpandDatabase: SourceDatabase { - #[salsa::transparent] - fn resolve_span(&self, span: Span) -> FileRange; - /// Fetches the expander for this macro. #[salsa::transparent] #[salsa::invoke(TokenExpander::macro_expander)] @@ -47,13 +43,6 @@ pub trait ExpandDatabase: SourceDatabase { ) -> &DeclarativeMacroExpander; } -fn resolve_span(db: &dyn ExpandDatabase, Span { range, anchor, ctx: _ }: Span) -> FileRange { - let file_id = EditionedFileId::from_span_file_id(db, anchor.file_id); - let anchor_offset = - HirFileId::from(file_id).ast_id_map(db).get_erased(anchor.ast_id).text_range().start(); - FileRange { file_id, range: range + anchor_offset } -} - impl<'db> TokenExpander<'db> { fn macro_expander(db: &'db dyn ExpandDatabase, id: MacroDefId) -> TokenExpander<'db> { match id.kind { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 75ede0c719af8..9d648b12a2c20 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -1381,7 +1381,7 @@ impl<'db> ExpansionInfo<'db> { let span = self.exp_map.span_at(token.start()); match &self.arg_map { SpanMap::RealSpanMap(_) => { - let range = db.resolve_span(span); + let range = resolve_span(db, span); InFile { file_id: range.file_id.into(), value: smallvec::smallvec![range.range] } } SpanMap::ExpansionSpanMap(arg_map) => { @@ -1435,7 +1435,7 @@ pub fn map_node_range_up_rooted( start = start.min(span.range.start()); end = end.max(span.range.end()); } - Some(db.resolve_span(Span { range: TextRange::new(start, end), anchor, ctx })) + Some(resolve_span(db, Span { range: TextRange::new(start, end), anchor, ctx })) } /// Maps up the text range out of the expansion hierarchy back into the original file its from. @@ -1458,7 +1458,7 @@ pub fn map_node_range_up( start = start.min(span.range.start()); end = end.max(span.range.end()); } - Some((db.resolve_span(Span { range: TextRange::new(start, end), anchor, ctx }), ctx)) + Some((resolve_span(db, Span { range: TextRange::new(start, end), anchor, ctx }), ctx)) } /// Looks up the span at the given offset. @@ -1468,7 +1468,17 @@ pub fn span_for_offset( offset: TextSize, ) -> (FileRange, SyntaxContext) { let span = exp_map.span_at(offset); - (db.resolve_span(span), span.ctx) + (resolve_span(db, span), span.ctx) +} + +// FIXME: This is only public because of its use in `load_cargo` (which we should consider removing +// by moving the implementations of the subrequests to `hir_expand`, and calling within `load-cargo`). +// Avoid adding any more outside uses. +pub fn resolve_span(db: &dyn ExpandDatabase, Span { range, anchor, ctx: _ }: Span) -> FileRange { + let file_id = EditionedFileId::from_span_file_id(db, anchor.file_id); + let anchor_offset = + HirFileId::from(file_id).ast_id_map(db).get_erased(anchor.ast_id).text_range().start(); + FileRange { file_id, range: range + anchor_offset } } /// In Rust, macros expand token trees to token trees. When we want to turn a diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 4a97a5f05e9b8..b5860cb0caace 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -659,7 +659,7 @@ impl ProcMacroExpander for Expander { let call_site_file = macro_call_loc.kind.file_id(); - let resolved = db.resolve_span(current_span); + let resolved = hir_expand::resolve_span(db, current_span); current_ctx = macro_call_loc.ctxt; current_span = Span { @@ -676,7 +676,7 @@ impl ProcMacroExpander for Expander { } } - let resolved = db.resolve_span(current_span); + let resolved = hir_expand::resolve_span(db, current_span); Ok(SubResponse::SpanSourceResult { file_id: resolved.file_id.span_file_id(db).as_u32(), @@ -761,7 +761,7 @@ fn resolve_sub_span( anchor: SpanAnchor { file_id: editioned_file_id, ast_id }, ctx: SyntaxContext::root(editioned_file_id.edition()), }; - db.resolve_span(span) + hir_expand::resolve_span(db, span) } #[cfg(test)] From 5aff04a62689df312e061d9d17c84669420114b3 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 7 Jul 2026 13:22:08 +0200 Subject: [PATCH 25/78] rm `ExpandDatabase::macro_expander` and hence `TokenExpander` --- .../rust-analyzer/crates/hir-expand/src/db.rs | 44 +------------------ 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index c0a73369a64f0..2edbd30dcf8cc 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -3,36 +3,10 @@ use base_db::{Crate, SourceDatabase}; use syntax::ast; -use crate::{ - AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander, - MacroDefId, MacroDefKind, declarative::DeclarativeMacroExpander, - proc_macro::CustomProcMacroExpander, -}; - -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub enum TokenExpander<'db> { - /// Old-style `macro_rules` or the new macros 2.0 - DeclarativeMacro(&'db DeclarativeMacroExpander), - /// Stuff like `line!` and `file!`. - BuiltIn(BuiltinFnLikeExpander), - /// Built-in eagerly expanded fn-like macros (`include!`, `concat!`, etc.) - BuiltInEager(EagerExpander), - /// `global_allocator` and such. - BuiltInAttr(BuiltinAttrExpander), - /// `derive(Copy)` and such. - BuiltInDerive(BuiltinDeriveExpander), - UnimplementedBuiltIn, - /// The thing we love the most here in rust-analyzer -- procedural macros. - ProcMacro(CustomProcMacroExpander), -} +use crate::{AstId, declarative::DeclarativeMacroExpander}; #[query_group::query_group] pub trait ExpandDatabase: SourceDatabase { - /// Fetches the expander for this macro. - #[salsa::transparent] - #[salsa::invoke(TokenExpander::macro_expander)] - fn macro_expander(&self, id: MacroDefId) -> TokenExpander<'_>; - /// Fetches (and compiles) the expander of this decl macro. #[salsa::invoke(DeclarativeMacroExpander::expander)] #[salsa::transparent] @@ -42,19 +16,3 @@ pub trait ExpandDatabase: SourceDatabase { id: AstId, ) -> &DeclarativeMacroExpander; } - -impl<'db> TokenExpander<'db> { - fn macro_expander(db: &'db dyn ExpandDatabase, id: MacroDefId) -> TokenExpander<'db> { - match id.kind { - MacroDefKind::Declarative(ast_id, _) => { - TokenExpander::DeclarativeMacro(db.decl_macro_expander(id.krate, ast_id)) - } - MacroDefKind::BuiltIn(_, expander) => TokenExpander::BuiltIn(expander), - MacroDefKind::BuiltInAttr(_, expander) => TokenExpander::BuiltInAttr(expander), - MacroDefKind::BuiltInDerive(_, expander) => TokenExpander::BuiltInDerive(expander), - MacroDefKind::BuiltInEager(_, expander) => TokenExpander::BuiltInEager(expander), - MacroDefKind::ProcMacro(_, expander, _) => TokenExpander::ProcMacro(expander), - MacroDefKind::UnimplementedBuiltIn(_) => TokenExpander::UnimplementedBuiltIn, - } - } -} From 8c35922905ca820a31fcf6237307bc84e8b16409 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 7 Jul 2026 17:05:08 +0200 Subject: [PATCH 26/78] migrate `ExpandDatabase::decl_macro_expander` --- .../crates/hir-def/src/nameres/collector.rs | 4 ++-- .../hir-def/src/nameres/tests/incremental.rs | 10 +++++----- .../rust-analyzer/crates/hir-expand/src/db.rs | 16 ++-------------- .../crates/hir-expand/src/declarative.rs | 8 +++++--- .../rust-analyzer/crates/hir-expand/src/lib.rs | 6 +++--- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- 6 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 25daf2c3f9177..0fefef123083e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -2561,7 +2561,7 @@ impl ModCollector<'_, '_> { } else { // Case 2: normal `macro_rules!` macro let id = InFile::new(self.file_id(), ast_id); - let decl_expander = self.def_collector.db.decl_macro_expander(krate, id.upcast()); + let decl_expander = id.upcast().decl_macro_expander(self.def_collector.db, krate); let styles = decl_expander.mac.rule_styles(); MacroExpander::Declarative { styles } }; @@ -2639,7 +2639,7 @@ impl ModCollector<'_, '_> { } else { // Case 2: normal `macro` let id = InFile::new(self.file_id(), ast_id); - let decl_expander = self.def_collector.db.decl_macro_expander(krate, id.upcast()); + let decl_expander = id.upcast().decl_macro_expander(self.def_collector.db, krate); let styles = decl_expander.mac.rule_styles(); MacroExpander::Declarative { styles } }; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs index 8e339fa973195..39ef72c40bba5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs @@ -227,7 +227,7 @@ pub struct S {} "HirFileId::ast_id_map_", "parse", "real_span_map", - "DeclarativeMacroExpander::expander_", + "AstId < ast :: Macro >::decl_macro_expander_", "file_item_tree_query", "HirFileId::ast_id_map_", "parse", @@ -415,7 +415,7 @@ pub struct S {} "HirFileId::ast_id_map_", "parse", "real_span_map", - "DeclarativeMacroExpander::expander_", + "AstId < ast :: Macro >::decl_macro_expander_", "file_item_tree_query", "HirFileId::ast_id_map_", "parse", @@ -429,7 +429,7 @@ pub struct S {} "HirFileId::ast_id_map_", "MacroCallId::parse_macro_expansion_", "MacroCallId::macro_arg_", - "DeclarativeMacroExpander::expander_", + "AstId < ast :: Macro >::decl_macro_expander_", "macro_def_shim", "file_item_tree_query", "HirFileId::ast_id_map_", @@ -451,7 +451,7 @@ pub struct S {} "file_item_tree_query", "real_span_map", "MacroCallId::macro_arg_", - "DeclarativeMacroExpander::expander_", + "AstId < ast :: Macro >::decl_macro_expander_", "MacroCallId::macro_arg_", "MacroCallId::macro_arg_", ] @@ -526,7 +526,7 @@ m!(Z); "HirFileId::ast_id_map_", "parse", "real_span_map", - "DeclarativeMacroExpander::expander_", + "AstId < ast :: Macro >::decl_macro_expander_", "file_item_tree_query", "HirFileId::ast_id_map_", "parse", diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index 2edbd30dcf8cc..d9e17cfb2db6d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -1,18 +1,6 @@ //! Defines database & queries for macro expansion. -use base_db::{Crate, SourceDatabase}; -use syntax::ast; - -use crate::{AstId, declarative::DeclarativeMacroExpander}; +use base_db::SourceDatabase; #[query_group::query_group] -pub trait ExpandDatabase: SourceDatabase { - /// Fetches (and compiles) the expander of this decl macro. - #[salsa::invoke(DeclarativeMacroExpander::expander)] - #[salsa::transparent] - fn decl_macro_expander( - &self, - def_crate: Crate, - id: AstId, - ) -> &DeclarativeMacroExpander; -} +pub trait ExpandDatabase: SourceDatabase {} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index 5ba30f12562ac..c250bc52708a8 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -80,13 +80,15 @@ impl DeclarativeMacroExpander { } #[salsa::tracked] -impl DeclarativeMacroExpander { +impl AstId { + /// Fetches (and compiles) the expander of this decl macro. #[salsa::tracked(returns(ref))] - pub(crate) fn expander( + pub fn decl_macro_expander( + self, db: &dyn ExpandDatabase, def_crate: Crate, - id: AstId, ) -> DeclarativeMacroExpander { + let id = self; let (root, map) = id.file_id.parse_with_map(db); let root = root.syntax_node(); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 9d648b12a2c20..293d73fb2d7fa 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -673,7 +673,7 @@ impl MacroCallId { let arg = macro_arg; let res = match loc.def.kind { MacroDefKind::Declarative(id, _) => { - db.decl_macro_expander(loc.def.krate, id).expand(db, arg, self, span) + id.decl_macro_expander(db, loc.def.krate).expand(db, arg, self, span) } MacroDefKind::BuiltIn(_, it) => { it.expand(db, self, arg, span).map_err(Into::into).zip_val(None) @@ -915,8 +915,8 @@ impl MacroCallId { MacroDefKind::BuiltInAttr(_, it) if it.is_derive() => { pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span) } - MacroDefKind::Declarative(it, _) => db - .decl_macro_expander(loc.krate, it) + MacroDefKind::Declarative(it, _) => it + .decl_macro_expander(db, loc.krate) .expand_unhygienic(db, &tt, loc.kind.call_style(), span), MacroDefKind::BuiltIn(_, it) => it.expand(db, self, &tt, span).map_err(Into::into), MacroDefKind::BuiltInDerive(_, it) => { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index a78ce2a2ab105..4793959fa074a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1190,7 +1190,7 @@ fn emit_macro_def_diagnostics<'db>( let id = db.macro_def(m.id); let krate = id.krate; if let hir_expand::MacroDefKind::Declarative(ast, _) = id.kind - && let expander = db.decl_macro_expander(krate, ast) + && let expander = ast.decl_macro_expander(db, krate) && let Some(e) = expander.mac.err() { let edition = krate.data(db).edition; From 73d9357b53d059d5cb53f4b54d868c7b197e327f Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 8 Jul 2026 07:09:52 +0200 Subject: [PATCH 27/78] rm `ExpandDatabase` --- src/tools/rust-analyzer/Cargo.lock | 1 - .../rust-analyzer/crates/hir-def/src/db.rs | 3 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 5 +- .../hir-def/src/macro_expansion_tests/mod.rs | 4 +- .../crates/hir-expand/Cargo.toml | 1 - .../crates/hir-expand/src/attrs.rs | 15 ++- .../hir-expand/src/builtin/attr_macro.rs | 13 ++- .../hir-expand/src/builtin/derive_macro.rs | 32 +++--- .../crates/hir-expand/src/builtin/fn_macro.rs | 71 ++++++------ .../crates/hir-expand/src/cfg_process.rs | 7 +- .../crates/hir-expand/src/change.rs | 6 +- .../rust-analyzer/crates/hir-expand/src/db.rs | 6 - .../crates/hir-expand/src/declarative.rs | 9 +- .../crates/hir-expand/src/eager.rs | 9 +- .../crates/hir-expand/src/files.rs | 82 ++++++-------- .../crates/hir-expand/src/hygiene.rs | 14 +-- .../crates/hir-expand/src/lib.rs | 106 +++++++++--------- .../crates/hir-expand/src/mod_path.rs | 26 ++--- .../crates/hir-expand/src/name.rs | 3 +- .../src/prettify_macro_expansion_.rs | 6 +- .../crates/hir-expand/src/proc_macro.rs | 14 +-- .../crates/hir-expand/src/span_map.rs | 9 +- src/tools/rust-analyzer/crates/hir/src/db.rs | 1 - .../rust-analyzer/crates/hir/src/semantics.rs | 3 +- .../crates/ide-db/src/apply_change.rs | 13 --- .../crates/ide-ssr/src/replacing.rs | 8 +- .../crates/ide/src/highlight_related.rs | 5 +- .../crates/load-cargo/src/lib.rs | 17 ++- .../src/bidirectional_protocol.rs | 2 +- .../crates/test-fixture/src/lib.rs | 25 ++--- 30 files changed, 235 insertions(+), 281 deletions(-) delete mode 100644 src/tools/rust-analyzer/crates/hir-expand/src/db.rs diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 2a45b6ea4f932..75e531e5bfe54 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -891,7 +891,6 @@ dependencies = [ "itertools 0.15.0", "mbe", "parser", - "query-group-macro", "rustc-hash 2.1.2", "salsa", "salsa-macros", diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index d1190686fc5e4..9cfb3621a89e6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -2,7 +2,6 @@ use base_db::{Crate, SourceDatabase}; use hir_expand::{ EditionedFileId, HirFileId, InFile, Lookup, MacroCallId, MacroDefId, MacroDefKind, - db::ExpandDatabase, }; use salsa::{Durability, Setter}; use triomphe::Arc; @@ -16,7 +15,7 @@ use crate::{ }; #[query_group::query_group] -pub trait DefDatabase: ExpandDatabase + SourceDatabase { +pub trait DefDatabase: SourceDatabase { /// Computes an [`ItemTree`] for the given file or macro expansion. #[salsa::invoke(file_item_tree)] #[salsa::transparent] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 2172e2412b89e..1d24a4604d9a7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -61,13 +61,12 @@ mod test_db; use std::hash::{Hash, Hasher}; -use base_db::{Crate, impl_intern_key}; +use base_db::{Crate, SourceDatabase, impl_intern_key}; use hir_expand::{ AstId, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallStyles, MacroDefId, MacroDefKind, attrs::AttrId, builtin::{BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander}, - db::ExpandDatabase, eager::expand_eager_macro_input, impl_intern_lookup, mod_path::ModPath, @@ -1312,7 +1311,7 @@ impl AstIdWithPath { } pub fn macro_call_as_call_id( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, ast_id: AstId, path: &ModPath, call_site: SyntaxContext, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 5cca220967fe9..a4f7d9c678573 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -16,11 +16,11 @@ mod proc_macros; use std::{any::TypeId, iter, ops::Range, sync}; +use base_db::SourceDatabase; use expect_test::Expect; use hir_expand::{ AstId, ExpansionInfo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroKind, builtin::quote::quote, - db::ExpandDatabase, proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind}, span_map::SpanMap, }; @@ -387,7 +387,7 @@ struct IdentityWhenValidProcMacroExpander; impl ProcMacroExpander for IdentityWhenValidProcMacroExpander { fn expand( &self, - _: &dyn ExpandDatabase, + _: &dyn SourceDatabase, subtree: &TopSubtree, _: Option<&TopSubtree>, _: &base_db::Env, diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml index 43b0bea891e39..9af75a0d8a6c1 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml @@ -20,7 +20,6 @@ rustc-hash.workspace = true itertools.workspace = true smallvec.workspace = true triomphe.workspace = true -query-group.workspace = true salsa.workspace = true salsa-macros.workspace = true thin-vec.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 896baacf04599..f5e581e8cf472 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -17,7 +17,7 @@ use std::{borrow::Cow, cell::OnceCell, convert::Infallible, fmt, ops::ControlFlow}; use ::tt::TextRange; -use base_db::Crate; +use base_db::{Crate, SourceDatabase}; use cfg::{CfgExpr, CfgOptions}; use either::Either; use intern::Interned; @@ -30,7 +30,6 @@ use syntax_bridge::DocCommentDesugarMode; use crate::{ AstId, - db::ExpandDatabase, mod_path::ModPath, span_map::SpanMap, tt::{self, TopSubtree}, @@ -293,7 +292,7 @@ impl Attr { /// Parses this attribute as a token tree consisting of comma separated paths. pub fn parse_path_comma_token_tree<'a>( &'a self, - db: &'a dyn ExpandDatabase, + db: &'a dyn SourceDatabase, ) -> Option)> + 'a> { let args = self.token_tree_value()?; @@ -305,7 +304,7 @@ impl Attr { } fn parse_path_comma_token_tree<'a>( - db: &'a dyn ExpandDatabase, + db: &'a dyn SourceDatabase, args: &'a tt::TopSubtree, ) -> impl Iterator)> { args.token_trees() @@ -366,7 +365,7 @@ impl AttrId { /// to `cfg_attr`) and its [`ast::Meta`]. pub fn find_attr_range( self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, krate: Crate, owner: AstId, ) -> (ast::Attr, ast::Meta) { @@ -380,7 +379,7 @@ impl AttrId { /// original file (not speculatively expanded macro output). pub fn find_attr_range_with_source( self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, krate: Crate, owner: &dyn ast::HasAttrs, ) -> (ast::Attr, ast::Meta) { @@ -393,7 +392,7 @@ impl AttrId { /// to `cfg_attr`) and its [`ast::Meta`]. pub(crate) fn find_attr_range_with_source_opt( self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, krate: Crate, owner: &dyn ast::HasAttrs, ) -> Option<(ast::Attr, ast::Meta)> { @@ -418,7 +417,7 @@ impl AttrId { pub fn find_derive_range( self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, krate: Crate, owner: AstId, derive_index: u32, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs index 9b13f9fb00eba..540b3e66ce199 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs @@ -1,8 +1,9 @@ //! Builtin attributes. +use base_db::SourceDatabase; use intern::sym; use span::Span; -use crate::{ExpandResult, MacroCallId, MacroCallKind, db::ExpandDatabase, name, tt}; +use crate::{ExpandResult, MacroCallId, MacroCallKind, name, tt}; use super::quote; @@ -14,7 +15,7 @@ macro_rules! register_builtin { } impl BuiltinAttrExpander { - pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult { + pub fn expander(&self) -> fn (&dyn SourceDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult { match *self { $( BuiltinAttrExpander::$variant => $expand, )* } @@ -34,7 +35,7 @@ macro_rules! register_builtin { impl BuiltinAttrExpander { pub fn expand( &self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -74,7 +75,7 @@ pub fn find_builtin_attr(ident: &name::Name) -> Option { } fn dummy_attr_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _id: MacroCallId, tt: &tt::TopSubtree, _span: Span, @@ -83,7 +84,7 @@ fn dummy_attr_expand( } fn dummy_gate_test_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -117,7 +118,7 @@ fn dummy_gate_test_expand( /// So this hacky approach is a lot more friendly for us, though it does require a bit of support in /// hir::Semantics to make this work. fn derive_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, id: MacroCallId, tt: &tt::TopSubtree, span: Span, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index 86ee3f153bdc9..5e85a710e0855 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -1,5 +1,6 @@ //! Builtin derives. +use base_db::SourceDatabase; use either::Either; use intern::sym; use itertools::{Itertools, izip}; @@ -13,7 +14,6 @@ use tracing::debug; use crate::{ ExpandError, ExpandResult, MacroCallId, builtin::quote::dollar_crate, - db::ExpandDatabase, hygiene::span_with_def_site_ctxt, name::{self, AsName, Name}, span_map::ExpansionSpanMap, @@ -35,7 +35,7 @@ macro_rules! register_builtin { } impl BuiltinDeriveExpander { - pub fn expander(&self) -> fn(&dyn ExpandDatabase, Span, &tt::TopSubtree) -> ExpandResult { + pub fn expander(&self) -> fn(&dyn SourceDatabase, Span, &tt::TopSubtree) -> ExpandResult { match *self { $( BuiltinDeriveExpander::$trait => $expand, )* } @@ -54,7 +54,7 @@ macro_rules! register_builtin { impl BuiltinDeriveExpander { pub fn expand( &self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -228,7 +228,7 @@ struct AdtParam { // FIXME: This whole thing needs a refactor. Each derive requires its special values, and the result is a mess. fn parse_adt( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, tt: &tt::TopSubtree, call_site: Span, ) -> Result { @@ -387,7 +387,7 @@ fn parse_adt_from_syntax( } fn to_adt_syntax( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, tt: &tt::TopSubtree, call_site: Span, ) -> Result<(ast::Adt, span::SpanMap), ExpandError> { @@ -448,7 +448,7 @@ fn name_to_token( /// where B1, ..., BN are the bounds given by `bounds_paths`. Z is a phantom type, and /// therefore does not get bound by the derived trait. fn expand_simple_derive( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, invoc_span: Span, tt: &tt::TopSubtree, trait_path: tt::TopSubtree, @@ -531,7 +531,7 @@ fn expand_simple_derive_with_parsed( } fn copy_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span: Span, tt: &tt::TopSubtree, ) -> ExpandResult { @@ -547,7 +547,7 @@ fn copy_expand( } fn clone_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span: Span, tt: &tt::TopSubtree, ) -> ExpandResult { @@ -602,7 +602,7 @@ fn and_and(span: Span) -> tt::TopSubtree { } fn default_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span: Span, tt: &tt::TopSubtree, ) -> ExpandResult { @@ -667,7 +667,7 @@ fn default_expand( } fn debug_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span: Span, tt: &tt::TopSubtree, ) -> ExpandResult { @@ -740,7 +740,7 @@ fn debug_expand( } fn hash_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span: Span, tt: &tt::TopSubtree, ) -> ExpandResult { @@ -787,7 +787,7 @@ fn hash_expand( } fn eq_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span: Span, tt: &tt::TopSubtree, ) -> ExpandResult { @@ -803,7 +803,7 @@ fn eq_expand( } fn partial_eq_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span: Span, tt: &tt::TopSubtree, ) -> ExpandResult { @@ -875,7 +875,7 @@ fn self_and_other_patterns( } fn ord_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span: Span, tt: &tt::TopSubtree, ) -> ExpandResult { @@ -933,7 +933,7 @@ fn ord_expand( } fn partial_ord_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span: Span, tt: &tt::TopSubtree, ) -> ExpandResult { @@ -996,7 +996,7 @@ fn partial_ord_expand( } fn coerce_pointee_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span: Span, tt: &tt::TopSubtree, ) -> ExpandResult { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index 68c9fc4a37bc7..5b9fe09077fbb 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; -use base_db::AnchoredPath; +use base_db::{AnchoredPath, SourceDatabase}; use cfg::CfgExpr; use either::Either; use intern::{Symbol, sym}; @@ -19,7 +19,6 @@ use syntax_bridge::syntax_node_to_token_tree; use crate::{ EditionedFileId, ExpandError, ExpandResult, MacroCallId, builtin::quote::{WithDelimiter, dollar_crate}, - db::ExpandDatabase, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt}, name, tt::{self, DelimSpan, TtElement, TtIter}, @@ -38,7 +37,7 @@ macro_rules! register_builtin { } impl BuiltinFnLikeExpander { - fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult { + fn expander(&self) -> fn (&dyn SourceDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult { match *self { $( BuiltinFnLikeExpander::$kind => $expand, )* } @@ -46,7 +45,7 @@ macro_rules! register_builtin { } impl EagerExpander { - fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult { + fn expander(&self) -> fn (&dyn SourceDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult { match *self { $( EagerExpander::$e_kind => $e_expand, )* } @@ -66,7 +65,7 @@ macro_rules! register_builtin { impl BuiltinFnLikeExpander { pub fn expand( &self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -83,7 +82,7 @@ impl BuiltinFnLikeExpander { impl EagerExpander { pub fn expand( &self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -154,7 +153,7 @@ fn mk_pound(span: Span) -> tt::Leaf { } fn module_path_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _id: MacroCallId, _tt: &tt::TopSubtree, span: Span, @@ -166,7 +165,7 @@ fn module_path_expand( } fn line_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _id: MacroCallId, _tt: &tt::TopSubtree, span: Span, @@ -181,7 +180,7 @@ fn line_expand( } fn log_syntax_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _id: MacroCallId, _tt: &tt::TopSubtree, span: Span, @@ -190,7 +189,7 @@ fn log_syntax_expand( } fn trace_macros_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _id: MacroCallId, _tt: &tt::TopSubtree, span: Span, @@ -199,7 +198,7 @@ fn trace_macros_expand( } fn stringify_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -214,7 +213,7 @@ fn stringify_expand( } fn assert_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -253,7 +252,7 @@ fn assert_expand( } fn file_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _id: MacroCallId, _tt: &tt::TopSubtree, span: Span, @@ -270,7 +269,7 @@ fn file_expand( } fn format_args_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -284,7 +283,7 @@ fn format_args_expand( } fn format_args_nl_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -307,7 +306,7 @@ fn format_args_nl_expand( } fn asm_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -322,7 +321,7 @@ fn asm_expand( } fn global_asm_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -337,7 +336,7 @@ fn global_asm_expand( } fn naked_asm_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -352,7 +351,7 @@ fn naked_asm_expand( } fn cfg_select_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -441,7 +440,7 @@ fn cfg_select_expand( } fn cfg_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -454,7 +453,7 @@ fn cfg_expand( } fn panic_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -481,7 +480,7 @@ fn panic_expand( } fn unreachable_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -510,7 +509,7 @@ fn unreachable_expand( } #[allow(clippy::never_loop)] -fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool { +fn use_panic_2021(db: &dyn SourceDatabase, span: Span) -> bool { // To determine the edition, we check the first span up the expansion // stack that does not have #[allow_internal_unstable(edition_panic)]. // (To avoid using the edition of e.g. the assert!() or debug_assert!() definition.) @@ -532,7 +531,7 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool { } fn compile_error_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -552,7 +551,7 @@ fn compile_error_expand( } fn concat_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _arg_id: MacroCallId, tt: &tt::TopSubtree, call_site: Span, @@ -659,7 +658,7 @@ fn concat_expand( } fn concat_bytes_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _arg_id: MacroCallId, tt: &tt::TopSubtree, call_site: Span, @@ -758,7 +757,7 @@ fn concat_bytes_expand_subtree( } fn relative_file( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, call_id: MacroCallId, path_str: &str, allow_recursion: bool, @@ -811,7 +810,7 @@ fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> { } fn include_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, arg_id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -835,7 +834,7 @@ fn include_expand( } pub fn include_input_to_file_id( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, arg_id: MacroCallId, arg: &tt::TopSubtree, ) -> Result { @@ -844,7 +843,7 @@ pub fn include_input_to_file_id( } fn include_bytes_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _arg_id: MacroCallId, _tt: &tt::TopSubtree, span: Span, @@ -858,7 +857,7 @@ fn include_bytes_expand( } fn include_str_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, arg_id: MacroCallId, tt: &tt::TopSubtree, call_site: Span, @@ -890,13 +889,13 @@ fn include_str_expand( ExpandResult::ok(quote!(call_site =>#text)) } -fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &Symbol) -> Option { +fn get_env_inner(db: &dyn SourceDatabase, arg_id: MacroCallId, key: &Symbol) -> Option { let krate = arg_id.loc(db).krate; krate.env(db).get(key.as_str()) } fn env_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, arg_id: MacroCallId, tt: &tt::TopSubtree, span: Span, @@ -934,7 +933,7 @@ fn env_expand( } fn option_env_expand( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, arg_id: MacroCallId, tt: &tt::TopSubtree, call_site: Span, @@ -961,7 +960,7 @@ fn option_env_expand( } fn quote_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _arg_id: MacroCallId, _tt: &tt::TopSubtree, span: Span, @@ -987,7 +986,7 @@ fn unescape_str(s: &str) -> Cow<'_, str> { } fn pattern_type_expand( - _db: &dyn ExpandDatabase, + _db: &dyn SourceDatabase, _arg_id: MacroCallId, tt: &tt::TopSubtree, call_site: Span, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs index 81edc9f2cffaf..45a81d3b3650f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs @@ -2,7 +2,7 @@ use std::{cell::OnceCell, ops::ControlFlow}; use ::tt::TextRange; -use base_db::Crate; +use base_db::{Crate, SourceDatabase}; use cfg::CfgExpr; use parser::T; use smallvec::SmallVec; @@ -14,7 +14,6 @@ use syntax_bridge::DocCommentDesugarMode; use crate::{ attrs::{AstPathExt, AttrId, expand_cfg_attr, is_item_tree_filtered_attr}, - db::ExpandDatabase, fixup::{self, SyntaxFixupUndoInfo}, span_map::SpanMap, tt::{self, DelimSpan, Span}, @@ -46,7 +45,7 @@ struct AstAttrToProcess { } fn macro_input_callback( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, is_derive: bool, censor_item_tree_attr_ids: &[AttrId], krate: Crate, @@ -293,7 +292,7 @@ fn macro_input_callback( } pub(crate) fn attr_macro_input_to_token_tree( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, node: &SyntaxNode, span_map: SpanMap<'_>, span: Span, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs index f75f45983c4f1..58808bc6c8741 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs @@ -1,9 +1,9 @@ //! Defines a unit of change that can applied to the database to get the next //! state. Changes are transactional. -use base_db::{CrateGraphBuilder, FileChange, SourceRoot}; +use base_db::{CrateGraphBuilder, FileChange, SourceDatabase, SourceRoot}; use span::FileId; -use crate::{db::ExpandDatabase, proc_macro::ProcMacrosBuilder}; +use crate::proc_macro::ProcMacrosBuilder; #[derive(Debug, Default)] pub struct ChangeWithProcMacros { @@ -12,7 +12,7 @@ pub struct ChangeWithProcMacros { } impl ChangeWithProcMacros { - pub fn apply(self, db: &mut impl ExpandDatabase) { + pub fn apply(self, db: &mut impl SourceDatabase) { let crates_id_map = self.source_change.apply(db); if let Some(proc_macros) = self.proc_macros { proc_macros.build_in( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs deleted file mode 100644 index d9e17cfb2db6d..0000000000000 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! Defines database & queries for macro expansion. - -use base_db::SourceDatabase; - -#[query_group::query_group] -pub trait ExpandDatabase: SourceDatabase {} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index c250bc52708a8..a3c9047d764f7 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; -use base_db::Crate; +use base_db::{Crate, SourceDatabase}; use span::{Edition, Span, SyntaxContext}; use stdx::TupleExt; use syntax::{ @@ -15,7 +15,6 @@ use crate::{ AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, Lookup, MacroCallId, MacroCallStyle, attrs::{AstKeyValueMetaExt, AstPathExt, expand_cfg_attr}, - db::ExpandDatabase, hygiene::{Transparency, apply_mark}, tt, }; @@ -31,7 +30,7 @@ pub struct DeclarativeMacroExpander { impl DeclarativeMacroExpander { pub fn expand( &self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, tt: &tt::TopSubtree, call_id: MacroCallId, span: Span, @@ -60,7 +59,7 @@ impl DeclarativeMacroExpander { pub fn expand_unhygienic( &self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, tt: &tt::TopSubtree, call_style: MacroCallStyle, call_site: Span, @@ -85,7 +84,7 @@ impl AstId { #[salsa::tracked(returns(ref))] pub fn decl_macro_expander( self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, def_crate: Crate, ) -> DeclarativeMacroExpander { let id = self; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs index 16053840b768a..00457ad44b303 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -18,7 +18,7 @@ //! //! //! See the full discussion : -use base_db::Crate; +use base_db::{Crate, SourceDatabase}; use span::SyntaxContext; use syntax::{ AstPtr, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent, syntax_editor::SyntaxEditor, @@ -29,7 +29,6 @@ use crate::{ AstId, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, ast::{self, AstNode}, - db::ExpandDatabase, mod_path::ModPath, }; @@ -39,7 +38,7 @@ pub type EagerCallBackFn<'a> = &'a mut dyn FnMut( ); pub fn expand_eager_macro_input( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, krate: Crate, macro_call: &ast::MacroCall, ast_id: AstId, @@ -119,7 +118,7 @@ pub fn expand_eager_macro_input( } fn lazy_expand<'db>( - db: &'db dyn ExpandDatabase, + db: &'db dyn SourceDatabase, def: &MacroDefId, macro_call: &ast::MacroCall, ast_id: AstId, @@ -142,7 +141,7 @@ fn lazy_expand<'db>( } fn eager_macro_recur( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span_map: &ExpansionSpanMap, expanded_map: &mut ExpansionSpanMap, mut offset: TextSize, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs index a6ae901973103..3524e0cb090cd 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs @@ -1,14 +1,14 @@ //! Things to wrap other things in file ids. use std::borrow::Borrow; +use base_db::SourceDatabase; use either::Either; use span::{AstIdNode, ErasedFileAstId, FileAstId, FileId, SyntaxContext}; use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize}; use crate::{ - EditionedFileId, HirFileId, MacroCallId, MacroKind, - db::{self, ExpandDatabase}, - map_node_range_up, map_node_range_up_rooted, span_for_offset, + EditionedFileId, HirFileId, MacroCallId, MacroKind, map_node_range_up, + map_node_range_up_rooted, span_for_offset, }; /// `InFile` stores a value of `T` inside a particular file/syntax tree. @@ -38,7 +38,7 @@ pub type FilePosition = FilePositionWrapper; impl FilePosition { #[inline] - pub fn into_file_id(self, db: &dyn ExpandDatabase) -> FilePositionWrapper { + pub fn into_file_id(self, db: &dyn SourceDatabase) -> FilePositionWrapper { FilePositionWrapper { file_id: self.file_id.file_id(db), offset: self.offset } } } @@ -72,17 +72,17 @@ pub type FileRange = FileRangeWrapper; impl FileRange { #[inline] - pub fn into_file_id(self, db: &dyn ExpandDatabase) -> FileRangeWrapper { + pub fn into_file_id(self, db: &dyn SourceDatabase) -> FileRangeWrapper { FileRangeWrapper { file_id: self.file_id.file_id(db), range: self.range } } #[inline] - pub fn file_text(self, db: &dyn ExpandDatabase) -> &triomphe::Arc { + pub fn file_text(self, db: &dyn SourceDatabase) -> &triomphe::Arc { db.file_text(self.file_id.file_id(db)).text(db) } #[inline] - pub fn text(self, db: &dyn ExpandDatabase) -> &str { + pub fn text(self, db: &dyn SourceDatabase) -> &str { &self.file_text(db)[self.range] } } @@ -93,16 +93,16 @@ impl FileRange { pub type AstId = crate::InFile>; impl AstId { - pub fn to_node(&self, db: &dyn ExpandDatabase) -> N { + pub fn to_node(&self, db: &dyn SourceDatabase) -> N { self.to_ptr(db).to_node(&self.file_id.parse_or_expand(db)) } - pub fn to_range(&self, db: &dyn ExpandDatabase) -> TextRange { + pub fn to_range(&self, db: &dyn SourceDatabase) -> TextRange { self.to_ptr(db).text_range() } - pub fn to_in_file_node(&self, db: &dyn ExpandDatabase) -> crate::InFile { + pub fn to_in_file_node(&self, db: &dyn SourceDatabase) -> crate::InFile { crate::InFile::new(self.file_id, self.to_ptr(db).to_node(&self.file_id.parse_or_expand(db))) } - pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> AstPtr { + pub fn to_ptr(&self, db: &dyn SourceDatabase) -> AstPtr { self.file_id.ast_id_map(db).get(self.value) } pub fn erase(&self) -> ErasedAstId { @@ -120,10 +120,10 @@ impl AstId { pub type ErasedAstId = crate::InFile; impl ErasedAstId { - pub fn to_range(&self, db: &dyn ExpandDatabase) -> TextRange { + pub fn to_range(&self, db: &dyn SourceDatabase) -> TextRange { self.to_ptr(db).text_range() } - pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> SyntaxNodePtr { + pub fn to_ptr(&self, db: &dyn SourceDatabase) -> SyntaxNodePtr { self.file_id.ast_id_map(db).get_erased(self.value) } } @@ -203,35 +203,35 @@ impl InFileWrapper> { // endregion:transpose impls trait FileIdToSyntax: Copy { - fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode; + fn file_syntax(self, db: &dyn SourceDatabase) -> SyntaxNode; } impl FileIdToSyntax for EditionedFileId { - fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode { + fn file_syntax(self, db: &dyn SourceDatabase) -> SyntaxNode { self.parse(db).syntax_node() } } impl FileIdToSyntax for MacroCallId { - fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode { + fn file_syntax(self, db: &dyn SourceDatabase) -> SyntaxNode { self.parse_macro_expansion(db).value.0.syntax_node() } } impl FileIdToSyntax for HirFileId { - fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode { + fn file_syntax(self, db: &dyn SourceDatabase) -> SyntaxNode { self.parse_or_expand(db) } } #[allow(private_bounds)] impl InFileWrapper { - pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode { + pub fn file_syntax(&self, db: &dyn SourceDatabase) -> SyntaxNode { FileIdToSyntax::file_syntax(self.file_id, db) } } #[allow(private_bounds)] impl InFileWrapper> { - pub fn to_node(&self, db: &dyn ExpandDatabase) -> N { + pub fn to_node(&self, db: &dyn SourceDatabase) -> N { self.value.to_node(&self.file_syntax(db)) } } @@ -262,7 +262,7 @@ impl> InFileWrapper { impl> InFile { pub fn parent_ancestors_with_macros( self, - db: &dyn db::ExpandDatabase, + db: &dyn SourceDatabase, ) -> impl Iterator> + '_ { let succ = move |node: &InFile| match node.value.parent() { Some(parent) => Some(node.with_value(parent)), @@ -281,7 +281,7 @@ impl> InFile { pub fn ancestors_with_macros( self, - db: &dyn db::ExpandDatabase, + db: &dyn SourceDatabase, ) -> impl Iterator> + '_ { let succ = move |node: &InFile| match node.value.parent() { Some(parent) => Some(node.with_value(parent)), @@ -310,21 +310,18 @@ impl> InFile { /// /// For attributes and derives, this will point back to the attribute only. /// For the entire item use `InFile::original_file_range_full`. - pub fn original_file_range_rooted(self, db: &dyn db::ExpandDatabase) -> FileRange { + pub fn original_file_range_rooted(self, db: &dyn SourceDatabase) -> FileRange { self.borrow().map(SyntaxNode::text_range).original_node_file_range_rooted(db) } /// Falls back to the macro call range if the node cannot be mapped up fully. - pub fn original_file_range_with_macro_call_input( - self, - db: &dyn db::ExpandDatabase, - ) -> FileRange { + pub fn original_file_range_with_macro_call_input(self, db: &dyn SourceDatabase) -> FileRange { self.borrow().map(SyntaxNode::text_range).original_node_file_range_with_macro_call_input(db) } pub fn original_syntax_node_rooted( self, - db: &dyn db::ExpandDatabase, + db: &dyn SourceDatabase, ) -> Option> { // This kind of upmapping can only be achieved in attribute expanded files, // as we don't have node inputs otherwise and therefore can't find an `N` node in the input @@ -362,24 +359,21 @@ impl InFile<&SyntaxNode> { /// Attempts to map the syntax node back up its macro calls. pub fn original_file_range_opt( self, - db: &dyn db::ExpandDatabase, + db: &dyn SourceDatabase, ) -> Option<(FileRange, SyntaxContext)> { self.borrow().map(SyntaxNode::text_range).original_node_file_range_opt(db) } } impl InMacroFile { - pub fn upmap_once( - self, - db: &dyn db::ExpandDatabase, - ) -> InFile> { + pub fn upmap_once(self, db: &dyn SourceDatabase) -> InFile> { self.file_id.expansion_info(db).map_range_up_once(db, self.value.text_range()) } } impl InFile { /// Falls back to the macro call range if the node cannot be mapped up fully. - pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange { + pub fn original_file_range(self, db: &dyn SourceDatabase) -> FileRange { match self.file_id { HirFileId::FileId(file_id) => FileRange { file_id, range: self.value.text_range() }, HirFileId::MacroFile(mac_file) => { @@ -403,7 +397,7 @@ impl InFile { } /// Attempts to map the syntax node back up its macro calls. - pub fn original_file_range_opt(self, db: &dyn db::ExpandDatabase) -> Option { + pub fn original_file_range_opt(self, db: &dyn SourceDatabase) -> Option { match self.file_id { HirFileId::FileId(file_id) => { Some(FileRange { file_id, range: self.value.text_range() }) @@ -424,16 +418,13 @@ impl InFile { } impl InMacroFile { - pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> (FileRange, SyntaxContext) { + pub fn original_file_range(self, db: &dyn SourceDatabase) -> (FileRange, SyntaxContext) { span_for_offset(db, self.file_id.expansion_span_map(db), self.value) } } impl InFile { - pub fn original_node_file_range( - self, - db: &dyn db::ExpandDatabase, - ) -> (FileRange, SyntaxContext) { + pub fn original_node_file_range(self, db: &dyn SourceDatabase) -> (FileRange, SyntaxContext) { match self.file_id { HirFileId::FileId(file_id) => { (FileRange { file_id, range: self.value }, SyntaxContext::root(file_id.edition(db))) @@ -453,7 +444,7 @@ impl InFile { } } - pub fn original_node_file_range_rooted(self, db: &dyn db::ExpandDatabase) -> FileRange { + pub fn original_node_file_range_rooted(self, db: &dyn SourceDatabase) -> FileRange { match self.file_id { HirFileId::FileId(file_id) => FileRange { file_id, range: self.value }, HirFileId::MacroFile(mac_file) => { @@ -470,7 +461,7 @@ impl InFile { pub fn original_node_file_range_with_macro_call_input( self, - db: &dyn db::ExpandDatabase, + db: &dyn SourceDatabase, ) -> FileRange { match self.file_id { HirFileId::FileId(file_id) => FileRange { file_id, range: self.value }, @@ -488,7 +479,7 @@ impl InFile { pub fn original_node_file_range_opt( self, - db: &dyn db::ExpandDatabase, + db: &dyn SourceDatabase, ) -> Option<(FileRange, SyntaxContext)> { match self.file_id { HirFileId::FileId(file_id) => Some(( @@ -501,10 +492,7 @@ impl InFile { } } - pub fn original_node_file_range_rooted_opt( - self, - db: &dyn db::ExpandDatabase, - ) -> Option { + pub fn original_node_file_range_rooted_opt(self, db: &dyn SourceDatabase) -> Option { match self.file_id { HirFileId::FileId(file_id) => Some(FileRange { file_id, range: self.value }), HirFileId::MacroFile(mac_file) => { @@ -515,7 +503,7 @@ impl InFile { } impl InFile { - pub fn original_ast_node_rooted(self, db: &dyn db::ExpandDatabase) -> Option> { + pub fn original_ast_node_rooted(self, db: &dyn SourceDatabase) -> Option> { // This kind of upmapping can only be achieved in attribute expanded files, // as we don't have node inputs otherwise and therefore can't find an `N` node in the input let file_id = match self.file_id { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs index 1cf8ce2a57f95..e471c6d2bd3e2 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs @@ -26,12 +26,12 @@ use std::convert::identity; use span::{Edition, MacroCallId, Span, SyntaxContext}; -use crate::db::ExpandDatabase; +use base_db::SourceDatabase; pub use span::Transparency; pub fn span_with_def_site_ctxt( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span: Span, expn_id: MacroCallId, edition: Edition, @@ -40,7 +40,7 @@ pub fn span_with_def_site_ctxt( } pub fn span_with_call_site_ctxt( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span: Span, expn_id: MacroCallId, edition: Edition, @@ -49,7 +49,7 @@ pub fn span_with_call_site_ctxt( } pub fn span_with_mixed_site_ctxt( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span: Span, expn_id: MacroCallId, edition: Edition, @@ -58,7 +58,7 @@ pub fn span_with_mixed_site_ctxt( } fn span_with_ctxt_from_mark( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span: Span, expn_id: MacroCallId, transparency: Transparency, @@ -71,7 +71,7 @@ fn span_with_ctxt_from_mark( } pub(super) fn apply_mark( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, ctxt: span::SyntaxContext, call_id: span::MacroCallId, transparency: Transparency, @@ -108,7 +108,7 @@ pub(super) fn apply_mark( } fn apply_mark_internal( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, ctxt: SyntaxContext, call_id: MacroCallId, transparency: Transparency, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 293d73fb2d7fa..9ebb346a373fe 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -12,7 +12,6 @@ pub use intern; pub mod attrs; pub mod builtin; pub mod change; -pub mod db; pub mod declarative; pub mod eager; pub mod files; @@ -34,7 +33,7 @@ use triomphe::Arc; use core::fmt; use std::{borrow::Cow, ops}; -use base_db::Crate; +use base_db::{Crate, SourceDatabase}; use either::Either; use mbe::MatchedArmIndex; use span::{ @@ -54,7 +53,6 @@ use crate::{ include_input_to_file_id, pseudo_derive_attr_expansion, }, cfg_process::attr_macro_input_to_token_tree, - db::ExpandDatabase, fixup::SyntaxFixupUndoInfo, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt}, proc_macro::{CustomProcMacroExpander, ProcMacroKind, ProcMacros}, @@ -117,7 +115,7 @@ pub trait Lookup { fn lookup<'db>(&self, db: &'db Self::Database) -> &'db Self::Data; } -impl_intern_lookup!(ExpandDatabase, MacroCallId, MacroCallLoc); +impl_intern_lookup!(SourceDatabase, MacroCallId, MacroCallLoc); pub type ExpandResult = ValueResult; @@ -140,7 +138,7 @@ impl ExpandError { self.inner.1 } - pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> RenderedExpandError { + pub fn render_to_string(&self, db: &dyn SourceDatabase) -> RenderedExpandError { self.inner.0.render_to_string(db) } } @@ -179,7 +177,7 @@ impl RenderedExpandError { } impl ExpandErrorKind { - pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> RenderedExpandError { + pub fn render_to_string(&self, db: &dyn SourceDatabase) -> RenderedExpandError { match self { ExpandErrorKind::ProcMacroAttrExpansionDisabled => RenderedExpandError { message: "procedural attribute macro expansion is disabled".to_owned(), @@ -413,10 +411,10 @@ pub enum MacroKind { } impl MacroCallId { - pub fn call_node(self, db: &dyn ExpandDatabase) -> InFile { + pub fn call_node(self, db: &dyn SourceDatabase) -> InFile { self.loc(db).to_node(db) } - pub fn expansion_level(self, db: &dyn ExpandDatabase) -> u32 { + pub fn expansion_level(self, db: &dyn SourceDatabase) -> u32 { let mut level = 0; let mut macro_file = self; loop { @@ -429,16 +427,16 @@ impl MacroCallId { }; } } - pub fn parent(self, db: &dyn ExpandDatabase) -> HirFileId { + pub fn parent(self, db: &dyn SourceDatabase) -> HirFileId { self.loc(db).kind.file_id() } /// Return expansion information if it is a macro-expansion file - pub fn expansion_info(self, db: &dyn ExpandDatabase) -> ExpansionInfo<'_> { + pub fn expansion_info(self, db: &dyn SourceDatabase) -> ExpansionInfo<'_> { ExpansionInfo::new(db, self) } - pub fn kind(self, db: &dyn ExpandDatabase) -> MacroKind { + pub fn kind(self, db: &dyn SourceDatabase) -> MacroKind { match self.loc(db).def.kind { MacroDefKind::Declarative(..) => MacroKind::Declarative, MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltInEager(..) => { @@ -453,24 +451,24 @@ impl MacroCallId { } } - pub fn is_include_macro(self, db: &dyn ExpandDatabase) -> bool { + pub fn is_include_macro(self, db: &dyn SourceDatabase) -> bool { self.loc(db).def.is_include() } - pub fn is_include_like_macro(self, db: &dyn ExpandDatabase) -> bool { + pub fn is_include_like_macro(self, db: &dyn SourceDatabase) -> bool { self.loc(db).def.is_include_like() } - pub fn is_env_or_option_env(self, db: &dyn ExpandDatabase) -> bool { + pub fn is_env_or_option_env(self, db: &dyn SourceDatabase) -> bool { self.loc(db).def.is_env_or_option_env() } - pub fn is_eager(self, db: &dyn ExpandDatabase) -> bool { + pub fn is_eager(self, db: &dyn SourceDatabase) -> bool { let loc = self.loc(db); matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) } - pub fn eager_arg(self, db: &dyn ExpandDatabase) -> Option { + pub fn eager_arg(self, db: &dyn SourceDatabase) -> Option { let loc = self.loc(db); match &loc.kind { MacroCallKind::FnLike { eager, .. } => eager.as_ref().map(|it| it.arg_id), @@ -478,7 +476,7 @@ impl MacroCallId { } } - pub fn is_derive_attr_pseudo_expansion(self, db: &dyn ExpandDatabase) -> bool { + pub fn is_derive_attr_pseudo_expansion(self, db: &dyn SourceDatabase) -> bool { let loc = self.loc(db); loc.def.is_attribute_derive() } @@ -492,7 +490,7 @@ impl MacroCallId { #[salsa::tracked(returns(ref), lru = 512)] pub fn parse_macro_expansion( self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, ) -> ExpandResult<(Parse, ExpansionSpanMap)> { let _p = tracing::info_span!("parse_macro_expansion").entered(); let loc = self.loc(db); @@ -507,7 +505,7 @@ impl MacroCallId { pub fn parse_macro_expansion_error( self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, ) -> Option>> { let e: ExpandResult> = self.parse_macro_expansion(db).as_ref().map(|it| Arc::from(it.0.errors())); @@ -523,7 +521,7 @@ impl MacroCallId { #[allow(deprecated)] // we are macro_arg_considering_derives pub fn macro_arg_considering_derives<'db>( self, - db: &'db dyn ExpandDatabase, + db: &'db dyn SourceDatabase, kind: &MacroCallKind, ) -> &'db MacroArgResult { match kind { @@ -538,7 +536,7 @@ impl MacroCallId { /// query, only typing in the macro call itself changes the returned /// subtree. #[salsa::tracked(returns(ref))] - fn macro_arg(self, db: &dyn ExpandDatabase) -> MacroArgResult { + fn macro_arg(self, db: &dyn SourceDatabase) -> MacroArgResult { let loc = self.loc(db); if let MacroCallLoc { @@ -619,7 +617,7 @@ impl MacroCallId { } // MacroCallKind::Derive should not be here. As we are getting the argument for the derive macro MacroCallKind::Derive { .. } => { - unreachable!("`ExpandDatabase::macro_arg` called with `MacroCallKind::Derive`") + unreachable!("`MacroCallId::macro_arg` called with `MacroCallKind::Derive`") } MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { let node = ast_id.to_ptr(db).to_node(&root); @@ -656,7 +654,7 @@ impl MacroCallId { fn macro_expand<'db>( self, - db: &'db dyn ExpandDatabase, + db: &'db dyn SourceDatabase, loc: &MacroCallLoc, ) -> ExpandResult<(Cow<'db, tt::TopSubtree>, MatchedArmIndex)> { let _p = tracing::info_span!("macro_expand").entered(); @@ -736,7 +734,7 @@ impl MacroCallId { /// non-determinism breaks salsa in a very, very, very bad way. /// @edwin0cheng heroically debugged this once! See #4315 for details #[salsa::tracked(returns(ref))] - fn expand_proc_macro(self, db: &dyn ExpandDatabase) -> ExpandResult { + fn expand_proc_macro(self, db: &dyn SourceDatabase) -> ExpandResult { let loc = self.loc(db); let (macro_arg, undo_info, span) = self.macro_arg_considering_derives(db, &loc.kind); @@ -782,7 +780,7 @@ impl MacroCallId { /// token(s) returned with their priority. pub fn expand_speculative( self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, speculative_args: &SyntaxNode, token_to_map: SyntaxToken, ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> { @@ -965,9 +963,9 @@ fn expand_unimplemented_builtin_macro(span: Span) -> ExpandResult) -> Span { +fn proc_macro_span(db: &dyn SourceDatabase, ast: AstId) -> Span { #[salsa::tracked] - fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId, _: ()) -> Span { + fn proc_macro_span(db: &dyn SourceDatabase, ast: AstId, _: ()) -> Span { let (parse, span_map) = ast.file_id.parse_with_map(db); let root = parse.syntax_node(); let ast_id_map = ast.file_id.ast_id_map(db); @@ -981,7 +979,7 @@ fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId) -> Span { } pub(crate) fn token_tree_to_syntax_node( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, tt: &tt::TopSubtree, expand_to: ExpandTo, ) -> (Parse, ExpansionSpanMap) { @@ -1016,7 +1014,7 @@ fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> { impl MacroDefId { pub fn make_call( self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, krate: Crate, kind: MacroCallKind, ctxt: SyntaxContext, @@ -1024,7 +1022,7 @@ impl MacroDefId { MacroCallId::new(db, MacroCallLoc { def: self, krate, kind, ctxt }) } - pub fn definition_range(&self, db: &dyn ExpandDatabase) -> InFile { + pub fn definition_range(&self, db: &dyn SourceDatabase) -> InFile { match self.kind { MacroDefKind::Declarative(id, _) | MacroDefKind::BuiltIn(id, _) @@ -1105,7 +1103,7 @@ impl MacroDefId { } impl MacroCallLoc { - pub fn to_node(&self, db: &dyn ExpandDatabase) -> InFile { + pub fn to_node(&self, db: &dyn SourceDatabase) -> InFile { match &self.kind { MacroCallKind::FnLike { ast_id, .. } => { ast_id.with_value(ast_id.to_node(db).syntax().clone()) @@ -1125,7 +1123,7 @@ impl MacroCallLoc { } } - pub fn to_node_item(&self, db: &dyn ExpandDatabase) -> InFile { + pub fn to_node_item(&self, db: &dyn SourceDatabase) -> InFile { match self.kind { MacroCallKind::FnLike { ast_id, .. } => { InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db)) @@ -1151,7 +1149,7 @@ impl MacroCallLoc { pub fn include_file_id( &self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, macro_call_id: MacroCallId, ) -> Option { if self.def.is_include() @@ -1197,7 +1195,7 @@ impl MacroCallKind { /// - fn_like! {}, it spans the path and token tree /// - #\[derive], it spans the `#[derive(...)]` attribute and the annotated item /// - #\[attr], it spans the `#[attr(...)]` attribute and the annotated item - pub fn original_call_range_with_input(&self, db: &dyn ExpandDatabase) -> FileRange { + pub fn original_call_range_with_input(&self, db: &dyn SourceDatabase) -> FileRange { let get_range = |kind: &_| match kind { MacroCallKind::FnLike { ast_id, .. } => ast_id.erase(), MacroCallKind::Derive { ast_id, .. } => ast_id.erase(), @@ -1225,7 +1223,7 @@ impl MacroCallKind { /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros /// get the macro path (rustc shows the whole `ast::MacroCall`), attribute macros get the /// attribute's range, and derives get only the specific derive that is being referred to. - pub fn original_call_range(&self, db: &dyn ExpandDatabase, krate: Crate) -> FileRange { + pub fn original_call_range(&self, db: &dyn SourceDatabase, krate: Crate) -> FileRange { let get_range = |kind: &_| match kind { MacroCallKind::FnLike { ast_id, .. } => { let node = ast_id.to_node(db); @@ -1261,7 +1259,7 @@ impl MacroCallKind { FileRange { range, file_id } } - fn arg(&self, db: &dyn ExpandDatabase) -> InFile> { + fn arg(&self, db: &dyn SourceDatabase) -> InFile> { match self { MacroCallKind::FnLike { ast_id, .. } => { ast_id.to_in_file_node(db).map(|it| Some(it.token_tree()?.syntax().clone())) @@ -1351,7 +1349,7 @@ impl<'db> ExpansionInfo<'db> { /// Looks up the span at the given offset. pub fn span_for_offset( &self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, offset: TextSize, ) -> (FileRange, SyntaxContext) { debug_assert!(self.expanded.value.text_range().contains(offset)); @@ -1361,7 +1359,7 @@ impl<'db> ExpansionInfo<'db> { /// Maps up the text range out of the expansion hierarchy back into the original file its from. pub fn map_node_range_up( &self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, range: TextRange, ) -> Option<(FileRange, SyntaxContext)> { debug_assert!(self.expanded.value.text_range().contains_range(range)); @@ -1374,7 +1372,7 @@ impl<'db> ExpansionInfo<'db> { /// and as such we may consider inputs that are unrelated. pub fn map_range_up_once( &self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, token: TextRange, ) -> InFile> { debug_assert!(self.expanded.value.text_range().contains_range(token)); @@ -1401,7 +1399,7 @@ impl<'db> ExpansionInfo<'db> { } } - pub fn new(db: &'db dyn ExpandDatabase, macro_file: MacroCallId) -> ExpansionInfo<'db> { + pub fn new(db: &'db dyn SourceDatabase, macro_file: MacroCallId) -> ExpansionInfo<'db> { let _p = tracing::info_span!("ExpansionInfo::new").entered(); let loc = macro_file.loc(db); @@ -1419,7 +1417,7 @@ impl<'db> ExpansionInfo<'db> { /// considering the root spans contained. /// Unlike [`map_node_range_up`], this will not return `None` if any anchors or syntax contexts differ. pub fn map_node_range_up_rooted( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, exp_map: &ExpansionSpanMap, range: TextRange, ) -> Option { @@ -1442,7 +1440,7 @@ pub fn map_node_range_up_rooted( /// /// this will return `None` if any anchors or syntax contexts differ. pub fn map_node_range_up( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, exp_map: &ExpansionSpanMap, range: TextRange, ) -> Option<(FileRange, SyntaxContext)> { @@ -1463,7 +1461,7 @@ pub fn map_node_range_up( /// Looks up the span at the given offset. pub fn span_for_offset( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, exp_map: &ExpansionSpanMap, offset: TextSize, ) -> (FileRange, SyntaxContext) { @@ -1474,7 +1472,7 @@ pub fn span_for_offset( // FIXME: This is only public because of its use in `load_cargo` (which we should consider removing // by moving the implementations of the subrequests to `hir_expand`, and calling within `load-cargo`). // Avoid adding any more outside uses. -pub fn resolve_span(db: &dyn ExpandDatabase, Span { range, anchor, ctx: _ }: Span) -> FileRange { +pub fn resolve_span(db: &dyn SourceDatabase, Span { range, anchor, ctx: _ }: Span) -> FileRange { let file_id = EditionedFileId::from_span_file_id(db, anchor.file_id); let anchor_offset = HirFileId::from(file_id).ast_id_map(db).get_erased(anchor.ast_id).text_range().start(); @@ -1624,7 +1622,7 @@ impl HirFileId { } } - pub fn syntax_context(self, db: &dyn ExpandDatabase, edition: Edition) -> SyntaxContext { + pub fn syntax_context(self, db: &dyn SourceDatabase, edition: Edition) -> SyntaxContext { match self { HirFileId::FileId(_) => SyntaxContext::root(edition), HirFileId::MacroFile(m) => { @@ -1634,14 +1632,14 @@ impl HirFileId { } } - pub fn edition(self, db: &dyn ExpandDatabase) -> Edition { + pub fn edition(self, db: &dyn SourceDatabase) -> Edition { match self { HirFileId::FileId(file_id) => file_id.edition(db), HirFileId::MacroFile(m) => m.loc(db).def.edition, } } - pub fn original_file(self, db: &dyn ExpandDatabase) -> EditionedFileId { + pub fn original_file(self, db: &dyn SourceDatabase) -> EditionedFileId { let mut file_id = self; loop { match file_id { @@ -1653,7 +1651,7 @@ impl HirFileId { } } - pub fn original_file_respecting_includes(mut self, db: &dyn ExpandDatabase) -> EditionedFileId { + pub fn original_file_respecting_includes(mut self, db: &dyn SourceDatabase) -> EditionedFileId { loop { match self { HirFileId::FileId(id) => break id, @@ -1671,7 +1669,7 @@ impl HirFileId { } } - pub fn original_call_node(self, db: &dyn ExpandDatabase) -> Option> { + pub fn original_call_node(self, db: &dyn SourceDatabase) -> Option> { let mut call = self.macro_file()?.loc(db).to_node(db); loop { match call.file_id { @@ -1685,13 +1683,13 @@ impl HirFileId { } } - pub fn call_node(self, db: &dyn ExpandDatabase) -> Option> { + pub fn call_node(self, db: &dyn SourceDatabase) -> Option> { Some(self.macro_file()?.loc(db).to_node(db)) } pub fn as_builtin_derive_attr_node( &self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, ) -> Option> { let macro_file = self.macro_file()?; let loc = macro_file.loc(db); @@ -1704,7 +1702,7 @@ impl HirFileId { /// Main public API -- parses a hir file, not caring whether it's a real /// file or a macro expansion. - pub fn parse_or_expand(self, db: &dyn ExpandDatabase) -> SyntaxNode { + pub fn parse_or_expand(self, db: &dyn SourceDatabase) -> SyntaxNode { match self { HirFileId::FileId(file_id) => file_id.parse(db).syntax_node(), HirFileId::MacroFile(macro_file) => { @@ -1715,7 +1713,7 @@ impl HirFileId { pub(crate) fn parse_with_map( self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, ) -> (Parse, SpanMap<'_>) { match self { HirFileId::FileId(file_id) => ( @@ -1733,7 +1731,7 @@ impl HirFileId { #[salsa::tracked] impl HirFileId { #[salsa::tracked(lru = 1024, returns(ref))] - pub fn ast_id_map(self, db: &dyn ExpandDatabase) -> AstIdMap { + pub fn ast_id_map(self, db: &dyn SourceDatabase) -> AstIdMap { AstIdMap::from_source(&self.parse_or_expand(db)) } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index b90bfac483b19..59578bae89554 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -6,12 +6,11 @@ use std::{ }; use crate::{ - db::ExpandDatabase, hygiene::Transparency, name::{AsName, Name}, tt, }; -use base_db::Crate; +use base_db::{Crate, SourceDatabase}; use intern::{Symbol, sym}; use parser::T; use smallvec::SmallVec; @@ -45,14 +44,14 @@ impl PathKind { impl ModPath { pub fn from_src( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, path: ast::Path, span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext, ) -> Option { convert_path(db, path, span_for_range) } - pub fn from_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Option { + pub fn from_tt(db: &dyn SourceDatabase, tt: tt::TokenTreesView<'_>) -> Option { convert_path_tt(db, tt) } @@ -68,7 +67,7 @@ impl ModPath { } pub fn from_tokens( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext, is_abs: bool, segments: impl Iterator, @@ -180,16 +179,13 @@ impl ModPath { _ => None, } } - pub fn display_verbatim<'a>( - &'a self, - db: &'a dyn crate::db::ExpandDatabase, - ) -> impl fmt::Display + 'a { + pub fn display_verbatim<'a>(&'a self, db: &'a dyn SourceDatabase) -> impl fmt::Display + 'a { Display { db, path: self, edition: None } } pub fn display<'a>( &'a self, - db: &'a dyn crate::db::ExpandDatabase, + db: &'a dyn SourceDatabase, edition: Edition, ) -> impl fmt::Display + 'a { Display { db, path: self, edition: Some(edition) } @@ -203,7 +199,7 @@ impl Extend for ModPath { } struct Display<'a> { - db: &'a dyn ExpandDatabase, + db: &'a dyn SourceDatabase, path: &'a ModPath, edition: Option, } @@ -221,7 +217,7 @@ impl From for ModPath { } fn display_fmt_path( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, path: &ModPath, f: &mut fmt::Formatter<'_>, edition: Option, @@ -261,7 +257,7 @@ fn display_fmt_path( } fn convert_path( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, path: ast::Path, span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext, ) -> Option { @@ -346,7 +342,7 @@ fn convert_path( Some(mod_path) } -fn convert_path_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Option { +fn convert_path_tt(db: &dyn SourceDatabase, tt: tt::TokenTreesView<'_>) -> Option { let mut leaves = tt.iter().filter_map(|tt| match tt { tt::TtElement::Leaf(leaf) => Some(leaf), tt::TtElement::Subtree(..) => None, @@ -388,7 +384,7 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Optio Some(ModPath { kind, segments }) } -pub fn resolve_crate_root(db: &dyn ExpandDatabase, mut ctxt: SyntaxContext) -> Option { +pub fn resolve_crate_root(db: &dyn SourceDatabase, mut ctxt: SyntaxContext) -> Option { // When resolving `$crate` from a `macro_rules!` invoked in a `macro`, // we don't want to pretend that the `macro_rules!` definition is in the `macro` // as described in `SyntaxContextId::apply_mark`, so we ignore prepended opaque marks. diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index b511bc3c15ff0..d91b0f378e191 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -2,6 +2,7 @@ use std::fmt; +use base_db::SourceDatabase; use intern::{Symbol, sym}; use span::{Edition, SyntaxContext}; use syntax::utils::is_raw_identifier; @@ -180,7 +181,7 @@ impl Name { #[inline] pub fn display<'a>( &'a self, - db: &dyn crate::db::ExpandDatabase, + db: &dyn SourceDatabase, edition: Edition, ) -> impl fmt::Display + 'a { _ = db; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs index 687bd1a71400f..d088b0d1a9f7c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs @@ -1,15 +1,15 @@ //! Pretty printing of macros output. -use base_db::Crate; +use base_db::{Crate, SourceDatabase}; use rustc_hash::FxHashMap; use syntax::SyntaxNode; -use crate::{db::ExpandDatabase, span_map::ExpansionSpanMap}; +use crate::span_map::ExpansionSpanMap; /// Inserts whitespace and replaces `$crate` in macro expansions. #[expect(deprecated)] pub fn prettify_macro_expansion( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, syn: SyntaxNode, span_map: &ExpansionSpanMap, target_crate_id: Crate, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs index 94c105761db36..f4a259dcc1acb 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs @@ -4,14 +4,14 @@ use core::fmt; use std::any::Any; use std::{panic::RefUnwindSafe, sync}; -use base_db::{Crate, CrateBuilderId, CratesIdMap, Env, ProcMacroLoadingError}; +use base_db::{Crate, CrateBuilderId, CratesIdMap, Env, ProcMacroLoadingError, SourceDatabase}; use intern::Symbol; use rustc_hash::FxHashMap; use salsa::{Durability, Setter}; use span::Span; use triomphe::Arc; -use crate::{ExpandError, ExpandErrorKind, ExpandResult, db::ExpandDatabase, tt}; +use crate::{ExpandError, ExpandErrorKind, ExpandResult, tt}; #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Hash)] pub enum ProcMacroKind { @@ -26,7 +26,7 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe + Any { /// [`ProcMacroKind::Attr`]), environment variables, and span information. fn expand( &self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, subtree: &tt::TopSubtree, attrs: Option<&tt::TopSubtree>, env: &Env, @@ -85,7 +85,7 @@ impl ProcMacrosBuilder { } /// Builds [`ProcMacros`] and adds id to `db` - pub(crate) fn build_in(self, db: &mut dyn ExpandDatabase, crates_id_map: &CratesIdMap) { + pub(crate) fn build_in(self, db: &mut dyn SourceDatabase, crates_id_map: &CratesIdMap) { let mut map = self .0 .into_iter() @@ -121,7 +121,7 @@ pub struct ProcMacros { } impl ProcMacros { - pub fn init_default(db: &dyn ExpandDatabase, durability: Durability) { + pub fn init_default(db: &dyn SourceDatabase, durability: Durability) { _ = Self::builder(Default::default()).durability(durability).new(db); } } @@ -130,7 +130,7 @@ impl ProcMacros { impl ProcMacros { /// Incrementality query to prevent queries from directly depending on [`Self::get`]. #[salsa::tracked(returns(as_ref))] - pub fn get_for_crate(db: &dyn ExpandDatabase, krate: Crate) -> Option> { + pub fn get_for_crate(db: &dyn SourceDatabase, krate: Crate) -> Option> { Self::get(db).by_crate(db).get(&krate).cloned() } } @@ -273,7 +273,7 @@ impl CustomProcMacroExpander { pub fn expand( self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, def_crate: Crate, calling_crate: Crate, tt: &tt::TopSubtree, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs index 8f2b74091f865..7b1a04de732bb 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs @@ -1,11 +1,12 @@ //! Span maps for real files and macro expansions. +use base_db::SourceDatabase; use span::Span; use syntax::{AstNode, TextRange, ast}; pub use span::RealSpanMap; -use crate::{HirFileId, MacroCallId, db::ExpandDatabase}; +use crate::{HirFileId, MacroCallId}; pub type ExpansionSpanMap = span::SpanMap; @@ -39,7 +40,7 @@ impl<'db> SpanMap<'db> { impl HirFileId { #[inline] - pub fn span_map<'db>(self, db: &'db dyn ExpandDatabase) -> SpanMap<'db> { + pub fn span_map<'db>(self, db: &'db dyn SourceDatabase) -> SpanMap<'db> { match self { HirFileId::FileId(file_id) => SpanMap::RealSpanMap(real_span_map(db, file_id)), HirFileId::MacroFile(m) => SpanMap::ExpansionSpanMap(m.expansion_span_map(db)), @@ -51,7 +52,7 @@ impl HirFileId { /// `HirFileId::from(file_id).span_map(db)` instead of `real_span_map(db, file_id)`. #[salsa_macros::tracked(returns(ref))] pub(crate) fn real_span_map( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, editioned_file_id: base_db::EditionedFileId, ) -> RealSpanMap { use syntax::ast::HasModuleItem; @@ -114,7 +115,7 @@ pub(crate) fn real_span_map( } impl MacroCallId { - pub fn expansion_span_map(self, db: &dyn ExpandDatabase) -> &ExpansionSpanMap { + pub fn expansion_span_map(self, db: &dyn SourceDatabase) -> &ExpansionSpanMap { &self.parse_macro_expansion(db).value.1 } } diff --git a/src/tools/rust-analyzer/crates/hir/src/db.rs b/src/tools/rust-analyzer/crates/hir/src/db.rs index b8f271108e63e..8ae3a024f0acd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir/src/db.rs @@ -4,5 +4,4 @@ //! //! But we need this for at least LRU caching at the query level. pub use hir_def::db::{DefDatabase, set_expand_proc_attr_macros}; -pub use hir_expand::db::ExpandDatabase; pub use hir_ty::db::HirDatabase; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 8023d239afd0a..ba488b8af35e5 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -26,7 +26,6 @@ use hir_expand::{ EditionedFileId, ExpandResult, FileRange, HirFileId, InMacroFile, MacroCallId, attrs::AstPathExt, builtin::{BuiltinFnLikeExpander, EagerExpander}, - db::ExpandDatabase, files::{FileRangeWrapper, HirFileRange, InRealFile}, mod_path::{ModPath, PathKind}, name::AsName, @@ -2604,7 +2603,7 @@ fn macro_call_to_macro_id( ctx: &mut SourceToDefCtx<'_, '_>, macro_call_id: MacroCallId, ) -> Option { - let db: &dyn ExpandDatabase = ctx.db; + let db = ctx.db; let loc = macro_call_id.loc(db); match loc.def.ast_id() { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs index 7a3c466daa50c..93cfee8135eed 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs @@ -195,19 +195,6 @@ impl RootDatabase { // hir::db::InternProcMacroQuery // hir::db::InternMacroRulesQuery - // // ExpandDatabase - // hir::db::AstIdMapQuery - // hir::db::DeclMacroExpanderQuery - // hir::db::ExpandProcMacroQuery - // hir::db::InternMacroCallQuery - // hir::db::InternSyntaxContextQuery - // hir::db::MacroArgQuery - // hir::db::ParseMacroExpansionErrorQuery - // hir::db::ParseMacroExpansionQuery - // hir::db::ProcMacroSpanQuery - // hir::db::ProcMacrosQuery - // hir::db::RealSpanMapQuery - // // LineIndexDatabase // crate::LineIndexQuery diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs index 16287a439c358..6bf4e72f200b7 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs @@ -15,7 +15,7 @@ use crate::{Match, SsrMatches, fragments, resolving::ResolvedRule}; /// template. Placeholders in the template will have been substituted with whatever they matched to /// in the original code. pub(crate) fn matches_to_edit<'db>( - db: &'db dyn hir::db::ExpandDatabase, + db: &'db dyn ide_db::base_db::SourceDatabase, matches: &SsrMatches, file_src: &str, rules: &[ResolvedRule<'db>], @@ -24,7 +24,7 @@ pub(crate) fn matches_to_edit<'db>( } fn matches_to_edit_at_offset<'db>( - db: &'db dyn hir::db::ExpandDatabase, + db: &'db dyn ide_db::base_db::SourceDatabase, matches: &SsrMatches, file_src: &str, relative_start: TextSize, @@ -41,7 +41,7 @@ fn matches_to_edit_at_offset<'db>( } struct ReplacementRenderer<'a, 'db> { - db: &'db dyn hir::db::ExpandDatabase, + db: &'db dyn ide_db::base_db::SourceDatabase, match_info: &'a Match, file_src: &'a str, rules: &'a [ResolvedRule<'db>], @@ -59,7 +59,7 @@ struct ReplacementRenderer<'a, 'db> { } fn render_replace<'db>( - db: &'db dyn hir::db::ExpandDatabase, + db: &'db dyn ide_db::base_db::SourceDatabase, match_info: &Match, file_src: &str, rules: &[ResolvedRule<'db>], diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 9fc89e90f2aa3..db5464a1fe93a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -1,8 +1,9 @@ use std::iter; -use hir::{EditionedFileId, FilePosition, FileRange, HirFileId, InFile, Semantics, db}; +use hir::{EditionedFileId, FilePosition, FileRange, HirFileId, InFile, Semantics}; use ide_db::{ FxHashMap, FxHashSet, RootDatabase, + base_db::SourceDatabase, defs::{Definition, IdentClass}, helpers::pick_best_token, search::{FileReference, ReferenceCategory, SearchScope}, @@ -676,7 +677,7 @@ fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSe } fn original_frange( - db: &dyn db::ExpandDatabase, + db: &dyn SourceDatabase, file_id: HirFileId, text_range: Option, ) -> Option { diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index b5860cb0caace..fb082d3209b1e 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -11,16 +11,15 @@ extern crate rustc_driver as _; use std::{any::Any, collections::hash_map::Entry, mem, path::Path, sync}; use crossbeam_channel::{Receiver, unbounded}; -use hir_expand::{ - db::ExpandDatabase, - proc_macro::{ - ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacroLoadResult, - ProcMacrosBuilder, - }, +use hir_expand::proc_macro::{ + ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacroLoadResult, + ProcMacrosBuilder, }; use ide_db::{ ChangeWithProcMacros, FxHashMap, RootDatabase, - base_db::{CrateGraphBuilder, Env, ProcMacroLoadingError, SourceRoot, SourceRootId}, + base_db::{ + CrateGraphBuilder, Env, ProcMacroLoadingError, SourceDatabase, SourceRoot, SourceRootId, + }, prime_caches, }; use itertools::Itertools; @@ -569,7 +568,7 @@ struct Expander(proc_macro_api::ProcMacro); impl ProcMacroExpander for Expander { fn expand( &self, - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, subtree: &tt::TopSubtree, attrs: Option<&tt::TopSubtree>, env: &Env, @@ -749,7 +748,7 @@ impl ProcMacroExpander for Expander { } fn resolve_sub_span( - db: &dyn ExpandDatabase, + db: &dyn SourceDatabase, file_id: u32, ast_id: u32, range: TextRange, diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs index 75c3bf8d35bb3..f070b1c9a3342 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs @@ -56,7 +56,7 @@ pub fn run_conversation( return Ok(BidirectionalMessage::Response(response)); } BidirectionalMessage::SubRequest(sr) => { - // TODO: Avoid `AssertUnwindSafe` by making the callback `UnwindSafe` once `ExpandDatabase` + // TODO: Avoid `AssertUnwindSafe` by making the callback `UnwindSafe` once `SourceDatabase` // becomes unwind-safe (currently blocked by `parking_lot::RwLock` in the VFS). let resp = match catch_unwind(AssertUnwindSafe(|| callback(sr))) { Ok(Ok(resp)) => BidirectionalMessage::SubResponse(resp), diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index f346535ca19c0..63e76d049b72e 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -17,7 +17,6 @@ use cfg::CfgOptions; use hir_expand::{ EditionedFileId, FileRange, change::ChangeWithProcMacros, - db::ExpandDatabase, files::FilePosition, proc_macro::{ ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacrosBuilder, @@ -139,7 +138,7 @@ pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0); /// /// Available proc macros: `identity` (attr), `DeriveIdentity` (derive), `input_replace` (attr), /// `mirror` (bang), `shorten` (bang) -pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { +pub trait WithFixture: Default + SourceDatabase + 'static { /// See the trait documentation for more information on fixtures. #[track_caller] fn with_single_file( @@ -231,7 +230,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { } } -impl WithFixture for DB {} +impl WithFixture for DB {} pub struct ChangeFixture { pub file_position: Option<(span::EditionedFileId, RangeOrOffset)>, @@ -847,7 +846,7 @@ struct IdentityProcMacroExpander; impl ProcMacroExpander for IdentityProcMacroExpander { fn expand( &self, - _: &dyn ExpandDatabase, + _: &dyn SourceDatabase, subtree: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -870,7 +869,7 @@ struct Issue18089ProcMacroExpander; impl ProcMacroExpander for Issue18089ProcMacroExpander { fn expand( &self, - _: &dyn ExpandDatabase, + _: &dyn SourceDatabase, subtree: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -906,7 +905,7 @@ struct AttributeInputReplaceProcMacroExpander; impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander { fn expand( &self, - _: &dyn ExpandDatabase, + _: &dyn SourceDatabase, _: &TopSubtree, attrs: Option<&TopSubtree>, _: &Env, @@ -930,7 +929,7 @@ struct Issue18840ProcMacroExpander; impl ProcMacroExpander for Issue18840ProcMacroExpander { fn expand( &self, - _: &dyn ExpandDatabase, + _: &dyn SourceDatabase, fn_: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -967,7 +966,7 @@ struct MirrorProcMacroExpander; impl ProcMacroExpander for MirrorProcMacroExpander { fn expand( &self, - _: &dyn ExpandDatabase, + _: &dyn SourceDatabase, input: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -1006,7 +1005,7 @@ struct ShortenProcMacroExpander; impl ProcMacroExpander for ShortenProcMacroExpander { fn expand( &self, - _: &dyn ExpandDatabase, + _: &dyn SourceDatabase, input: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -1051,7 +1050,7 @@ struct Issue17479ProcMacroExpander; impl ProcMacroExpander for Issue17479ProcMacroExpander { fn expand( &self, - _: &dyn ExpandDatabase, + _: &dyn SourceDatabase, subtree: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -1082,7 +1081,7 @@ struct Issue18898ProcMacroExpander; impl ProcMacroExpander for Issue18898ProcMacroExpander { fn expand( &self, - _: &dyn ExpandDatabase, + _: &dyn SourceDatabase, subtree: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -1136,7 +1135,7 @@ struct DisallowCfgProcMacroExpander; impl ProcMacroExpander for DisallowCfgProcMacroExpander { fn expand( &self, - _: &dyn ExpandDatabase, + _: &dyn SourceDatabase, subtree: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -1168,7 +1167,7 @@ struct GenerateSuffixedTypeProcMacroExpander; impl ProcMacroExpander for GenerateSuffixedTypeProcMacroExpander { fn expand( &self, - _: &dyn ExpandDatabase, + _: &dyn SourceDatabase, subtree: &TopSubtree, _attrs: Option<&TopSubtree>, _env: &Env, From ec4462ad7564982f5c07e307ea4475a3a5a33916 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 3 Jul 2026 18:49:21 +0200 Subject: [PATCH 28/78] internal: simplify `fn_macro::register_builtin!` Instead of constructing both kinds of expanders in the same macro call, construct them in two. This allows for some deduplication in the macro definition. AFAICT having everything in the same macro was necessary in order to implement `find_by_name`, but I've changed it to be a method on each of the expanders, with `find_builtin_macro` calling both and combining their results. Also the macro now accepts an optional trailing comma. All of this makes the macro more similar to the ones in `attr_macro` and `derive_macro`. --- .../crates/hir-expand/src/builtin/fn_macro.rs | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index 5b9fe09077fbb..b173f44f34ab1 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -25,41 +25,27 @@ use crate::{ }; macro_rules! register_builtin { - ( $LAZY:ident: $(($name:ident, $kind: ident) => $expand:ident),* , $EAGER:ident: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => { + ( $EXPANDER:ident: $(($name:ident, $kind: ident) => $expand:ident),* $(,)? ) => { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub enum $LAZY { + pub enum $EXPANDER { $($kind),* } - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub enum $EAGER { - $($e_kind),* - } - - impl BuiltinFnLikeExpander { + impl $EXPANDER { fn expander(&self) -> fn (&dyn SourceDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult { match *self { - $( BuiltinFnLikeExpander::$kind => $expand, )* + $( Self::$kind => $expand, )* } } - } - impl EagerExpander { - fn expander(&self) -> fn (&dyn SourceDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult { - match *self { - $( EagerExpander::$e_kind => $e_expand, )* + fn find_by_name(ident: &name::Name) -> Option { + match ident { + $( id if *id == sym::$name => Some(Self::$kind), )* + _ => None, } } } - - fn find_by_name(ident: &name::Name) -> Option> { - match ident { - $( id if id == &sym::$name => Some(Either::Left(BuiltinFnLikeExpander::$kind)), )* - $( id if id == &sym::$e_name => Some(Either::Right(EagerExpander::$e_kind)), )* - _ => return None, - } - } - }; + } } impl BuiltinFnLikeExpander { @@ -110,7 +96,8 @@ impl EagerExpander { pub fn find_builtin_macro( ident: &name::Name, ) -> Option> { - find_by_name(ident) + (BuiltinFnLikeExpander::find_by_name(ident).map(Either::Left)) + .or_else(|| EagerExpander::find_by_name(ident).map(Either::Right)) } register_builtin! { @@ -136,7 +123,9 @@ register_builtin! { (format_args_nl, FormatArgsNl) => format_args_nl_expand, (quote, Quote) => quote_expand, (pattern_type, PatternType) => pattern_type_expand, +} +register_builtin! { EagerExpander: (compile_error, CompileError) => compile_error_expand, (concat, Concat) => concat_expand, @@ -145,7 +134,7 @@ register_builtin! { (include_bytes, IncludeBytes) => include_bytes_expand, (include_str, IncludeStr) => include_str_expand, (env, Env) => env_expand, - (option_env, OptionEnv) => option_env_expand + (option_env, OptionEnv) => option_env_expand, } fn mk_pound(span: Span) -> tt::Leaf { From 5c69628481fd76a6c30e522f0dca44f7462c0c89 Mon Sep 17 00:00:00 2001 From: bendn Date: Mon, 6 Jul 2026 00:29:21 +0700 Subject: [PATCH 29/78] suggest code action fixes produced from diagnostics under cursor with effects elsewhere --- .../rust-analyzer/crates/rust-analyzer/src/diagnostics.rs | 5 +++-- .../crates/rust-analyzer/src/handlers/request.rs | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 889d20a184b25..f690e38585a7b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -60,7 +60,7 @@ pub(crate) struct DiagnosticCollection { #[derive(Debug, Clone)] pub(crate) struct Fix { // Fixes may be triggerable from multiple ranges. - pub(crate) ranges: SmallVec<[lsp_types::Range; 1]>, + pub(crate) ranges: SmallVec<[lsp_types::Range; 2]>, pub(crate) action: lsp_ext::CodeAction, } @@ -181,11 +181,12 @@ impl DiagnosticCollection { } } - if let Some(fix) = fix { + if let Some(mut fix) = fix { let check_fixes = Arc::make_mut(&mut self.check_fixes); if check_fixes.len() <= flycheck_id { check_fixes.resize_with(flycheck_id + 1, Default::default); } + fix.ranges.push(diagnostic.range); check_fixes[flycheck_id] .entry(package_id.clone()) .or_default() diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index e57b05660d5a7..41e3db7f3191a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1575,6 +1575,7 @@ pub(crate) fn handle_code_action( .copied() .filter_map(|range| from_proto::text_range(&line_index, range).ok()) .any(|fix_range| fix_range.intersect(frange.range).is_some()); + if intersect_fix_range { res.push(fix.action.clone()); } From ef080604784dfb9133f4bce687d57b4ba090b015 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 8 Jul 2026 10:50:21 +0200 Subject: [PATCH 30/78] misc improvements - Remove useless (and outdated) docs - Consolidate imports --- .../rust-analyzer/crates/hir-def/src/lib.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 1d24a4604d9a7..7ecf533902695 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -35,9 +35,8 @@ pub mod builtin_derive; pub mod lang_item; pub mod unstable_features; -pub mod hir; -pub use self::hir::type_ref; pub mod expr_store; +pub mod hir; pub mod resolver; pub mod nameres; @@ -48,12 +47,6 @@ pub mod find_path; pub mod import_map; pub mod visibility; -use intern::{Interned, sym}; -use rustc_abi::ExternAbi; -use thin_vec::ThinVec; - -pub use crate::signatures::LocalFieldId; - #[cfg(test)] mod macro_expansion_tests; #[cfg(test)] @@ -73,12 +66,13 @@ use hir_expand::{ name::Name, proc_macro::{CustomProcMacroExpander, ProcMacroKind}, }; +use intern::{Interned, sym}; use nameres::DefMap; +use rustc_abi::ExternAbi; use span::{AstIdNode, Edition, FileAstId, SyntaxContext}; use stdx::impl_from; use syntax::{AstNode, ast}; - -pub use hir_expand::{Intern, Lookup, tt}; +use thin_vec::ThinVec; use crate::{ attrs::AttrFlags, @@ -96,8 +90,11 @@ use crate::{ signatures::{EnumVariants, InactiveEnumVariantCode, VariantFields}, }; +pub use crate::{hir::type_ref, signatures::LocalFieldId}; +pub use hir_expand::{Intern, Lookup, tt}; + type FxIndexMap = indexmap::IndexMap; -/// A wrapper around three booleans + #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] pub struct FindPathConfig { /// If true, prefer to unconditionally use imports of the `core` and `alloc` crate From 15f86eeb2ca701d880ce8c0b9a9125f05f45e465 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 8 Jul 2026 17:07:30 +0200 Subject: [PATCH 31/78] move `FindPathConfig` to `find_path.rs` --- .../crates/hir-def/src/find_path.rs | 16 +++++++++++++++- .../rust-analyzer/crates/hir-def/src/lib.rs | 16 +--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 40f74d280261c..9f7f3f8c7695f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -12,7 +12,7 @@ use intern::sym; use rustc_hash::FxHashSet; use crate::{ - FindPathConfig, ModuleDefId, ModuleId, + ModuleDefId, ModuleId, db::DefDatabase, import_map::ImportMap, item_scope::ItemInNs, @@ -20,6 +20,20 @@ use crate::{ visibility::{Visibility, VisibilityExplicitness}, }; +#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] +pub struct FindPathConfig { + /// If true, prefer to unconditionally use imports of the `core` and `alloc` crate + /// over the std. + pub prefer_no_std: bool, + /// If true, prefer import paths containing a prelude module. + pub prefer_prelude: bool, + /// If true, prefer abs path (starting with `::`) where it is available. + pub prefer_absolute: bool, + /// If true, paths containing `#[unstable]` segments may be returned, but only if if there is no + /// stable path. This does not check, whether the item itself that is being imported is `#[unstable]`. + pub allow_unstable: bool, +} + /// Find a path that can be used to refer to a certain item. This can depend on /// *from where* you're referring to the item, hence the `from` parameter. pub fn find_path( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 7ecf533902695..9be6e26286df3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -90,25 +90,11 @@ use crate::{ signatures::{EnumVariants, InactiveEnumVariantCode, VariantFields}, }; -pub use crate::{hir::type_ref, signatures::LocalFieldId}; +pub use crate::{find_path::FindPathConfig, hir::type_ref, signatures::LocalFieldId}; pub use hir_expand::{Intern, Lookup, tt}; type FxIndexMap = indexmap::IndexMap; -#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] -pub struct FindPathConfig { - /// If true, prefer to unconditionally use imports of the `core` and `alloc` crate - /// over the std. - pub prefer_no_std: bool, - /// If true, prefer import paths containing a prelude module. - pub prefer_prelude: bool, - /// If true, prefer abs path (starting with `::`) where it is available. - pub prefer_absolute: bool, - /// If true, paths containing `#[unstable]` segments may be returned, but only if if there is no - /// stable path. This does not check, whether the item itself that is being imported is `#[unstable]`. - pub allow_unstable: bool, -} - #[derive(Debug)] pub struct ItemLoc { pub container: ModuleId, From 60aa191c32e3e3f36e6a354b6b5e0b248128a3cf Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 23 Jun 2026 02:26:19 +0000 Subject: [PATCH 32/78] Pretty-print user self types too. --- compiler/rustc_middle/src/ty/generic_args.rs | 6 +- compiler/rustc_middle/src/ty/print/pretty.rs | 23 + .../rustc_middle/src/ty/typeck_results.rs | 24 +- ..._of_reborrow.SimplifyCfg-initial.after.mir | 18 +- ...ignment.main.SimplifyCfg-initial.after.mir | 4 +- .../issue_101867.main.built.after.mir | 4 +- ...otations.match_assoc_const.built.after.mir | 4 +- ...ns.match_assoc_const_range.built.after.mir | 8 +- ...d_in_vec.build-{closure#0}.built.after.mir | 399 ++++++++++++++++++ tests/mir-opt/coroutine/unwind_in_vec.rs | 10 + .../issue_99325.main.built.after.32bit.mir | 4 +- .../issue_99325.main.built.after.64bit.mir | 4 +- 12 files changed, 464 insertions(+), 44 deletions(-) create mode 100644 tests/mir-opt/coroutine/unwind_in_vec.build-{closure#0}.built.after.mir create mode 100644 tests/mir-opt/coroutine/unwind_in_vec.rs diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index 6b5733e87b14c..a9ca6bfef5534 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -8,7 +8,7 @@ use std::ptr::NonNull; use rustc_data_structures::intern::Interned; use rustc_errors::{DiagArgValue, IntoDiagArg}; use rustc_hir::def_id::DefId; -use rustc_macros::{StableHash, TyDecodable, TyEncodable, extension}; +use rustc_macros::{Lift, StableHash, TyDecodable, TyEncodable, extension}; use rustc_serialize::{Decodable, Encodable}; use rustc_type_ir::WithCachedTypeInfo; use rustc_type_ir::walk::TypeWalker; @@ -715,7 +715,7 @@ impl<'tcx, T: TypeVisitable>> TypeVisitable> for &'tcx /// Stores the user-given args to reach some fully qualified path /// (e.g., `::Item` or `::Item`). #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(StableHash, TypeFoldable, TypeVisitable)] +#[derive(StableHash, TypeFoldable, TypeVisitable, Lift)] pub struct UserArgs<'tcx> { /// The args for the item as given by the user. pub args: GenericArgsRef<'tcx>, @@ -742,7 +742,7 @@ pub struct UserArgs<'tcx> { /// the self type, giving `Foo`. Finally, we unify that with /// the self type here, which contains `?A` to be `&'static u32` #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(StableHash, TypeFoldable, TypeVisitable)] +#[derive(StableHash, TypeFoldable, TypeVisitable, Lift)] pub struct UserSelfTy<'tcx> { pub impl_def_id: DefId, pub self_ty: Ty<'tcx>, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 03d13e6a81a6e..2d45b6feb59f0 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3418,6 +3418,29 @@ define_print_and_forward_display! { self.kind().print(p)?; } + ty::UserTypeKind<'tcx> { + match *self { + Self::Ty(ty) => { + write!(p, "Ty(")?; + ty.print(p)?; + } + Self::TypeOf(def_id, ty::UserArgs { args, user_self_ty }) => { + write!(p, "TypeOf(")?; + p.print_def_path(def_id, args)?; + if let Some(ty::UserSelfTy { impl_def_id, self_ty }) = user_self_ty { + write!(p, " at for ", key.disambiguated_data.as_sym(false))?; + self_ty.print(p)?; + write!(p, ">")?; + } + } + } + write!(p, ")")?; + } + GenericArg<'tcx> { match self.kind() { GenericArgKind::Lifetime(lt) => lt.print(p)?, diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 65a713602ed07..e11bc6d38495b 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -13,7 +13,7 @@ use rustc_hir::{ self as hir, BindingMode, ByRef, HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, Mutability, }; use rustc_index::IndexVec; -use rustc_macros::{StableHash, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; +use rustc_macros::{Lift, StableHash, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_session::Session; use rustc_span::Span; @@ -798,7 +798,7 @@ impl<'tcx> UserType<'tcx> { /// from constants that are named via paths, like `Foo::::new` and /// so forth. #[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)] -#[derive(Eq, Hash, StableHash, TypeFoldable, TypeVisitable)] +#[derive(Eq, Hash, StableHash, TypeFoldable, TypeVisitable, Lift)] pub enum UserTypeKind<'tcx> { Ty(Ty<'tcx>), @@ -863,24 +863,12 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> { impl<'tcx> std::fmt::Display for UserType<'tcx> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.bounds.is_empty() { - self.kind.fmt(f) - } else { - self.kind.fmt(f)?; + self.kind.fmt(f)?; + for b in self.bounds { write!(f, " + ")?; - std::fmt::Debug::fmt(&self.bounds, f) - } - } -} - -impl<'tcx> std::fmt::Display for UserTypeKind<'tcx> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Ty(arg0) => { - ty::print::with_no_trimmed_paths!(write!(f, "Ty({})", arg0)) - } - Self::TypeOf(arg0, arg1) => write!(f, "TypeOf({:?}, {:?})", arg0, arg1), + b.fmt(f)?; } + Ok(()) } } diff --git a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir index 40527446e5dd5..039046198419f 100644 --- a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir @@ -2,33 +2,33 @@ | User Type Annotations | 0: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10] -| 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:10:10: 10:25, inferred_ty: *const dyn std::marker::Send +| 1: user_ty: Canonical { value: Ty(*const dyn Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:10:10: 10:25, inferred_ty: *const dyn std::marker::Send | 2: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] | 3: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] | 4: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] | 5: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] -| 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send -| 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send +| 6: user_ty: Canonical { value: Ty(*const dyn Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send +| 7: user_ty: Canonical { value: Ty(*const dyn Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send | 8: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] | 9: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] | 10: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10] -| 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:21:10: 21:25, inferred_ty: *const dyn std::marker::Send +| 11: user_ty: Canonical { value: Ty(*const dyn Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:21:10: 21:25, inferred_ty: *const dyn std::marker::Send | 12: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] | 13: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] | 14: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] | 15: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] -| 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send -| 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send +| 16: user_ty: Canonical { value: Ty(*const dyn Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send +| 17: user_ty: Canonical { value: Ty(*const dyn Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send | 18: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] | 19: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] | 20: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10] -| 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:31:10: 31:23, inferred_ty: *mut dyn std::marker::Send +| 21: user_ty: Canonical { value: Ty(*mut dyn Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:31:10: 31:23, inferred_ty: *mut dyn std::marker::Send | 22: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] | 23: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] | 24: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] | 25: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] -| 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send -| 27: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send +| 26: user_ty: Canonical { value: Ty(*mut dyn Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send +| 27: user_ty: Canonical { value: Ty(*mut dyn Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send | 28: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] | 29: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] | diff --git a/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir b/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir index aa7d75242b408..af4d568c7c768 100644 --- a/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir @@ -1,8 +1,8 @@ // MIR for `main` after SimplifyCfg-initial | User Type Annotations -| 0: user_ty: Canonical { value: Ty(std::option::Option>), max_universe: U0, var_kinds: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option> -| 1: user_ty: Canonical { value: Ty(std::option::Option>), max_universe: U0, var_kinds: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option> +| 0: user_ty: Canonical { value: Ty(Option>), max_universe: U0, var_kinds: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option> +| 1: user_ty: Canonical { value: Ty(Option>), max_universe: U0, var_kinds: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option> | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/issue_101867.main.built.after.mir b/tests/mir-opt/building/issue_101867.main.built.after.mir index f68217a497b0b..70f602aa236e8 100644 --- a/tests/mir-opt/building/issue_101867.main.built.after.mir +++ b/tests/mir-opt/building/issue_101867.main.built.after.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty(std::option::Option), max_universe: U0, var_kinds: [] }, span: $DIR/issue_101867.rs:5:12: 5:22, inferred_ty: std::option::Option -| 1: user_ty: Canonical { value: Ty(std::option::Option), max_universe: U0, var_kinds: [] }, span: $DIR/issue_101867.rs:5:12: 5:22, inferred_ty: std::option::Option +| 0: user_ty: Canonical { value: Ty(Option), max_universe: U0, var_kinds: [] }, span: $DIR/issue_101867.rs:5:12: 5:22, inferred_ty: std::option::Option +| 1: user_ty: Canonical { value: Ty(Option), max_universe: U0, var_kinds: [] }, span: $DIR/issue_101867.rs:5:12: 5:22, inferred_ty: std::option::Option | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/user_type_annotations.match_assoc_const.built.after.mir b/tests/mir-opt/building/user_type_annotations.match_assoc_const.built.after.mir index 8ec5028250b0d..c8c3fe6445763 100644 --- a/tests/mir-opt/building/user_type_annotations.match_assoc_const.built.after.mir +++ b/tests/mir-opt/building/user_type_annotations.match_assoc_const.built.after.mir @@ -1,8 +1,8 @@ // MIR for `match_assoc_const` after built | User Type Annotations -| 0: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:55:9: 55:44, inferred_ty: u32 -| 1: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:55:9: 55:44, inferred_ty: u32 +| 0: user_ty: Canonical { value: TypeOf(>::FOO), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:55:9: 55:44, inferred_ty: u32 +| 1: user_ty: Canonical { value: TypeOf(>::FOO), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:55:9: 55:44, inferred_ty: u32 | fn match_assoc_const() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/user_type_annotations.match_assoc_const_range.built.after.mir b/tests/mir-opt/building/user_type_annotations.match_assoc_const_range.built.after.mir index 61e5d9b459d0e..0797cf0a1f8e7 100644 --- a/tests/mir-opt/building/user_type_annotations.match_assoc_const_range.built.after.mir +++ b/tests/mir-opt/building/user_type_annotations.match_assoc_const_range.built.after.mir @@ -1,10 +1,10 @@ // MIR for `match_assoc_const_range` after built | User Type Annotations -| 0: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:63:11: 63:46, inferred_ty: u32 -| 1: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:63:11: 63:46, inferred_ty: u32 -| 2: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:64:9: 64:44, inferred_ty: u32 -| 3: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:64:9: 64:44, inferred_ty: u32 +| 0: user_ty: Canonical { value: TypeOf(>::FOO), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:63:11: 63:46, inferred_ty: u32 +| 1: user_ty: Canonical { value: TypeOf(>::FOO), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:63:11: 63:46, inferred_ty: u32 +| 2: user_ty: Canonical { value: TypeOf(>::FOO), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:64:9: 64:44, inferred_ty: u32 +| 3: user_ty: Canonical { value: TypeOf(>::FOO), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:64:9: 64:44, inferred_ty: u32 | fn match_assoc_const_range() -> () { let mut _0: (); diff --git a/tests/mir-opt/coroutine/unwind_in_vec.build-{closure#0}.built.after.mir b/tests/mir-opt/coroutine/unwind_in_vec.build-{closure#0}.built.after.mir new file mode 100644 index 0000000000000..fc75f261ea01b --- /dev/null +++ b/tests/mir-opt/coroutine/unwind_in_vec.build-{closure#0}.built.after.mir @@ -0,0 +1,399 @@ +// MIR for `build::{closure#0}` after built + +| User Type Annotations +| 0: user_ty: Canonical { value: TypeOf(Box<^c_0>::new_uninit at for Box<^c_1, ^c_2>>), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }, Ty { ui: U0, sub_root: 1 }, Ty { ui: U0, sub_root: 2 }] }, span: $SRC_DIR/alloc/src/macros.rs:LL:COL, inferred_ty: fn() -> std::boxed::Box> {std::boxed::Box::<[std::string::String; 5]>::new_uninit} +| +fn build::{closure#0}(_1: {async fn body of build()}, _2: std::future::ResumeTy) -> Vec +yields () + { + debug _task_context => _2; + debug s => (_1.0: u64); + let mut _0: std::vec::Vec; + let _3: u64; + let mut _4: std::boxed::Box>; + let mut _5: std::boxed::Box>; + let mut _6: std::string::String; + let mut _7: &str; + let _8: &str; + let mut _9: std::string::String; + let mut _10: &str; + let _11: &str; + let mut _12: std::string::String; + let mut _13: &str; + let _14: &str; + let mut _15: std::string::String; + let mut _16: &str; + let _17: &str; + let mut _18: std::string::String; + let mut _19: &str; + let _20: &str; + scope 1 { + debug s => _3; + } + + bb0: { + StorageLive(_3); + _3 = copy (_1.0: u64); + FakeRead(ForLet(None), _3); + StorageLive(_4); + StorageLive(_5); + _5 = Box::<[String; 5]>::new_uninit() -> [return: bb1, unwind: bb50]; + } + + bb1: { + StorageLive(_6); + StorageLive(_7); + StorageLive(_8); + _8 = const "0"; + _7 = &(*_8); + _6 = ::to_string(move _7) -> [return: bb2, unwind: bb47]; + } + + bb2: { + StorageDead(_7); + StorageLive(_9); + StorageLive(_10); + StorageLive(_11); + _11 = const "1"; + _10 = &(*_11); + _9 = ::to_string(move _10) -> [return: bb3, unwind: bb43]; + } + + bb3: { + StorageDead(_10); + StorageLive(_12); + StorageLive(_13); + StorageLive(_14); + _14 = const "2"; + _13 = &(*_14); + _12 = ::to_string(move _13) -> [return: bb4, unwind: bb38]; + } + + bb4: { + StorageDead(_13); + StorageLive(_15); + StorageLive(_16); + StorageLive(_17); + _17 = const "3"; + _16 = &(*_17); + _15 = ::to_string(move _16) -> [return: bb5, unwind: bb32]; + } + + bb5: { + StorageDead(_16); + StorageLive(_18); + StorageLive(_19); + StorageLive(_20); + _20 = const "4"; + _19 = &(*_20); + _18 = ::to_string(move _19) -> [return: bb6, unwind: bb24]; + } + + bb6: { + StorageDead(_19); + ((((*_5).1: std::mem::ManuallyDrop<[std::string::String; 5]>).0: std::mem::MaybeDangling<[std::string::String; 5]>).0: [std::string::String; 5]) = [move _6, move _9, move _12, move _15, move _18]; + drop(_18) -> [return: bb7, unwind: bb25, drop: bb15]; + } + + bb7: { + StorageDead(_18); + drop(_15) -> [return: bb8, unwind: bb26, drop: bb16]; + } + + bb8: { + StorageDead(_15); + drop(_12) -> [return: bb9, unwind: bb27, drop: bb17]; + } + + bb9: { + StorageDead(_12); + drop(_9) -> [return: bb10, unwind: bb28, drop: bb18]; + } + + bb10: { + StorageDead(_9); + drop(_6) -> [return: bb11, unwind: bb29, drop: bb19]; + } + + bb11: { + StorageDead(_6); + _4 = move _5; + drop(_5) -> [return: bb12, unwind: bb22]; + } + + bb12: { + StorageDead(_5); + _0 = std::boxed::box_assume_init_into_vec_unsafe::(move _4) -> [return: bb13, unwind: bb23]; + } + + bb13: { + StorageDead(_4); + StorageDead(_20); + StorageDead(_17); + StorageDead(_14); + StorageDead(_11); + StorageDead(_8); + StorageDead(_3); + drop(_1) -> [return: bb14, unwind: bb52, drop: bb21]; + } + + bb14: { + return; + } + + bb15: { + StorageDead(_18); + drop(_15) -> [return: bb16, unwind: bb53]; + } + + bb16: { + StorageDead(_15); + drop(_12) -> [return: bb17, unwind: bb54]; + } + + bb17: { + StorageDead(_12); + drop(_9) -> [return: bb18, unwind: bb55]; + } + + bb18: { + StorageDead(_9); + drop(_6) -> [return: bb19, unwind: bb56]; + } + + bb19: { + StorageDead(_6); + drop(_5) -> [return: bb20, unwind: bb57]; + } + + bb20: { + StorageDead(_5); + StorageDead(_4); + StorageDead(_20); + StorageDead(_17); + StorageDead(_14); + StorageDead(_11); + StorageDead(_8); + StorageDead(_3); + drop(_1) -> [return: bb21, unwind: bb52]; + } + + bb21: { + coroutine_drop; + } + + bb22 (cleanup): { + StorageDead(_5); + goto -> bb23; + } + + bb23 (cleanup): { + drop(_4) -> [return: bb31, unwind terminate(cleanup)]; + } + + bb24 (cleanup): { + StorageDead(_19); + goto -> bb25; + } + + bb25 (cleanup): { + StorageDead(_18); + drop(_15) -> [return: bb26, unwind terminate(cleanup)]; + } + + bb26 (cleanup): { + StorageDead(_15); + drop(_12) -> [return: bb27, unwind terminate(cleanup)]; + } + + bb27 (cleanup): { + StorageDead(_12); + drop(_9) -> [return: bb28, unwind terminate(cleanup)]; + } + + bb28 (cleanup): { + StorageDead(_9); + drop(_6) -> [return: bb29, unwind terminate(cleanup)]; + } + + bb29 (cleanup): { + StorageDead(_6); + drop(_5) -> [return: bb30, unwind terminate(cleanup)]; + } + + bb30 (cleanup): { + StorageDead(_5); + goto -> bb31; + } + + bb31 (cleanup): { + StorageDead(_4); + StorageDead(_20); + goto -> bb37; + } + + bb32 (cleanup): { + StorageDead(_16); + StorageDead(_15); + drop(_12) -> [return: bb33, unwind terminate(cleanup)]; + } + + bb33 (cleanup): { + StorageDead(_12); + drop(_9) -> [return: bb34, unwind terminate(cleanup)]; + } + + bb34 (cleanup): { + StorageDead(_9); + drop(_6) -> [return: bb35, unwind terminate(cleanup)]; + } + + bb35 (cleanup): { + StorageDead(_6); + drop(_5) -> [return: bb36, unwind terminate(cleanup)]; + } + + bb36 (cleanup): { + StorageDead(_5); + StorageDead(_4); + goto -> bb37; + } + + bb37 (cleanup): { + StorageDead(_17); + goto -> bb42; + } + + bb38 (cleanup): { + StorageDead(_13); + StorageDead(_12); + drop(_9) -> [return: bb39, unwind terminate(cleanup)]; + } + + bb39 (cleanup): { + StorageDead(_9); + drop(_6) -> [return: bb40, unwind terminate(cleanup)]; + } + + bb40 (cleanup): { + StorageDead(_6); + drop(_5) -> [return: bb41, unwind terminate(cleanup)]; + } + + bb41 (cleanup): { + StorageDead(_5); + StorageDead(_4); + goto -> bb42; + } + + bb42 (cleanup): { + StorageDead(_14); + goto -> bb46; + } + + bb43 (cleanup): { + StorageDead(_10); + StorageDead(_9); + drop(_6) -> [return: bb44, unwind terminate(cleanup)]; + } + + bb44 (cleanup): { + StorageDead(_6); + drop(_5) -> [return: bb45, unwind terminate(cleanup)]; + } + + bb45 (cleanup): { + StorageDead(_5); + StorageDead(_4); + goto -> bb46; + } + + bb46 (cleanup): { + StorageDead(_11); + goto -> bb49; + } + + bb47 (cleanup): { + StorageDead(_7); + StorageDead(_6); + drop(_5) -> [return: bb48, unwind terminate(cleanup)]; + } + + bb48 (cleanup): { + StorageDead(_5); + StorageDead(_4); + goto -> bb49; + } + + bb49 (cleanup): { + StorageDead(_8); + goto -> bb51; + } + + bb50 (cleanup): { + StorageDead(_5); + StorageDead(_4); + goto -> bb51; + } + + bb51 (cleanup): { + StorageDead(_3); + drop(_1) -> [return: bb52, unwind terminate(cleanup)]; + } + + bb52 (cleanup): { + resume; + } + + bb53 (cleanup): { + StorageDead(_15); + drop(_12) -> [return: bb54, unwind terminate(cleanup)]; + } + + bb54 (cleanup): { + StorageDead(_12); + drop(_9) -> [return: bb55, unwind terminate(cleanup)]; + } + + bb55 (cleanup): { + StorageDead(_9); + drop(_6) -> [return: bb56, unwind terminate(cleanup)]; + } + + bb56 (cleanup): { + StorageDead(_6); + drop(_5) -> [return: bb57, unwind terminate(cleanup)]; + } + + bb57 (cleanup): { + StorageDead(_5); + StorageDead(_4); + StorageDead(_20); + StorageDead(_17); + StorageDead(_14); + StorageDead(_11); + StorageDead(_8); + StorageDead(_3); + drop(_1) -> [return: bb52, unwind terminate(cleanup)]; + } +} + +ALLOC0 (size: 1, align: 1) { + 34 │ 4 +} + +ALLOC1 (size: 1, align: 1) { + 33 │ 3 +} + +ALLOC2 (size: 1, align: 1) { + 32 │ 2 +} + +ALLOC3 (size: 1, align: 1) { + 31 │ 1 +} + +ALLOC4 (size: 1, align: 1) { + 30 │ 0 +} diff --git a/tests/mir-opt/coroutine/unwind_in_vec.rs b/tests/mir-opt/coroutine/unwind_in_vec.rs new file mode 100644 index 0000000000000..46389c91d9abc --- /dev/null +++ b/tests/mir-opt/coroutine/unwind_in_vec.rs @@ -0,0 +1,10 @@ +// Regression test for #154720 +//@ edition: 2018 +//@ needs-unwind +//@ skip-filecheck + +// EMIT_MIR unwind_in_vec.build-{closure#0}.built.after.mir +#[inline(never)] +pub async fn build(s: u64) -> Vec { + vec!["0".to_string(), "1".to_string(), "2".to_string(), "3".to_string(), "4".to_string()] +} diff --git a/tests/mir-opt/issue_99325.main.built.after.32bit.mir b/tests/mir-opt/issue_99325.main.built.after.32bit.mir index 4a028248d6633..ea20caa3ca2c7 100644 --- a/tests/mir-opt/issue_99325.main.built.after.32bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.32bit.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [AliasConst(No, Alias { kind: Anon { def_id: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}) }, args: [], .. })], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 0: user_ty: Canonical { value: TypeOf(function_with_bytes<&*b"AAAA">), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(function_with_bytes<{ &[0x41, 0x41, 0x41, 0x41] }>), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/issue_99325.main.built.after.64bit.mir b/tests/mir-opt/issue_99325.main.built.after.64bit.mir index 4a028248d6633..ea20caa3ca2c7 100644 --- a/tests/mir-opt/issue_99325.main.built.after.64bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.64bit.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [AliasConst(No, Alias { kind: Anon { def_id: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}) }, args: [], .. })], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 0: user_ty: Canonical { value: TypeOf(function_with_bytes<&*b"AAAA">), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(function_with_bytes<{ &[0x41, 0x41, 0x41, 0x41] }>), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); From 1c85606cee682d29ad32164e77876a727274c691 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Wed, 24 Jun 2026 07:55:01 -0700 Subject: [PATCH 33/78] Bump gen-lsp-types to v0.10.0 Includes various metaModel updates, fixes, and API improvements to make e.g. LspRequestMethod more ergonomic (and use less allocations). --- src/tools/rust-analyzer/Cargo.lock | 8 +- .../crates/rust-analyzer/Cargo.toml | 2 +- .../crates/rust-analyzer/src/diagnostics.rs | 2 +- .../src/diagnostics/flycheck_to_proto.rs | 4 +- .../test_data/clippy_pass_by_ref.txt | 12 ++- .../test_data/handles_macro_location.txt | 4 +- .../test_data/macro_compiler_error.txt | 12 ++- ...easonable_line_numbers_from_empty_file.txt | 4 +- .../rustc_incompatible_type_for_trait.txt | 4 +- .../test_data/rustc_mismatched_type.txt | 4 +- .../rustc_range_map_lsp_position.txt | 8 +- .../test_data/rustc_unused_variable.txt | 8 +- .../rustc_unused_variable_as_hint.txt | 8 +- .../rustc_unused_variable_as_info.txt | 8 +- .../rustc_wrong_number_of_parameters.txt | 8 +- .../test_data/snap_multi_line_fix.txt | 12 ++- .../crates/rust-analyzer/src/global_state.rs | 19 +++- .../crates/rust-analyzer/src/lsp/ext.rs | 95 ++++++++++--------- .../rust-analyzer/src/lsp/semantic_tokens.rs | 17 +--- .../crates/rust-analyzer/src/lsp/utils.rs | 1 + .../tests/slow-tests/flycheck.rs | 15 ++- .../book/src/contributing/lsp-extensions.md | 2 +- .../rust-analyzer/lib/lsp-server/Cargo.toml | 2 +- .../lib/lsp-server/examples/minimal_lsp.rs | 13 +-- 24 files changed, 168 insertions(+), 104 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index e6acaa08fac6f..75ccd77262a07 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -720,9 +720,9 @@ dependencies = [ [[package]] name = "gen-lsp-types" -version = "0.4.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b8ec601e62362b666a3def1fed667ee87b10a4507402618376d142a05373c6" +checksum = "4cd635c5206acd03ea024d6b5902539e5c903de3afa220fdb5c94b583af77f4f" dependencies = [ "serde", "serde_json", @@ -2636,9 +2636,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "indexmap", "itoa", diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index dd8649e09529c..ae0c9571210ac 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -29,7 +29,7 @@ ide-completion.workspace = true indexmap.workspace = true itertools.workspace = true scip = "0.7.1" -lsp-types = { version = "0.4.0", package = "gen-lsp-types", features=["url"] } +lsp-types = { version = "0.10.0", package = "gen-lsp-types", features = ["url"] } parking_lot = "0.12.4" xflags = "0.3.2" oorandom = "11.1.5" diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 889d20a184b25..9edcdf075bacd 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -362,7 +362,7 @@ pub(crate) fn convert_diagnostic( href: lsp_types::Uri::parse(&d.code.url()).unwrap(), }), source: Some("rust-analyzer".to_owned()), - message: d.message, + message: lsp_types::Message::String(d.message), related_information: None, tags: d.unused.then(|| vec![lsp_types::DiagnosticTag::Unnecessary]), data: None, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/flycheck_to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/flycheck_to_proto.rs index 8568f4798c032..59ab748c2f62a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/flycheck_to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/flycheck_to_proto.rs @@ -375,7 +375,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( if needs_primary_span_label && let Some(primary_span_label) = &primary_span.label { format_to!(message, "\n{}", primary_span_label); } - message + lsp_types::Message::String(message) }; let mut related_info_macro_calls = vec![]; @@ -475,7 +475,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( code: code.map(ToOwned::to_owned).map(lsp_types::Code::String), code_description: code_description.clone(), source: Some(source.to_owned()), - message: sub.related.message.clone(), + message: sub.related.message.clone().into(), related_information: Some(vec![back_ref.clone()]), tags: None, // don't apply modifiers again data: None, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt index 71f99874b173c..4f6d2009d2c6d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt @@ -54,7 +54,9 @@ source: Some( "clippy", ), - message: "this argument is passed by reference, but would be more efficient if passed by value\n#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\nfor further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref", + message: String( + "this argument is passed by reference, but would be more efficient if passed by value\n#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\nfor further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref", + ), tags: None, related_information: Some( [ @@ -171,7 +173,9 @@ source: Some( "clippy", ), - message: "lint level defined here", + message: String( + "lint level defined here", + ), tags: None, related_information: Some( [ @@ -262,7 +266,9 @@ source: Some( "clippy", ), - message: "consider passing by value instead: `self`", + message: String( + "consider passing by value instead: `self`", + ), tags: None, related_information: Some( [ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt index bd1abfe921185..f1bcc06991c69 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt @@ -54,7 +54,9 @@ source: Some( "rustc", ), - message: "can't compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", + message: String( + "can't compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", + ), tags: None, related_information: None, data: None, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt index cc870c48af74c..2405831682a7a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt @@ -30,7 +30,9 @@ source: Some( "rustc", ), - message: "Please register your known path in the path module", + message: String( + "Please register your known path in the path module", + ), tags: None, related_information: Some( [ @@ -97,7 +99,9 @@ source: Some( "rustc", ), - message: "Please register your known path in the path module", + message: String( + "Please register your known path in the path module", + ), tags: None, related_information: Some( [ @@ -164,7 +168,9 @@ source: Some( "rustc", ), - message: "Please register your known path in the path module", + message: String( + "Please register your known path in the path module", + ), tags: None, related_information: Some( [ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/reasonable_line_numbers_from_empty_file.txt b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/reasonable_line_numbers_from_empty_file.txt index 176d7198ac206..8500e61d300b6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/reasonable_line_numbers_from_empty_file.txt +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/reasonable_line_numbers_from_empty_file.txt @@ -54,7 +54,9 @@ source: Some( "rustc", ), - message: "`main` function not found in crate `current`\nconsider adding a `main` function to `src/bin/current.rs`", + message: String( + "`main` function not found in crate `current`\nconsider adding a `main` function to `src/bin/current.rs`", + ), tags: None, related_information: None, data: None, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt index e78ac4b27dab4..909bbe2f6a2f7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt @@ -54,7 +54,9 @@ source: Some( "rustc", ), - message: "method `next` has an incompatible type for trait\nexpected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref>`", + message: String( + "method `next` has an incompatible type for trait\nexpected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref>`", + ), tags: None, related_information: None, data: None, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt index add343d24566f..39941652fda3f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt @@ -54,7 +54,9 @@ source: Some( "rustc", ), - message: "mismatched types\nexpected usize, found u32", + message: String( + "mismatched types\nexpected usize, found u32", + ), tags: None, related_information: None, data: None, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_range_map_lsp_position.txt b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_range_map_lsp_position.txt index a510943965b48..3c10dacafb413 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_range_map_lsp_position.txt +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_range_map_lsp_position.txt @@ -54,7 +54,9 @@ source: Some( "rustc", ), - message: "mismatched types\nexpected `u32`, found `&str`", + message: String( + "mismatched types\nexpected `u32`, found `&str`", + ), tags: None, related_information: Some( [ @@ -145,7 +147,9 @@ source: Some( "rustc", ), - message: "expected due to this", + message: String( + "expected due to this", + ), tags: None, related_information: Some( [ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt index f5bf2dce3057a..7410e14972870 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt @@ -34,7 +34,9 @@ source: Some( "rustc", ), - message: "unused variable: `foo`\n#[warn(unused_variables)] on by default", + message: String( + "unused variable: `foo`\n#[warn(unused_variables)] on by default", + ), tags: Some( [ Unnecessary, @@ -109,7 +111,9 @@ source: Some( "rustc", ), - message: "consider prefixing with an underscore: `_foo`", + message: String( + "consider prefixing with an underscore: `_foo`", + ), tags: None, related_information: Some( [ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt index 5f4aa7157644d..0cff5ebba8021 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt @@ -34,7 +34,9 @@ source: Some( "rustc", ), - message: "unused variable: `foo`\n#[warn(unused_variables)] on by default", + message: String( + "unused variable: `foo`\n#[warn(unused_variables)] on by default", + ), tags: Some( [ Unnecessary, @@ -109,7 +111,9 @@ source: Some( "rustc", ), - message: "consider prefixing with an underscore: `_foo`", + message: String( + "consider prefixing with an underscore: `_foo`", + ), tags: None, related_information: Some( [ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt index 26adf5311857b..23797634ae057 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt @@ -34,7 +34,9 @@ source: Some( "rustc", ), - message: "unused variable: `foo`\n#[warn(unused_variables)] on by default", + message: String( + "unused variable: `foo`\n#[warn(unused_variables)] on by default", + ), tags: Some( [ Unnecessary, @@ -109,7 +111,9 @@ source: Some( "rustc", ), - message: "consider prefixing with an underscore: `_foo`", + message: String( + "consider prefixing with an underscore: `_foo`", + ), tags: None, related_information: Some( [ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt index cd3c24f0c3cf6..900f99701b2b6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt @@ -54,7 +54,9 @@ source: Some( "rustc", ), - message: "this function takes 2 parameters but 3 parameters were supplied\nexpected 2 parameters", + message: String( + "this function takes 2 parameters but 3 parameters were supplied\nexpected 2 parameters", + ), tags: None, related_information: Some( [ @@ -145,7 +147,9 @@ source: Some( "rustc", ), - message: "defined here", + message: String( + "defined here", + ), tags: None, related_information: Some( [ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt index a977d14cf7840..bb8612f43ecf3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt @@ -54,7 +54,9 @@ source: Some( "clippy", ), - message: "returning the result of a let binding from a block\n`#[warn(clippy::let_and_return)]` on by default\nfor further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return", + message: String( + "returning the result of a let binding from a block\n`#[warn(clippy::let_and_return)]` on by default\nfor further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return", + ), tags: None, related_information: Some( [ @@ -171,7 +173,9 @@ source: Some( "clippy", ), - message: "unnecessary let binding", + message: String( + "unnecessary let binding", + ), tags: None, related_information: Some( [ @@ -262,7 +266,9 @@ source: Some( "clippy", ), - message: "return the expression directly: `(0..10).collect()`", + message: String( + "return the expression directly: `(0..10).collect()`", + ), tags: None, related_information: Some( [ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 2d01aa4d515eb..5388f68f02194 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -661,17 +661,28 @@ impl GlobalState { // See https://github.com/rust-lang/rust-analyzer/issues/11404 // See https://github.com/rust-lang/rust-analyzer/issues/13130 - let patch_empty = |message: &mut String| { - if message.is_empty() { - " ".clone_into(message); + let patch_empty = |message: &mut lsp_types::Message| match message { + lsp_types::Message::String(m) if m.is_empty() => { + " ".clone_into(m); } + lsp_types::Message::MarkupContent(lsp_types::MarkupContent { + value, + kind: _, + }) if value.is_empty() => { + " ".clone_into(value); + } + _ => {} }; for d in &mut diagnostics { patch_empty(&mut d.message); if let Some(dri) = &mut d.related_information { for dri in dri { - patch_empty(&mut dri.message); + // The LSP does not (yet?) specify that related diagnostic messages can + // be in Markdown format (in addition to plain text). + if dri.message.is_empty() { + " ".clone_into(&mut dri.message); + } } } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index 3a1945c881709..8e0bb285c2318 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -35,7 +35,7 @@ impl Request for InternalTestingFetchConfigRequest { type Params = InternalTestingFetchConfigParams; // Option is solely to circumvent Default bound. type Result = Option; - const METHOD: LspRequestMethod = + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer-internal/internalTestingFetchConfig"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -51,7 +51,7 @@ pub enum AnalyzerStatusRequest {} impl Request for AnalyzerStatusRequest { type Params = AnalyzerStatusParams; type Result = String; - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/analyzerStatus"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/analyzerStatus"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -73,7 +73,7 @@ pub enum FetchDependencyListRequest {} impl Request for FetchDependencyListRequest { type Params = FetchDependencyListParams; type Result = FetchDependencyListResult; - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/fetchDependencyList"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/fetchDependencyList"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -92,7 +92,7 @@ pub enum MemoryUsageRequest {} impl Request for MemoryUsageRequest { type Params = (); type Result = String; - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/memoryUsage"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/memoryUsage"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -101,7 +101,7 @@ pub enum ReloadWorkspaceRequest {} impl Request for ReloadWorkspaceRequest { type Params = (); type Result = (); - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/reloadWorkspace"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/reloadWorkspace"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -110,7 +110,7 @@ pub enum RebuildProcMacrosRequest {} impl Request for RebuildProcMacrosRequest { type Params = (); type Result = (); - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/rebuildProcMacros"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/rebuildProcMacros"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -119,7 +119,7 @@ pub enum ViewSyntaxTreeRequest {} impl Request for ViewSyntaxTreeRequest { type Params = ViewSyntaxTreeParams; type Result = String; - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/viewSyntaxTree"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/viewSyntaxTree"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -134,7 +134,7 @@ pub enum ViewHirRequest {} impl Request for ViewHirRequest { type Params = lsp_types::TextDocumentPositionParams; type Result = String; - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/viewHir"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/viewHir"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -143,7 +143,7 @@ pub enum ViewMirRequest {} impl Request for ViewMirRequest { type Params = lsp_types::TextDocumentPositionParams; type Result = String; - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/viewMir"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/viewMir"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -152,7 +152,7 @@ pub enum InterpretFunctionRequest {} impl Request for InterpretFunctionRequest { type Params = lsp_types::TextDocumentPositionParams; type Result = String; - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/interpretFunction"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/interpretFunction"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -161,7 +161,7 @@ pub enum ViewFileTextRequest {} impl Request for ViewFileTextRequest { type Params = lsp_types::TextDocumentIdentifier; type Result = String; - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/viewFileText"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/viewFileText"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -177,7 +177,7 @@ pub enum ViewCrateGraphRequest {} impl Request for ViewCrateGraphRequest { type Params = ViewCrateGraphParams; type Result = String; - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/viewCrateGraph"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/viewCrateGraph"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -192,7 +192,7 @@ pub enum ViewItemTreeRequest {} impl Request for ViewItemTreeRequest { type Params = ViewItemTreeParams; type Result = String; - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/viewItemTree"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/viewItemTree"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -236,7 +236,7 @@ pub enum DiscoverTestRequest {} impl Request for DiscoverTestRequest { type Params = DiscoverTestParams; type Result = DiscoverTestResults; - const METHOD: LspRequestMethod = LspRequestMethod::new("experimental/discoverTest"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("experimental/discoverTest"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -244,7 +244,7 @@ pub enum DiscoveredTestsNotification {} impl Notification for DiscoveredTestsNotification { type Params = DiscoverTestResults; - const METHOD: LspNotificationMethod = + const METHOD: LspNotificationMethod<'_> = LspNotificationMethod::new("experimental/discoveredTests"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -261,7 +261,7 @@ pub enum RunTestRequest {} impl Request for RunTestRequest { type Params = RunTestParams; type Result = (); - const METHOD: LspRequestMethod = LspRequestMethod::new("experimental/runTest"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("experimental/runTest"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -269,7 +269,7 @@ pub enum EndRunTestNotification {} impl Notification for EndRunTestNotification { type Params = (); - const METHOD: LspNotificationMethod = LspNotificationMethod::new("experimental/endRunTest"); + const METHOD: LspNotificationMethod<'_> = LspNotificationMethod::new("experimental/endRunTest"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -277,7 +277,7 @@ pub enum AppendOutputToRunTestNotification {} impl Notification for AppendOutputToRunTestNotification { type Params = String; - const METHOD: LspNotificationMethod = + const METHOD: LspNotificationMethod<'_> = LspNotificationMethod::new("experimental/appendOutputToRunTest"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -286,7 +286,8 @@ pub enum AbortRunTestNotification {} impl Notification for AbortRunTestNotification { type Params = (); - const METHOD: LspNotificationMethod = LspNotificationMethod::new("experimental/abortRunTest"); + const METHOD: LspNotificationMethod<'_> = + LspNotificationMethod::new("experimental/abortRunTest"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -311,7 +312,7 @@ pub enum ChangeTestStateNotification {} impl Notification for ChangeTestStateNotification { type Params = ChangeTestStateParams; - const METHOD: LspNotificationMethod = + const METHOD: LspNotificationMethod<'_> = LspNotificationMethod::new("experimental/changeTestState"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -321,7 +322,7 @@ pub enum ExpandMacroRequest {} impl Request for ExpandMacroRequest { type Params = ExpandMacroParams; type Result = Option; - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/expandMacro"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/expandMacro"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -344,7 +345,7 @@ pub enum ViewRecursiveMemoryLayoutRequest {} impl Request for ViewRecursiveMemoryLayoutRequest { type Params = lsp_types::TextDocumentPositionParams; type Result = Option; - const METHOD: LspRequestMethod = + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/viewRecursiveMemoryLayout"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -372,7 +373,7 @@ pub enum CancelFlycheckNotification {} impl Notification for CancelFlycheckNotification { type Params = (); - const METHOD: LspNotificationMethod = + const METHOD: LspNotificationMethod<'_> = LspNotificationMethod::new("rust-analyzer/cancelFlycheck"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -381,7 +382,8 @@ pub enum RunFlycheckNotification {} impl Notification for RunFlycheckNotification { type Params = RunFlycheckParams; - const METHOD: LspNotificationMethod = LspNotificationMethod::new("rust-analyzer/runFlycheck"); + const METHOD: LspNotificationMethod<'_> = + LspNotificationMethod::new("rust-analyzer/runFlycheck"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -389,7 +391,8 @@ pub enum ClearFlycheckNotification {} impl Notification for ClearFlycheckNotification { type Params = (); - const METHOD: LspNotificationMethod = LspNotificationMethod::new("rust-analyzer/clearFlycheck"); + const METHOD: LspNotificationMethod<'_> = + LspNotificationMethod::new("rust-analyzer/clearFlycheck"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -397,7 +400,7 @@ pub enum OpenServerLogsNotification {} impl Notification for OpenServerLogsNotification { type Params = (); - const METHOD: LspNotificationMethod = + const METHOD: LspNotificationMethod<'_> = LspNotificationMethod::new("rust-analyzer/openServerLogs"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -413,7 +416,7 @@ pub enum MatchingBraceRequest {} impl Request for MatchingBraceRequest { type Params = MatchingBraceParams; type Result = Vec; - const METHOD: LspRequestMethod = LspRequestMethod::new("experimental/matchingBrace"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("experimental/matchingBrace"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -429,7 +432,7 @@ pub enum ParentModuleRequest {} impl Request for ParentModuleRequest { type Params = lsp_types::TextDocumentPositionParams; type Result = Option; - const METHOD: LspRequestMethod = LspRequestMethod::new("experimental/parentModule"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("experimental/parentModule"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -438,7 +441,7 @@ pub enum ChildModulesRequest {} impl Request for ChildModulesRequest { type Params = lsp_types::TextDocumentPositionParams; type Result = Option; - const METHOD: LspRequestMethod = LspRequestMethod::new("experimental/childModules"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("experimental/childModules"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -447,7 +450,7 @@ pub enum JoinLinesRequest {} impl Request for JoinLinesRequest { type Params = JoinLinesParams; type Result = Vec; - const METHOD: LspRequestMethod = LspRequestMethod::new("experimental/joinLines"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("experimental/joinLines"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -463,7 +466,7 @@ pub enum OnEnterRequest {} impl Request for OnEnterRequest { type Params = lsp_types::TextDocumentPositionParams; type Result = Option>; - const METHOD: LspRequestMethod = LspRequestMethod::new("experimental/onEnter"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("experimental/onEnter"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -472,7 +475,7 @@ pub enum RunnablesRequest {} impl Request for RunnablesRequest { type Params = RunnablesParams; type Result = Vec; - const METHOD: LspRequestMethod = LspRequestMethod::new("experimental/runnables"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("experimental/runnables"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -531,7 +534,7 @@ pub enum RelatedTestsRequest {} impl Request for RelatedTestsRequest { type Params = lsp_types::TextDocumentPositionParams; type Result = Vec; - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/relatedTests"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/relatedTests"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -545,7 +548,7 @@ pub enum SsrRequest {} impl Request for SsrRequest { type Params = SsrParams; type Result = lsp_types::WorkspaceEdit; - const METHOD: LspRequestMethod = LspRequestMethod::new("experimental/ssr"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("experimental/ssr"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -568,7 +571,8 @@ pub enum ServerStatusNotification {} impl Notification for ServerStatusNotification { type Params = ServerStatusParams; - const METHOD: LspNotificationMethod = LspNotificationMethod::new("experimental/serverStatus"); + const METHOD: LspNotificationMethod<'_> = + LspNotificationMethod::new("experimental/serverStatus"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -603,7 +607,7 @@ pub enum CodeActionRequest {} impl Request for CodeActionRequest { type Params = lsp_types::CodeActionParams; type Result = Option>; - const METHOD: LspRequestMethod = LspRequestMethod::new("textDocument/codeAction"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::TextDocumentCodeAction; const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -612,7 +616,7 @@ pub enum CodeActionResolveRequest {} impl Request for CodeActionResolveRequest { type Params = CodeAction; type Result = CodeAction; - const METHOD: LspRequestMethod = LspRequestMethod::new("codeAction/resolve"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::CodeActionResolve; const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -690,7 +694,7 @@ pub enum HoverRequest {} impl Request for HoverRequest { type Params = HoverParams; type Result = Option; - const METHOD: LspRequestMethod = lsp_types::HoverRequest::METHOD; + const METHOD: LspRequestMethod<'_> = lsp_types::HoverRequest::METHOD; const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -740,7 +744,7 @@ pub enum ExternalDocsRequest {} impl Request for ExternalDocsRequest { type Params = lsp_types::TextDocumentPositionParams; type Result = ExternalDocsResponse; - const METHOD: LspRequestMethod = LspRequestMethod::new("experimental/externalDocs"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("experimental/externalDocs"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -769,7 +773,7 @@ pub enum OpenCargoTomlRequest {} impl Request for OpenCargoTomlRequest { type Params = OpenCargoTomlParams; type Result = Option; - const METHOD: LspRequestMethod = LspRequestMethod::new("experimental/openCargoToml"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("experimental/openCargoToml"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -799,7 +803,7 @@ pub enum MoveItemRequest {} impl Request for MoveItemRequest { type Params = MoveItemParams; type Result = Vec; - const METHOD: LspRequestMethod = LspRequestMethod::new("experimental/moveItem"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("experimental/moveItem"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -823,7 +827,7 @@ pub enum WorkspaceSymbolRequest {} impl Request for WorkspaceSymbolRequest { type Params = WorkspaceSymbolParams; type Result = Option; - const METHOD: LspRequestMethod = LspRequestMethod::new("workspace/symbol"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::WorkspaceSymbol; const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -868,7 +872,7 @@ pub enum DocumentOnTypeFormattingRequest {} impl Request for DocumentOnTypeFormattingRequest { type Params = DocumentOnTypeFormattingParams; type Result = Option>; - const METHOD: LspRequestMethod = LspRequestMethod::new("textDocument/onTypeFormatting"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::TextDocumentOnTypeFormatting; const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -938,7 +942,7 @@ pub enum PredicateEvaluationStatus { impl Request for EvaluatePredicateRequest { type Params = EvaluatePredicateParams; type Result = EvaluatePredicateResult; - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/evaluatePredicate"); + const METHOD: LspRequestMethod<'_> = LspRequestMethod::new("rust-analyzer/evaluatePredicate"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } @@ -954,7 +958,8 @@ pub struct GetFailedObligationsParams { impl Request for GetFailedObligationsRequest { type Params = GetFailedObligationsParams; type Result = String; - const METHOD: LspRequestMethod = LspRequestMethod::new("rust-analyzer/getFailedObligations"); + const METHOD: LspRequestMethod<'_> = + LspRequestMethod::new("rust-analyzer/getFailedObligations"); const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer; } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs index 78198064238ca..fcfe3fb27c29d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs @@ -344,10 +344,11 @@ pub(crate) fn diff_tokens( // The lsp data field is actually a byte-diff but we // travel in tokens so `start` and `delete_count` are in multiples of the // serialized size of `SemanticToken`. + let data = new.iter().copied().flat_map(<[u32; 5]>::from).collect(); vec![SemanticTokensEdit { start: 5 * offset as u32, delete_count: 5 * old.len() as u32, - data: Some(new.into()), + data: Some(data), }] } } @@ -378,11 +379,7 @@ mod tests { let edits = diff_tokens(&before, &after); assert_eq!( edits[0], - SemanticTokensEdit { - start: 10, - delete_count: 0, - data: Some(vec![from((11, 12, 13, 14, 15))]) - } + SemanticTokensEdit { start: 10, delete_count: 0, data: Some(vec![11, 12, 13, 14, 15]) } ); } @@ -394,11 +391,7 @@ mod tests { let edits = diff_tokens(&before, &after); assert_eq!( edits[0], - SemanticTokensEdit { - start: 0, - delete_count: 0, - data: Some(vec![from((11, 12, 13, 14, 15))]) - } + SemanticTokensEdit { start: 0, delete_count: 0, data: Some(vec![11, 12, 13, 14, 15]) } ); } @@ -418,7 +411,7 @@ mod tests { SemanticTokensEdit { start: 5, delete_count: 0, - data: Some(vec![from((10, 20, 30, 40, 50)), from((60, 70, 80, 90, 100))]) + data: Some(vec![10, 20, 30, 40, 50, 60, 70, 80, 90, 100]) } ); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs index ebec0f990a178..00e94fb50c856 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs @@ -48,6 +48,7 @@ impl GlobalState { message, actions: Some(vec![lsp_types::MessageActionItem { title: "Open server logs".to_owned(), + properties: Default::default(), }]), }, |this, resp| { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs index c6f1f81139d28..7700643f03e75 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs @@ -2,6 +2,13 @@ use test_utils::skip_slow_tests; use crate::support::Project; +fn message_contains(message: &lsp_types::Message, p: &str) -> bool { + match message { + lsp_types::Message::String(s) => s.contains(p), + lsp_types::Message::MarkupContent(mc) => mc.value.contains(p), + } +} + #[test] fn test_flycheck_diagnostics_for_unused_variable() { if skip_slow_tests() { @@ -29,7 +36,7 @@ fn main() { let diagnostics = server.wait_for_diagnostics(); assert!( - diagnostics.diagnostics.iter().any(|d| d.message.contains("unused variable")), + diagnostics.diagnostics.iter().any(|d| message_contains(&d.message, "unused variable")), "expected unused variable diagnostic, got: {:?}", diagnostics.diagnostics, ); @@ -63,7 +70,7 @@ fn main() { // Wait for the unused variable diagnostic to appear. let diagnostics = server.wait_for_diagnostics(); assert!( - diagnostics.diagnostics.iter().any(|d| d.message.contains("unused variable")), + diagnostics.diagnostics.iter().any(|d| message_contains(&d.message, "unused variable")), "expected unused variable diagnostic, got: {:?}", diagnostics.diagnostics, ); @@ -105,7 +112,7 @@ fn main() {} let diagnostics = server.wait_for_diagnostics(); assert!( - diagnostics.diagnostics.iter().any(|d| d.message.contains("unused variable")), + diagnostics.diagnostics.iter().any(|d| message_contains(&d.message, "unused variable")), "expected unused variable diagnostic, got: {:?}", diagnostics.diagnostics, ); @@ -143,7 +150,7 @@ fn main() {} let diags = server.wait_for_diagnostics(); assert!( - diags.diagnostics.iter().any(|d| d.message.contains("unused variable")), + diags.diagnostics.iter().any(|d| message_contains(&d.message, "unused variable")), "expected unused variable diagnostic, got: {:?}", diags.diagnostics, ); diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md index 6902bce83e6cd..da4a5aaa686c5 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md @@ -1,5 +1,5 @@ $DIR/method_call.rs:24:19 | LL | let _ = f.clone(); - | ^^^^^ method cannot be called on `ForeignType` due to unsatisfied trait bounds - | - ::: $DIR/auxiliary/foreign_blanket_impl.rs:1:1 - | -LL | pub struct ForeignType(pub T); - | ------------------------- doesn't satisfy `ForeignType: Clone` - | - = note: the following trait bounds were not satisfied: - `u8: DoNotMentionThis` - which is required by `ForeignType: Clone` + | ^^^^^ method not found in `ForeignType` error[E0277]: the trait bound `LocalType: Clone` is not satisfied --> $DIR/method_call.rs:31:30 @@ -31,22 +22,15 @@ LL + #[derive(Clone)] LL | pub struct LocalType(pub T); | -error[E0599]: the method `clone` exists for struct `LocalType`, but its trait bounds were not satisfied +error[E0599]: no method named `clone` found for struct `LocalType` in the current scope --> $DIR/method_call.rs:34:19 | LL | pub struct LocalType(pub T); - | ----------------------- method `clone` not found for this struct because it doesn't satisfy `LocalType: Clone` + | ----------------------- method `clone` not found for this struct ... LL | let _ = l.clone(); - | ^^^^^ method cannot be called on `LocalType` due to unsatisfied trait bounds - | -note: trait bound `u8: DoNotMentionThis` was not satisfied - --> $DIR/method_call.rs:12:9 + | ^^^^^ method not found in `LocalType` | -LL | impl Clone for LocalType { - | ^^^^^^^^^^^^^^^^ ----- ------------ - | | - | unsatisfied trait bound introduced here = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: candidate #1: `Clone` diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/method_call.rs b/tests/ui/diagnostic_namespace/do_not_recommend/method_call.rs index 2b10cacd30227..c28c86fae209a 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/method_call.rs +++ b/tests/ui/diagnostic_namespace/do_not_recommend/method_call.rs @@ -2,7 +2,7 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver - +//@forbid-output: DoNotMentionThis extern crate foreign_blanket_impl; use foreign_blanket_impl::{DoNotMentionThis, ForeignType}; @@ -22,8 +22,8 @@ fn main() { //~^ERROR the trait bound `ForeignType: Clone` is not satisfied [E0277] let _ = f.clone(); - //[next]~^ERROR no method named `clone` found for struct `ForeignType` in the current scope [E0599] - //[current]~^^ERROR the method `clone` exists for struct `ForeignType`, but its trait bounds were not satisfied [E0599] + //~^ERROR no method named `clone` found for struct `ForeignType` in the current scope [E0599] + } { @@ -32,7 +32,7 @@ fn main() { //~^ERROR the trait bound `LocalType: Clone` is not satisfied [E0277] let _ = l.clone(); - //[next]~^ERROR no method named `clone` found for struct `LocalType` in the current scope [E0599] - //[current]~^^ERROR the method `clone` exists for struct `LocalType`, but its trait bounds were not satisfied [E0599] + //~^ERROR no method named `clone` found for struct `LocalType` in the current scope [E0599] + } } From 03b474ff810d2eea5be86b4a2060a00546353f62 Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Thu, 9 Jul 2026 21:56:15 -0700 Subject: [PATCH 52/78] fix!: make lsp-server `Response` type closer aligned to JSON-RPC From [the JSON-RPC 2.0 protocol](https://www.jsonrpc.org/specification#response_object): > result > This member is REQUIRED on success. > This member MUST NOT exist if there was an error invoking the method. > The value of this member is determined by the method invoked on the Server. > error > This member is REQUIRED on error. > This member MUST NOT exist if there was no error triggered during invocation. > The value for this member MUST be an Object as defined in section 5.1. > > ... > > Either the result member or error member MUST be included, but both members MUST NOT be included. The current typing of the `Response` object allows both the `result` and `error` members to be present, or for neither to be present. This commit strengthens the typing of the response to prevent these cases from being expressed. This is a breaking change. The response kind definition is borrowed from the [tower-lsp](https://github.com/ebkalderon/tower-lsp) definition. --- src/tools/rust-analyzer/Cargo.lock | 2 +- .../rust-analyzer/lib/lsp-server/Cargo.toml | 2 +- .../lib/lsp-server/examples/minimal_lsp.rs | 16 +++++++-------- .../rust-analyzer/lib/lsp-server/src/lib.rs | 4 +++- .../rust-analyzer/lib/lsp-server/src/msg.rs | 20 +++++++++++++------ .../lib/lsp-server/src/req_queue.rs | 4 ++-- 6 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index e0f051908cb45..8d9304dfaf316 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1455,7 +1455,7 @@ dependencies = [ [[package]] name = "lsp-server" -version = "0.8.0" +version = "0.9.0" dependencies = [ "anyhow", "crossbeam-channel", diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml index 285f58e7fca5e..06a452984fc32 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml +++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsp-server" -version = "0.8.0" +version = "0.9.0" description = "Generic LSP server scaffold." license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server" diff --git a/src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs b/src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs index 3558a6c3bfdb8..eb0832745560a 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs @@ -82,7 +82,9 @@ use toolchain::command; // clippy-approved wrapper #[allow(clippy::print_stderr, clippy::disallowed_types, clippy::disallowed_methods)] use anyhow::{Context, Result, anyhow, bail}; -use lsp_server::{Connection, Message, Request as ServerRequest, RequestId, Response}; +use lsp_server::{ + Connection, Message, Request as ServerRequest, RequestId, Response, ResponseKind, +}; // ===================================================================== // main @@ -304,7 +306,8 @@ fn full_range(text: &str) -> Range { } fn send_ok(conn: &Connection, id: RequestId, result: &T) -> Result<()> { - let resp = Response { id, result: Some(serde_json::to_value(result)?), error: None }; + let resp = + Response { id, response_kind: ResponseKind::Ok { result: serde_json::to_value(result)? } }; conn.sender.send(Message::Response(resp))?; Ok(()) } @@ -317,12 +320,9 @@ fn send_err( ) -> Result<()> { let resp = Response { id, - result: None, - error: Some(lsp_server::ResponseError { - code: code as i32, - message: msg.into(), - data: None, - }), + response_kind: ResponseKind::Err { + error: lsp_server::ResponseError { code: code as i32, message: msg.into(), data: None }, + }, }; conn.sender.send(Message::Response(resp))?; Ok(()) diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs index 3f29c39c49f09..5eaedbb6429d2 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs @@ -22,7 +22,9 @@ use crossbeam_channel::{Receiver, RecvError, RecvTimeoutError, Sender}; pub use crate::{ error::{ExtractError, ProtocolError}, - msg::{ErrorCode, Message, Notification, Request, RequestId, Response, ResponseError}, + msg::{ + ErrorCode, Message, Notification, Request, RequestId, Response, ResponseError, ResponseKind, + }, req_queue::{Incoming, Outgoing, ReqQueue}, stdio::IoThreads, }; diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs index 305008e69ae1a..64e2ba7509047 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs @@ -84,10 +84,15 @@ pub struct Response { // request id. We fail deserialization in that case, so we just // make this field mandatory. pub id: RequestId, - #[serde(skip_serializing_if = "Option::is_none", default)] - pub result: Option, - #[serde(skip_serializing_if = "Option::is_none", default)] - pub error: Option, + #[serde(flatten)] + pub response_kind: ResponseKind, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(untagged)] +pub enum ResponseKind { + Ok { result: serde_json::Value }, + Err { error: ResponseError }, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -198,11 +203,14 @@ impl Message { impl Response { pub fn new_ok(id: RequestId, result: R) -> Response { - Response { id, result: Some(serde_json::to_value(result).unwrap()), error: None } + Response { + id, + response_kind: ResponseKind::Ok { result: serde_json::to_value(result).unwrap() }, + } } pub fn new_err(id: RequestId, code: i32, message: String) -> Response { let error = ResponseError { code, message, data: None }; - Response { id, result: None, error: Some(error) } + Response { id, response_kind: ResponseKind::Err { error } } } } diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs b/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs index 84748bbca8d77..0c39a1dc7a1f7 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::{ErrorCode, Request, RequestId, Response, ResponseError}; +use crate::{ErrorCode, Request, RequestId, Response, ResponseError, msg::ResponseKind}; /// Manages the set of pending requests, both incoming and outgoing. #[derive(Debug)] @@ -47,7 +47,7 @@ impl Incoming { message: "canceled by client".to_owned(), data: None, }; - Some(Response { id, result: None, error: Some(error) }) + Some(Response { id, response_kind: ResponseKind::Err { error } }) } pub fn complete(&mut self, id: &RequestId) -> Option { From 1a4befe6d9c9328168b93ccab6579570a9f276f6 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 10 Jul 2026 14:37:32 +0800 Subject: [PATCH 53/78] internal: match_ast macro supports guard Example --- ```rust match_ast! { match parent { ast::Expr(it) if !it.is_block_like() => replace(it), _ => return, } } ``` --- src/tools/rust-analyzer/crates/syntax/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index ab24ed9231186..614678536a512 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -270,12 +270,12 @@ macro_rules! match_ast { (match $node:ident { $($tt:tt)* }) => { $crate::match_ast!(match ($node) { $($tt)* }) }; (match ($node:expr) { - $( $( $path:ident )::+ ($it:pat) => $res:expr, )* + $( $( $path:ident )::+ ($it:pat) $(if $guard:expr)? => $res:expr, )* _ => $catch_all:expr $(,)? }) => {{ #[allow(clippy::question_mark, reason = "if `$catch_all` is `return None` Clippy can mark this")] { - $( if let Some($it) = $($path::)+cast($node.clone()) { $res } else )* + $( if let Some($it) = $($path::)+cast($node.clone()) $(&& $guard)? { $res } else )* { $catch_all } } }}; From 08d79a6ec93089cf3f7c5a1db239b5f7371a12dc Mon Sep 17 00:00:00 2001 From: Amirhossein Akhlaghpour Date: Fri, 10 Jul 2026 14:28:39 +0330 Subject: [PATCH 54/78] chore: add codegen test for issue 91010 Signed-off-by: Amirhossein Akhlaghpour --- tests/codegen-llvm/issues/issue-91010.rs | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/codegen-llvm/issues/issue-91010.rs diff --git a/tests/codegen-llvm/issues/issue-91010.rs b/tests/codegen-llvm/issues/issue-91010.rs new file mode 100644 index 0000000000000..0b1b2526ca1cd --- /dev/null +++ b/tests/codegen-llvm/issues/issue-91010.rs @@ -0,0 +1,28 @@ +// Regression test for preserving constant return values after a use of the +// same local through `black_box` or formatting. + +//@ compile-flags: -Copt-level=3 + +#![crate_type = "lib"] + +use std::hint::black_box; + +// CHECK-LABEL: @black_box_ref_constant +#[no_mangle] +pub fn black_box_ref_constant() -> i32 { + let x = 1; + black_box(&x); + + // CHECK: ret i32 1 + x +} + +// CHECK-LABEL: @format_ref_constant +#[no_mangle] +pub fn format_ref_constant() -> i32 { + let x = 1; + println!("{}", x); + + // CHECK: ret i32 1 + x +} From 8c61f2f292a53dae72dfa6762930ff37ba6850e4 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 8 Jul 2026 21:50:18 +0800 Subject: [PATCH 55/78] fix: add parens in transformed dyn type in ref type Example --- ```rust trait A { fn a(&self) -> &T; } trait B {} impl<'a, T: B> A for T {$0} ``` **Before this PR** ```rust impl<'a, T: B> A for T { fn a(&self) -> &dyn 'a + B { // ^^^^^^^^^^ ambiguous `+` in a type ${0:todo!()} } } ``` **After this PR** ```rust impl<'a, T: B> A for T { fn a(&self) -> &(dyn 'a + B) { ${0:todo!()} } } ``` --- .../src/handlers/add_missing_impl_members.rs | 19 +++++++++++++++++++ .../crates/ide-db/src/path_transform.rs | 18 ++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 1e8fb51a3e18a..6e07f5f99238f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -2704,4 +2704,23 @@ impl Drop for Foo { "#, ); } + + #[test] + fn issue_10326() { + check_assist( + add_missing_impl_members, + r#" +trait A { fn a(&self) -> &T; } +trait B {} +impl<'a, T: B> A for T {$0}"#, + r#" +trait A { fn a(&self) -> &T; } +trait B {} +impl<'a, T: B> A for T { + fn a(&self) -> &(dyn 'a + B) { + ${0:todo!()} + } +}"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 7cf8dff48bf59..1f4d5c4020fe5 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -415,16 +415,21 @@ impl Ctx<'_> { editor.replace(path.syntax(), qualified.clone().syntax()); } else if let Some(path_ty) = ast::PathType::cast(parent) { let old = path_ty.syntax(); + let needs_paren = type_needs_parens(old.parent(), subst); + let subst = if needs_paren { + make.ty(&format!("({subst})")) + } else { + subst.clone() + }; if old.parent().is_some() { - editor.replace(old, subst.clone().syntax()); + editor.replace(old, subst.syntax()); } else { let start = path_ty.syntax().first_child().map(NodeOrToken::Node)?; let end = path_ty.syntax().last_child().map(NodeOrToken::Node)?; editor.replace_all( start..=end, subst - .clone() .syntax() .children() .map(NodeOrToken::Node) @@ -598,6 +603,15 @@ impl Ctx<'_> { } } +fn type_needs_parens(parent: Option, new: &ast::Type) -> bool { + if !matches!(new, ast::Type::DynTraitType(_)) { + return false; + } + let Some(parent) = parent else { return false }; + let kind = parent.kind(); + ast::CastExpr::can_cast(kind) || ast::RefType::can_cast(kind) || ast::PtrType::can_cast(kind) +} + // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the // trait ref, and then go from the types in the substs back to the syntax). fn get_syntactic_substs(impl_def: ast::Impl) -> Option { From 308e86c8c8edf0309e7ca8f5200c740e29528bdc Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 11 Jul 2026 17:50:12 +0800 Subject: [PATCH 56/78] Move util to prec.rs and add make ty_paren() --- .../crates/ide-db/src/path_transform.rs | 18 +++--------------- .../crates/syntax/src/ast/make.rs | 3 +++ .../crates/syntax/src/ast/prec.rs | 12 ++++++++++++ .../src/ast/syntax_factory/constructors.rs | 12 ++++++++++++ 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 1f4d5c4020fe5..101046cf54436 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -415,12 +415,9 @@ impl Ctx<'_> { editor.replace(path.syntax(), qualified.clone().syntax()); } else if let Some(path_ty) = ast::PathType::cast(parent) { let old = path_ty.syntax(); - let needs_paren = type_needs_parens(old.parent(), subst); - let subst = if needs_paren { - make.ty(&format!("({subst})")) - } else { - subst.clone() - }; + let needs_paren = old.parent().is_some_and(|it| subst.needs_parens_in(&it)); + let subst = + if needs_paren { make.ty_paren(subst.clone()) } else { subst.clone() }; if old.parent().is_some() { editor.replace(old, subst.syntax()); @@ -603,15 +600,6 @@ impl Ctx<'_> { } } -fn type_needs_parens(parent: Option, new: &ast::Type) -> bool { - if !matches!(new, ast::Type::DynTraitType(_)) { - return false; - } - let Some(parent) = parent else { return false }; - let kind = parent.kind(); - ast::CastExpr::can_cast(kind) || ast::RefType::can_cast(kind) || ast::PtrType::can_cast(kind) -} - // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the // trait ref, and then go from the types in the substs back to the syntax). fn get_syntactic_substs(impl_def: ast::Impl) -> Option { diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index 16aa49e3578fb..8b7f91c93724f 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -187,6 +187,9 @@ pub fn ty_tuple(types: impl IntoIterator) -> ast::Type { ty_from_text(&format!("({contents})")) } +pub fn ty_paren(ty: ast::Type) -> ast::Type { + ty_from_text(&format!("({ty})")) +} pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { ty_from_text(&if exclusive { format!("&mut {target}") } else { format!("&{target}") }) } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs index 2a50d233c3bfb..a6c04b0650519 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs @@ -575,3 +575,15 @@ impl Expr { } } } + +impl ast::Type { + pub fn needs_parens_in(&self, parent: &SyntaxNode) -> bool { + if !matches!(self, ast::Type::DynTraitType(_)) { + return false; + } + let kind = parent.kind(); + ast::CastExpr::can_cast(kind) + || ast::RefType::can_cast(kind) + || ast::PtrType::can_cast(kind) + } +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 7f9cd1fce94eb..22c8c842d890c 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -610,6 +610,18 @@ impl SyntaxFactory { ast } + pub fn ty_paren(&self, ty: ast::Type) -> ast::Type { + let ast::Type::ParenType(paren_ty) = make::ty_paren(ty.clone()) else { unreachable!() }; + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(paren_ty.syntax().clone()); + builder.map_node(ty.syntax().clone(), paren_ty.ty().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + paren_ty.into() + } + pub fn path_segment_generics( &self, name_ref: ast::NameRef, From 7d20cf852fd1de9aa62ed0d2b4f2e5d2afdbff29 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 11 Jul 2026 17:52:28 +0800 Subject: [PATCH 57/78] Add ImplTraitType prec condition --- src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs index a6c04b0650519..f473df9b1548f 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs @@ -578,7 +578,7 @@ impl Expr { impl ast::Type { pub fn needs_parens_in(&self, parent: &SyntaxNode) -> bool { - if !matches!(self, ast::Type::DynTraitType(_)) { + if !matches!(self, ast::Type::DynTraitType(_) | ast::Type::ImplTraitType(_)) { return false; } let kind = parent.kind(); From a8f0a765c08e50216bf4d1d06d4f2f075e4df8fd Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 11 Jul 2026 12:08:28 +0200 Subject: [PATCH 58/78] internal: Use expression parsing entrypoint in `make` constructor --- .../crates/syntax/src/ast/make.rs | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index 16aa49e3578fb..481f381d23bcc 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -657,12 +657,12 @@ pub fn expr_if( else_branch: Option, ) -> ast::IfExpr { let else_branch = match else_branch { - Some(ast::ElseBranch::Block(block)) => format!("else {block}"), - Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {if_expr}"), + Some(ast::ElseBranch::Block(block)) => format!(" else {block}"), + Some(ast::ElseBranch::IfExpr(if_expr)) => format!(" else {if_expr}"), None => String::new(), }; let ws = block_whitespace(&condition); - expr_from_text(&format!("if {condition}{ws}{then_branch} {else_branch}")) + expr_from_text(&format!("if {condition}{ws}{then_branch}{else_branch}")) } pub fn expr_for_loop(pat: ast::Pat, expr: ast::Expr, block: ast::BlockExpr) -> ast::ForExpr { let ws = block_whitespace(&expr); @@ -728,16 +728,9 @@ pub fn expr_tuple(elements: impl IntoIterator) -> ast::TupleEx pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::BinExpr { expr_from_text(&format!("{lhs} = {rhs}")) } -fn expr_from_text + AstNode>(text: &str) -> E { - ast_from_text(&format!("const C: () = {text};")) -} fn block_whitespace(after: &impl AstNode) -> &'static str { if after.syntax().text().contains_char('\n') { "\n" } else { " " } } -pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr { - ast_from_text(&format!("const _: () = while let {pattern} = {expr} {{}};")) -} - pub fn arg_list(args: impl IntoIterator) -> ast::ArgList { let args = args.into_iter().format(", "); ast_from_text(&format!("fn main() {{ ()({args}) }}")) @@ -1349,6 +1342,30 @@ pub fn token_tree( ast_from_text(&format!("tt!{l_delimiter}{tt}{r_delimiter}")) } +pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr { + expr_from_text(&format!("while let {pattern} = {expr} {{}}")) +} + +#[track_caller] +fn expr_from_text + AstNode>(text: &str) -> E { + expr_from_text_with_edition(text, Edition::CURRENT) +} + +#[track_caller] +fn expr_from_text_with_edition + AstNode>(text: &str, edition: Edition) -> E { + let parse = ast::Expr::parse(text, edition); + let node = match parse.tree().syntax().descendants().find_map(E::cast) { + Some(it) => it, + None => { + let node = std::any::type_name::(); + panic!("Failed to make ast node `{node}` from text {text}") + } + }; + let node = node.clone_subtree(); + assert_eq!(node.syntax().text_range().start(), 0.into()); + node +} + #[track_caller] fn ast_from_text(text: &str) -> N { ast_from_text_with_edition(text, Edition::CURRENT) @@ -1470,6 +1487,16 @@ mod tests { ); } + #[test] + fn expr_if_without_else_has_no_trailing_whitespace() { + let if_expr = expr_if(ext::expr_underscore(), block_expr(None, None), None); + assert_eq!(if_expr.syntax().to_string(), "if _ {\n}"); + + let stmt = expr_stmt(if_expr.into()); + let block = block_expr([stmt.into()], None); + assert_eq!(block.syntax().to_string(), "{\n if _ {\n}\n}"); + } + #[test] fn test_untyped_param() { check( From 77bce37c6503f032cdce56ae8a5b30eb4f018c54 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sat, 11 Jul 2026 13:56:19 +0200 Subject: [PATCH 59/78] Rename `select_trait_candidate` --- compiler/rustc_hir_typeck/src/method/probe.rs | 8 +++++--- .../diagnostic_namespace/do_not_recommend/method_call.rs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 8d51f7d101d8b..d071e0625a398 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1880,7 +1880,7 @@ impl<'tcx> Pick<'tcx> { } impl<'a, 'tcx> ProbeContext<'a, 'tcx> { - fn select_trait_candidate( + fn select_trait_candidate_for_diagnostics( &self, trait_ref: ty::TraitRef<'tcx>, ) -> traits::SelectionResult<'tcx, traits::Selection<'tcx>> { @@ -1920,7 +1920,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { xform_self_ty, self_ty, ); - match self.select_trait_candidate(trait_ref) { + match self.select_trait_candidate_for_diagnostics(trait_ref) { Ok(Some(traits::ImplSource::UserDefined(ref impl_data))) => { // If only a single impl matches, make the error message point // to that impl. @@ -2089,7 +2089,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ocx.register_obligation(obligation); } else { result = ProbeResult::NoMatch; - if let Ok(Some(candidate)) = self.select_trait_candidate(trait_ref) { + if let Ok(Some(candidate)) = + self.select_trait_candidate_for_diagnostics(trait_ref) + { for nested_obligation in candidate.nested_obligations() { if !self.infcx.predicate_may_hold(&nested_obligation) { possibly_unsatisfied_predicates.push(( diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/method_call.rs b/tests/ui/diagnostic_namespace/do_not_recommend/method_call.rs index c28c86fae209a..5f59984a6ddd9 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/method_call.rs +++ b/tests/ui/diagnostic_namespace/do_not_recommend/method_call.rs @@ -33,6 +33,6 @@ fn main() { let _ = l.clone(); //~^ERROR no method named `clone` found for struct `LocalType` in the current scope [E0599] - + } } From 13fac73cecec9871a0cf51d80c95036a83a2c5f7 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 11 Jul 2026 15:00:41 +0200 Subject: [PATCH 60/78] internal: Share same proc-macro servers between workspaces --- .../crates/proc-macro-api/src/lib.rs | 4 +- .../crates/rust-analyzer/src/config.rs | 2 +- .../crates/rust-analyzer/src/reload.rs | 45 ++++++++++++------- .../docs/book/src/configuration_generated.md | 2 +- .../rust-analyzer/editors/code/package.json | 2 +- 5 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index 149f6e44f9bea..7b2e209b8e038 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -78,9 +78,9 @@ pub enum ProcMacroKind { Bang, } -/// A handle to an external process which load dylibs with macros (.so or .dll) +/// A handle to proc-macro server process pool which load dylibs with macros (.so or .dll) /// and runs actual macro expansion functions. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ProcMacroClient { /// Currently, the proc macro process expands all procedural macros sequentially. /// diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 552fb079c2b8a..f464a2487507e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -395,7 +395,7 @@ config_data! { /// /// Controls how many independent `proc-macro-srv` processes rust-analyzer /// runs in parallel to handle macro expansion. - procMacro_processes: NumProcesses = NumProcesses::Concrete(1), + procMacro_processes: NumProcesses = NumProcesses::Concrete(2), /// Internal config, path to proc-macro server executable. procMacro_server: Option = None, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index ace44b02fe39c..c72f6782052f0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -659,11 +659,16 @@ impl GlobalState { Config::user_config_dir_path().as_deref(), ); - if (self.proc_macro_clients.len() < self.workspaces.len() || !same_workspaces) - && self.config.expand_proc_macros() - { + if !same_workspaces && self.config.expand_proc_macros() { info!("Spawning proc-macro servers"); + // Workspaces referring to the same proc-macro server executable (i.e. the same + // sysroot) with an identical spawn environment share a single client, and thereby + // a single set of server processes. + let mut clients: Vec<( + (AbsPathBuf, Option, FxHashMap>), + ProcMacroClient, + )> = Vec::new(); self.proc_macro_clients = Arc::from_iter(self.workspaces.iter().map(|ws| { let path = match self.config.proc_macro_srv() { Some(path) => path, @@ -695,20 +700,30 @@ impl GlobalState { _ => Default::default(), }; - info!("Using proc-macro server at {path}"); + + let key = (path, ws.toolchain.clone(), env); + if let Some((_, client)) = clients.iter().find(|(k, _)| *k == key) { + return Some(Ok(client.clone())); + } + + let (path, toolchain, env) = &key; + info!("Spawning proc-macro server at {path}"); let num_process = self.config.proc_macro_num_processes(); - Some( - ProcMacroClient::spawn(&path, &env, ws.toolchain.as_ref(), num_process) - .map_err(|err| { - tracing::error!( - "Failed to run proc-macro server from path {path}, error: {err:?}", - ); - anyhow::format_err!( - "Failed to run proc-macro server from path {path}, error: {err:?}", - ) - }), - ) + Some(match ProcMacroClient::spawn(path, env, toolchain.as_ref(), num_process) { + Ok(client) => { + clients.push((key.clone(), client.clone())); + Ok(client) + } + Err(err) => { + tracing::error!( + "Failed to run proc-macro server from path {path}, error: {err:?}", + ); + Err(anyhow::format_err!( + "Failed to run proc-macro server from path {path}, error: {err:?}", + )) + } + }) })) } diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 1f94639074f81..9d865a7936a95 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -1368,7 +1368,7 @@ This config takes a map of crate names with the exported proc-macro names to ign ## rust-analyzer.procMacro.processes {#procMacro.processes} -Default: `1` +Default: `2` Number of proc-macro server processes to spawn. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index c9ab57be9927a..92279a8c0d69c 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -2873,7 +2873,7 @@ "properties": { "rust-analyzer.procMacro.processes": { "markdownDescription": "Number of proc-macro server processes to spawn.\n\nControls how many independent `proc-macro-srv` processes rust-analyzer\nruns in parallel to handle macro expansion.", - "default": 1, + "default": 2, "anyOf": [ { "type": "number", From efa67aed58f803b30498491f5591c03978a53c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9?= Date: Sat, 11 Jul 2026 19:50:09 +0100 Subject: [PATCH 61/78] fix: strange behavior onEnter deleting $bar solved by reusing the function escape_snippet_bits used in postfix.rs, the issue seems to be gone completely. made one test cases, using $bar as per the issue #22596 by kpreid. tried making a $0 test case but although it was working on the IDE, the test case always failed. a possible improvement would be moving escape_snippet_bits to place where both postfix.rs and on_enter.rs can see it, but im not sure where I would do that... --- .../crates/ide/src/typing/on_enter.rs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs index 7d04594a5bf8d..4811dd69e0bf4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs @@ -108,12 +108,19 @@ fn on_enter_in_comment( Some(edit) } +// for lack of a better place to put this function +fn escape_snippet_bits(text: &mut String) { + stdx::replace(text, '\\', "\\\\"); + stdx::replace(text, '$', "\\$"); +} + fn on_enter_in_braces(l_curly: SyntaxToken, position: FilePosition) -> Option { if l_curly.text_range().end() != position.offset { return None; } - let (r_curly, content) = brace_contents_on_same_line(&l_curly)?; + let (r_curly, mut content) = brace_contents_on_same_line(&l_curly)?; + escape_snippet_bits(&mut content); let indent = IndentLevel::from_token(&l_curly); Some(TextEdit::replace( TextRange::new(position.offset, r_curly.text_range().start()), @@ -683,4 +690,22 @@ use path::{$0 "#, ); } + + #[test] + fn escapes_dollar_sign_in_brace_contents() { + do_check( + r#" +fn f() { + const {$0$bar}; +} +"#, + r#" +fn f() { + const { + $0\$bar + }; +} +"#, + ); + } } From e8f64cc4f6b881bf7784537b9af937d6dffc43bd Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 12 Jul 2026 12:39:39 +0900 Subject: [PATCH 62/78] rustc_target: Add acquire-release to implied features of v8 --- compiler/rustc_target/src/target_features.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 611bf5950d27d..8c1ebee8416e5 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -194,7 +194,7 @@ static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("v6m", Unstable(sym::arm_target_feature), &["v6"]), ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "v8m", "thumb2"]), ("v7", Unstable(sym::arm_target_feature), &["v6t2"]), - ("v8", Unstable(sym::arm_target_feature), &["v7"]), + ("v8", Unstable(sym::arm_target_feature), &["v7", "acquire-release"]), ("v8.1m.main", Unstable(sym::arm_target_feature), &["v8m.main"]), ("v8m", Unstable(sym::arm_target_feature), &["v6m"]), ("v8m.main", Unstable(sym::arm_target_feature), &["v7"]), From 0a8e33a72666a6eddbf73f572385d13ff0b5b7a0 Mon Sep 17 00:00:00 2001 From: qti3e Date: Sat, 11 Jul 2026 23:12:39 -0700 Subject: [PATCH 63/78] fix: clamp cttz const-eval result to type width --- .../hir-ty/src/consteval/tests/intrinsics.rs | 27 +++++++++++++++++++ .../crates/hir-ty/src/mir/eval/shim.rs | 4 ++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs index 85e917fe1a0d1..1772e3c172413 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -691,6 +691,33 @@ fn cttz() { "#, 3, ); + check_number( + r#" + #[rustc_intrinsic] + pub fn cttz(x: T) -> T; + + const GOAL: i8 = cttz(0i8); + "#, + 8, + ); + check_number( + r#" + #[rustc_intrinsic] + pub fn cttz(x: T) -> T; + + const GOAL: u32 = cttz(0u32); + "#, + 32, + ); + check_number( + r#" + #[rustc_intrinsic] + pub fn cttz(x: T) -> T; + + const GOAL: u64 = cttz(0u64); + "#, + 64, + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index b59d6c1cfbe77..7a6cbb8ca3739 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -1100,7 +1100,9 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let [arg] = args else { return Err(MirEvalError::InternalError("cttz arg is not provided".into())); }; - let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).trailing_zeros(); + let arg: &[u8] = arg.get(self)?; + let bit_count = arg.len() as u32 * 8; + let result = u128::from_le_bytes(pad16(arg, false)).trailing_zeros().min(bit_count); destination .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size]) } From dc0ec741a8a06b0c3bb5596fbb00a4e29fad3877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9?= Date: Sun, 12 Jul 2026 12:55:14 +0100 Subject: [PATCH 64/78] fix: moved escape_snippet_bits to ide_db under SnippetEdit --- .../crates/ide-completion/src/completions/postfix.rs | 12 ++---------- .../src/completions/postfix/format_like.rs | 9 ++++----- .../rust-analyzer/crates/ide-db/src/source_change.rs | 9 +++++++++ .../rust-analyzer/crates/ide/src/typing/on_enter.rs | 10 ++-------- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 540089cf91052..5a3a3ac39cb2f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -8,6 +8,7 @@ use ide_db::{ RootDatabase, SnippetCap, documentation::{Documentation, HasDocs}, imports::insert_use::ImportScope, + source_change::SnippetEdit, syntax_helpers::suggest_name::NameGenerator, text_edit::TextEdit, ty_filter::TryEnum, @@ -401,7 +402,7 @@ fn get_receiver_text( // The receiver texts should be interpreted as-is, as they are expected to be // normal Rust expressions. - escape_snippet_bits(&mut text); + SnippetEdit::escape_snippet_bits(&mut text); return text; fn indent_of_tail_line(text: &str) -> usize { @@ -411,15 +412,6 @@ fn get_receiver_text( } } -/// Escapes `\` and `$` so that they don't get interpreted as snippet-specific constructs. -/// -/// Note that we don't need to escape the other characters that can be escaped, -/// because they wouldn't be treated as snippet-specific constructs without '$'. -fn escape_snippet_bits(text: &mut String) { - stdx::replace(text, '\\', "\\\\"); - stdx::replace(text, '$', "\\$"); -} - fn receiver_accessor(receiver: &ast::Expr) -> ast::Expr { receiver .syntax() diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs index 3b22e8a266e73..76eddc558bdb5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs @@ -18,14 +18,13 @@ use ide_db::{ SnippetCap, + source_change::SnippetEdit, syntax_helpers::format_string_exprs::{Arg, parse_format_exprs, with_placeholders}, }; use syntax::{AstToken, ast}; use crate::{ - Completions, - completions::postfix::{build_postfix_snippet_builder, escape_snippet_bits}, - context::CompletionContext, + Completions, completions::postfix::build_postfix_snippet_builder, context::CompletionContext, }; /// Mapping ("postfix completion item" => "macro to use") @@ -57,10 +56,10 @@ pub(crate) fn add_format_like_completions( if let Ok((mut out, mut exprs)) = parse_format_exprs(receiver_text.text()) { // Escape any snippet bits in the out text and any of the exprs. - escape_snippet_bits(&mut out); + SnippetEdit::escape_snippet_bits(&mut out); for arg in &mut exprs { if let Arg::Ident(text) | Arg::Expr(text) = arg { - escape_snippet_bits(text) + SnippetEdit::escape_snippet_bits(text) } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index 07bf294405394..1a3fc0f358c1a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -210,6 +210,15 @@ impl SnippetEdit { pub fn into_edit_ranges(self) -> Vec<(u32, TextRange)> { self.0 } + + /// Escapes `\` and `$` so that they don't get interpreted as snippet-specific constructs. + /// + /// Note that we don't need to escape the other characters that can be escaped, + /// because they wouldn't be treated as snippet-specific constructs without '$'. + pub fn escape_snippet_bits(text: &mut String) { + stdx::replace(text, '\\', "\\\\"); + stdx::replace(text, '$', "\\$"); + } } pub struct SourceChangeBuilder { diff --git a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs index 4811dd69e0bf4..4e3c491418754 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs @@ -1,7 +1,7 @@ //! Handles the `Enter` key press, including comment continuation and //! indentation in brace-delimited constructs. -use ide_db::{FilePosition, RootDatabase}; +use ide_db::{FilePosition, RootDatabase, source_change::SnippetEdit}; use syntax::{ AstNode, SmolStr, SourceFile, SyntaxKind::*, @@ -108,19 +108,13 @@ fn on_enter_in_comment( Some(edit) } -// for lack of a better place to put this function -fn escape_snippet_bits(text: &mut String) { - stdx::replace(text, '\\', "\\\\"); - stdx::replace(text, '$', "\\$"); -} - fn on_enter_in_braces(l_curly: SyntaxToken, position: FilePosition) -> Option { if l_curly.text_range().end() != position.offset { return None; } let (r_curly, mut content) = brace_contents_on_same_line(&l_curly)?; - escape_snippet_bits(&mut content); + SnippetEdit::escape_snippet_bits(&mut content); let indent = IndentLevel::from_token(&l_curly); Some(TextEdit::replace( TextRange::new(position.offset, r_curly.text_range().start()), From 844a8b956a559520f20479e4f2e721dd0d272344 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 7 Jul 2026 22:41:03 +0800 Subject: [PATCH 65/78] feat: add fixes for array length for type_mismatch Example --- ```rust const VALS: [i32; 2$0] = [1, 2, 3]; ``` **Before this PR** No available fixes **After this PR** ```rust const VALS: [i32; 3] = [1, 2, 3]; ``` --- .../src/handlers/type_mismatch.rs | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 8950850d4a814..53e3cd16b0ee5 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -75,6 +75,7 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option< remove_semicolon(ctx, d, expr_ptr, &mut fixes); str_ref_to_owned(ctx, d, expr_ptr, &mut fixes); add_await(ctx, d, expr_ptr, &mut fixes); + array_length(ctx, d, expr_ptr, &mut fixes); } if fixes.is_empty() { None } else { Some(fixes) } @@ -402,6 +403,58 @@ fn add_await( Some(()) } +fn array_length( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::TypeMismatch<'_>, + expr_ptr: &InFile>, + acc: &mut Vec, +) -> Option<()> { + let (ty1, expected) = d.expected.as_array(ctx.db())?; + let (ty2, actual) = d.actual.as_array(ctx.db())?; + + if ty1 != ty2 || expected == actual { + return None; + } + + let root = expr_ptr.file_id.parse_or_expand(ctx.db()); + let expr = expr_ptr.value.to_node(&root); + let container = skip_transparent(expr).syntax().parent()?; + let ty = syntax::match_ast! { + match container { + ast::LetStmt(it) => it.ty()?, + ast::Static(it) => it.ty()?, + ast::Const(it) => it.ty()?, + _ => return None, + } + }; + let ast::Type::ArrayType(ty) = ty else { return None }; + let len = ty.const_arg()?.expr()?; + let hir::FileRange { file_id, range } = ctx.sema.original_range_opt(len.syntax())?; + + let edit = TextEdit::replace(range, actual.to_string()); + let source_change = SourceChange::from_text_edit(file_id.file_id(ctx.db()), edit); + let label = &format!("Change array length to {actual}"); + acc.push(fix("array_length", label, source_change, range)); + + fn skip_transparent(mut expr: Expr) -> Expr { + while let Some(parent) = expr.syntax().parent() { + if let Some(it) = ast::StmtList::cast(parent.clone()) + && it.statements().next().is_none() + && let Some(parent) = it.syntax().parent().and_then(Expr::cast) + { + expr = parent; + } else if let Some(parent) = ast::ParenExpr::cast(parent) { + expr = parent.into(); + } else { + break; + } + } + expr + } + + Some(()) +} + #[cfg(test)] mod tests { use crate::tests::{ @@ -1460,6 +1513,51 @@ identity! { ); } + #[test] + fn array_length() { + check_fix( + r#" +const VALS: [i32; 2$0] = [1, 2, 3]; + "#, + r#" +const VALS: [i32; 3] = [1, 2, 3]; + "#, + ); + + check_fix( + r#" +static VALS: [i32; 2$0] = [1, 2, 3]; + "#, + r#" +static VALS: [i32; 3] = [1, 2, 3]; + "#, + ); + + check_fix( + r#" +static VALS: [i32; 2$0] = {[1, 2, 3]}; + "#, + r#" +static VALS: [i32; 3] = {[1, 2, 3]}; + "#, + ); + + check_fix( + r#" +macro_rules! identity { ($($t:tt)*) => ($($t)*) } +identity! { + const VALS: [i32; 2$0] = [1, 2, 3]; +} + "#, + r#" +macro_rules! identity { ($($t:tt)*) => ($($t)*) } +identity! { + const VALS: [i32; 3] = [1, 2, 3]; +} + "#, + ); + } + #[test] fn type_mismatch_range_adjustment() { cov_mark::check!(type_mismatch_range_adjustment); From 89418c2d14b119f8a5f7005c093fdb4056861038 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 12 Jul 2026 20:28:41 +0800 Subject: [PATCH 66/78] Use Type::could_unify_with instead Type::eq --- .../crates/ide-diagnostics/src/handlers/type_mismatch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 53e3cd16b0ee5..b3dbb2099cb1d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -412,7 +412,7 @@ fn array_length( let (ty1, expected) = d.expected.as_array(ctx.db())?; let (ty2, actual) = d.actual.as_array(ctx.db())?; - if ty1 != ty2 || expected == actual { + if !ty1.could_unify_with(ctx.db(), &ty2) || expected == actual { return None; } From abeb7e41b41db16d710d1c9a8d0240faba3d3d4a Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 12 Jul 2026 23:32:37 +0800 Subject: [PATCH 67/78] Use container's range to trigger --- .../ide-diagnostics/src/handlers/type_mismatch.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index b3dbb2099cb1d..295f37ab1d6b3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -429,9 +429,10 @@ fn array_length( }; let ast::Type::ArrayType(ty) = ty else { return None }; let len = ty.const_arg()?.expr()?; - let hir::FileRange { file_id, range } = ctx.sema.original_range_opt(len.syntax())?; + let hir::FileRange { range: len_range, .. } = ctx.sema.original_range_opt(len.syntax())?; + let hir::FileRange { range, file_id } = ctx.sema.original_range_opt(&container)?; - let edit = TextEdit::replace(range, actual.to_string()); + let edit = TextEdit::replace(len_range, actual.to_string()); let source_change = SourceChange::from_text_edit(file_id.file_id(ctx.db()), edit); let label = &format!("Change array length to {actual}"); acc.push(fix("array_length", label, source_change, range)); @@ -1542,6 +1543,16 @@ static VALS: [i32; 3] = {[1, 2, 3]}; "#, ); + // Convenient trigger range + check_fix( + r#" +static VALS: [i32; 2] = [$01, 2, 3]; + "#, + r#" +static VALS: [i32; 3] = [1, 2, 3]; + "#, + ); + check_fix( r#" macro_rules! identity { ($($t:tt)*) => ($($t)*) } From fb737bfd92c0487718e757a79d3748f00d3d6c5e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 13 Jul 2026 03:20:42 +0300 Subject: [PATCH 68/78] Remove the `Database` assoc type from the `Intern` & `Lookup` traits After we removed `DefDatabase`, it is now always `SourceDatabase`. --- .../rust-analyzer/crates/hir-def/src/attrs.rs | 2 +- .../crates/hir-def/src/expr_store/pretty.rs | 2 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 8 ++++---- .../rust-analyzer/crates/hir-def/src/resolver.rs | 5 +---- .../rust-analyzer/crates/hir-expand/src/lib.rs | 16 ++++++---------- .../crates/hir-ty/src/diagnostics/decl_check.rs | 3 +-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 4 ++-- .../crates/hir/src/semantics/child_by_source.rs | 2 +- .../rust-analyzer/crates/hir/src/symbols.rs | 4 ++-- 9 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs index 5286cb2587398..27f9763f088a4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -52,7 +52,7 @@ pub use self::docs::{Docs, IsInnerDoc}; #[inline] fn attrs_from_ast_id_loc>( db: &dyn SourceDatabase, - lookup: impl Lookup + HasModule>, + lookup: impl Lookup + HasModule>, ) -> (InFile, Crate) { let loc = lookup.lookup(db); let source = loc.source(db); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 960d8734ef131..0058ddc1e4a44 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -56,7 +56,7 @@ pub enum LineFormat { fn item_name(db: &dyn SourceDatabase, id: Id, default: &str) -> String where - Id: Lookup, + Id: Lookup, Loc: HasSource, Loc::Value: ast::HasName, { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index c629aef9ce3e2..fa7cb525bbcd8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -225,7 +225,7 @@ impl AstIdLoc for AssocItemLoc { macro_rules! impl_intern { ($id:ident, $loc:ident) => { impl_intern_key!($id, $loc); - impl_intern_lookup!(SourceDatabase, $id, $loc); + impl_intern_lookup!($id, $loc); }; } @@ -936,7 +936,7 @@ impl GenericDefId { ) -> (HirFileId, Option) { fn file_id_and_params_of_item_loc( db: &dyn SourceDatabase, - def: impl Lookup, + def: impl Lookup, ) -> (HirFileId, Option) where Loc: src::HasSource, @@ -1145,7 +1145,7 @@ pub trait HasModule { impl HasModule for ItemId where N: AstIdNode, - ItemId: Lookup> + Copy, + ItemId: Lookup> + Copy, { #[inline] fn module(&self, db: &dyn SourceDatabase) -> ModuleId { @@ -1170,7 +1170,7 @@ where #[inline] fn module_for_assoc_item_loc<'db>( db: &(dyn 'db + SourceDatabase), - id: impl Lookup>, + id: impl Lookup>, ) -> ModuleId { id.lookup(db).container.module(db) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index cc59f66f7092c..28d460b503fd6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -1479,10 +1479,7 @@ impl HasResolver for MacroRulesId { fn lookup_resolver( db: &dyn SourceDatabase, - lookup: impl Lookup< - Database = dyn SourceDatabase, - Data = impl AstIdLoc, - >, + lookup: impl Lookup>, ) -> Resolver<'_> { lookup.lookup(db).container().resolver(db) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 9ebb346a373fe..511b94d7418f1 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -83,19 +83,17 @@ const TOKEN_LIMIT: usize = 2_097_152; #[macro_export] macro_rules! impl_intern_lookup { - ($db:ident, $id:ident, $loc:ident) => { + ($id:ident, $loc:ident) => { impl $crate::Intern for $loc { - type Database = dyn $db; type ID = $id; - fn intern(self, db: &Self::Database) -> Self::ID { + fn intern(self, db: &dyn ::base_db::SourceDatabase) -> Self::ID { $id::new(db, self) } } impl $crate::Lookup for $id { - type Database = dyn $db; type Data = $loc; - fn lookup<'db>(&self, db: &'db Self::Database) -> &'db Self::Data { + fn lookup<'db>(&self, db: &'db dyn ::base_db::SourceDatabase) -> &'db Self::Data { self.loc(db) } } @@ -104,18 +102,16 @@ macro_rules! impl_intern_lookup { // ideally these would be defined in base-db, but the orphan rule doesn't let us pub trait Intern { - type Database: ?Sized; type ID; - fn intern(self, db: &Self::Database) -> Self::ID; + fn intern(self, db: &dyn SourceDatabase) -> Self::ID; } pub trait Lookup { - type Database: ?Sized; type Data; - fn lookup<'db>(&self, db: &'db Self::Database) -> &'db Self::Data; + fn lookup<'db>(&self, db: &'db dyn SourceDatabase) -> &'db Self::Data; } -impl_intern_lookup!(SourceDatabase, MacroCallId, MacroCallLoc); +impl_intern_lookup!(MacroCallId, MacroCallLoc); pub type ExpandResult = ValueResult; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 8431f7512a06b..a465f8be4e175 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -15,7 +15,6 @@ mod case_conv; use std::fmt; -use base_db::SourceDatabase; use hir_def::{ AdtId, ConstId, EnumId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId, @@ -710,7 +709,7 @@ impl<'a> DeclValidator<'a> { ) where N: AstNode + HasName + fmt::Debug, S: HasSource, - L: Lookup + HasModule + Copy, + L: Lookup + HasModule + Copy, { let to_expected_case_type = match expected_case { CaseType::LowerSnakeCase => to_lower_snake_case, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 96fb65882c6a1..fc04ea1f4978e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -3622,7 +3622,7 @@ fn as_assoc_item<'db, ID, DEF, LOC>( id: ID, ) -> Option where - ID: Lookup>, + ID: Lookup>, DEF: From, LOC: AstIdNode, { @@ -3638,7 +3638,7 @@ fn as_extern_assoc_item<'db, ID, DEF, LOC>( id: ID, ) -> Option where - ID: Lookup>, + ID: Lookup>, DEF: From, LOC: AstIdNode, { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index 33384568b992d..bca8c8c503dfd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -283,7 +283,7 @@ fn insert_item_loc( id: ID, key: Key, ) where - ID: Lookup + 'static, + ID: Lookup + 'static, Data: AstIdLoc, N: AstIdNode + 'static, { diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index 6b8638cff001a..021a20ae1b71e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; -use base_db::{FxIndexSet, SourceDatabase}; +use base_db::FxIndexSet; use either::Either; use hir_def::{ AdtId, AssocItemId, AstIdLoc, Complete, DefWithBodyId, ExternCrateId, HasModule, ImplId, @@ -459,7 +459,7 @@ impl<'a> SymbolCollector<'a> { trait_do_not_complete: Option, ) -> Complete where - L: Lookup + Into, + L: Lookup + Into, ::Data: HasSource, <::Data as HasSource>::Value: HasName, { From 9a3aed41e7e5339d83cbc55bb18f4a7a1cc6ad0d Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Tue, 30 Jun 2026 18:55:53 +0200 Subject: [PATCH 69/78] borrowck: Represent 'best blame constraint' as index into `Vec` This makes the code simpler and easier to understand, since we don't have to partially copy an `OutlivesConstraint` into the `BlameConstraint` struct (which we can remove entirely) and the `BorrowExplanation::MustBeValidFor` enum variant (which we can simplify). Instead, we reference the "best" `OutlivesConstraint` by an index into `Vec`. This is not only simpler, it also opens up the possibility for diagnostics to access the full `OutlivesConstraint` info. --- .../src/diagnostics/conflict_errors.rs | 70 ++++++++++--------- .../src/diagnostics/explain_borrow.rs | 27 +++---- .../src/diagnostics/region_errors.rs | 21 +++--- .../rustc_borrowck/src/region_infer/mod.rs | 63 +++++++++-------- 4 files changed, 91 insertions(+), 90 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 86e5a6700b039..7d29a931f9c13 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -46,6 +46,7 @@ use tracing::{debug, instrument}; use super::explain_borrow::{BorrowExplanation, LaterUseKind}; use super::{DescribePlaceOpt, RegionName, RegionNameSource, UseSpans}; use crate::borrow_set::{BorrowData, TwoPhaseActivation}; +use crate::consumers::OutlivesConstraint; use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead; use crate::diagnostics::{CapturedMessageOpt, call_kind, find_all_local_uses}; use crate::{InitializationRequiringAction, MirBorrowckCtxt, WriteKind, borrowck_errors}; @@ -3076,40 +3077,48 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ), ( Some(name), - BorrowExplanation::MustBeValidFor { - category: - category @ (ConstraintCategory::Return(_) - | ConstraintCategory::CallArgument(_) - | ConstraintCategory::OpaqueType), - from_closure: false, - ref region_name, - span, - .. - }, - ) if borrow_spans.for_coroutine() || borrow_spans.for_closure() => self - .report_escaping_closure_capture( + BorrowExplanation::MustBeValidFor { ref best_blame, ref region_name, .. }, + ) if let OutlivesConstraint { + category: + category @ (ConstraintCategory::Return(_) + | ConstraintCategory::CallArgument(_) + | ConstraintCategory::OpaqueType), + from_closure: false, + span, + .. + } = best_blame.constraint() + && (borrow_spans.for_coroutine() || borrow_spans.for_closure()) => + { + self.report_escaping_closure_capture( borrow_spans, borrow_span, region_name, - category, - span, + *category, + *span, &format!("`{name}`"), "function", - ), + ) + } ( name, BorrowExplanation::MustBeValidFor { - category: ConstraintCategory::Assignment, - from_closure: false, + ref best_blame, region_name: RegionName { source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name), .. }, - span, .. }, - ) => self.report_escaping_data(borrow_span, &name, upvar_span, upvar_name, span), + ) if let OutlivesConstraint { + category: ConstraintCategory::Assignment, + from_closure: false, + span, + .. + } = best_blame.constraint() => + { + self.report_escaping_data(borrow_span, &name, upvar_span, upvar_name, *span) + } (Some(name), explanation) => self.report_local_value_does_not_live_long_enough( location, &name, @@ -3143,18 +3152,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { explanation: BorrowExplanation<'tcx>, ) -> Diag<'infcx> { let borrow_span = borrow_spans.var_or_use_path_span(); - if let BorrowExplanation::MustBeValidFor { - category, - span, - ref opt_place_desc, - from_closure: false, - .. - } = explanation + if let BorrowExplanation::MustBeValidFor { best_blame, opt_place_desc, .. } = &explanation + && let OutlivesConstraint { category, span, from_closure: false, .. } = + best_blame.constraint() && let Err(diag) = self.try_report_cannot_return_reference_to_local( borrow, borrow_span, - span, - category, + *span, + *category, opt_place_desc.as_ref(), ) { @@ -3356,14 +3361,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { proper_span: Span, explanation: BorrowExplanation<'tcx>, ) -> Diag<'infcx> { - if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } = - explanation + if let BorrowExplanation::MustBeValidFor { ref best_blame, .. } = explanation + && let OutlivesConstraint { category, span, from_closure: false, .. } = + best_blame.constraint() { if let Err(diag) = self.try_report_cannot_return_reference_to_local( borrow, proper_span, - span, - category, + *span, + *category, None, ) { return diag; diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 15e3cf28aac32..1b633aefc22f8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -22,7 +22,7 @@ use super::{RegionName, UseSpans, find_use}; use crate::borrow_set::BorrowData; use crate::constraints::OutlivesConstraint; use crate::nll::ConstraintDescription; -use crate::region_infer::{BlameConstraint, Cause}; +use crate::region_infer::{BestBlame, Cause}; use crate::{MirBorrowckCtxt, WriteKind}; #[derive(Debug)] @@ -35,12 +35,9 @@ pub(crate) enum BorrowExplanation<'tcx> { should_note_order: bool, }, MustBeValidFor { - category: ConstraintCategory<'tcx>, - from_closure: bool, - span: Span, + best_blame: BestBlame<'tcx>, region_name: RegionName, opt_place_desc: Option, - path: Vec>, }, Unexplained, } @@ -376,13 +373,13 @@ impl<'tcx> BorrowExplanation<'tcx> { } } BorrowExplanation::MustBeValidFor { - category, - span, + ref best_blame, ref region_name, ref opt_place_desc, - from_closure: _, - ref path, } => { + let OutlivesConstraint { category, span, .. } = *best_blame.constraint(); + let path = best_blame.path(); + region_name.highlight_region_name(err); if let Some(desc) = opt_place_desc { @@ -403,8 +400,8 @@ impl<'tcx> BorrowExplanation<'tcx> { ); }; - cx.add_placeholder_from_predicate_note(err, &path); - cx.add_sized_or_copy_bound_info(err, category, &path); + cx.add_placeholder_from_predicate_note(err, path); + cx.add_sized_or_copy_bound_info(err, category, path); if let ConstraintCategory::Cast { is_raw_ptr_dyn_type_cast: _, @@ -689,22 +686,18 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // Here, under NLL: no cause was found. Under polonius: no cause was found, or a // boring local was found, which we ignore like NLLs do to match its diagnostics. if let Some(region) = self.regioncx.to_error_region_vid(borrow_region_vid) { - let (blame_constraint, path) = self.regioncx.best_blame_constraint( + let best_blame = self.regioncx.best_blame_constraint( borrow_region_vid, NllRegionVariableOrigin::FreeRegion, region, ); - let BlameConstraint { category, from_closure, span, .. } = blame_constraint; if let Some(region_name) = self.give_region_a_name(region) { let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref()); BorrowExplanation::MustBeValidFor { - category, - from_closure, - span, + best_blame, region_name, opt_place_desc, - path, } } else { debug!("Could not generate a region name"); diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index a82cd5c74c330..112f3e2406943 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -28,9 +28,9 @@ use rustc_trait_selection::traits::{Obligation, ObligationCtxt}; use tracing::{debug, instrument, trace}; use super::{LIMITATION_NOTE, OutlivesSuggestionBuilder, RegionName, RegionNameSource}; -use crate::consumers::RegionInferenceContext; +use crate::consumers::{OutlivesConstraint, RegionInferenceContext}; use crate::nll::ConstraintDescription; -use crate::region_infer::{BlameConstraint, TypeTest}; +use crate::region_infer::TypeTest; use crate::session_diagnostics::{ FnMutError, FnMutReturnTypeErr, GenericDoesNotLiveLongEnough, LifetimeOutliveErr, LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote, @@ -414,9 +414,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { }; // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. - let (blame_constraint, path) = - self.regioncx.best_blame_constraint(longer_fr, origin_longer, error_vid); - let cause = blame_constraint.to_obligation_cause_from_path(&path); + let best_blame = self.regioncx.best_blame_constraint(longer_fr, origin_longer, error_vid); + let cause = best_blame.to_obligation_cause(); // FIXME these methods should have better names, and also probably not be this generic. // FIXME note that we *throw away* the error element here! We probably want to @@ -447,9 +446,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ) { debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr); - let (blame_constraint, path) = - self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr); - let BlameConstraint { category, span, variance_info, .. } = blame_constraint; + let best_blame = self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr); + let OutlivesConstraint { category, span, variance_info, .. } = *best_blame.constraint(); + let path = best_blame.path(); debug!("report_region_error: category={:?} {:?} {:?}", category, span, variance_info); @@ -563,10 +562,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - self.add_placeholder_from_predicate_note(&mut diag, &path); - self.add_sized_or_copy_bound_info(&mut diag, category, &path); + self.add_placeholder_from_predicate_note(&mut diag, path); + self.add_sized_or_copy_bound_info(&mut diag, category, path); - for constraint in &path { + for constraint in path { if let ConstraintCategory::Cast { is_raw_ptr_dyn_type_cast: true, .. } = constraint.category { diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 505aa74b04437..effdc7dc34af1 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1285,9 +1285,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { return RegionRelationCheckResult::Error; } - let blame_constraint = self - .best_blame_constraint(longer_fr, NllRegionVariableOrigin::FreeRegion, shorter_fr) - .0; + let best_blame = self.best_blame_constraint( + longer_fr, + NllRegionVariableOrigin::FreeRegion, + shorter_fr, + ); + let OutlivesConstraint { category, span, .. } = best_blame.constraint(); // Grow `shorter_fr` until we find some non-local regions. // We will always find at least one: `'static`. We'll call @@ -1346,8 +1349,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements.push(ClosureOutlivesRequirement { subject: ClosureOutlivesSubject::Region(fr_minus), outlived_free_region: fr_plus, - blame_span: blame_constraint.span, - category: blame_constraint.category, + blame_span: *span, + category: *category, }); } return RegionRelationCheckResult::Propagated; @@ -1614,7 +1617,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { from_region: RegionVid, from_region_origin: NllRegionVariableOrigin<'tcx>, to_region: RegionVid, - ) -> (BlameConstraint<'tcx>, Vec>) { + ) -> BestBlame<'tcx> { assert!(from_region != to_region, "Trying to blame a region for itself!"); let path = self.constraint_path_between_regions(from_region, to_region).unwrap(); @@ -1787,13 +1790,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!(?best_choice, ?blame_source); - let best_constraint = if let Some(next) = path.get(best_choice + 1) + let best_blame_idx = if let Some(next) = path.get(best_choice + 1) && matches!(path[best_choice].category, ConstraintCategory::Return(_)) && next.category == ConstraintCategory::OpaqueType { // The return expression is being influenced by the return type being // impl Trait, point at the return type and not the return expr. - *next + best_choice + 1 } else if path[best_choice].category == ConstraintCategory::Return(ReturnConstraint::Normal) && let Some(field) = path.iter().find_map(|p| { if let ConstraintCategory::ClosureUpvar(f) = p.category { Some(f) } else { None } @@ -1801,26 +1804,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { { path[best_choice].category = ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)); - path[best_choice] + best_choice } else { - path[best_choice] + best_choice }; assert!( !matches!( - best_constraint.category, + path[best_blame_idx].category, ConstraintCategory::OutlivesUnnameablePlaceholder(_) ), "Illegal placeholder constraint blamed; should have redirected to other region relation" ); - let blame_constraint = BlameConstraint { - category: best_constraint.category, - from_closure: best_constraint.from_closure, - span: best_constraint.span, - variance_info: best_constraint.variance_info, - }; - (blame_constraint, path) + BestBlame { path, idx: best_blame_idx } } pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { @@ -1895,21 +1892,19 @@ impl<'tcx> RegionInferenceContext<'tcx> { } #[derive(Clone, Debug)] -pub(crate) struct BlameConstraint<'tcx> { - pub category: ConstraintCategory<'tcx>, - pub from_closure: bool, - pub span: Span, - pub variance_info: ty::VarianceDiagInfo>, +pub(crate) struct BestBlame<'tcx> { + /// See docs on [`RegionInferenceContext::best_blame_constraint`] for what this is. + path: Vec>, + /// Index into `path` of the constraint most relevant to report to users. + idx: usize, } -impl<'tcx> BlameConstraint<'tcx> { - pub(crate) fn to_obligation_cause_from_path( - &self, - path: &[OutlivesConstraint<'tcx>], - ) -> ObligationCause<'tcx> { +impl<'tcx> BestBlame<'tcx> { + pub(crate) fn to_obligation_cause(&self) -> ObligationCause<'tcx> { // FIXME - determine what we should do if we encounter multiple // `ConstraintCategory::Predicate` constraints. Currently, we just pick the first one. - let cause_code = path + let cause_code = self + .path .iter() .find_map(|constraint| { if let ConstraintCategory::Predicate(predicate_span) = constraint.category { @@ -1923,6 +1918,14 @@ impl<'tcx> BlameConstraint<'tcx> { }) .unwrap_or_else(|| ObligationCauseCode::Misc); - ObligationCause::new(self.span, CRATE_DEF_ID, cause_code.clone()) + ObligationCause::new(self.constraint().span, CRATE_DEF_ID, cause_code.clone()) + } + + pub(crate) fn constraint(&self) -> &OutlivesConstraint<'tcx> { + &self.path[self.idx] + } + + pub(crate) fn path(&self) -> &[OutlivesConstraint<'tcx>] { + &self.path } } From eef0fd974176c2741fde578aa81c6fd9514c23ab Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 13 Jul 2026 14:23:20 +1000 Subject: [PATCH 70/78] Don't use `ShouldRun::crates` in `clean_crate_tree!` This implementation of `should_run` was identical to `crate_or_deps`. --- src/bootstrap/src/core/build_steps/clean.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index 6dc03236053c7..fec4a79de3c75 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -58,8 +58,7 @@ macro_rules! clean_crate_tree { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - let crates = run.builder.in_tree_crates($root_crate, None); - run.crates(crates) + run.crate_or_deps($root_crate) } fn make_run(run: RunConfig<'_>) { From 36559cec16703e22f353c432fbf9ffad1cb05af7 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 13 Jul 2026 14:33:31 +1000 Subject: [PATCH 71/78] Replace `ShouldRun::crates` with `crate_or_deps_filtered` The only remaining direct caller of `crates` was the `compile::Rustc` step, which wants to remove `rustc-main` from the expanded list of in-tree crates. --- src/bootstrap/src/core/build_steps/compile.rs | 11 ++------ src/bootstrap/src/core/builder/mod.rs | 28 ++++++++++++------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 4f96df6452d78..a1effa38387ca 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1018,16 +1018,11 @@ impl Step for Rustc { const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - let mut crates = run.builder.in_tree_crates("rustc-main", None); - for (i, krate) in crates.iter().enumerate() { + run.crate_or_deps_filtered("rustc-main", |krate| { // We can't allow `build rustc` as an alias for this Step, because that's reserved by `Assemble`. // Ideally Assemble would use `build compiler` instead, but that seems too confusing to be worth the breaking change. - if krate.name == "rustc-main" { - crates.swap_remove(i); - break; - } - } - run.crates(crates) + krate.name != "rustc-main" + }) } fn is_default_step(_builder: &Builder<'_>) -> bool { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 0d17f0f792a01..b21a2fe77d94f 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -524,22 +524,30 @@ impl<'a> ShouldRun<'a> { ShouldRun { builder, kind, paths: BTreeSet::new(), default_to_suites_only: false } } - /// Indicates it should run if the command-line selects the given crate or - /// any of its (local) dependencies. + /// The corresponding step should run if the bootstrap command-line selects + /// the given crate or any of its (local) dependencies. /// - /// `make_run` will be called a single time with all matching command-line paths. - pub fn crate_or_deps(self, name: &str) -> Self { - let crates = self.builder.in_tree_crates(name, None); - self.crates(crates) + /// Delegates to [`Self::crate_or_deps_filtered`] with a filter that accepts all crates. + pub(crate) fn crate_or_deps(self, root_crate_name: &str) -> Self { + self.crate_or_deps_filtered(root_crate_name, |_: &Crate| true) } - /// Indicates it should run if the command-line selects any of the given crates. + /// The corresponding step should run if the bootstrap command-line selects + /// the given crate or any of its (local) dependencies, not counting any + /// crates rejected by the given filter function. /// /// `make_run` will be called a single time with all matching command-line paths. - /// - /// Prefer [`ShouldRun::crate_or_deps`] to this function where possible. - pub(crate) fn crates(mut self, crates: Vec<&Crate>) -> Self { + pub(crate) fn crate_or_deps_filtered( + mut self, + root_crate_name: &str, + crate_filter_fn: impl Fn(&Crate) -> bool, + ) -> Self { + let crates = self.builder.in_tree_crates(root_crate_name, None); for krate in crates { + if !crate_filter_fn(krate) { + continue; + } + let path = krate.local_path(self.builder); self.paths.insert(PathSet::one(path, self.kind)); } From 6b3bc86f927dc5e2f9d334a45c026f38be8026bc Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 13 Jul 2026 14:41:39 +1000 Subject: [PATCH 72/78] Avoid some useless string clones when sorting crates by name --- src/bootstrap/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index f7ffc682e2bd7..f08346789e7e8 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1735,7 +1735,9 @@ impl Build { } } } - ret.sort_unstable_by_key(|krate| krate.name.clone()); // reproducible order needed for tests + + // Sort the crates so that bootstrap unit tests can assume a deterministic order. + ret.sort_unstable_by(|a, b| Ord::cmp(&a.name, &b.name)); ret } From c6748782afce501b8f3a8ba1c83165ebf1320f91 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 13 Jul 2026 16:10:59 +1000 Subject: [PATCH 73/78] Add a case to the `multiple-tail-expr-behind-cfg.rs` test This demonstrates the `if cfg!(..)` suggestion being applied wrongly, when a `cfg` attribute is followed by a non-`cfg` attribute. --- .../multiple-tail-expr-behind-cfg.rs | 7 +++++ .../multiple-tail-expr-behind-cfg.stderr | 29 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.rs b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.rs index 371f19d487268..fcf0dcf478444 100644 --- a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.rs +++ b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.rs @@ -14,6 +14,13 @@ fn bar() -> String { String::new() } +fn baz() -> String { + #[cfg(false)] + [1, 2, 3].iter().map(|c| c.to_string()).collect::() //~ ERROR expected `;`, found `#` + #[allow(unused)] + String::new() +} + fn main() { println!("{}", foo()); } diff --git a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr index 3a97a14b3c301..f5bbb78882687 100644 --- a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr +++ b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr @@ -44,11 +44,38 @@ help: alternatively, consider surrounding the expression with a block LL | { [1, 2, 3].iter().map(|c| c.to_string()).collect::() } | + + +error: expected `;`, found `#` + --> $DIR/multiple-tail-expr-behind-cfg.rs:19:64 + | +LL | #[cfg(false)] + | ------------- only `;` terminated statements or tail expressions are allowed after this attribute +LL | [1, 2, 3].iter().map(|c| c.to_string()).collect::() + | ^ expected `;` here +LL | #[allow(unused)] + | - unexpected token + | +help: add `;` here + | +LL | [1, 2, 3].iter().map(|c| c.to_string()).collect::(); + | + +help: alternatively, consider surrounding the expression with a block + | +LL | { [1, 2, 3].iter().map(|c| c.to_string()).collect::() } + | + + +help: it seems like you are trying to provide different expressions depending on `cfg`, consider using `if cfg!(..)` + | +LL ~ if cfg!(false) { +LL ~ [1, 2, 3].iter().map(|c| c.to_string()).collect::() +LL ~ } else if cfg!(unused) { +LL ~ String::new() +LL + } + | + error: cannot find attribute `attr` in this scope --> $DIR/multiple-tail-expr-behind-cfg.rs:13:7 | LL | #[attr] | ^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors From 895538515599db58b843620d251d5b977d09dcc9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 13 Jul 2026 15:55:44 +1000 Subject: [PATCH 74/78] Fix typo in `attr_on_non_tail_expr` The line `&& segment.ident.name == sym::cfg` duplicates a line from just a few lines above in the same if-let chain. It's clear from context that the `segment` is a copy/paste error and should be `next_segment`. This fixes the erroneous suggestion given for `multiple-tail-expr-behind-cfg.rs`. --- compiler/rustc_parse/src/parser/diagnostics.rs | 2 +- .../parser/attribute/multiple-tail-expr-behind-cfg.stderr | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 254e7e410f579..a4f0f5610081d 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -873,7 +873,7 @@ impl<'a> Parser<'a> { && let ast::AttrKind::Normal(next_attr_kind) = next_attr.kind && let Some(next_attr_args_span) = next_attr_kind.item.args.span() && let [next_segment] = &next_attr_kind.item.path.segments[..] - && segment.ident.name == sym::cfg + && next_segment.ident.name == sym::cfg { let next_expr = match snapshot.parse_expr() { Ok(next_expr) => next_expr, diff --git a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr index f5bbb78882687..c8f3d50edd103 100644 --- a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr +++ b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr @@ -62,14 +62,6 @@ help: alternatively, consider surrounding the expression with a block | LL | { [1, 2, 3].iter().map(|c| c.to_string()).collect::() } | + + -help: it seems like you are trying to provide different expressions depending on `cfg`, consider using `if cfg!(..)` - | -LL ~ if cfg!(false) { -LL ~ [1, 2, 3].iter().map(|c| c.to_string()).collect::() -LL ~ } else if cfg!(unused) { -LL ~ String::new() -LL + } - | error: cannot find attribute `attr` in this scope --> $DIR/multiple-tail-expr-behind-cfg.rs:13:7 From 5a5d1305fdc5256dbbfab7f536a4703df2ceb923 Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Sat, 6 Jun 2026 01:36:14 -0400 Subject: [PATCH 75/78] add regression tests --- ...suggestion-path-156244.edition_2015.stderr | 67 ++++++++++++++++++- ...suggestion-path-156244.edition_2018.stderr | 67 ++++++++++++++++++- .../private-import-suggestion-path-156244.rs | 38 +++++++++++ 3 files changed, 170 insertions(+), 2 deletions(-) diff --git a/tests/ui/imports/private-import-suggestion-path-156244.edition_2015.stderr b/tests/ui/imports/private-import-suggestion-path-156244.edition_2015.stderr index 95b1760e6a239..aa531667c03ad 100644 --- a/tests/ui/imports/private-import-suggestion-path-156244.edition_2015.stderr +++ b/tests/ui/imports/private-import-suggestion-path-156244.edition_2015.stderr @@ -76,6 +76,71 @@ LL - use crate::rename::inner::Item as Item1; LL + use outer::actual::Item as Item1; | -error: aborting due to 4 previous errors +error[E0603]: struct import `Hi` is private + --> $DIR/private-import-suggestion-path-156244.rs:51:14 + | +LL | use testing::Hi; + | ^^ private struct import + | +note: the struct import `Hi` is defined here... + --> $DIR/private-import-suggestion-path-156244.rs:48:9 + | +LL | use super::public::Hi; + | ^^^^^^^^^^^^^^^^^ +note: ...and refers to the struct `Hi` which is defined here + --> $DIR/private-import-suggestion-path-156244.rs:44:5 + | +LL | pub struct Hi; + | ^^^^^^^^^^^^^^ you could import this directly +help: import `Hi` directly + | +LL - use testing::Hi; +LL + use super::public::Hi; + | + +error[E0603]: struct import `Hi` is private + --> $DIR/private-import-suggestion-path-156244.rs:67:37 + | +LL | use inaccessible_ancestor::testing::Hi; + | ^^ private struct import + | +note: the struct import `Hi` is defined here... + --> $DIR/private-import-suggestion-path-156244.rs:63:13 + | +LL | use super::private::public::Hi; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...and refers to the struct `Hi` which is defined here + --> $DIR/private-import-suggestion-path-156244.rs:58:13 + | +LL | pub struct Hi; + | ^^^^^^^^^^^^^^ you could import this directly +help: import `Hi` directly + | +LL - use inaccessible_ancestor::testing::Hi; +LL + use super::private::public::Hi; + | + +error[E0603]: module import `mem` is private + --> $DIR/private-import-suggestion-path-156244.rs:77:21 + | +LL | use external_alias::mem; + | ^^^ private module import + | +note: the module import `mem` is defined here... + --> $DIR/private-import-suggestion-path-156244.rs:74:9 + | +LL | use super::s::mem; + | ^^^^^^^^^^^^^ +note: ...and refers to the module `mem` which is defined here + --> $SRC_DIR/std/src/lib.rs:LL:COL + | + = note: you could import this directly +help: import `mem` directly + | +LL - use external_alias::mem; +LL + use core::mem; + | + +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0603`. diff --git a/tests/ui/imports/private-import-suggestion-path-156244.edition_2018.stderr b/tests/ui/imports/private-import-suggestion-path-156244.edition_2018.stderr index e153b0cdc95aa..20b0c198830e0 100644 --- a/tests/ui/imports/private-import-suggestion-path-156244.edition_2018.stderr +++ b/tests/ui/imports/private-import-suggestion-path-156244.edition_2018.stderr @@ -76,6 +76,71 @@ LL - use crate::rename::inner::Item as Item1; LL + use crate::outer::actual::Item as Item1; | -error: aborting due to 4 previous errors +error[E0603]: struct import `Hi` is private + --> $DIR/private-import-suggestion-path-156244.rs:51:14 + | +LL | use testing::Hi; + | ^^ private struct import + | +note: the struct import `Hi` is defined here... + --> $DIR/private-import-suggestion-path-156244.rs:48:9 + | +LL | use super::public::Hi; + | ^^^^^^^^^^^^^^^^^ +note: ...and refers to the struct `Hi` which is defined here + --> $DIR/private-import-suggestion-path-156244.rs:44:5 + | +LL | pub struct Hi; + | ^^^^^^^^^^^^^^ you could import this directly +help: import `Hi` directly + | +LL - use testing::Hi; +LL + use super::public::Hi; + | + +error[E0603]: struct import `Hi` is private + --> $DIR/private-import-suggestion-path-156244.rs:67:37 + | +LL | use inaccessible_ancestor::testing::Hi; + | ^^ private struct import + | +note: the struct import `Hi` is defined here... + --> $DIR/private-import-suggestion-path-156244.rs:63:13 + | +LL | use super::private::public::Hi; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...and refers to the struct `Hi` which is defined here + --> $DIR/private-import-suggestion-path-156244.rs:58:13 + | +LL | pub struct Hi; + | ^^^^^^^^^^^^^^ you could import this directly +help: import `Hi` directly + | +LL - use inaccessible_ancestor::testing::Hi; +LL + use super::private::public::Hi; + | + +error[E0603]: module import `mem` is private + --> $DIR/private-import-suggestion-path-156244.rs:77:21 + | +LL | use external_alias::mem; + | ^^^ private module import + | +note: the module import `mem` is defined here... + --> $DIR/private-import-suggestion-path-156244.rs:74:9 + | +LL | use super::s::mem; + | ^^^^^^^^^^^^^ +note: ...and refers to the module `mem` which is defined here + --> $SRC_DIR/std/src/lib.rs:LL:COL + | + = note: you could import this directly +help: import `mem` directly + | +LL - use external_alias::mem; +LL + use core::mem; + | + +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0603`. diff --git a/tests/ui/imports/private-import-suggestion-path-156244.rs b/tests/ui/imports/private-import-suggestion-path-156244.rs index 3f9d247097f7d..8732564920720 100644 --- a/tests/ui/imports/private-import-suggestion-path-156244.rs +++ b/tests/ui/imports/private-import-suggestion-path-156244.rs @@ -39,4 +39,42 @@ mod bad { //~^ ERROR module import `inner` is private [E0603] } +// Regression test for https://github.com/rust-lang/rust/issues/157455: no root `super`. +mod public { + pub struct Hi; +} + +mod testing { + use super::public::Hi; +} + +use testing::Hi; +//~^ ERROR struct import `Hi` is private [E0603] + +// Regression test for https://github.com/rust-lang/rust/issues/157455: no private ancestors. +mod inaccessible_ancestor { + mod private { + pub mod public { + pub struct Hi; + } + } + + pub mod testing { + use super::private::public::Hi; + } +} + +use inaccessible_ancestor::testing::Hi; +//~^ ERROR struct import `Hi` is private [E0603] + +// Regression test for https://github.com/rust-lang/rust/issues/157455: no external alias rewrite. +use std as s; + +mod external_alias { + use super::s::mem; +} + +use external_alias::mem; +//~^ ERROR module import `mem` is private [E0603] + fn main() {} From 6be6d756605c5f7e0de39981afc11cf1b5cf44a2 Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Sat, 6 Jun 2026 02:59:55 -0400 Subject: [PATCH 76/78] fix relative paths in private import suggestions --- .../rustc_resolve/src/diagnostics/impls.rs | 99 +++++++++++++++++-- ...suggestion-path-156244.edition_2015.stderr | 7 +- ...suggestion-path-156244.edition_2018.stderr | 7 +- 3 files changed, 91 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index ab8554f2652ee..800f4f1e482c3 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -2668,17 +2668,96 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match binding.kind { DeclKind::Import { source_decl, import, .. } => { - // Don't include `{{root}}` in suggestions - it's an internal symbol - // that should never be shown to users. - let path = import - .module_path - .iter() - .filter(|seg| seg.ident.name != kw::PathRoot) - .map(|seg| seg.ident.clone()) - .chain(std::iter::once(ident)) - .collect::>(); let through_reexport = !matches!(source_decl.kind, DeclKind::Def(_)); - sugg_paths.push((path, through_reexport)); + let uses_relative_path = import + .module_path + .first() + .is_some_and(|seg| matches!(seg.ident.name, kw::SelfLower | kw::Super)); + let res_def_id = res.opt_def_id(); + let path = if uses_relative_path { + // A path recovered from `self`/`super` is only useful if both the + // target and every module segment can be named from the failing use site. + let module_path = if let Some(ModuleOrUniformRoot::Module(module)) = + import.imported_module.get() + && module.is_local() + && let Some(module_path) = self.module_path_names(module) + && let Some(mut def_id) = module.opt_def_id() + && res_def_id.is_none_or(|def_id| { + self.is_accessible_from( + self.tcx.visibility(def_id), + parent_scope.module, + ) + }) { + // `module_path_names` tells us the resolved module's canonical path. + // Before suggesting that path from the failing use site, make sure + // every segment in it can actually be named from there. + let mut visible_from_use_site = true; + while let Some(parent) = self.tcx.opt_parent(def_id) { + if !self.is_accessible_from( + self.tcx.visibility(def_id), + parent_scope.module, + ) { + visible_from_use_site = false; + break; + } + if parent.is_top_level_module() { + break; + } + def_id = parent; + } + if visible_from_use_site { Some(module_path) } else { None } + } else { + None + }; + + if let Some(module_path) = module_path { + // `import.module_path` is relative to the import's module, not to the + // failing use site. + let mut suggestion = ImportSuggestion { + did: res_def_id, + descr: "", + path: Path { + span: ident.span, + segments: module_path + .into_iter() + .chain(std::iter::once(ident.name)) + .map(|name| { + ast::PathSegment::from_ident(Ident::with_dummy_span( + name, + )) + }) + .collect(), + tokens: None, + }, + accessible: true, + doc_visible: true, + via_import: false, + note: None, + is_stable: true, + }; + // Reuse the existing relative shortening policy. The fields above + // other than `did` and `path` are not used by this helper. + self.shorten_candidate_path(&mut suggestion, parent_scope.module); + Some(suggestion.path.segments.iter().map(|seg| seg.ident).collect()) + } else { + None + } + } else { + // Don't include `{{root}}` in suggestions - it's an internal symbol + // that should never be shown to users. + Some( + import + .module_path + .iter() + .filter(|seg| seg.ident.name != kw::PathRoot) + .map(|seg| seg.ident.clone()) + .chain(std::iter::once(ident)) + .collect::>(), + ) + }; + if let Some(path) = path { + sugg_paths.push((path, through_reexport)); + } } DeclKind::Def(_) => {} } diff --git a/tests/ui/imports/private-import-suggestion-path-156244.edition_2015.stderr b/tests/ui/imports/private-import-suggestion-path-156244.edition_2015.stderr index aa531667c03ad..9e51641447f89 100644 --- a/tests/ui/imports/private-import-suggestion-path-156244.edition_2015.stderr +++ b/tests/ui/imports/private-import-suggestion-path-156244.edition_2015.stderr @@ -95,7 +95,7 @@ LL | pub struct Hi; help: import `Hi` directly | LL - use testing::Hi; -LL + use super::public::Hi; +LL + use public::Hi; | error[E0603]: struct import `Hi` is private @@ -114,11 +114,6 @@ note: ...and refers to the struct `Hi` which is defined here | LL | pub struct Hi; | ^^^^^^^^^^^^^^ you could import this directly -help: import `Hi` directly - | -LL - use inaccessible_ancestor::testing::Hi; -LL + use super::private::public::Hi; - | error[E0603]: module import `mem` is private --> $DIR/private-import-suggestion-path-156244.rs:77:21 diff --git a/tests/ui/imports/private-import-suggestion-path-156244.edition_2018.stderr b/tests/ui/imports/private-import-suggestion-path-156244.edition_2018.stderr index 20b0c198830e0..c1f059a09d4d9 100644 --- a/tests/ui/imports/private-import-suggestion-path-156244.edition_2018.stderr +++ b/tests/ui/imports/private-import-suggestion-path-156244.edition_2018.stderr @@ -95,7 +95,7 @@ LL | pub struct Hi; help: import `Hi` directly | LL - use testing::Hi; -LL + use super::public::Hi; +LL + use public::Hi; | error[E0603]: struct import `Hi` is private @@ -114,11 +114,6 @@ note: ...and refers to the struct `Hi` which is defined here | LL | pub struct Hi; | ^^^^^^^^^^^^^^ you could import this directly -help: import `Hi` directly - | -LL - use inaccessible_ancestor::testing::Hi; -LL + use super::private::public::Hi; - | error[E0603]: module import `mem` is private --> $DIR/private-import-suggestion-path-156244.rs:77:21 From 59a09407e01949c4bca2fd5a312f15e33d763681 Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Fri, 10 Jul 2026 20:33:40 -0400 Subject: [PATCH 77/78] address suggested nits --- .../rustc_resolve/src/diagnostics/impls.rs | 71 ++++++++----------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index 800f4f1e482c3..b56e1263c17c8 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -2433,18 +2433,27 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Some(path) } - /// Shortens a candidate import path to use `super::` (up to 1 level) or `self::` (same module) - /// relative to the current scope, if possible. Only applies to crate-local items and - /// only when the resulting path is actually shorter than the original. fn shorten_candidate_path( &self, suggestion: &mut ImportSuggestion, current_module: Module<'ra>, + ) { + self.shorten_import_path(suggestion.did, &mut suggestion.path, current_module); + } + + /// Shortens an import path to use `super::` (up to 1 level) or `self::` (same module) + /// relative to the current scope, if possible. Only applies to crate-local items and + /// only when the resulting path is actually shorter than the original. + fn shorten_import_path( + &self, + did: Option, + path: &mut Path, + current_module: Module<'ra>, ) { const MAX_SUPER_PATH_ITEMS_IN_SUGGESTION: usize = 1; // Only shorten local items. - if suggestion.did.is_none_or(|did| !did.is_local()) { + if did.is_none_or(|did| !did.is_local()) { return; } @@ -2457,12 +2466,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // doesn't start with `Crate`, prepend it (edition 2015 paths are relative // to the crate root without an explicit `crate::` prefix). let candidate_names = { - let filtered_segments: Vec<_> = suggestion - .path - .segments - .iter() - .filter(|segment| segment.ident.name != kw::PathRoot) - .collect(); + let filtered_segments: Vec<_> = + path.segments.iter().filter(|segment| segment.ident.name != kw::PathRoot).collect(); let mut candidate_names: Vec = filtered_segments.iter().map(|segment| segment.ident.name).collect(); @@ -2511,11 +2516,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // Only apply if the result is strictly shorter than the original path. - if new_segments.len() >= suggestion.path.segments.len() { + if new_segments.len() >= path.segments.len() { return; } - suggestion.path = Path { span: suggestion.path.span, segments: new_segments }; + *path = Path { span: path.span, segments: new_segments }; } fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { @@ -2710,38 +2715,22 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None }; - if let Some(module_path) = module_path { + module_path.map(|module_path| { // `import.module_path` is relative to the import's module, not to the // failing use site. - let mut suggestion = ImportSuggestion { - did: res_def_id, - descr: "", - path: Path { - span: ident.span, - segments: module_path - .into_iter() - .chain(std::iter::once(ident.name)) - .map(|name| { - ast::PathSegment::from_ident(Ident::with_dummy_span( - name, - )) - }) - .collect(), - tokens: None, - }, - accessible: true, - doc_visible: true, - via_import: false, - note: None, - is_stable: true, + let mut path = Path { + span: ident.span, + segments: module_path + .into_iter() + .chain(std::iter::once(ident.name)) + .map(|name| { + ast::PathSegment::from_ident(Ident::with_dummy_span(name)) + }) + .collect(), }; - // Reuse the existing relative shortening policy. The fields above - // other than `did` and `path` are not used by this helper. - self.shorten_candidate_path(&mut suggestion, parent_scope.module); - Some(suggestion.path.segments.iter().map(|seg| seg.ident).collect()) - } else { - None - } + self.shorten_import_path(res_def_id, &mut path, parent_scope.module); + path.segments.iter().map(|seg| seg.ident).collect() + }) } else { // Don't include `{{root}}` in suggestions - it's an internal symbol // that should never be shown to users. From db1ab0b18facd9cd231d2a2fbcf625d7817b021d Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Fri, 10 Jul 2026 20:33:54 -0400 Subject: [PATCH 78/78] split only rustfix eligible test --- ...suggestion-path-156244.edition_2015.stderr | 34 ++++--------------- ...suggestion-path-156244.edition_2018.stderr | 34 ++++--------------- .../private-import-suggestion-path-156244.rs | 12 ------- .../private-import-suggestion-relative.fixed | 17 ++++++++++ .../private-import-suggestion-relative.rs | 17 ++++++++++ .../private-import-suggestion-relative.stderr | 25 ++++++++++++++ 6 files changed, 71 insertions(+), 68 deletions(-) create mode 100644 tests/ui/imports/private-import-suggestion-relative.fixed create mode 100644 tests/ui/imports/private-import-suggestion-relative.rs create mode 100644 tests/ui/imports/private-import-suggestion-relative.stderr diff --git a/tests/ui/imports/private-import-suggestion-path-156244.edition_2015.stderr b/tests/ui/imports/private-import-suggestion-path-156244.edition_2015.stderr index 9e51641447f89..2a8795b6ab93c 100644 --- a/tests/ui/imports/private-import-suggestion-path-156244.edition_2015.stderr +++ b/tests/ui/imports/private-import-suggestion-path-156244.edition_2015.stderr @@ -77,52 +77,30 @@ LL + use outer::actual::Item as Item1; | error[E0603]: struct import `Hi` is private - --> $DIR/private-import-suggestion-path-156244.rs:51:14 - | -LL | use testing::Hi; - | ^^ private struct import - | -note: the struct import `Hi` is defined here... - --> $DIR/private-import-suggestion-path-156244.rs:48:9 - | -LL | use super::public::Hi; - | ^^^^^^^^^^^^^^^^^ -note: ...and refers to the struct `Hi` which is defined here - --> $DIR/private-import-suggestion-path-156244.rs:44:5 - | -LL | pub struct Hi; - | ^^^^^^^^^^^^^^ you could import this directly -help: import `Hi` directly - | -LL - use testing::Hi; -LL + use public::Hi; - | - -error[E0603]: struct import `Hi` is private - --> $DIR/private-import-suggestion-path-156244.rs:67:37 + --> $DIR/private-import-suggestion-path-156244.rs:55:37 | LL | use inaccessible_ancestor::testing::Hi; | ^^ private struct import | note: the struct import `Hi` is defined here... - --> $DIR/private-import-suggestion-path-156244.rs:63:13 + --> $DIR/private-import-suggestion-path-156244.rs:51:13 | LL | use super::private::public::Hi; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...and refers to the struct `Hi` which is defined here - --> $DIR/private-import-suggestion-path-156244.rs:58:13 + --> $DIR/private-import-suggestion-path-156244.rs:46:13 | LL | pub struct Hi; | ^^^^^^^^^^^^^^ you could import this directly error[E0603]: module import `mem` is private - --> $DIR/private-import-suggestion-path-156244.rs:77:21 + --> $DIR/private-import-suggestion-path-156244.rs:65:21 | LL | use external_alias::mem; | ^^^ private module import | note: the module import `mem` is defined here... - --> $DIR/private-import-suggestion-path-156244.rs:74:9 + --> $DIR/private-import-suggestion-path-156244.rs:62:9 | LL | use super::s::mem; | ^^^^^^^^^^^^^ @@ -136,6 +114,6 @@ LL - use external_alias::mem; LL + use core::mem; | -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0603`. diff --git a/tests/ui/imports/private-import-suggestion-path-156244.edition_2018.stderr b/tests/ui/imports/private-import-suggestion-path-156244.edition_2018.stderr index c1f059a09d4d9..9f112fe4e7551 100644 --- a/tests/ui/imports/private-import-suggestion-path-156244.edition_2018.stderr +++ b/tests/ui/imports/private-import-suggestion-path-156244.edition_2018.stderr @@ -77,52 +77,30 @@ LL + use crate::outer::actual::Item as Item1; | error[E0603]: struct import `Hi` is private - --> $DIR/private-import-suggestion-path-156244.rs:51:14 - | -LL | use testing::Hi; - | ^^ private struct import - | -note: the struct import `Hi` is defined here... - --> $DIR/private-import-suggestion-path-156244.rs:48:9 - | -LL | use super::public::Hi; - | ^^^^^^^^^^^^^^^^^ -note: ...and refers to the struct `Hi` which is defined here - --> $DIR/private-import-suggestion-path-156244.rs:44:5 - | -LL | pub struct Hi; - | ^^^^^^^^^^^^^^ you could import this directly -help: import `Hi` directly - | -LL - use testing::Hi; -LL + use public::Hi; - | - -error[E0603]: struct import `Hi` is private - --> $DIR/private-import-suggestion-path-156244.rs:67:37 + --> $DIR/private-import-suggestion-path-156244.rs:55:37 | LL | use inaccessible_ancestor::testing::Hi; | ^^ private struct import | note: the struct import `Hi` is defined here... - --> $DIR/private-import-suggestion-path-156244.rs:63:13 + --> $DIR/private-import-suggestion-path-156244.rs:51:13 | LL | use super::private::public::Hi; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...and refers to the struct `Hi` which is defined here - --> $DIR/private-import-suggestion-path-156244.rs:58:13 + --> $DIR/private-import-suggestion-path-156244.rs:46:13 | LL | pub struct Hi; | ^^^^^^^^^^^^^^ you could import this directly error[E0603]: module import `mem` is private - --> $DIR/private-import-suggestion-path-156244.rs:77:21 + --> $DIR/private-import-suggestion-path-156244.rs:65:21 | LL | use external_alias::mem; | ^^^ private module import | note: the module import `mem` is defined here... - --> $DIR/private-import-suggestion-path-156244.rs:74:9 + --> $DIR/private-import-suggestion-path-156244.rs:62:9 | LL | use super::s::mem; | ^^^^^^^^^^^^^ @@ -136,6 +114,6 @@ LL - use external_alias::mem; LL + use core::mem; | -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0603`. diff --git a/tests/ui/imports/private-import-suggestion-path-156244.rs b/tests/ui/imports/private-import-suggestion-path-156244.rs index 8732564920720..71e01470060bf 100644 --- a/tests/ui/imports/private-import-suggestion-path-156244.rs +++ b/tests/ui/imports/private-import-suggestion-path-156244.rs @@ -39,18 +39,6 @@ mod bad { //~^ ERROR module import `inner` is private [E0603] } -// Regression test for https://github.com/rust-lang/rust/issues/157455: no root `super`. -mod public { - pub struct Hi; -} - -mod testing { - use super::public::Hi; -} - -use testing::Hi; -//~^ ERROR struct import `Hi` is private [E0603] - // Regression test for https://github.com/rust-lang/rust/issues/157455: no private ancestors. mod inaccessible_ancestor { mod private { diff --git a/tests/ui/imports/private-import-suggestion-relative.fixed b/tests/ui/imports/private-import-suggestion-relative.fixed new file mode 100644 index 0000000000000..52579f0a08eb7 --- /dev/null +++ b/tests/ui/imports/private-import-suggestion-relative.fixed @@ -0,0 +1,17 @@ +//@ run-rustfix + +#![allow(unused)] + +// Regression test for https://github.com/rust-lang/rust/issues/157455. +mod public { + pub struct Hi; +} + +mod testing { + use super::public::Hi; +} + +use public::Hi; +//~^ ERROR struct import `Hi` is private [E0603] + +fn main() {} diff --git a/tests/ui/imports/private-import-suggestion-relative.rs b/tests/ui/imports/private-import-suggestion-relative.rs new file mode 100644 index 0000000000000..9a012e8252879 --- /dev/null +++ b/tests/ui/imports/private-import-suggestion-relative.rs @@ -0,0 +1,17 @@ +//@ run-rustfix + +#![allow(unused)] + +// Regression test for https://github.com/rust-lang/rust/issues/157455. +mod public { + pub struct Hi; +} + +mod testing { + use super::public::Hi; +} + +use testing::Hi; +//~^ ERROR struct import `Hi` is private [E0603] + +fn main() {} diff --git a/tests/ui/imports/private-import-suggestion-relative.stderr b/tests/ui/imports/private-import-suggestion-relative.stderr new file mode 100644 index 0000000000000..033351e27c3c5 --- /dev/null +++ b/tests/ui/imports/private-import-suggestion-relative.stderr @@ -0,0 +1,25 @@ +error[E0603]: struct import `Hi` is private + --> $DIR/private-import-suggestion-relative.rs:14:14 + | +LL | use testing::Hi; + | ^^ private struct import + | +note: the struct import `Hi` is defined here... + --> $DIR/private-import-suggestion-relative.rs:11:9 + | +LL | use super::public::Hi; + | ^^^^^^^^^^^^^^^^^ +note: ...and refers to the struct `Hi` which is defined here + --> $DIR/private-import-suggestion-relative.rs:7:5 + | +LL | pub struct Hi; + | ^^^^^^^^^^^^^^ you could import this directly +help: import `Hi` directly + | +LL - use testing::Hi; +LL + use public::Hi; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0603`.