From d4ee30d95721a0dfa68c47bf9983a1f702238887 Mon Sep 17 00:00:00 2001 From: Lander Brandt Date: Fri, 17 Apr 2026 20:03:06 -0700 Subject: [PATCH 001/289] perf: optimize allocation strategies of output/parser/event --- .../rust-analyzer/crates/parser/src/event.rs | 46 +++++++++++-------- .../rust-analyzer/crates/parser/src/lib.rs | 12 ++--- .../rust-analyzer/crates/parser/src/output.rs | 8 ++++ .../rust-analyzer/crates/parser/src/parser.rs | 36 +++++++++++---- 4 files changed, 67 insertions(+), 35 deletions(-) diff --git a/src/tools/rust-analyzer/crates/parser/src/event.rs b/src/tools/rust-analyzer/crates/parser/src/event.rs index 5be9cb2a24699..9177f4210b5dc 100644 --- a/src/tools/rust-analyzer/crates/parser/src/event.rs +++ b/src/tools/rust-analyzer/crates/parser/src/event.rs @@ -2,7 +2,7 @@ //! It is intended to be completely decoupled from the //! parser, so as to allow to evolve the tree representation //! and the parser algorithm independently. -use std::mem; +use std::{mem, num::NonZeroU32}; use crate::{ SyntaxKind::{self, *}, @@ -12,6 +12,12 @@ use crate::{ /// `Parser` produces a flat list of `Event`s. /// They are converted to a tree-structure in /// a separate pass, via `TreeBuilder`. +/// +/// Kept to 8 bytes: error messages live in a side table on the `Parser` +/// (the `errors` vec) and `Event::Error` only stores an index into it. +/// `forward_parent` uses `NonZeroU32` so `Option` is niche-optimised away +/// (the offset is always ≥ 1 because the forward parent sits later in the +/// event stream). #[derive(Debug, PartialEq)] pub(crate) enum Event { /// This event signifies the start of the node. @@ -53,10 +59,7 @@ pub(crate) enum Event { /// ``` /// /// See also `CompletedMarker::precede`. - Start { - kind: SyntaxKind, - forward_parent: Option, - }, + Start { kind: SyntaxKind, forward_parent: Option }, /// Complete the previous `Start` event Finish, @@ -65,20 +68,14 @@ pub(crate) enum Event { /// `n_raw_tokens` is used to glue complex contextual tokens. /// For example, lexer tokenizes `>>` as `>`, `>`, and /// `n_raw_tokens = 2` is used to produced a single `>>`. - Token { - kind: SyntaxKind, - n_raw_tokens: u8, - }, + Token { kind: SyntaxKind, n_raw_tokens: u8 }, /// When we parse `foo.0.0` or `foo. 0. 0` the lexer will hand us a float literal /// instead of an integer literal followed by a dot as the lexer has no contextual knowledge. /// This event instructs whatever consumes the events to split the float literal into /// the corresponding parts. - FloatSplitHack { - ends_in_dot: bool, - }, - Error { - msg: String, - }, + FloatSplitHack { ends_in_dot: bool }, + /// Index into the parser's side `errors` vec. + Error { err: u32 }, } impl Event { @@ -87,9 +84,12 @@ impl Event { } } -/// Generate the syntax tree with the control of events. -pub(super) fn process(mut events: Vec) -> Output { - let mut res = Output::default(); +/// Generate the syntax tree with the control of events. `errors` is the +/// side table of error messages built up alongside the `events` stream. +pub(super) fn process(mut events: Vec, mut errors: Vec) -> Output { + // Each event becomes roughly one u32 in Output, so preallocate to avoid + // the amortized grow-one churn we used to see in Output::enter_node. + let mut res = Output::with_event_capacity(events.len()); let mut forward_parents = Vec::new(); for i in 0..events.len() { @@ -104,7 +104,7 @@ pub(super) fn process(mut events: Vec) -> Output { let mut idx = i; let mut fp = forward_parent; while let Some(fwd) = fp { - idx += fwd as usize; + idx += fwd.get() as usize; // append `A`'s forward_parent `B` fp = match mem::replace(&mut events[idx], Event::tombstone()) { Event::Start { kind, forward_parent } => { @@ -131,7 +131,13 @@ pub(super) fn process(mut events: Vec) -> Output { let ev = mem::replace(&mut events[i + 1], Event::tombstone()); assert!(matches!(ev, Event::Finish), "{ev:?}"); } - Event::Error { msg } => res.error(msg), + Event::Error { err } => { + // Move the string out of the side table; each index is visited + // exactly once, so swapping with an empty String is cheap and + // avoids any clone. + let msg = mem::take(&mut errors[err as usize]); + res.error(msg); + } } } diff --git a/src/tools/rust-analyzer/crates/parser/src/lib.rs b/src/tools/rust-analyzer/crates/parser/src/lib.rs index 4478bf4e37333..3e0e0d4c42bf9 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lib.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lib.rs @@ -104,8 +104,8 @@ impl TopEntryPoint { }; let mut p = parser::Parser::new(input); entry_point(&mut p); - let events = p.finish(); - let res = event::process(events); + let (events, errors) = p.finish(); + let res = event::process(events, errors); if cfg!(debug_assertions) { let mut depth = 0; @@ -169,8 +169,8 @@ impl PrefixEntryPoint { }; let mut p = parser::Parser::new(input); entry_point(&mut p); - let events = p.finish(); - event::process(events) + let (events, errors) = p.finish(); + event::process(events, errors) } } @@ -195,7 +195,7 @@ impl Reparser { let Reparser(r) = self; let mut p = parser::Parser::new(tokens); r(&mut p); - let events = p.finish(); - event::process(events) + let (events, errors) = p.finish(); + event::process(events, errors) } } diff --git a/src/tools/rust-analyzer/crates/parser/src/output.rs b/src/tools/rust-analyzer/crates/parser/src/output.rs index 0ea15a656c1bb..2f09b1121891b 100644 --- a/src/tools/rust-analyzer/crates/parser/src/output.rs +++ b/src/tools/rust-analyzer/crates/parser/src/output.rs @@ -33,6 +33,14 @@ pub enum Step<'a> { } impl Output { + /// Preallocate the event buffer. Each `Event` in the input stream maps to + /// roughly one `u32` in the output, so passing the event count as the + /// initial capacity avoids most of the amortized `Vec::grow` allocations + /// during `event::process`. + pub(crate) fn with_event_capacity(cap: usize) -> Self { + Output { event: Vec::with_capacity(cap), error: Vec::new() } + } + const EVENT_MASK: u32 = 0b1; const TAG_MASK: u32 = 0x0000_00F0; const N_INPUT_TOKEN_MASK: u32 = 0x0000_FF00; diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs index 4557078de9916..57c501eda224e 100644 --- a/src/tools/rust-analyzer/crates/parser/src/parser.rs +++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs @@ -1,6 +1,6 @@ //! See [`Parser`]. -use std::cell::Cell; +use std::{cell::Cell, num::NonZeroU32}; use drop_bomb::DropBomb; @@ -12,6 +12,14 @@ use crate::{ input::Input, }; +/// Build a forward-parent offset. The offset is always ≥ 1 because the +/// forward-parent event is created *after* the event it forwards to, so +/// `NonZeroU32` is always valid here. Panics only on a parser bug. +#[inline] +fn fwd_parent(offset: u32) -> NonZeroU32 { + NonZeroU32::new(offset).expect("forward-parent offset must be non-zero") +} + /// `Parser` struct provides the low-level API for /// navigating through the stream of tokens and /// constructing the parse tree. The actual parsing @@ -25,6 +33,9 @@ pub(crate) struct Parser<'t> { inp: &'t Input, pos: usize, events: Vec, + /// Side table of error messages. `Event::Error { err }` carries an index + /// into this vec, keeping `Event` itself a flat 8-byte enum. + errors: Vec, steps: Cell, } @@ -32,11 +43,17 @@ const PARSER_STEP_LIMIT: usize = if cfg!(debug_assertions) { 150_000 } else { 15 impl<'t> Parser<'t> { pub(super) fn new(inp: &'t Input) -> Parser<'t> { - Parser { inp, pos: 0, events: Vec::with_capacity(2 * inp.len()), steps: Cell::new(0) } + Parser { + inp, + pos: 0, + events: Vec::with_capacity(2 * inp.len()), + errors: Vec::new(), + steps: Cell::new(0), + } } - pub(crate) fn finish(self) -> Vec { - self.events + pub(crate) fn finish(self) -> (Vec, Vec) { + (self.events, self.errors) } /// Returns the kind of the current token. @@ -206,7 +223,7 @@ impl<'t> Parser<'t> { match &mut self.events[idx] { Event::Start { forward_parent, kind } => { *kind = SyntaxKind::FIELD_EXPR; - *forward_parent = Some(new_marker.pos - marker.pos); + *forward_parent = Some(fwd_parent(new_marker.pos - marker.pos)); } _ => unreachable!(), } @@ -237,8 +254,9 @@ impl<'t> Parser<'t> { /// structured errors with spans and notes, like rustc /// does. pub(crate) fn error>(&mut self, message: T) { - let msg = message.into(); - self.push_event(Event::Error { msg }); + let err = self.errors.len() as u32; + self.errors.push(message.into()); + self.push_event(Event::Error { err }); } /// Consume the next token if it is `kind` or emit an error @@ -366,7 +384,7 @@ impl CompletedMarker { let idx = self.start_pos as usize; match &mut p.events[idx] { Event::Start { forward_parent, .. } => { - *forward_parent = Some(new_pos.pos - self.start_pos); + *forward_parent = Some(fwd_parent(new_pos.pos - self.start_pos)); } _ => unreachable!(), } @@ -379,7 +397,7 @@ impl CompletedMarker { let idx = m.pos as usize; match &mut p.events[idx] { Event::Start { forward_parent, .. } => { - *forward_parent = Some(self.start_pos - m.pos); + *forward_parent = Some(fwd_parent(self.start_pos - m.pos)); } _ => unreachable!(), } From 44ad35f445500cd710b28a62385293f34e334976 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 18 Apr 2026 12:54:34 +0800 Subject: [PATCH 002/289] Add matches macro to minicore --- .../crates/test-utils/src/minicore.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 86fb08073253c..8d2538182e2d9 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -50,6 +50,7 @@ //! iterator: option //! iterators: iterator, fn //! manually_drop: drop +//! matches: //! non_null: //! non_zero: //! option: panic @@ -2181,6 +2182,20 @@ macro_rules! column { } // endregion:column +// region:matches +#[macro_export] +#[allow_internal_unstable(non_exhaustive_omitted_patterns_lint, stmt_expr_attributes)] +macro_rules! matches { + ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { + #[allow(non_exhaustive_omitted_patterns)] + match $expression { + $pattern $(if $guard)? => true, + _ => false + } + }; +} +// endregion:matches + pub mod prelude { pub mod v1 { pub use crate::{ From 6fd7c9d515de3ebec58c74c41a10ee7190dee528 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 18 Apr 2026 12:54:59 +0800 Subject: [PATCH 003/289] feat: handle if matches!() for replace_if_let_with_match Example --- ```rust fn foo(x: Result) { $0if matches!(x, Ok(a @ 1..2)) { } } ``` **Before this PR** ```rust fn foo(x: Result) { match matches!(x, Ok(a @ 1..2)) { true => {} false => (), } } ``` **After this PR** ```rust fn foo(x: Result) { match x { Ok(a@1..2) => {} _ => (), } } ``` --- .../src/handlers/replace_if_let_with_match.rs | 134 ++++++++++++++++-- 1 file changed, 124 insertions(+), 10 deletions(-) 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 0badad7d0cbe6..a47e3a4086794 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,3 +1,5 @@ +use hir::db::ExpandDatabase; +use itertools::Itertools; use std::iter::successors; use ide_db::{RootDatabase, defs::NameClass, ty_filter::TryEnum}; @@ -66,8 +68,8 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' } }); let scrutinee_to_be_expr = if_expr.condition()?; - let scrutinee_to_be_expr = match let_and_guard(&scrutinee_to_be_expr) { - (Some(let_expr), _) => let_expr.expr()?, + let scrutinee_to_be_expr = match let_and_guard(&scrutinee_to_be_expr, ctx)? { + (Some((_, expr)), _) => expr, (None, cond) => cond?, }; @@ -75,11 +77,9 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' let mut cond_bodies = Vec::new(); for if_expr in if_exprs { let cond = if_expr.condition()?; - let (cond, guard) = match let_and_guard(&cond) { + let (cond, guard) = match let_and_guard(&cond, ctx)? { (None, guard) => (None, Some(guard?)), - (Some(let_), guard) => { - let pat = let_.pat()?; - let expr = let_.expr()?; + (Some((pat, expr)), guard) => { if scrutinee_to_be_expr.syntax().text() != expr.syntax().text() { // Only if all condition expressions are equal we can merge them into a match return None; @@ -120,6 +120,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' // Dedent from original position, then indent for match arm let body = body.dedent(indent); let body = unwrap_trivial_block(body); + let pat = pretty_pat_inside_macro(pat, &ctx.sema); match (pat, guard.map(|it| make.match_guard(it))) { (Some(pat), guard) => make.match_arm(pat, guard, body), (None, _) if !pat_seen => { @@ -394,13 +395,18 @@ fn is_sad_pat(sema: &hir::Semantics<'_, RootDatabase>, pat: &ast::Pat) -> bool { .is_some_and(|it| does_pat_match_variant(pat, &it.sad_pattern())) } -fn let_and_guard(cond: &ast::Expr) -> (Option, Option) { +fn let_and_guard( + cond: &ast::Expr, + ctx: &AssistContext<'_>, +) -> Option<(Option<(ast::Pat, ast::Expr)>, Option)> { if let ast::Expr::ParenExpr(expr) = cond && let Some(sub_expr) = expr.expr() { - let_and_guard(&sub_expr) + let_and_guard(&sub_expr, ctx)? } else if let ast::Expr::LetExpr(let_expr) = cond { - (Some(let_expr.clone()), None) + (Some((let_expr.pat()?, let_expr.expr()?)), None) + } else if let Some((pat, expr, guard)) = parse_matches_macro(cond, ctx) { + (Some((pat, expr)), guard) } else if let ast::Expr::BinExpr(bin_expr) = cond && let Some(ast::Expr::LetExpr(let_expr)) = and_bin_expr_left(bin_expr).lhs() { @@ -418,10 +424,11 @@ fn let_and_guard(cond: &ast::Expr) -> (Option, Option) } let new_expr = editor.finish().new_root().clone(); - (Some(let_expr), ast::Expr::cast(new_expr)) + (Some((let_expr.pat()?, let_expr.expr()?)), ast::Expr::cast(new_expr)) } else { (None, Some(cond.clone())) } + .into() } fn match_scrutinee_needs_paren(expr: &ast::Expr) -> bool { @@ -445,6 +452,66 @@ fn and_bin_expr_left(expr: &ast::BinExpr) -> ast::BinExpr { } } +fn parse_matches_macro( + expr: &ast::Expr, + ctx: &AssistContext<'_>, +) -> Option<(ast::Pat, ast::Expr, Option)> { + let ast::Expr::MacroExpr(macro_expr) = expr else { return None }; + let macro_call = macro_expr.macro_call()?; + let tt = macro_call.token_tree()?; + let r_delim = syntax::NodeOrToken::Token(tt.right_delimiter_token()?); + + if macro_call.path()?.segment()?.name_ref()?.text() != "matches" { + return None; + } + + let parse = |src: String| syntax::hacks::parse_expr_from_str(&src, ctx.edition()); + let input = tt.syntax().children_with_tokens().skip(1).take_while(|it| *it != r_delim); + // Only supports single top-level comma case + let [comma] = input.clone().filter(|it| it.kind() == T![,]).collect_array()?; + let input_rest = input.clone().skip_while(|it| *it != comma).skip(1); + let if_kwd = input_rest.clone().find(|it| it.kind() == T![if]); + let input_expr = input.clone().take_while(|it| *it != comma).join(""); + let input_pat = input_rest.clone().take_while(|it| Some(it) != if_kwd.as_ref()); + let input_guard = + if_kwd.as_ref().map(|if_kwd| { input_rest }.skip_while(|it| it != if_kwd).skip(1).join("")); + + let pat_token = match { input_pat }.find(|it| !it.kind().is_trivia())? { + syntax::NodeOrToken::Node(node) => node.first_token()?, + syntax::NodeOrToken::Token(t) => t, + }; + // XXX: Use descend pat for sema analysis + let descend_pat = ctx.sema.descend_into_macros(pat_token).into_iter().find_map(|token| { + token + .parent_ancestors() + .take_while(|it| !matches!(it.kind(), SyntaxKind::MATCH_ARM | SyntaxKind::ITEM_LIST)) + .filter_map(ast::Pat::cast) + .last() + })?; + + Some((descend_pat, parse(input_expr)?, input_guard.and_then(parse))) +} + +fn pretty_pat_inside_macro( + pat: Option, + sema: &hir::Semantics<'_, RootDatabase>, +) -> Option { + let pretty = |pat| { + let db = sema.db; + let scope = sema.scope(&pat)?; + let file_id = scope.file_id().macro_file()?; + // Don't call `prettify_macro_expansion()` outside the actual assist action; see inline_macro assist + let pretty_node = hir::prettify_macro_expansion( + db, + pat, + &db.expansion_span_map(file_id), + scope.module().krate(db).into(), + ); + ast::Pat::cast(pretty_node) + }; + pat.map(|pat| pretty(pat.syntax().clone()).unwrap_or(pat)) +} + #[cfg(test)] mod tests { use super::*; @@ -1815,6 +1882,53 @@ fn foo(x: Result) { ); } + #[test] + fn test_if_matches_with_match() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result, matches +fn foo(x: Result) { + $0if matches!(x, Ok(a @ 1..2)) { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result) { + match x { + Ok(a@1..2) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result, matches +fn foo(x: Result) { + $0if matches!(x, Ok(ref a)) { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result) { + match x { + Ok(ref a) => (), + Err(_) => (), + } +} +"#, + ); + } + #[test] fn test_replace_match_with_if_let_unwraps_simple_expressions() { check_assist( From 60766ec8451c4048949d7c754806721f190dde09 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 18 Mar 2026 21:40:44 +0100 Subject: [PATCH 004/289] misc improvements --- .../ide-completion/src/context/analysis.rs | 10 ++--- .../crates/ide-completion/src/render.rs | 41 +++++++++++-------- .../ide-db/src/imports/import_assets.rs | 2 +- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 58c0f683a344c..c8068d6fc4da1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -2094,12 +2094,12 @@ fn next_non_trivia_token(e: impl Into) -> Option { } fn next_non_trivia_sibling(ele: SyntaxElement) -> Option { - let mut e = ele.next_sibling_or_token(); - while let Some(inner) = e { - if !inner.kind().is_trivia() { - return Some(inner); + let mut e = ele; + while let Some(next) = e.next_sibling_or_token() { + if !next.kind().is_trivia() { + return Some(next); } else { - e = inner.next_sibling_or_token(); + e = next; } } None diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index a636c0603ba52..7e6e76541e7a4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -828,7 +828,7 @@ mod tests { items.push(format!( "{tag} {} {} {relevance}\n", it.label.primary, - it.label.detail_right.clone().unwrap_or_default(), + it.label.detail_right.as_deref().unwrap_or_default(), )); if let Some((label, _indel, relevance)) = it.ref_match() { @@ -844,24 +844,31 @@ mod tests { expect.assert_eq(&actual); fn display_relevance(relevance: CompletionRelevance) -> String { - let relevance_factors = vec![ - (relevance.type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"), - ( - relevance.type_match == Some(CompletionRelevanceTypeMatch::CouldUnify), - "type_could_unify", - ), - (relevance.exact_name_match, "name"), - (relevance.is_local, "local"), - ( - relevance.postfix_match == Some(CompletionRelevancePostfixMatch::Exact), - "snippet", - ), - (relevance.trait_.is_some_and(|it| it.is_op_method), "op_method"), - (relevance.requires_import, "requires_import"), - (relevance.has_local_inherent_impl, "has_local_inherent_impl"), + let CompletionRelevance { + exact_name_match, + type_match, + is_local, + trait_, + is_name_already_imported: _, + requires_import, + is_private_editable: _, + postfix_match, + function: _, + is_skipping_completion: _, + has_local_inherent_impl, + } = relevance; + let relevance_factors = [ + (type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"), + (type_match == Some(CompletionRelevanceTypeMatch::CouldUnify), "type_could_unify"), + (exact_name_match, "name"), + (is_local, "local"), + (postfix_match == Some(CompletionRelevancePostfixMatch::Exact), "snippet"), + (trait_.is_some_and(|it| it.is_op_method), "op_method"), + (requires_import, "requires_import"), + (has_local_inherent_impl, "has_local_inherent_impl"), ] .into_iter() - .filter_map(|(cond, desc)| if cond { Some(desc) } else { None }) + .filter_map(|(cond, desc)| cond.then_some(desc)) .join("+"); format!("[{relevance_factors}]") diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 9018552afb4d7..4f05714a556a4 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -314,7 +314,7 @@ pub struct LocatedImport { pub item_to_import: ItemInNs, /// The path import candidate, resolved. /// - /// Not necessary matches the import: + /// Not necessarily matches the import: /// For any associated constant from the trait, we try to access as `some::path::SomeStruct::ASSOC_` /// the original item is the associated constant, but the import has to be a trait that /// defines this constant. From 83940843a5510bf90fe63667bdb276c11717248d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 17 Mar 2026 23:32:56 +0100 Subject: [PATCH 005/289] Add test --- .../crates/ide-completion/src/render.rs | 19 +++++++++++++ .../crates/test-utils/src/minicore.rs | 27 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 7e6e76541e7a4..607ca6ff54f47 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -3647,6 +3647,25 @@ fn f() { ); } + #[test] + /// Issue: https://github.com/rust-lang/rust-analyzer/issues/18554 + fn float_consts_relevance() { + check_relevance( + r#" +//- minicore: float_consts +fn main() { + let x = f32::INF$0 +} +"#, + expect![[r#" + ct INFINITY f32 [type_could_unify+requires_import] + ct NEG_INFINITY f32 [type_could_unify+requires_import] + ct INFINITY pub const INFINITY: f32 [] + ct NEG_INFINITY pub const NEG_INFINITY: f32 [] + "#]], + ); + } + #[test] fn completes_struct_with_raw_identifier() { check_edit( diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 86fb08073253c..85bd821ebbd23 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -33,6 +33,7 @@ //! env: option //! eq: sized //! error: fmt +//! float_consts: //! fmt: option, result, transmute, coerce_unsized, copy, clone, derive //! fmt_before_1_93_0: fmt //! fmt_before_1_89_0: fmt_before_1_93_0 @@ -2173,6 +2174,32 @@ pub mod error { } // endregion:error +// region:float_consts +impl f32 { + pub const INFINITY: f32 = 0.0; + pub const NEG_INFINITY: f32 = -0.0; +} + +impl f64 { + pub const INFINITY: f64 = 0.0; + pub const NEG_INFINITY: f64 = -0.0; +} + +pub mod f32 { + #[deprecated] + pub const INFINITY: f32 = 0.0; + #[deprecated] + pub const NEG_INFINITY: f32 = -0.0; +} + +pub mod f64 { + #[deprecated] + pub const INFINITY: f64 = 0.0; + #[deprecated] + pub const NEG_INFINITY: f64 = -0.0; +} +// endregion:float_consts + // region:column #[rustc_builtin_macro] #[macro_export] From 43c77e79cfce2b18248a3089be427304b1335ca3 Mon Sep 17 00:00:00 2001 From: Souradip Pal Date: Mon, 20 Apr 2026 02:40:50 +0530 Subject: [PATCH 006/289] fix: avoid prelude paths when imports.preferPrelude is false When prefer_prelude=false (the default), the import path algorithm now actively deprioritizes paths that pass through a module named `prelude`, rather than ignoring the setting entirely. Two changes in find_path.rs: - Rename Choice::prefer_due_to_prelude to has_prelude_segment and track it unconditionally (not gated on the config). Make try_select compare bidirectionally: prefer prelude paths when prefer_prelude=true, avoid them when false. - Add container_max_len helper that relaxes the search length limit by 1 when the current best is a prelude path and prefer_prelude=false, so non-prelude alternatives one segment longer are not silently cut off. Fixes the case where krate::prelude::Foo (3 segs) was always chosen over krate::module::sub::Foo (4 segs) regardless of the preferPrelude setting. Generated with AI assistance (Claude). --- .../crates/hir-def/src/find_path.rs | 98 +++++++++++++++---- 1 file changed, 78 insertions(+), 20 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 8308203693368..44e4d002def33 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 @@ -177,7 +177,7 @@ fn find_path_for_module( path: ModPath::from_segments(PathKind::Crate, None), path_text_len: 5, stability: Stable, - prefer_due_to_prelude: false, + has_prelude_segment: false, }); } // - otherwise if the item is the crate root of a dependency crate, return the name from the extern prelude @@ -205,7 +205,7 @@ fn find_path_for_module( } else { PathKind::Plain }; - return Some(Choice::new(ctx.cfg.prefer_prelude, kind, name.clone(), Stable)); + return Some(Choice::new(kind, name.clone(), Stable)); } } @@ -223,12 +223,7 @@ fn find_path_for_module( ); if let Some(scope_name) = scope_name { // - if the item is already in scope, return the name under which it is - return Some(Choice::new( - ctx.cfg.prefer_prelude, - ctx.prefix.path_kind(), - scope_name, - Stable, - )); + return Some(Choice::new(ctx.prefix.path_kind(), scope_name, Stable)); } } @@ -240,7 +235,7 @@ fn find_path_for_module( path: ModPath::from_segments(kind, None), path_text_len: path_kind_len(kind), stability: Stable, - prefer_due_to_prelude: false, + has_prelude_segment: false, }); } @@ -302,7 +297,7 @@ fn find_in_prelude( }); if found_and_same_def.unwrap_or(true) { - Some(Choice::new(false, PathKind::Plain, name.clone(), Stable)) + Some(Choice::new(PathKind::Plain, name.clone(), Stable)) } else { None } @@ -419,6 +414,19 @@ fn find_in_sysroot( }); } +/// Computes the maximum path length allowed for a container module when searching for an item. +/// +/// When `prefer_prelude` is false and the current best path goes through a `prelude` module, +/// we relax the limit by 1 so that non-prelude paths one segment longer can still be found +/// and then preferred by `Choice::try_select`. +fn container_max_len(prefer_prelude: bool, max_len: usize, best_choice: &Option) -> usize { + match best_choice { + Some(best) if !prefer_prelude && best.has_prelude_segment => best.path.len(), + Some(best) => best.path.len() - 1, + None => max_len, + } +} + fn find_in_dep( ctx: &FindPathCtx<'_>, visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, @@ -444,7 +452,7 @@ fn find_in_dep( visited_modules, info.container, true, - best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1, + container_max_len(ctx.cfg.prefer_prelude, max_len, best_choice), ); let Some(mut choice) = choice else { continue; @@ -478,7 +486,7 @@ fn calculate_best_path_local( visited_modules, module_id, false, - best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1, + container_max_len(ctx.cfg.prefer_prelude, max_len, best_choice), ) { Choice::try_select(best_choice, choice, ctx.cfg.prefer_prelude, name.clone()); } @@ -492,23 +500,23 @@ struct Choice { path_text_len: usize, /// The stability of the path stability: Stability, - /// Whether this path contains a prelude segment and preference for it has been signaled - prefer_due_to_prelude: bool, + /// Whether any segment of this path is named `prelude` + has_prelude_segment: bool, } impl Choice { - fn new(prefer_prelude: bool, kind: PathKind, name: Name, stability: Stability) -> Self { + fn new(kind: PathKind, name: Name, stability: Stability) -> Self { Self { path_text_len: path_kind_len(kind) + name.as_str().len(), stability, - prefer_due_to_prelude: prefer_prelude && name == sym::prelude, + has_prelude_segment: name == sym::prelude, path: ModPath::from_segments(kind, iter::once(name)), } } - fn push(mut self, prefer_prelude: bool, name: Name) -> Self { + fn push(mut self, name: Name) -> Self { self.path_text_len += name.as_str().len(); - self.prefer_due_to_prelude |= prefer_prelude && name == sym::prelude; + self.has_prelude_segment |= name == sym::prelude; self.path.push_segment(name); self } @@ -520,13 +528,19 @@ impl Choice { name: Name, ) { let Some(current) = current else { - *current = Some(other.push(prefer_prelude, name)); + *current = Some(other.push(name)); return; }; match other .stability .cmp(¤t.stability) - .then_with(|| other.prefer_due_to_prelude.cmp(¤t.prefer_due_to_prelude)) + .then_with(|| { + if prefer_prelude { + other.has_prelude_segment.cmp(¤t.has_prelude_segment) + } else { + current.has_prelude_segment.cmp(&other.has_prelude_segment) + } + }) .then_with(|| (current.path.len()).cmp(&(other.path.len() + 1))) { Ordering::Less => return, @@ -2031,6 +2045,50 @@ pub mod foo { ); } + #[test] + fn avoids_prelude_when_prefer_prelude_false() { + let ra_fixture = r#" +//- /main.rs crate:main deps:krate +$0 +//- /krate.rs crate:krate +pub mod prelude { + pub use crate::module::sub::*; +} +pub mod module { + pub mod sub { + pub struct Foo; + } +} +"#; + // krate::prelude::Foo (3 segs) is shorter than krate::module::sub::Foo (4 segs), + // but prefer_prelude=false should pick the longer canonical path. + check_found_path( + ra_fixture, + "krate::module::sub::Foo", + expect![[r#" + Plain (imports ✔): krate::module::sub::Foo + Plain (imports ✖): krate::module::sub::Foo + ByCrate(imports ✔): krate::module::sub::Foo + ByCrate(imports ✖): krate::module::sub::Foo + BySelf (imports ✔): krate::module::sub::Foo + BySelf (imports ✖): krate::module::sub::Foo + "#]], + ); + // prefer_prelude=true should still pick the shorter prelude path. + check_found_path_prelude( + ra_fixture, + "krate::prelude::Foo", + expect![[r#" + Plain (imports ✔): krate::prelude::Foo + Plain (imports ✖): krate::prelude::Foo + ByCrate(imports ✔): krate::prelude::Foo + ByCrate(imports ✖): krate::prelude::Foo + BySelf (imports ✔): krate::prelude::Foo + BySelf (imports ✖): krate::prelude::Foo + "#]], + ); + } + #[test] fn respects_absolute_setting() { let ra_fixture = r#" From b1ec3982011b6bcdc51bcf38b85e3daf9de86555 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 20 Apr 2026 14:31:48 +0300 Subject: [PATCH 007/289] Create a struct of bools for unstable features, like we did for lang items --- .../crates/hir-def/src/lang_item.rs | 2 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 1 + .../crates/hir-def/src/nameres.rs | 12 ++- .../crates/hir-def/src/nameres/collector.rs | 4 +- .../crates/hir-def/src/unstable_features.rs | 91 +++++++++++++++++++ .../diagnostics/match_check/pat_analysis.rs | 6 +- .../crates/hir-ty/src/dyn_compatibility.rs | 16 ++-- .../rust-analyzer/crates/hir-ty/src/infer.rs | 11 +-- .../crates/hir-ty/src/infer/fallback.rs | 3 +- .../crates/hir-ty/src/method_resolution.rs | 26 +----- .../hir-ty/src/method_resolution/confirm.rs | 5 +- .../hir-ty/src/method_resolution/probe.rs | 12 +-- .../crates/hir-ty/src/specialization.rs | 11 +-- .../rust-analyzer/crates/hir/src/attrs.rs | 9 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 18 ++-- .../crates/hir/src/source_analyzer.rs | 3 +- 16 files changed, 147 insertions(+), 83 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-def/src/unstable_features.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 37d70b1e33a91..5b6ebe2a0b2da 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -40,7 +40,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option, /// Unstable features of Rust enabled with `#![feature(A, B)]`. - unstable_features: FxHashSet, + unstable_features: UnstableFeatures, /// `#[rustc_coherence_is_core]` rustc_coherence_is_core: bool, no_core: bool, @@ -233,7 +234,7 @@ impl DefMapCrateData { fn_proc_macro_mapping: FxHashMap::default(), fn_proc_macro_mapping_back: FxHashMap::default(), registered_tools: PREDEFINED_TOOLS.iter().map(|it| Symbol::intern(it)).collect(), - unstable_features: FxHashSet::default(), + unstable_features: UnstableFeatures::default(), rustc_coherence_is_core: false, no_core: false, no_std: false, @@ -554,8 +555,9 @@ impl DefMap { &self.data.registered_tools } - pub fn is_unstable_feature_enabled(&self, feature: &Symbol) -> bool { - self.data.unstable_features.contains(feature) + #[inline] + pub fn features(&self) -> &UnstableFeatures { + &self.data.unstable_features } pub fn is_rustc_coherence_is_core(&self) -> bool { 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 703f070dbaeac..5ef51dbb2ca53 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 @@ -316,7 +316,7 @@ impl<'db> DefCollector<'db> { _ => None, }, ); - crate_data.unstable_features.extend(features); + features.for_each(|feature| crate_data.unstable_features.enable(feature)); } () if *attr_name == sym::register_tool => { if let Some(ident) = attr.single_ident_value() { @@ -2840,6 +2840,6 @@ foo!(KABOOM); assert_eq!(def_map.recursion_limit(), 4); assert!(def_map.is_no_core()); assert!(def_map.is_no_std()); - assert!(def_map.is_unstable_feature_enabled(&sym::register_tool)); + assert!(def_map.features().is_enabled(&sym::register_tool)); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/unstable_features.rs b/src/tools/rust-analyzer/crates/hir-def/src/unstable_features.rs new file mode 100644 index 0000000000000..3b7f83081f8ea --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-def/src/unstable_features.rs @@ -0,0 +1,91 @@ +//! Handling of unstable features. +//! +//! We define two kinds of handling: we have a map of all unstable features for a crate +//! as `Symbol`s. This is mostly for external consumers. +//! +//! For analysis, we store them as a struct of bools, for fast access. + +use std::fmt; + +use base_db::Crate; +use intern::{Symbol, sym}; +use rustc_hash::FxHashSet; + +use crate::db::DefDatabase; + +impl fmt::Debug for UnstableFeatures { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(&self.all).finish() + } +} + +impl PartialEq for UnstableFeatures { + fn eq(&self, other: &Self) -> bool { + self.all == other.all + } +} + +impl Eq for UnstableFeatures {} + +impl UnstableFeatures { + #[inline] + pub fn is_enabled(&self, feature: &Symbol) -> bool { + self.all.contains(feature) + } + + #[inline] + pub fn iter(&self) -> impl Iterator { + self.all.iter().cloned() + } + + pub(crate) fn shrink_to_fit(&mut self) { + self.all.shrink_to_fit(); + } +} + +#[salsa::tracked] +impl UnstableFeatures { + /// Query unstable features for a crate. + /// + /// This is also available as `DefMap::features()`. Use that if you have a DefMap available. + /// Otherwise, use this, to not draw a dependency to the def map. + #[salsa::tracked(returns(ref))] + pub fn query(db: &dyn DefDatabase, krate: Crate) -> UnstableFeatures { + crate::crate_def_map(db, krate).features().clone() + } +} + +macro_rules! define_unstable_features { + ( $( $feature:ident, )* ) => { + #[derive(Clone, Default)] + pub struct UnstableFeatures { + all: FxHashSet, + + $( pub $feature: bool, )* + } + + impl UnstableFeatures { + pub(crate) fn enable(&mut self, feature: Symbol) { + match () { + $( () if feature == sym::$feature => self.$feature = true, )* + _ => {} + } + + self.all.insert(feature); + } + } + }; +} + +define_unstable_features! { + lang_items, + exhaustive_patterns, + generic_associated_type_extended, + arbitrary_self_types, + arbitrary_self_types_pointers, + supertrait_item_shadowing, + new_range, + never_type_fallback, + specialization, + min_specialization, +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index bc3d9bbec6fa5..5ed7eb7a66e12 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -4,9 +4,8 @@ use std::{cell::LazyCell, fmt}; use hir_def::{ EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId, attrs::AttrFlags, - signatures::VariantFields, + signatures::VariantFields, unstable_features::UnstableFeatures, }; -use intern::sym; use rustc_pattern_analysis::{ IndexVec, PatCx, PrivateUninhabitedField, constructor::{Constructor, ConstructorSet, VariantVisibility}, @@ -82,8 +81,7 @@ pub(crate) struct MatchCheckCtx<'a, 'db> { impl<'a, 'db> MatchCheckCtx<'a, 'db> { pub(crate) fn new(module: ModuleId, infcx: &'a InferCtxt<'db>, env: ParamEnv<'db>) -> Self { let db = infcx.interner.db; - let def_map = module.crate_def_map(db); - let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns); + let exhaustive_patterns = UnstableFeatures::query(db, module.krate(db)).exhaustive_patterns; Self { module, db, exhaustive_patterns, env, infcx } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index ba63343d49351..ee7527446a952 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -6,8 +6,8 @@ use hir_def::{ AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, hir::generics::{GenericParams, LocalTypeOrConstParamId}, - nameres::crate_def_map, signatures::{FunctionSignature, TraitFlags, TraitSignature}, + unstable_features::UnstableFeatures, }; use rustc_hash::FxHashSet; use rustc_type_ir::{ @@ -111,8 +111,9 @@ where // rustc checks for non-lifetime binders here, but we don't support HRTB yet let trait_data = trait_.trait_items(db); + let mut features = None; for (_, assoc_item) in &trait_data.items { - dyn_compatibility_violation_for_assoc_item(db, trait_, *assoc_item, cb)?; + dyn_compatibility_violation_for_assoc_item(db, &mut features, trait_, *assoc_item, cb)?; } ControlFlow::Continue(()) @@ -274,8 +275,9 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable( - db: &dyn HirDatabase, +fn dyn_compatibility_violation_for_assoc_item<'db, F>( + db: &'db dyn HirDatabase, + features: &mut Option<&'db UnstableFeatures>, trait_: TraitId, item: AssocItemId, cb: &mut F, @@ -297,8 +299,10 @@ where }) } AssocItemId::TypeAliasId(it) => { - let def_map = crate_def_map(db, trait_.krate(db)); - if def_map.is_unstable_feature_enabled(&intern::sym::generic_associated_type_extended) { + if features + .get_or_insert_with(|| UnstableFeatures::query(db, trait_.krate(db))) + .generic_associated_type_extended + { ControlFlow::Continue(()) } else { let generic_params = GenericParams::of(db, item.into()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 339ce7933af13..9acd5c0e0fc34 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -44,6 +44,7 @@ use hir_def::{ resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, signatures::{ConstSignature, EnumSignature, FunctionSignature, StaticSignature}, type_ref::{ConstRef, LifetimeRefId, TypeRef, TypeRefId}, + unstable_features::UnstableFeatures, }; use hir_expand::{mod_path::ModPath, name::Name}; use indexmap::IndexSet; @@ -78,7 +79,7 @@ use crate::{ lower::{ ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic, }, - method_resolution::{CandidateId, MethodResolutionUnstableFeatures}, + method_resolution::CandidateId, next_solver::{ AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Region, StoredGenericArgs, StoredTy, StoredTys, Ty, TyKind, Tys, @@ -1112,11 +1113,11 @@ pub(crate) struct InferenceContext<'body, 'db> { /// and resolve the path via its methods. This will ensure proper error reporting. pub(crate) resolver: Resolver<'db>, target_features: OnceCell<(TargetFeatures<'db>, TargetFeatureIsSafeInTarget)>, - pub(crate) unstable_features: MethodResolutionUnstableFeatures, pub(crate) edition: Edition, pub(crate) generic_def: GenericDefId, pub(crate) table: unify::InferenceTable<'db>, pub(crate) lang_items: &'db LangItems, + pub(crate) features: &'db UnstableFeatures, /// The traits in scope, disregarding block modules. This is used for caching purposes. traits_in_scope: FxHashSet, pub(crate) result: InferenceResult, @@ -1218,10 +1219,8 @@ impl<'body, 'db> InferenceContext<'body, 'db> { return_ty: types.types.error, // set in collect_* calls types, target_features: OnceCell::new(), - unstable_features: MethodResolutionUnstableFeatures::from_def_map( - resolver.top_level_def_map(), - ), lang_items: table.interner().lang_items(), + features: resolver.top_level_def_map().features(), edition: resolver.krate().data(db).edition, table, tuple_field_accesses_rev: Default::default(), @@ -2317,7 +2316,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } fn has_new_range_feature(&self) -> bool { - self.resolver.top_level_def_map().is_unstable_feature_enabled(&sym::new_range) + self.features.new_range } fn resolve_range(&self) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs index c7669b346fe8d..160749591d89d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs @@ -1,6 +1,5 @@ //! Fallback of infer vars to `!` and `i32`/`f64`. -use intern::sym; use petgraph::{ Graph, visit::{Dfs, Walker}, @@ -80,7 +79,7 @@ impl<'db> InferenceContext<'_, 'db> { return DivergingFallbackBehavior::ToNever; } - if self.resolver.def_map().is_unstable_feature_enabled(&sym::never_type_fallback) { + if self.features.never_type_fallback { return DivergingFallbackBehavior::ContextDependent; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 68c4833d81b01..25c2df0d9f7d9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -23,8 +23,9 @@ use hir_def::{ nameres::{DefMap, block_def_map, crate_def_map}, resolver::Resolver, signatures::{ConstSignature, FunctionSignature}, + unstable_features::UnstableFeatures, }; -use intern::{Symbol, sym}; +use intern::Symbol; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ TypeVisitableExt, @@ -57,32 +58,13 @@ pub use self::probe::{ Candidate, CandidateKind, CandidateStep, CandidateWithPrivate, Mode, Pick, PickKind, }; -#[derive(Debug, Clone)] -pub struct MethodResolutionUnstableFeatures { - arbitrary_self_types: bool, - arbitrary_self_types_pointers: bool, - supertrait_item_shadowing: bool, -} - -impl MethodResolutionUnstableFeatures { - pub fn from_def_map(def_map: &DefMap) -> Self { - Self { - arbitrary_self_types: def_map.is_unstable_feature_enabled(&sym::arbitrary_self_types), - arbitrary_self_types_pointers: def_map - .is_unstable_feature_enabled(&sym::arbitrary_self_types_pointers), - supertrait_item_shadowing: def_map - .is_unstable_feature_enabled(&sym::supertrait_item_shadowing), - } - } -} - pub struct MethodResolutionContext<'a, 'db> { pub infcx: &'a InferCtxt<'db>, pub resolver: &'a Resolver<'db>, pub param_env: ParamEnv<'db>, pub traits_in_scope: &'a FxHashSet, pub edition: Edition, - pub unstable_features: &'a MethodResolutionUnstableFeatures, + pub features: &'a UnstableFeatures, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)] @@ -201,7 +183,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { param_env: self.table.param_env, traits_in_scope, edition: self.edition, - unstable_features: &self.unstable_features, + features: self.features, }; f(&ctx) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs index 94c70c29f74bc..3bdef44dd3664 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs @@ -320,8 +320,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { // We don't need to gate this behind arbitrary self types // per se, but it does make things a bit more gated. - if self.ctx.unstable_features.arbitrary_self_types - || self.ctx.unstable_features.arbitrary_self_types_pointers + if self.ctx.features.arbitrary_self_types || self.ctx.features.arbitrary_self_types_pointers { autoderef = autoderef.use_receiver_trait(); } @@ -483,7 +482,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { self.ctx.table.register_infer_ok(infer_ok); } Err(_) => { - if self.ctx.unstable_features.arbitrary_self_types { + if self.ctx.features.arbitrary_self_types { self.ctx.result.type_mismatches.get_or_insert_default().insert( self.expr.into(), TypeMismatch { expected: method_self_ty.store(), actual: self_ty.store() }, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index 3604076ccdc77..afdc62183e7ad 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs @@ -315,7 +315,7 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { // ambiguous. if let Some(bad_ty) = &steps.opt_bad_ty { if bad_ty.reached_raw_pointer - && !self.unstable_features.arbitrary_self_types_pointers + && !self.features.arbitrary_self_types_pointers && self.edition.at_least_2018() { // this case used to be allowed by the compiler, @@ -404,8 +404,8 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { Autoderef::new(infcx, self.param_env, self_ty).include_raw_pointers(); let mut reached_raw_pointer = false; - let arbitrary_self_types_enabled = self.unstable_features.arbitrary_self_types - || self.unstable_features.arbitrary_self_types_pointers; + let arbitrary_self_types_enabled = + self.features.arbitrary_self_types || self.features.arbitrary_self_types_pointers; let (mut steps, reached_recursion_limit) = if arbitrary_self_types_enabled { let reachable_via_deref = autoderef_via_deref.by_ref().map(|_| true).chain(std::iter::repeat(false)); @@ -613,7 +613,7 @@ impl<'db> ProbeChoice<'db> for ProbeForNameChoice<'db> { // We collapse to a subtrait pick *after* filtering unstable candidates // to make sure we don't prefer a unstable subtrait method over a stable // supertrait method. - if this.ctx.unstable_features.supertrait_item_shadowing + if this.ctx.features.supertrait_item_shadowing && let Some(pick) = this.collapse_candidates_to_subtrait_pick(self_ty, &applicable_candidates) { @@ -1183,8 +1183,8 @@ impl<'a, 'db> ProbeContext<'a, 'db, ProbeForNameChoice<'db>> { // The errors emitted by this function are part of // the arbitrary self types work, and should not impact // other users. - if !self.ctx.unstable_features.arbitrary_self_types - && !self.ctx.unstable_features.arbitrary_self_types_pointers + if !self.ctx.features.arbitrary_self_types + && !self.ctx.features.arbitrary_self_types_pointers { return Ok(()); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs index 8bc6c51fae6f0..a0bac482500d5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs @@ -1,10 +1,9 @@ //! Impl specialization related things use hir_def::{ - ExpressionStoreOwnerId, GenericDefId, HasModule, ImplId, nameres::crate_def_map, - signatures::ImplSignature, + ExpressionStoreOwnerId, GenericDefId, HasModule, ImplId, signatures::ImplSignature, + unstable_features::UnstableFeatures, }; -use intern::sym; use tracing::debug; use crate::{ @@ -153,10 +152,8 @@ pub(crate) fn specializes( // `#[allow_internal_unstable(specialization)]`, but `#[allow_internal_unstable]` // is an internal feature, std is not using it for specialization nor is likely to // ever use it, and we don't have the span information necessary to replicate that. - let def_map = crate_def_map(db, module.krate(db)); - if !def_map.is_unstable_feature_enabled(&sym::specialization) - && !def_map.is_unstable_feature_enabled(&sym::min_specialization) - { + let features = UnstableFeatures::query(db, module.krate(db)); + if !features.specialization && !features.min_specialization { return false; } diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 223103b6e5d26..9943b718fd38b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -18,9 +18,7 @@ use hir_expand::{ }; use hir_ty::{ db::HirDatabase, - method_resolution::{ - self, CandidateId, MethodError, MethodResolutionContext, MethodResolutionUnstableFeatures, - }, + method_resolution::{self, CandidateId, MethodError, MethodResolutionContext}, next_solver::{DbInterner, TypingMode, infer::DbInternerInferExt}, }; use intern::Symbol; @@ -498,15 +496,14 @@ fn resolve_impl_trait_item<'db>( // FIXME: resolve type aliases (which are not yielded by iterate_path_candidates) let interner = DbInterner::new_with(db, environment.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let unstable_features = - MethodResolutionUnstableFeatures::from_def_map(resolver.top_level_def_map()); + let features = resolver.top_level_def_map().features(); let ctx = MethodResolutionContext { infcx: &infcx, resolver: &resolver, param_env: environment.param_env, traits_in_scope: &traits_in_scope, edition: krate.edition(db), - unstable_features: &unstable_features, + features, }; let resolution = ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty.ty); let resolution = match resolution { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index d24e2c0cb5837..92ab82cf37d4a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -75,6 +75,7 @@ use hir_def::{ TypeAliasSignature, UnionSignature, VariantFields, }, src::HasSource as _, + unstable_features::UnstableFeatures, visibility::visibility_from_ast, }; use hir_expand::{ @@ -89,9 +90,7 @@ use hir_ty::{ diagnostics::BodyValidationDiagnostic, direct_super_traits, known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, - method_resolution::{ - self, InherentImpls, MethodResolutionContext, MethodResolutionUnstableFeatures, - }, + method_resolution::{self, InherentImpls, MethodResolutionContext}, mir::interpret_mir, next_solver::{ AliasTy, AnyImplId, ClauseKind, ConstKind, DbInterner, EarlyBinder, EarlyParamRegion, @@ -343,7 +342,7 @@ impl Crate { } pub fn is_unstable_feature_enabled(self, db: &dyn HirDatabase, feature: &Symbol) -> bool { - crate_def_map(db, self.id).is_unstable_feature_enabled(feature) + UnstableFeatures::query(db, self.id).is_enabled(feature) } } @@ -984,10 +983,8 @@ impl Module { // relationship here would be significantly more expensive. if !missing.is_empty() { let krate = self.krate(db).id; - let def_map = crate_def_map(db, krate); - if def_map.is_unstable_feature_enabled(&sym::specialization) - || def_map.is_unstable_feature_enabled(&sym::min_specialization) - { + let features = UnstableFeatures::query(db, krate); + if features.specialization || features.min_specialization { missing.retain(|(assoc_name, assoc_item)| { let AssocItem::Function(_) = assoc_item else { return true; @@ -6178,8 +6175,7 @@ impl<'db> Type<'db> { TypingMode::non_body_analysis() }; let infcx = interner.infer_ctxt().build(typing_mode); - let unstable_features = - MethodResolutionUnstableFeatures::from_def_map(resolver.top_level_def_map()); + let features = resolver.top_level_def_map().features(); let environment = param_env_from_resolver(db, resolver); let ctx = MethodResolutionContext { infcx: &infcx, @@ -6187,7 +6183,7 @@ impl<'db> Type<'db> { param_env: environment.param_env, traits_in_scope, edition: resolver.krate().data(db).edition, - unstable_features: &unstable_features, + features, }; f(&ctx) } 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 6c43f80ce8789..bbe1e670dee2b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -626,8 +626,7 @@ impl<'db> SourceAnalyzer<'db> { has_start: bool, has_end: bool, ) -> Option { - let has_new_range = - self.resolver.top_level_def_map().is_unstable_feature_enabled(&sym::new_range); + let has_new_range = self.resolver.top_level_def_map().features().new_range; let lang_items = self.lang_items(db); match (op_kind, has_start, has_end) { (RangeOp::Exclusive, false, false) => lang_items.RangeFull, From dc9195f23196986cfa6980364b0e49a4d7ee87cf Mon Sep 17 00:00:00 2001 From: Amit5601 Date: Mon, 20 Apr 2026 13:03:04 +0530 Subject: [PATCH 008/289] fix: resolve comparison operators to explicit trait impls (rust-lang/rust-analyzer#13332) --- .../crates/ide/src/goto_definition.rs | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) 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 4cdf0eac7568f..4890badcbf5b5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -95,6 +95,11 @@ pub(crate) fn goto_definition( continue; } + if let Some(n) = find_definition_for_comparison_operators(sema, &token.value) { + navs.extend(n); + continue; + } + let parent = token.value.parent()?; if let Some(question_mark_conversion) = goto_question_mark_conversions(sema, &parent) { @@ -264,6 +269,62 @@ fn find_definition_for_known_blanket_dual_impls( Some(def_to_nav(sema, def)) } +// If the token is a comparison operator (!=, <, <=, >, >=) that resolves to a default trait method, navigate to the corresponding primary method (eq for ne, partial_cmp for the others). +fn find_definition_for_comparison_operators( + sema: &Semantics<'_, RootDatabase>, + original_token: &SyntaxToken, +) -> Option> { + let bin_expr = ast::BinExpr::cast(original_token.parent()?)?; + + let f = sema.resolve_bin_expr(&bin_expr)?; + let assoc = f.as_assoc_item(sema.db)?; + + let lhs_type = sema.type_of_expr(&bin_expr.lhs()?)?.original; + let rhs_type = sema.type_of_expr(&bin_expr.rhs()?)?.original; + + let t = match assoc.container(sema.db) { + hir::AssocItemContainer::Trait(t) => t, + hir::AssocItemContainer::Impl(_) => return None, // Already implemented by the type + }; + + let fn_name = f.name(sema.db); + let fn_name_str = fn_name.as_str(); + + let trait_name = t.name(sema.db); + let trait_name_str = trait_name.as_str(); + + let (target_fn_name, expected_trait) = match fn_name_str { + "ne" => ("eq", "PartialEq"), + "lt" | "le" | "gt" | "ge" => ("partial_cmp", "PartialOrd"), + _ => return None, + }; + + if trait_name_str != expected_trait { + return None; + } + + let primary_f = t.items(sema.db).into_iter().find_map(|item| { + if let hir::AssocItem::Function(func) = item + && func.name(sema.db).as_str() == target_fn_name + { + return Some(func); + } + None + })?; + + // Chalk requires ALL trait substitutions, including `Self`! + // We must pass [Self, Rhs] + let resolved_f = sema.resolve_trait_impl_method( + lhs_type.clone(), + t, + primary_f, + [lhs_type.clone(), rhs_type.clone()], + )?; + + let def = Definition::from(resolved_f); + + Some(def_to_nav(sema, def)) +} fn try_lookup_include_path( sema: &Semantics<'_, RootDatabase>, token: InFile, @@ -4099,4 +4160,24 @@ fn foo() -> Result<(), Bar> { "#, ); } + + #[test] + fn goto_definition_for_comparison_operators() { + check( + r#" +//- minicore: eq, ord +struct Foo; +impl PartialEq for Foo { + fn eq(&self, other: &Self) -> bool { true } + //^^ +} + +fn main() { + let a = Foo; + let b = Foo; + let _ = a !=$0 b; +} +"#, + ); + } } From 58bf1b492dc23bf15838e20f4dcfa579b578ae97 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 15 Apr 2026 15:09:54 +0200 Subject: [PATCH 009/289] Duplicate `CompletionItem.deprecated` as `CompletionRelevance.is_deprecated` This also adds `deprecated` reconciliation logic to `Builder` --- .../crates/ide-completion/src/item.rs | 41 ++++- .../crates/ide-completion/src/render.rs | 150 +++++++++++++++++- 2 files changed, 187 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 62211a808ca6f..da0bdcff0131d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -61,6 +61,9 @@ pub struct CompletionItem { pub documentation: Option>, /// Whether this item is marked as deprecated + /// + /// NOTE: this field is used in the LSP protocol. For the use of this information in completion + /// scoring, see [`CompletionRelevance::is_deprecated`]. pub deprecated: bool, /// If completing a function call, ask the editor to show parameter popup @@ -186,6 +189,11 @@ pub struct CompletionRelevance { pub is_skipping_completion: bool, /// if inherent impl already exists in current module, user may not want to implement it again. pub has_local_inherent_impl: bool, + /// Set when the completion item is deprecated. + /// + /// NOTE: This is duplicated from [`CompletionItem::deprecated`] in order to allow using this + /// information in the calculation of the relevance score. + pub is_deprecated: bool, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct CompletionRelevanceTraitInfo { @@ -278,6 +286,7 @@ impl CompletionRelevance { function, is_skipping_completion, has_local_inherent_impl, + is_deprecated: _, } = self; // only applicable for completions within use items @@ -582,6 +591,9 @@ impl Builder { None => TextEdit::replace(self.source_range, insert_text), }; + // Copy `deprecated` to `self.relevance.is_deprecated` + let relevance = CompletionRelevance { is_deprecated: self.deprecated, ..self.relevance }; + let import_to_add = self .imports_to_add .into_iter() @@ -603,7 +615,7 @@ impl Builder { kind: self.kind, deprecated: self.deprecated, trigger_call_info: self.trigger_call_info, - relevance: self.relevance, + relevance, ref_match: self.ref_match, import_to_add, } @@ -674,6 +686,15 @@ impl Builder { self } pub(crate) fn set_relevance(&mut self, relevance: CompletionRelevance) -> &mut Builder { + // The default value of `CompletionRelevance.is_deprecated` is `false`, so it being `true` + // would mean it was set manually. Advise using the other function instead. + // + // This is technically not necessary, because `deprecated` will get reconciled in + // `Builder::build` anyway -- it just helps keep the callers consistent. + assert!( + !relevance.is_deprecated, + "`deprecated` should be set using `Builder::set_deprecated` instead" + ); self.relevance = relevance; self } @@ -708,9 +729,25 @@ mod tests { use test_utils::assert_eq_text; use super::{ - CompletionRelevance, CompletionRelevancePostfixMatch, CompletionRelevanceTypeMatch, + CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch, + CompletionRelevanceTypeMatch, }; + #[test] + fn builder_deprecated_from_set_deprecated() { + // setting just `item.deprecated` also sets `item.relevance.is_deprecated` + let mut builder = CompletionItem::new( + CompletionItemKind::Expression, + Default::default(), + "", + syntax::Edition::DEFAULT, + ); + builder.set_deprecated(true); + let item = builder.build(&Default::default()); + assert!(item.deprecated); + assert!(item.relevance.is_deprecated); + } + /// Check that these are CompletionRelevance are sorted in ascending order /// by their relevance score. /// diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 607ca6ff54f47..9eaa63040a301 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -856,6 +856,7 @@ mod tests { function: _, is_skipping_completion: _, has_local_inherent_impl, + is_deprecated, } = relevance; let relevance_factors = [ (type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"), @@ -866,6 +867,7 @@ mod tests { (trait_.is_some_and(|it| it.is_op_method), "op_method"), (requires_import, "requires_import"), (has_local_inherent_impl, "has_local_inherent_impl"), + (is_deprecated, "deprecated"), ] .into_iter() .filter_map(|(cond, desc)| cond.then_some(desc)) @@ -1249,6 +1251,7 @@ fn main() { Foo::Fo$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, trigger_call_info: true, }, @@ -1300,6 +1303,7 @@ fn main() { Foo::Fo$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, trigger_call_info: true, }, @@ -1444,6 +1448,7 @@ fn main() { Foo::Fo$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, trigger_call_info: true, }, @@ -1528,6 +1533,7 @@ fn main() { let _: m::Spam = S$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, trigger_call_info: true, }, @@ -1565,6 +1571,7 @@ fn main() { let _: m::Spam = S$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, trigger_call_info: true, }, @@ -1596,6 +1603,20 @@ fn main() { som$0 } Module, ), deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1641,6 +1662,20 @@ fn main() { som$0 } lookup: "something_deprecated", detail: "fn()", deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1670,6 +1705,20 @@ fn main() { A$0 } ), detail: "A", deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1699,6 +1748,20 @@ fn main() { A$0 } ), detail: "A", deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1748,6 +1811,7 @@ fn main() { A::$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, trigger_call_info: true, }, @@ -1783,6 +1847,7 @@ fn main() { A::$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: true, }, trigger_call_info: true, }, @@ -1814,6 +1879,20 @@ fn main() { A$0 } ), detail: "i32", deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1843,6 +1922,20 @@ fn main() { A$0 } ), detail: "i32", deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1869,6 +1962,20 @@ impl A$0 Trait, ), deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1895,6 +2002,20 @@ fn main() { A$0 } TypeAlias, ), deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1925,6 +2046,20 @@ fn main() { a$0 } lookup: "a!", detail: "macro_rules! a", deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1967,6 +2102,7 @@ fn main() { A { the$0 } } function: None, is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: true, }, }, ] @@ -2027,6 +2163,7 @@ impl S { ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, }, CompletionItem { @@ -2119,6 +2256,7 @@ use self::E::*; ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, trigger_call_info: true, }, @@ -2190,6 +2328,7 @@ fn foo(s: S) { s.$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, }, ] @@ -2403,6 +2542,7 @@ fn f() -> i32 { function: None, is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, }, ] @@ -2509,6 +2649,7 @@ fn main() { function: None, is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, ref_match: "&@65", }, @@ -3306,6 +3447,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, ref_match: "&@107", }, @@ -3394,6 +3536,7 @@ fn foo() { function: None, is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, }, ] @@ -3453,6 +3596,7 @@ fn main() { ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, ref_match: "&@92", }, @@ -3658,8 +3802,8 @@ fn main() { } "#, expect![[r#" - ct INFINITY f32 [type_could_unify+requires_import] - ct NEG_INFINITY f32 [type_could_unify+requires_import] + ct INFINITY f32 [type_could_unify+requires_import+deprecated] + ct NEG_INFINITY f32 [type_could_unify+requires_import+deprecated] ct INFINITY pub const INFINITY: f32 [] ct NEG_INFINITY pub const NEG_INFINITY: f32 [] "#]], @@ -3943,6 +4087,7 @@ fn main() { function: None, is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, }, CompletionItem { @@ -3978,6 +4123,7 @@ fn main() { function: None, is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, }, ] From 9fdf6e5c23e748c332522bcf1540dfa01f0b356c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Apr 2026 12:05:11 +0200 Subject: [PATCH 010/289] Reduce completion score for deprecated items --- src/tools/rust-analyzer/crates/ide-completion/src/item.rs | 7 ++++++- .../rust-analyzer/crates/ide-completion/src/render.rs | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index da0bdcff0131d..a138273e7faea 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -286,7 +286,7 @@ impl CompletionRelevance { function, is_skipping_completion, has_local_inherent_impl, - is_deprecated: _, + is_deprecated, } = self; // only applicable for completions within use items @@ -363,6 +363,11 @@ impl CompletionRelevance { score -= 5; } + // lower rank for deprecated items + if is_deprecated { + score -= 5; + } + score } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 9eaa63040a301..608dec1285dc4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -3802,10 +3802,10 @@ fn main() { } "#, expect![[r#" - ct INFINITY f32 [type_could_unify+requires_import+deprecated] - ct NEG_INFINITY f32 [type_could_unify+requires_import+deprecated] ct INFINITY pub const INFINITY: f32 [] ct NEG_INFINITY pub const NEG_INFINITY: f32 [] + ct INFINITY f32 [type_could_unify+requires_import+deprecated] + ct NEG_INFINITY f32 [type_could_unify+requires_import+deprecated] "#]], ); } From 4801302874e465266a27f9f4a04d4f9be4a5c5d4 Mon Sep 17 00:00:00 2001 From: Kao-Wei Yeh Date: Tue, 21 Apr 2026 00:14:42 +0800 Subject: [PATCH 011/289] Remove outdate `FIXME` --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index d24e2c0cb5837..ebcc1da43dc9e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -881,7 +881,6 @@ impl Module { } let drop_maybe_dangle = (|| { - // FIXME: This can be simplified a lot by exposing hir-ty's utils.rs::Generics helper let trait_ = trait_?; let drop_trait = interner.lang_items().Drop?; if drop_trait != trait_.into() { From 1f63cbd431b8b6bc41e55e73ea7734517fd0c7d8 Mon Sep 17 00:00:00 2001 From: AB Date: Mon, 20 Apr 2026 15:25:06 -0400 Subject: [PATCH 012/289] fix: mark enum variants as deprecated when their parent enum is deprecated --- .../crates/ide-completion/src/render.rs | 10 ++++++ .../ide-completion/src/render/literal.rs | 4 +-- .../ide-completion/src/tests/expression.rs | 35 +++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 608dec1285dc4..433d49820d056 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -120,6 +120,15 @@ impl<'a> RenderContext<'a> { }) } + /// Whether an enum variant should be rendered as deprecated. + /// + /// A variant inherits deprecation from its parent enum, matching rustc's + /// behavior where `#[deprecated]` on an enum applies to its variants. + fn is_variant_deprecated(&self, variant: hir::EnumVariant) -> bool { + let db = self.db(); + variant.attrs(db).is_deprecated() || variant.parent_enum(db).attrs(db).is_deprecated() + } + // FIXME: remove this fn docs(&self, def: impl HasDocs) -> Option> { def.docs(self.db()) @@ -583,6 +592,7 @@ fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option, resolution: ScopeDef) -> bool { let db = ctx.db(); match resolution { + ScopeDef::ModuleDef(hir::ModuleDef::EnumVariant(it)) => ctx.is_variant_deprecated(it), ScopeDef::ModuleDef(it) => ctx.is_deprecated(it, it.as_assoc_item(db)), ScopeDef::GenericParam(it) => { ctx.is_deprecated(it, None /* generic params can't be assoc items */) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index b7de3da468dde..eb03e27cd9767 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs @@ -192,9 +192,7 @@ impl Variant { Variant::Struct(it) => { ctx.is_deprecated(it, None /* structs can't be assoc items */) } - Variant::EnumVariant(it) => { - ctx.is_deprecated(it, None /* enum variants can't be assoc items */) - } + Variant::EnumVariant(it) => ctx.is_variant_deprecated(it), } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 294434297eccb..081977342cb0c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -3161,6 +3161,41 @@ fn foo() { ); } +#[test] +fn deprecated_enum_marks_variants_deprecated() { + // regression: rust-lang/rust-analyzer#22090 — a variant inherits the + // `#[deprecated]` attribute of its parent enum. + check( + r#" +#[deprecated] +enum Foo { Bar } +fn main() { let _ = Foo::$0; } +"#, + expect![[r#" + ev Bar Bar DEPRECATED + "#]], + ); +} + +#[test] +fn deprecated_variant_of_undeprecated_enum_still_deprecated() { + // regression guard for rust-lang/rust-analyzer#22090 — the existing + // per-variant `#[deprecated]` behavior must keep working. + check( + r#" +enum Foo { + #[deprecated] Bar, + Baz, +} +fn main() { let _ = Foo::$0; } +"#, + expect![[r#" + ev Bar Bar DEPRECATED + ev Baz Baz + "#]], + ); +} + #[test] fn non_std_test_attr_macro() { check( From 956018e6679961fb31cfe2fb54206ece76c59d2f Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 21 Apr 2026 16:27:48 +0530 Subject: [PATCH 013/289] remove TreeMutator export for ide-assist --- .../rust-analyzer/crates/ide-assists/src/assist_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs index 207a7548f49b5..0b8f35bfc3c6c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs @@ -13,7 +13,7 @@ use crate::{ Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel, assist_config::AssistConfig, }; -pub(crate) use ide_db::source_change::{SourceChangeBuilder, TreeMutator}; +pub(crate) use ide_db::source_change::SourceChangeBuilder; /// `AssistContext` allows to apply an assist or check if it could be applied. /// From 5f9d6f57433abf9e1ef85be1a5bf60aa8c20aedc Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 21 Apr 2026 16:28:08 +0530 Subject: [PATCH 014/289] make all tree mutator method private --- .../rust-analyzer/crates/ide-db/src/source_change.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 81b679ead233c..29a454bf7a753 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 @@ -229,12 +229,12 @@ pub struct SourceChangeBuilder { pub snippet_annotations: Vec<(AnnotationSnippet, SyntaxAnnotation)>, /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. - pub mutated_tree: Option, + mutated_tree: Option, /// Keeps track of where to place snippets pub snippet_builder: Option, } -pub struct TreeMutator { +struct TreeMutator { immutable: SyntaxNode, mutable_clone: SyntaxNode, } @@ -246,17 +246,17 @@ pub struct SnippetBuilder { } impl TreeMutator { - pub fn new(immutable: &SyntaxNode) -> TreeMutator { + fn new(immutable: &SyntaxNode) -> TreeMutator { let immutable = immutable.ancestors().last().unwrap(); let mutable_clone = immutable.clone_for_update(); TreeMutator { immutable, mutable_clone } } - pub fn make_mut(&self, node: &N) -> N { + fn make_mut(&self, node: &N) -> N { N::cast(self.make_syntax_mut(node.syntax())).unwrap() } - pub fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode { + fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode { let ptr = SyntaxNodePtr::new(node); ptr.to_node(&self.mutable_clone) } From 5a50b9b266ce837ab11eca759f196bd67b6a1a03 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 21 Apr 2026 16:28:55 +0530 Subject: [PATCH 015/289] remove generate_impl non syntaxFactory variant --- .../ide-assists/src/handlers/generate_impl.rs | 4 +- .../crates/ide-assists/src/utils.rs | 140 ++---------------- 2 files changed, 13 insertions(+), 131 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index c5a46f6981f59..27a1eb643978a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -7,7 +7,7 @@ use crate::{ AssistContext, AssistId, Assists, utils::{ self, DefaultMethods, IgnoreAssocItems, add_trait_assoc_items_to_impl, - generate_impl_with_factory, generate_trait_impl_intransitive, + generate_trait_impl_intransitive, }, }; @@ -61,7 +61,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio |edit| { let editor = edit.make_editor(nominal.syntax()); let make = editor.make(); - let impl_ = generate_impl_with_factory(make, &nominal); + let impl_ = utils::generate_impl(make, &nominal); let impl_ = insert_impl(&editor, &impl_, &nominal); // Add a tabstop after the left curly brace 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 bf1062d207bba..8efc1bb230f0a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -536,15 +536,11 @@ pub(crate) fn generate_impl_with_item( adt: &ast::Adt, body: Option, ) -> ast::Impl { - generate_impl_inner_with_factory(make, false, adt, None, true, body) + generate_impl_inner(make, false, adt, None, true, body) } -pub(crate) fn generate_impl_with_factory(make: &SyntaxFactory, adt: &ast::Adt) -> ast::Impl { - generate_impl_inner_with_factory(make, false, adt, None, true, None) -} - -pub(crate) fn generate_impl(adt: &ast::Adt) -> ast::Impl { - generate_impl_inner(false, adt, None, true, None) +pub(crate) fn generate_impl(make: &SyntaxFactory, adt: &ast::Adt) -> ast::Impl { + generate_impl_inner(make, false, adt, None, true, None) } /// Generates the corresponding `impl for Type {}` including type @@ -557,7 +553,7 @@ pub(crate) fn generate_trait_impl( adt: &ast::Adt, trait_: ast::Type, ) -> ast::Impl { - generate_impl_inner_with_factory(make, is_unsafe, adt, Some(trait_), true, None) + generate_impl_inner(make, is_unsafe, adt, Some(trait_), true, None) } /// Generates the corresponding `impl for Type {}` including type @@ -569,7 +565,7 @@ pub(crate) fn generate_trait_impl_intransitive( adt: &ast::Adt, trait_: ast::Type, ) -> ast::Impl { - generate_impl_inner_with_factory(make, false, adt, Some(trait_), false, None) + generate_impl_inner(make, false, adt, Some(trait_), false, None) } pub(crate) fn generate_trait_impl_intransitive_with_item( @@ -578,7 +574,7 @@ pub(crate) fn generate_trait_impl_intransitive_with_item( trait_: ast::Type, body: ast::AssocItemList, ) -> ast::Impl { - generate_impl_inner_with_factory(make, false, adt, Some(trait_), false, Some(body)) + generate_impl_inner(make, false, adt, Some(trait_), false, Some(body)) } pub(crate) fn generate_trait_impl_with_item( @@ -588,79 +584,10 @@ pub(crate) fn generate_trait_impl_with_item( trait_: ast::Type, body: ast::AssocItemList, ) -> ast::Impl { - generate_impl_inner_with_factory(make, is_unsafe, adt, Some(trait_), true, Some(body)) + generate_impl_inner(make, is_unsafe, adt, Some(trait_), true, Some(body)) } fn generate_impl_inner( - is_unsafe: bool, - adt: &ast::Adt, - trait_: Option, - trait_is_transitive: bool, - body: Option, -) -> ast::Impl { - // Ensure lifetime params are before type & const params - let generic_params = adt.generic_param_list().map(|generic_params| { - let lifetime_params = - generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam); - let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| { - let param = match param { - ast::TypeOrConstParam::Type(param) => { - // remove defaults since they can't be specified in impls - let mut bounds = - param.type_bound_list().map_or_else(Vec::new, |it| it.bounds().collect()); - if let Some(trait_) = &trait_ { - // Add the current trait to `bounds` if the trait is transitive, - // meaning `impl Trait for U` requires `T: Trait`. - if trait_is_transitive { - bounds.push(make::type_bound(trait_.clone())); - } - }; - // `{ty_param}: {bounds}` - let param = make::type_param(param.name()?, make::type_bound_list(bounds)); - ast::GenericParam::TypeParam(param) - } - ast::TypeOrConstParam::Const(param) => { - // remove defaults since they can't be specified in impls - let param = make::const_param(param.name()?, param.ty()?); - ast::GenericParam::ConstParam(param) - } - }; - Some(param) - }); - - make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params)) - }); - let generic_args = - generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update()); - let adt_assoc_bounds = trait_ - .as_ref() - .zip(generic_params.as_ref()) - .and_then(|(trait_, params)| generic_param_associated_bounds(adt, trait_, params)); - - let ty = make::ty_path(make::ext::ident_path(&adt.name().unwrap().text())); - - let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))); - match trait_ { - Some(trait_) => make::impl_trait( - cfg_attrs, - is_unsafe, - None, - None, - generic_params, - generic_args, - false, - trait_, - ty, - adt_assoc_bounds, - adt.where_clause(), - body, - ), - None => make::impl_(cfg_attrs, generic_params, generic_args, ty, adt.where_clause(), body), - } - .clone_for_update() -} - -fn generate_impl_inner_with_factory( make: &SyntaxFactory, is_unsafe: bool, adt: &ast::Adt, @@ -702,10 +629,10 @@ fn generate_impl_inner_with_factory( }); let generic_args = generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update()); - let adt_assoc_bounds = - trait_.as_ref().zip(generic_params.as_ref()).and_then(|(trait_, params)| { - generic_param_associated_bounds_with_factory(make, adt, trait_, params) - }); + let adt_assoc_bounds = trait_ + .as_ref() + .zip(generic_params.as_ref()) + .and_then(|(trait_, params)| generic_param_associated_bounds(make, adt, trait_, params)); let ty: ast::Type = make.ty_path(make.ident_path(&adt.name().unwrap().text())).into(); @@ -730,51 +657,6 @@ fn generate_impl_inner_with_factory( } fn generic_param_associated_bounds( - adt: &ast::Adt, - trait_: &ast::Type, - generic_params: &ast::GenericParamList, -) -> Option { - let in_type_params = |name: &ast::NameRef| { - generic_params - .generic_params() - .filter_map(|param| match param { - ast::GenericParam::TypeParam(type_param) => type_param.name(), - _ => None, - }) - .any(|param| param.text() == name.text()) - }; - let adt_body = match adt { - ast::Adt::Enum(e) => e.variant_list().map(|it| it.syntax().clone()), - ast::Adt::Struct(s) => s.field_list().map(|it| it.syntax().clone()), - ast::Adt::Union(u) => u.record_field_list().map(|it| it.syntax().clone()), - }; - let mut trait_where_clause = adt_body - .into_iter() - .flat_map(|it| it.descendants()) - .filter_map(ast::Path::cast) - .filter_map(|path| { - let qualifier = path.qualifier()?.as_single_segment()?; - let qualifier = qualifier - .name_ref() - .or_else(|| match qualifier.type_anchor()?.ty()? { - ast::Type::PathType(path_type) => path_type.path()?.as_single_name_ref(), - _ => None, - }) - .filter(in_type_params)?; - Some((qualifier, path.segment()?.name_ref()?)) - }) - .map(|(qualifier, assoc_name)| { - let segments = [qualifier, assoc_name].map(make::path_segment); - let path = make::path_from_segments(segments, false); - let bounds = Some(make::type_bound(trait_.clone())); - make::where_pred(either::Either::Right(make::ty_path(path)), bounds) - }) - .unique_by(|it| it.syntax().to_string()) - .peekable(); - trait_where_clause.peek().is_some().then(|| make::where_clause(trait_where_clause)) -} - -fn generic_param_associated_bounds_with_factory( make: &SyntaxFactory, adt: &ast::Adt, trait_: &ast::Type, From e04e2c40284aef310c3051fcf28247a8178c61ba Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 21 Apr 2026 16:29:38 +0530 Subject: [PATCH 016/289] add mod_path_to_ast_with_factory --- .../crates/ide-db/src/helpers.rs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs index 08cf1eeed33ba..838aac2283f12 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs @@ -7,7 +7,7 @@ use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics}; use span::{Edition, FileId}; use syntax::{ AstToken, SyntaxKind, SyntaxToken, ToSmolStr, TokenAtOffset, - ast::{self, make}, + ast::{self, make, syntax_factory::SyntaxFactory}, }; use crate::{ @@ -57,6 +57,31 @@ pub fn mod_path_to_ast(path: &hir::ModPath, edition: Edition) -> ast::Path { make::path_from_segments(segments, is_abs) } +pub fn mod_path_to_ast_with_factory( + make: &SyntaxFactory, + path: &hir::ModPath, + edition: Edition, +) -> ast::Path { + let _p = tracing::info_span!("mod_path_to_ast").entered(); + + let mut segments = Vec::new(); + let mut is_abs = false; + match path.kind { + hir::PathKind::Plain => {} + hir::PathKind::SELF => segments.push(make.path_segment_self()), + hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make.path_segment_super())), + hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => { + segments.push(make.path_segment_crate()) + } + hir::PathKind::Abs => is_abs = true, + } + + segments.extend(path.segments().iter().map(|segment| { + make.path_segment(make.name_ref(&segment.display_no_db(edition).to_smolstr())) + })); + make.path_from_segments(segments, is_abs) +} + /// Iterates all `ModuleDef`s and `Impl` blocks of the given file. pub fn visit_file_defs( sema: &Semantics<'_, RootDatabase>, From 2a92e730326a6905e9a815d96a82c6ab8c400ff5 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 21 Apr 2026 16:30:30 +0530 Subject: [PATCH 017/289] add new syntax factory constructor method to support extract function migration --- .../src/ast/syntax_factory/constructors.rs | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) 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 0f3b3d301c544..cb05c41ade9d6 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 @@ -2,7 +2,7 @@ use either::Either; use crate::{ - AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, + AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, ast::{ self, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, HasTypeBounds, HasVisibility, Lifetime, Param, RangeItem, make, @@ -491,6 +491,18 @@ impl SyntaxFactory { ast } + pub fn path_segment_self(&self) -> ast::PathSegment { + make::path_segment_self().clone_for_update() + } + + pub fn path_segment_super(&self) -> ast::PathSegment { + make::path_segment_super().clone_for_update() + } + + pub fn path_segment_crate(&self) -> ast::PathSegment { + make::path_segment_crate().clone_for_update() + } + pub fn generic_ty_path_segment( &self, name_ref: ast::NameRef, @@ -551,6 +563,14 @@ impl SyntaxFactory { make::ty_placeholder().clone_for_update() } + pub fn ty_unit(&self) -> ast::Type { + make::ty_unit().clone_for_update() + } + + pub fn ty_tuple(&self, types: impl IntoIterator) -> ast::Type { + make::ty_tuple(types).clone_for_update() + } + pub fn path_segment_generics( &self, name_ref: ast::NameRef, @@ -676,6 +696,18 @@ impl SyntaxFactory { ast } + pub fn simple_ident_pat(&self, name: ast::Name) -> ast::IdentPat { + let ast = make::ext::simple_ident_pat(name.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + pub fn wildcard_pat(&self) -> ast::WildcardPat { make::wildcard_pat().clone_for_update() } @@ -2127,6 +2159,30 @@ impl SyntaxFactory { ast } + pub fn expr_try(&self, expr: ast::Expr) -> ast::Expr { + let ast = make::expr_try(expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + if let ast::Expr::TryExpr(try_expr) = &ast + && let Some(inner) = try_expr.expr() + { + builder.map_node(expr.syntax().clone(), inner.syntax().clone()); + } + builder.finish(&mut mapping); + } + + ast + } + + pub fn hacky_block_expr( + &self, + elements: impl IntoIterator, + tail_expr: Option, + ) -> ast::BlockExpr { + make::hacky_block_expr(elements, tail_expr).clone_for_update() + } + pub fn expr_break(&self, label: Option, expr: Option) -> ast::BreakExpr { let ast::Expr::BreakExpr(ast) = make::expr_break(label.clone(), expr.clone()).clone_for_update() From 0cc24e41867346d1e13962d893da64855013075d Mon Sep 17 00:00:00 2001 From: andrew Date: Tue, 21 Apr 2026 08:29:01 -0400 Subject: [PATCH 018/289] Remove redundant comments --- .../rust-analyzer/crates/ide-completion/src/tests/expression.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 081977342cb0c..7d3c5aaad36c0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -3179,8 +3179,6 @@ fn main() { let _ = Foo::$0; } #[test] fn deprecated_variant_of_undeprecated_enum_still_deprecated() { - // regression guard for rust-lang/rust-analyzer#22090 — the existing - // per-variant `#[deprecated]` behavior must keep working. check( r#" enum Foo { From 64d37db57271a3e3d2bdef6b1747a7a53222cc3c Mon Sep 17 00:00:00 2001 From: Brumbelow Date: Tue, 21 Apr 2026 09:17:46 -0400 Subject: [PATCH 019/289] remove other redundant test comment --- .../rust-analyzer/crates/ide-completion/src/tests/expression.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 7d3c5aaad36c0..f6da07a6f2ed7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -3163,8 +3163,6 @@ fn foo() { #[test] fn deprecated_enum_marks_variants_deprecated() { - // regression: rust-lang/rust-analyzer#22090 — a variant inherits the - // `#[deprecated]` attribute of its parent enum. check( r#" #[deprecated] From f42dea2ac79595623e7cffb5126a8e3bcfd83508 Mon Sep 17 00:00:00 2001 From: Brumbelow Date: Tue, 21 Apr 2026 11:27:25 -0400 Subject: [PATCH 020/289] fix: generate-method skips trait impl blocks when picking insertion site --- .../src/handlers/generate_function.rs | 82 ++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 6ef492619b50c..e1e456fd5eda5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -148,7 +148,9 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let enclosing_impl = ctx.find_node_at_offset::(); let cursor_impl = enclosing_impl.filter(|impl_| { - ctx.sema.to_def(impl_).map_or(false, |def| def.self_ty(ctx.sema.db).as_adt() == Some(adt)) + ctx.sema.to_def(impl_).is_some_and(|def| { + def.self_ty(ctx.sema.db).as_adt() == Some(adt) && def.trait_(ctx.sema.db).is_none() + }) }); let (impl_, file) = if let Some(impl_) = cursor_impl { @@ -3239,6 +3241,84 @@ impl Foo { ${0:todo!()} } } +", + ) + } + + #[test] + fn generate_method_skips_trait_impl_for_inherent() { + // regression: rust-lang/rust-analyzer#22123 + check_assist( + generate_function, + r" +struct Bar; + +impl Bar { + fn func1() {} +} + +trait Foo { fn foo(&self); } + +impl Foo for Bar { + fn foo(&self) { + self.func2$0(); + } +} +", + r" +struct Bar; + +impl Bar { + fn func1() {} + + fn func2(&self) ${0:-> _} { + todo!() + } +} + +trait Foo { fn foo(&self); } + +impl Foo for Bar { + fn foo(&self) { + self.func2(); + } +} +", + ) + } + + #[test] + fn generate_method_from_trait_impl_creates_new_inherent_impl() { + // #22123: no inherent impl exists, so the assist must synthesize one + // instead of inserting into the trait impl. + check_assist( + generate_function, + r" +struct Bar; + +trait Foo { fn foo(&self); } + +impl Foo for Bar { + fn foo(&self) { + self.func2$0(); + } +} +", + r" +struct Bar; +impl Bar { + fn func2(&self) ${0:-> _} { + todo!() + } +} + +trait Foo { fn foo(&self); } + +impl Foo for Bar { + fn foo(&self) { + self.func2(); + } +} ", ) } From 239e2c98d8f947d462942de98e8374b089575611 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 21 Apr 2026 22:33:30 +0530 Subject: [PATCH 021/289] migrate replace qualified name with use to SyntaxEditor --- .../replace_qualified_name_with_use.rs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index eebe93f005f99..92fd11918474b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -1,11 +1,11 @@ use hir::{AsAssocItem, ModuleDef, PathResolution}; use ide_db::{ - helpers::mod_path_to_ast, - imports::insert_use::{ImportScope, insert_use}, + helpers::mod_path_to_ast_with_factory, + imports::insert_use::{ImportScope, insert_use_with_editor}, }; use syntax::{ AstNode, Edition, SyntaxNode, - ast::{self, HasGenericArgs, make}, + ast::{self, HasGenericArgs}, match_ast, syntax_editor::SyntaxEditor, }; @@ -75,7 +75,7 @@ pub(crate) fn replace_qualified_name_with_use( let scope_node = scope.as_syntax_node(); let editor = builder.make_editor(scope_node); shorten_paths(&editor, scope_node, &original_path); - builder.add_file_edits(ctx.vfs_file_id(), editor); + let make = editor.make(); let path = drop_generic_args(&original_path); let edition = ctx .sema @@ -83,13 +83,14 @@ pub(crate) fn replace_qualified_name_with_use( .map(|semantics_scope| semantics_scope.krate().edition(ctx.db())) .unwrap_or(Edition::CURRENT); // stick the found import in front of the to be replaced path - let path = - match path_to_qualifier.and_then(|it| mod_path_to_ast(&it, edition).qualifier()) { - Some(qualifier) => make::path_concat(qualifier, path), - None => path, - }; - let scope = builder.make_import_scope_mut(scope); - insert_use(&scope, path, &ctx.config.insert_use); + let path = match path_to_qualifier + .and_then(|it| mod_path_to_ast_with_factory(make, &it, edition).qualifier()) + { + Some(qualifier) => make.path_concat(qualifier, path), + None => path, + }; + insert_use_with_editor(&scope, path, &ctx.config.insert_use, &editor); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } From 95895122861da2dde4332fbe585db31e191829d6 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 21 Apr 2026 22:41:29 +0530 Subject: [PATCH 022/289] add mod_path_to_ast_with_factory variant --- .../crates/ide-db/src/helpers.rs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs index 08cf1eeed33ba..838aac2283f12 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs @@ -7,7 +7,7 @@ use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics}; use span::{Edition, FileId}; use syntax::{ AstToken, SyntaxKind, SyntaxToken, ToSmolStr, TokenAtOffset, - ast::{self, make}, + ast::{self, make, syntax_factory::SyntaxFactory}, }; use crate::{ @@ -57,6 +57,31 @@ pub fn mod_path_to_ast(path: &hir::ModPath, edition: Edition) -> ast::Path { make::path_from_segments(segments, is_abs) } +pub fn mod_path_to_ast_with_factory( + make: &SyntaxFactory, + path: &hir::ModPath, + edition: Edition, +) -> ast::Path { + let _p = tracing::info_span!("mod_path_to_ast").entered(); + + let mut segments = Vec::new(); + let mut is_abs = false; + match path.kind { + hir::PathKind::Plain => {} + hir::PathKind::SELF => segments.push(make.path_segment_self()), + hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make.path_segment_super())), + hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => { + segments.push(make.path_segment_crate()) + } + hir::PathKind::Abs => is_abs = true, + } + + segments.extend(path.segments().iter().map(|segment| { + make.path_segment(make.name_ref(&segment.display_no_db(edition).to_smolstr())) + })); + make.path_from_segments(segments, is_abs) +} + /// Iterates all `ModuleDef`s and `Impl` blocks of the given file. pub fn visit_file_defs( sema: &Semantics<'_, RootDatabase>, From ffae5b2116eb6abc9814991c61b536491f32d0dd Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 21 Apr 2026 22:41:43 +0530 Subject: [PATCH 023/289] update constructor with new methods --- .../syntax/src/ast/syntax_factory/constructors.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 0f3b3d301c544..885d48b064c87 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 @@ -491,6 +491,18 @@ impl SyntaxFactory { ast } + pub fn path_segment_self(&self) -> ast::PathSegment { + make::path_segment_self().clone_for_update() + } + + pub fn path_segment_super(&self) -> ast::PathSegment { + make::path_segment_super().clone_for_update() + } + + pub fn path_segment_crate(&self) -> ast::PathSegment { + make::path_segment_crate().clone_for_update() + } + pub fn generic_ty_path_segment( &self, name_ref: ast::NameRef, From 88ddf37b652fb046e1e4b9b6de2afc9209cd8d66 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 7 Apr 2026 04:34:01 +0300 Subject: [PATCH 024/289] When renaming a field, rename variables in constructors as well --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 8 ++ .../rust-analyzer/crates/hir/src/semantics.rs | 38 +++++++ .../rust-analyzer/crates/ide-db/src/rename.rs | 107 +++++++++++++++++- .../crates/ide-db/src/text_edit.rs | 4 + .../rust-analyzer/crates/ide/src/rename.rs | 67 ++++++++++- 5 files changed, 219 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 91b48bbfb6de8..ecd11fb5d767b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1984,6 +1984,14 @@ impl Variant { Variant::EnumVariant(e) => (*e).name(db), } } + + pub fn adt(&self, db: &dyn HirDatabase) -> Adt { + match *self { + Variant::Struct(it) => it.into(), + Variant::Union(it) => it.into(), + Variant::EnumVariant(it) => it.parent_enum(db).into(), + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index b7cc780ae42f7..38656e18708e9 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1635,6 +1635,44 @@ impl<'db> SemanticsImpl<'db> { .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) } + /// Returns the `return` expressions in this function's body, + /// excluding those inside closures or async blocks. + pub fn fn_return_points(&self, func: Function) -> Vec> { + let func_id = match func.id { + AnyFunctionId::FunctionId(id) => id, + _ => return vec![], + }; + let (body, source_map) = Body::with_source_map(self.db, func_id.into()); + + fn collect_returns( + sema: &SemanticsImpl<'_>, + body: &Body, + source_map: &hir_def::expr_store::ExpressionStoreSourceMap, + expr_id: ExprId, + acc: &mut Vec>, + ) { + match &body[expr_id] { + Expr::Closure { .. } | Expr::Const(_) => return, + Expr::Return { .. } => { + if let Ok(source) = source_map.expr_syntax(expr_id) + && let Some(ret_expr) = source.value.cast::() + { + let root = sema.parse_or_expand(source.file_id); + acc.push(InFile::new(source.file_id, ret_expr.to_node(&root))); + } + } + _ => {} + } + body.walk_child_exprs(expr_id, |child| { + collect_returns(sema, body, source_map, child, acc); + }); + } + + let mut returns = vec![]; + collect_returns(self, body, source_map, body.root_expr(), &mut returns); + returns + } + pub fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option { let text = lifetime.text(); let lifetime_param = lifetime.syntax().ancestors().find_map(|syn| { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index b18ed69d80fec..ac0b0997132a2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -28,7 +28,9 @@ use crate::{ }; use base_db::AnchoredPathBuf; use either::Either; -use hir::{FieldSource, FileRange, InFile, ModuleSource, Name, Semantics, sym}; +use hir::{FieldSource, FileRange, HasCrate, InFile, ModuleSource, Name, Semantics, sym}; +use itertools::Itertools; +use rustc_hash::FxHashSet; use span::{Edition, FileId, SyntaxContext}; use stdx::{TupleExt, never}; use syntax::{ @@ -405,6 +407,11 @@ fn rename_reference( source_edit_from_references(sema.db, references, def, &new_name, edition), ) })); + + if let Definition::Field(field) = def { + rename_field_constructors(sema, field, &new_name, &mut source_change, config); + } + if rename_definition == RenameDefinition::Yes { // This needs to come after the references edits, because we change the annotation of existing edits // if a conflict is detected. @@ -415,6 +422,104 @@ fn rename_reference( Ok(source_change) } +fn rename_field_constructors( + sema: &Semantics<'_, RootDatabase>, + field: hir::Field, + new_name: &Name, + source_change: &mut SourceChange, + config: &RenameConfig, +) { + let db = sema.db; + let old_name = field.name(db); + let adt = field.parent_def(db).adt(db); + adt.ty(db).iterate_assoc_items(db, |assoc_item| { + let ctor = assoc_item.as_function()?; + if ctor.has_self_param(db) { + return None; + } + if ctor.ret_type(db).as_adt() != Some(adt) { + return None; + } + + let source = sema.source(ctor); + let return_values = sema + .fn_return_points(ctor) + .into_iter() + .filter_map(|ret| ret.value.expr()) + .chain(source.and_then(|source| source.value.body()?.tail_expr())); + // FIXME: We could maybe skip ifs etc.. + + let get_renamed_field = |mut expr| { + while let ast::Expr::ParenExpr(e) = &expr { + expr = e.expr()?; + } + let ast::Expr::RecordExpr(expr) = expr else { return None }; + if sema.type_of_expr(&expr.clone().into())?.original.as_adt()? != adt { + return None; + }; + expr.record_expr_field_list()?.fields().find_map(|record_field| { + if record_field.name_ref().is_none() + && Name::new_root(&record_field.field_name()?.text()) == old_name + && let ast::Expr::PathExpr(field_name) = record_field.expr()? + { + field_name.path() + } else { + None + } + }) + }; + let renamed_fields = return_values + .map(get_renamed_field) + .map(|renamed_field| { + let renamed_field = renamed_field?; + let hir::PathResolution::Local(local) = sema.resolve_path(&renamed_field)? else { + return None; + }; + let range = sema.original_range_opt(renamed_field.syntax())?.range; + Some((range, local)) + }) + .collect::>>()?; + + let edition = ctor.krate(db).edition(db); + let locals = renamed_fields.iter().map(|&(_, local)| local).collect::>(); + let mut all_locals_source_change = SourceChange::default(); + for local in locals { + let mut local_source_change = Definition::Local(local) + .rename(sema, new_name.as_str(), RenameDefinition::Yes, config) + .ok()?; + + let (edit, _snippet) = + local_source_change.source_file_edits.values_mut().exactly_one().ok()?; + + // The struct literal will have an edit `old_name -> old_name: new_name`, and we need to remove + // that, as we want an overlapping edit `old_name -> new_name`. + for &(field_range, _) in &renamed_fields { + edit.cancel_edits_touching(field_range); + } + + all_locals_source_change = + std::mem::take(&mut all_locals_source_change).merge(local_source_change); + } + let (edit, _snippet) = + all_locals_source_change.source_file_edits.values_mut().exactly_one().ok()?; + for &(field_range, _) in &renamed_fields { + edit.union(TextEdit::replace(field_range, new_name.display(db, edition).to_string())) + .unwrap(); + } + + let file_id = *all_locals_source_change.source_file_edits.keys().exactly_one().ok()?; + if let Some((edit, _snippet)) = source_change.source_file_edits.get_mut(&file_id) { + for &(field_range, _) in &renamed_fields { + edit.cancel_edits_touching(field_range); + } + } + + *source_change = std::mem::take(source_change).merge(all_locals_source_change); + + None:: + }); +} + pub fn source_edit_from_references( db: &RootDatabase, references: &[FileReference], diff --git a/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs b/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs index d2a73710d58bb..f93b2cc74eb29 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs @@ -151,6 +151,10 @@ impl TextEdit { pub fn change_annotation(&self) -> Option { self.annotation } + + pub fn cancel_edits_touching(&mut self, touching: TextRange) { + self.indels.retain(|indel| indel.delete.intersect(touching).is_none()); + } } impl IntoIterator for TextEdit { diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index 900a885a64de8..2c6116f745761 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -1326,8 +1326,8 @@ impl Foo { struct Foo { foo$0: i32 } impl Foo { - fn new(foo: i32) -> Self { - Self { foo } + fn foo(foo: i32) { + Self { foo }; } } "#, @@ -1335,8 +1335,8 @@ impl Foo { struct Foo { field: i32 } impl Foo { - fn new(foo: i32) -> Self { - Self { field: foo } + fn foo(foo: i32) { + Self { field: foo }; } } "#, @@ -3926,6 +3926,65 @@ impl Foo { fn bar() { Foo::foo(&Foo, 1); +} + "#, + ); + } + + #[test] + fn rename_constructor_locals() { + check( + "field", + r#" +struct Struct { + struct_field$0: String, +} + +impl Struct { + fn new(struct_field: String) -> Self { + if false { + return Self { struct_field }; + } + Self { struct_field } + } +} + +mod foo { + macro_rules! m { + ($it:expr) => { return $it }; + } + + impl crate::Struct { + fn with_foo(struct_field: String) -> crate::Struct { + m!(crate::Struct { struct_field }); + } + } +} + "#, + r#" +struct Struct { + field: String, +} + +impl Struct { + fn new(field: String) -> Self { + if false { + return Self { field }; + } + Self { field } + } +} + +mod foo { + macro_rules! m { + ($it:expr) => { return $it }; + } + + impl crate::Struct { + fn with_foo(field: String) -> crate::Struct { + m!(crate::Struct { field }); + } + } } "#, ); From 0249c794f79930c773e59d9eaebafb387196ab5d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 00:03:39 +0300 Subject: [PATCH 025/289] Do not lower normal assignments into `Pat::Path` Lower them as `Pat::Expr` instead. This is a bit difficult to implement due to the way rustc checks whether a pat is a destructuring assignment, but it gains us the very desirable property that `Pat::Expr` is the only way to have an normal assignment, which simplifies the code and prevents bugs. --- .../crates/hir-def/src/expr_store/lower.rs | 50 ++++++++++++++++--- .../rust-analyzer/crates/hir-def/src/hir.rs | 1 - .../closure/analysis/expr_use_visitor.rs | 39 +++------------ .../crates/hir-ty/src/infer/expr.rs | 25 +--------- .../crates/hir-ty/src/infer/pat.rs | 4 +- .../crates/hir-ty/src/tests/simple.rs | 1 + .../rust-analyzer/crates/hir-ty/src/upvars.rs | 18 +------ .../rust-analyzer/crates/hir/src/semantics.rs | 19 +------ 8 files changed, 55 insertions(+), 102 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 04437a59ac815..4ace4aef168d5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -13,6 +13,7 @@ use cfg::CfgOptions; use either::Either; use hir_expand::{ HirFileId, InFile, MacroDefId, + mod_path::ModPath, name::{AsName, Name}, span_map::SpanMapRef, }; @@ -1611,6 +1612,37 @@ impl<'db> ExprCollector<'db> { }) } + /// Whether this path should be lowered as destructuring assignment, or as a normal assignment. + fn path_is_destructuring_assignment(&self, path: &ModPath) -> bool { + // rustc has access to a full resolver here, including local variables and generic params, and it checks the following + // criteria: a path not lowered as destructuring assignment if it can *fully resolve* to something that is *not* + // a const, a unit struct or a variant. + // We don't have access to a full resolver here. So we should do the same as rustc, but assuming that local variables + // could be resolved to nothing (fortunately, there cannot be a local variable shadowing a unit struct/variant/const, + // as that is an error). We don't need to consider const params as it's an error to refer to these in patterns. + let (resolution, unresolved_idx, _) = self.def_map.resolve_path_locally( + self.local_def_map, + self.db, + self.module, + path, + BuiltinShadowMode::Other, + ); + match unresolved_idx { + Some(_) => { + // If `Some(_)`, path could be resolved to unit struct/variant/const with type information, i.e. an assoc type or const. + // If `None`, path could be a local variable. + resolution.take_types().is_some() + } + None => match resolution.take_values() { + // We don't need to consider non-unit structs/variants, as those are not value types. + Some(ModuleDefId::EnumVariantId(_)) + | Some(ModuleDefId::AdtId(_)) + | Some(ModuleDefId::ConstId(_)) => true, + _ => false, + }, + } + } + fn collect_expr_as_pat_opt(&mut self, expr: Option) -> PatId { match expr { Some(expr) => self.collect_expr_as_pat(expr), @@ -1676,15 +1708,17 @@ impl<'db> ExprCollector<'db> { self.alloc_pat_from_expr(Pat::TupleStruct { path, args, ellipsis }, syntax_ptr) } ast::Expr::PathExpr(e) => { - let (path, hygiene) = self - .collect_expr_path(e.clone()) - .map(|(path, hygiene)| (Pat::Path(path), hygiene)) - .unwrap_or((Pat::Missing, HygieneId::ROOT)); - let pat_id = self.alloc_pat_from_expr(path, syntax_ptr); - if !hygiene.is_root() { - self.store.ident_hygiene.insert(pat_id.into(), hygiene); + let (path, hygiene) = self.collect_expr_path(e.clone())?; + let mod_path = path.mod_path().expect("should not lower to lang path"); + if self.path_is_destructuring_assignment(mod_path) { + let pat_id = self.alloc_pat_from_expr(Pat::Path(path), syntax_ptr); + if !hygiene.is_root() { + self.store.ident_hygiene.insert(pat_id.into(), hygiene); + } + pat_id + } else { + return None; } - pat_id } ast::Expr::MacroExpr(e) => { let e = e.macro_call()?; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 9e51d0eac98a1..031280b6e92ef 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -687,7 +687,6 @@ pub enum Pat { slice: Option, suffix: Box<[PatId]>, }, - /// This might refer to a variable if a single segment path (specifically, on destructuring assignment). Path(Path), Lit(ExprId), Bind { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index 099fa18168b2c..45696c402a662 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -446,14 +446,6 @@ pub(crate) struct ExprUseVisitor<'a, 'b, 'db, D: Delegate<'db>> { upvars: UpvarsRef<'db>, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum PatWalkMode { - /// `let`, `match`. - Declaration, - /// Destructuring assignment. - Assignment, -} - impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { /// Creates the ExprUseVisitor, configuring it with the various options provided: /// @@ -477,7 +469,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { let param_place = self.cat_rvalue(param.into(), param_ty); self.fake_read_scrutinee(param_place.clone(), false); - self.walk_pat(param_place, param, false, PatWalkMode::Declaration)?; + self.walk_pat(param_place, param, false)?; } self.consume_expr(body)?; @@ -701,7 +693,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { let expr_place = self.cat_expr(value)?; let update_guard = self.cx.resolver.update_to_inner_scope(self.cx.db, self.cx.owner, expr); - self.walk_pat(expr_place, target, false, PatWalkMode::Assignment)?; + self.walk_pat(expr_place, target, false)?; self.cx.resolver.reset_to_guard(update_guard); } @@ -784,7 +776,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { let expr_place = self.cat_expr(expr)?; f(self)?; self.fake_read_scrutinee(expr_place.clone(), els.is_some()); - self.walk_pat(expr_place, pat, false, PatWalkMode::Declaration)?; + self.walk_pat(expr_place, pat, false)?; if let Some(els) = els { self.walk_expr(els)?; } @@ -896,7 +888,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { } fn walk_arm(&mut self, discr_place: PlaceWithOrigin, arm: &MatchArm) -> Result { - self.walk_pat(discr_place, arm.pat, arm.guard.is_some(), PatWalkMode::Declaration)?; + self.walk_pat(discr_place, arm.pat, arm.guard.is_some())?; if let Some(e) = arm.guard { self.consume_expr(e)?; @@ -920,13 +912,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { /// Do note that discrepancies like these do still create obscure corners /// in the semantics of the language, and should be avoided if possible. #[instrument(skip(self), level = "debug")] - fn walk_pat( - &mut self, - discr_place: PlaceWithOrigin, - pat: PatId, - has_guard: bool, - mode: PatWalkMode, - ) -> Result { + fn walk_pat(&mut self, discr_place: PlaceWithOrigin, pat: PatId, has_guard: bool) -> Result { self.cat_pattern(discr_place.clone(), pat, &mut |this, place, pat| { debug!("walk_pat: pat.kind={:?}", this.cx.store[pat]); let read_discriminant = { @@ -987,13 +973,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { this.cx.store.pat_path_hygiene(pat), ); let is_normal_const = matches!(resolution, Some(ValueNs::ConstId(_))); - if mode == PatWalkMode::Assignment - && let Some(ValueNs::LocalBinding(local)) = resolution - { - let pat_ty = this.pat_ty(pat)?; - let place = this.cat_local(pat.into(), pat_ty, local)?; - this.delegate.mutate(place, this.cx); - } else if is_assoc_const || is_normal_const { + if is_assoc_const || is_normal_const { // Named constants have to be equated with the value // being matched, so that's a read of the value being matched. // @@ -1035,7 +1015,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { read_discriminant(this); } } - Pat::Expr(expr) if mode == PatWalkMode::Assignment => { + Pat::Expr(expr) => { // Destructuring assignment. this.mutate_expr(expr)?; } @@ -1050,7 +1030,6 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { // If the PatKind is Missing, Wild or Err, any relevant accesses are made when processing // the other patterns that are part of the match } - Pat::Expr(_) => {} } Ok(()) @@ -1196,10 +1175,6 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { self.node_ty(expr.into()) } - fn pat_ty(&mut self, pat: PatId) -> Result> { - self.node_ty(pat.into()) - } - fn expr_ty_adjusted(&mut self, expr: ExprId) -> Result> { self.expect_and_resolve_type(self.cx.result.type_of_expr_with_adjust(expr)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index d80ea71674775..f26be638067f0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -299,7 +299,7 @@ impl<'db> InferenceContext<'_, 'db> { } #[tracing::instrument(level = "debug", skip(self, is_read), ret)] - fn infer_expr_inner( + pub(super) fn infer_expr_inner( &mut self, tgt_expr: ExprId, expected: &Expectation<'db>, @@ -715,29 +715,6 @@ impl<'db> InferenceContext<'_, 'db> { &Pat::Expr(expr) => { Some(self.infer_expr(expr, &Expectation::none(), ExprIsRead::No)) } - Pat::Path(path) => { - let resolver_guard = - self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); - let resolution = self.resolver.resolve_path_in_value_ns_fully( - self.db, - path, - self.store.pat_path_hygiene(target), - ); - self.resolver.reset_to_guard(resolver_guard); - - if matches!( - resolution, - Some( - ValueNs::ConstId(_) - | ValueNs::StructId(_) - | ValueNs::EnumVariantId(_) - ) - ) { - None - } else { - Some(self.infer_expr_path(path, target.into(), tgt_expr)) - } - } _ => None, }; let is_destructuring_assignment = lhs_ty.is_none(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 8033680dcc5cf..a437253672014 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -241,8 +241,6 @@ impl<'db> InferenceContext<'_, 'db> { pat_ty } - /// The resolver needs to be updated to the surrounding expression when inside assignment - /// (because there, `Pat::Path` can refer to a variable). pub(super) fn infer_top_pat( &mut self, pat: PatId, @@ -405,7 +403,7 @@ impl<'db> InferenceContext<'_, 'db> { // LHS of assignment doesn't constitute reads. let expr_is_read = ExprIsRead::No; let result = - self.infer_expr_coerce(*expr, &Expectation::has_type(expected), expr_is_read); + self.infer_expr_inner(*expr, &Expectation::has_type(expected), expr_is_read); // We are returning early to avoid the unifiability check below. let lhs_ty = self.insert_type_vars_shallow(result); let ty = match self.coerce( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 3ea21f8265a5f..c30b24cd27d37 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -2271,6 +2271,7 @@ fn infer_generic_from_later_assignment() { 89..127 'loop {... }': ! 94..127 '{ ... }': () 104..107 'end': Option + 104..107 'end': Option 104..120 'end = ...(true)': () 110..114 'Some': fn Some(bool) -> Option 110..120 'Some(true)': Option diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs b/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs index 48f3c803d8322..026e6ab18301a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs @@ -3,7 +3,7 @@ use hir_def::{ DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, VariantId, expr_store::{ExpressionStore, path::Path}, - hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat}, + hir::{BindingId, Expr, ExprId, ExprOrPatId}, resolver::{HasResolver, Resolver, ValueNs}, }; use hir_expand::mod_path::PathKind; @@ -181,22 +181,6 @@ pub fn upvars_mentioned_impl( path, ); } - &Expr::Assignment { target, .. } => { - body.walk_pats(target, &mut |pat| { - let Pat::Path(path) = &body[pat] else { return }; - resolve_maybe_upvar( - db, - resolver, - owner, - body, - current_closure, - expr, - pat.into(), - upvars, - path, - ); - }); - } &Expr::Closure { body: body_expr, .. } => { let mut closure_upvars = FxHashSet::default(); handle_expr_inside_closure( diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index b7cc780ae42f7..fe0955fc58eb8 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -17,7 +17,7 @@ use hir_def::{ TraitId, VariantId, attrs::parse_extra_crate_attrs, expr_store::{Body, ExprOrPatSource, ExpressionStore, HygieneId, path::Path}, - hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat}, + hir::{BindingId, Expr, ExprId, ExprOrPatId}, nameres::{ModuleOrigin, crate_def_map}, resolver::{self, HasResolver, Resolver, TypeNs, ValueNs}, type_ref::Mutability, @@ -2371,13 +2371,7 @@ impl<'db> SemanticsImpl<'db> { None } } - ExprOrPatId::PatId(pat_id) => { - if let Pat::Path(path) = &store[pat_id] { - Some(path) - } else { - None - } - } + ExprOrPatId::PatId(_) => None, }; if let Some(path) = path @@ -2761,15 +2755,6 @@ impl RenameConflictsVisitor<'_> { self.resolve_path(expr.into(), path); self.resolver.reset_to_guard(guard); } - &Expr::Assignment { target, .. } => { - let guard = self.resolver.update_to_inner_scope(self.db, self.owner, expr); - self.body.walk_pats(target, &mut |pat| { - if let Pat::Path(path) = &self.body[pat] { - self.resolve_path(pat.into(), path); - } - }); - self.resolver.reset_to_guard(guard); - } _ => {} } From 5bf7d2231d7a52bdcb48a9332719e85b4cda7144 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 21 Apr 2026 16:39:39 +0530 Subject: [PATCH 026/289] migrate extract_function to syntaxEditor --- .../src/handlers/extract_function.rs | 617 ++++++++++-------- 1 file changed, 333 insertions(+), 284 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 4219e6845fad5..99dd2ea237746 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -1,6 +1,5 @@ use std::{iter, ops::RangeInclusive}; -use ast::make; use either::Either; use hir::{ HasSource, HirDisplay, InFile, Local, LocalSource, ModuleDef, PathResolution, Semantics, @@ -11,10 +10,9 @@ use ide_db::{ assists::GroupLabel, defs::Definition, famous_defs::FamousDefs, - helpers::mod_path_to_ast, - imports::insert_use::{ImportScope, insert_use}, + helpers::mod_path_to_ast_with_factory, + imports::insert_use::{ImportScope, insert_use_with_editor}, search::{FileReference, ReferenceCategory, SearchScope}, - source_change::SourceChangeBuilder, syntax_helpers::node_ext::{ for_each_tail_expr, preorder_expr, walk_pat, walk_patterns_in_expr, }, @@ -25,15 +23,17 @@ use syntax::{ SyntaxKind::{self, COMMENT}, SyntaxNode, SyntaxToken, T, TextRange, TextSize, TokenAtOffset, WalkEvent, ast::{ - self, AstNode, AstToken, HasAttrs, HasGenericParams, HasName, edit::IndentLevel, - edit_in_place::Indent, + self, AstNode, AstToken, HasAttrs, HasGenericParams, HasName, + edit::{AstNodeEdit, IndentLevel}, + syntax_factory::SyntaxFactory, }, - match_ast, ted, + match_ast, + syntax_editor::{Position, SyntaxEditor}, }; use crate::{ AssistId, - assist_context::{AssistContext, Assists, TreeMutator}, + assist_context::{AssistContext, Assists}, utils::generate_impl, }; @@ -97,8 +97,9 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let module = semantics_scope.module(); let edition = semantics_scope.krate().edition(ctx.db()); + let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); let (container_info, contains_tail_expr) = - body.analyze_container(&ctx.sema, edition, trait_name)?; + body.analyze_container(editor.make(), &ctx.sema, edition, trait_name)?; let ret_ty = body.return_ty(ctx)?; let control_flow = body.external_control_flow(ctx, &container_info)?; @@ -114,6 +115,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op "Extract into function", target_range, move |builder| { + let make = editor.make(); let outliving_locals: Vec<_> = ret_values.collect(); if stdx::never!(!outliving_locals.is_empty() && !ret_ty.is_unit()) { // We should not have variables that outlive body if we have expression block @@ -122,7 +124,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let params = body.extracted_function_params(ctx, &container_info, locals_used); - let name = make_function_name(&semantics_scope, &body); + let name = make_function_name(make, &semantics_scope, &body); let fun = Function { name, @@ -139,67 +141,47 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let new_indent = IndentLevel::from_node(&insert_after); let old_indent = fun.body.indent_level(); - let insert_after = builder.make_syntax_mut(insert_after); + let call_expr = make_call(make, ctx, &fun, old_indent); - let call_expr = make_call(ctx, &fun, old_indent); - - // Map the element range to replace into the mutable version let elements = match &fun.body { FunctionBody::Expr(expr) => { - // expr itself becomes the replacement target - let expr = &builder.make_mut(expr.clone()); let node = SyntaxElement::Node(expr.syntax().clone()); - node.clone()..=node } - FunctionBody::Span { parent, elements, .. } => { - // Map the element range into the mutable versions - let parent = builder.make_mut(parent.clone()); - - let start = parent - .syntax() - .children_with_tokens() - .nth(elements.start().index()) - .expect("should be able to find mutable start element"); - - let end = parent - .syntax() - .children_with_tokens() - .nth(elements.end().index()) - .expect("should be able to find mutable end element"); - - start..=end - } + FunctionBody::Span { elements, .. } => elements.clone(), }; let has_impl_wrapper = insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after); - let fn_def = format_function(ctx, module, &fun, old_indent).clone_for_update(); - - if let Some(cap) = ctx.config.snippet_cap - && let Some(name) = fn_def.name() - { - builder.add_tabstop_before(cap, name); - } + let fn_def = format_function(ctx, module, &fun, old_indent, make); // FIXME: wrap non-adt types let fn_def = match fun.self_param_adt(ctx) { Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => { - fn_def.indent(1.into()); + let fn_def = fn_def.indent_with_mapping(1.into(), make); - let impl_ = generate_impl(&adt); - impl_.indent(new_indent); + let impl_ = generate_impl(make, &adt).indent(new_indent); impl_.get_or_create_assoc_item_list().add_item(fn_def.into()); impl_.syntax().clone() } - _ => { - fn_def.indent(new_indent); - - fn_def.syntax().clone() - } + _ => fn_def.indent_with_mapping(new_indent, make).syntax().clone(), }; + if let Some(cap) = ctx.config.snippet_cap { + let extracted_fn = fn_def.descendants().find_map(ast::Fn::cast); + if let Some(fn_) = extracted_fn { + if let Some(ws) = fn_ + .fn_token() + .and_then(|tok| tok.next_token()) + .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE) + { + editor.add_annotation(ws, builder.make_tabstop_after(cap)); + } else if let Some(name) = fn_.name() { + editor.add_annotation(name.syntax(), builder.make_tabstop_before(cap)); + } + } + } // There are external control flows if fun @@ -207,7 +189,6 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op .kind .is_some_and(|kind| matches!(kind, FlowKind::Break(_, _) | FlowKind::Continue(_))) { - let scope = builder.make_import_scope_mut(scope); let control_flow_enum = FamousDefs(&ctx.sema, module.krate(ctx.db())).core_ops_ControlFlow(); @@ -222,29 +203,45 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op ); if let Some(mod_path) = mod_path { - insert_use( + insert_use_with_editor( &scope, - mod_path_to_ast(&mod_path, edition), + mod_path_to_ast_with_factory(make, &mod_path, edition), &ctx.config.insert_use, + &editor, ); } } } // Replace the call site with the call to the new function - fixup_call_site(builder, &fun.body); - ted::replace_all(elements, vec![call_expr.into()]); + let needs_match_arm_comma = fun + .body + .parent() + .and_then(ast::MatchArm::cast) + .is_some_and(|arm| arm.comma_token().is_none()); + match &fun.body { + FunctionBody::Expr(expr) => { + let mut replacement = vec![call_expr.clone().into()]; + if needs_match_arm_comma { + replacement.push(make.token(T![,]).into()); + } + editor.replace_with_many(expr.syntax(), replacement); + } + FunctionBody::Span { .. } => editor.replace_all(elements, vec![call_expr.into()]), + } // Insert the newly extracted function (or impl) - ted::insert_all_raw( - ted::Position::after(insert_after), - vec![make::tokens::whitespace(&format!("\n\n{new_indent}")).into(), fn_def.into()], + editor.insert_all( + Position::after(insert_after), + vec![make.whitespace(&format!("\n\n{new_indent}")).into(), fn_def.into()], ); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } fn make_function_name( + make: &SyntaxFactory, semantics_scope: &hir::SemanticsScope<'_>, body: &FunctionBody, ) -> ast::NameRef { @@ -267,7 +264,7 @@ fn make_function_name( counter += 1; name = format!("{default_name}{counter}") } - make::name_ref(&name) + make.name_ref(&name) } /// Try to guess what user wants to extract @@ -510,38 +507,44 @@ impl<'db> Param<'db> { } } - fn to_arg(&self, ctx: &AssistContext<'db>, edition: Edition) -> ast::Expr { - let var = path_expr_from_local(ctx, self.var, edition); + fn to_arg( + &self, + make: &SyntaxFactory, + ctx: &AssistContext<'db>, + edition: Edition, + ) -> ast::Expr { + let var = path_expr_from_local(make, ctx, self.var, edition); match self.kind() { ParamKind::Value | ParamKind::MutValue => var, - ParamKind::SharedRef => make::expr_ref(var, false), - ParamKind::MutRef => make::expr_ref(var, true), + ParamKind::SharedRef => make.expr_ref(var, false), + ParamKind::MutRef => make.expr_ref(var, true), } } fn to_param( &self, + make: &SyntaxFactory, ctx: &AssistContext<'_>, module: hir::Module, edition: Edition, ) -> ast::Param { let var = self.var.name(ctx.db()).display(ctx.db(), edition).to_string(); - let var_name = make::name(&var); + let var_name = make.name(&var); let pat = match self.kind() { - ParamKind::MutValue => make::ident_pat(false, true, var_name), + ParamKind::MutValue => make.ident_pat(false, true, var_name), ParamKind::Value | ParamKind::SharedRef | ParamKind::MutRef => { - make::ext::simple_ident_pat(var_name) + make.simple_ident_pat(var_name) } }; - let ty = make_ty(&self.ty, ctx, module); + let ty = make_ty(make, &self.ty, ctx, module); let ty = match self.kind() { ParamKind::Value | ParamKind::MutValue => ty, - ParamKind::SharedRef => make::ty_ref(ty, false), - ParamKind::MutRef => make::ty_ref(ty, true), + ParamKind::SharedRef => make.ty_ref(ty, false), + ParamKind::MutRef => make.ty_ref(ty, true), }; - make::param(pat.into(), ty) + make.param(pat.into(), ty) } } @@ -569,17 +572,17 @@ impl<'db> TryKind<'db> { } impl<'db> FlowKind<'db> { - fn make_result_handler(&self, expr: Option) -> ast::Expr { + fn make_result_handler(&self, make: &SyntaxFactory, expr: Option) -> ast::Expr { match self { - FlowKind::Return(_) => make::expr_return(expr), - FlowKind::Break(label, _) => make::expr_break(label.clone(), expr), + FlowKind::Return(_) => make.expr_return(expr).into(), + FlowKind::Break(label, _) => make.expr_break(label.clone(), expr).into(), FlowKind::Try { .. } => { stdx::never!("cannot have result handler with try"); - expr.unwrap_or_else(|| make::expr_return(None)) + expr.unwrap_or_else(|| make.expr_return(None).into()) } FlowKind::Continue(label) => { stdx::always!(expr.is_none(), "continue with value is not possible"); - make::expr_continue(label.clone()) + make.expr_continue(label.clone()).into() } } } @@ -840,6 +843,7 @@ impl FunctionBody { fn analyze_container<'db>( &self, + make: &SyntaxFactory, sema: &Semantics<'db, RootDatabase>, edition: Edition, trait_name: Option, @@ -930,7 +934,7 @@ impl FunctionBody { }; // FIXME: make trait arguments - let trait_name = trait_name.map(|name| make::ty_path(make::ext::ident_path(&name.text()))); + let trait_name = trait_name.map(|name| make.ty_path(make.ident_path(&name.text())).into()); let parent = self.parent()?; let parents = generic_parents(&parent); @@ -1191,7 +1195,7 @@ fn reference_is_exclusive( } // we take `&mut` reference to variable: `&mut v` - let path = match path_element_of_reference(node, reference) { + let path = match path_element_of_reference(node, reference.range) { Some(path) => path, None => return false, }; @@ -1282,10 +1286,10 @@ impl HasTokenAtOffset for FunctionBody { /// `node` must cover `reference`, that is `node.text_range().contains_range(reference.range)` fn path_element_of_reference( node: &dyn HasTokenAtOffset, - reference: &FileReference, + reference_range: TextRange, ) -> Option { - let token = node.token_at_offset(reference.range.start()).right_biased().or_else(|| { - stdx::never!(false, "cannot find token at variable usage: {:?}", reference); + let token = node.token_at_offset(reference_range.start()).right_biased().or_else(|| { + stdx::never!(false, "cannot find token at variable usage: {:?}", reference_range); None })?; let path = token.parent_ancestors().find_map(ast::Expr::cast).or_else(|| { @@ -1413,58 +1417,50 @@ fn impl_type_name(impl_node: &ast::Impl) -> Option { Some(impl_node.self_ty()?.to_string()) } -/// Fixes up the call site before the target expressions are replaced with the call expression -fn fixup_call_site(builder: &mut SourceChangeBuilder, body: &FunctionBody) { - let parent_match_arm = body.parent().and_then(ast::MatchArm::cast); - - if let Some(parent_match_arm) = parent_match_arm - && parent_match_arm.comma_token().is_none() - { - let parent_match_arm = builder.make_mut(parent_match_arm); - ted::append_child_raw(parent_match_arm.syntax(), make::token(T![,])); - } -} - -fn make_call(ctx: &AssistContext<'_>, fun: &Function<'_>, indent: IndentLevel) -> SyntaxNode { +fn make_call( + make: &SyntaxFactory, + ctx: &AssistContext<'_>, + fun: &Function<'_>, + indent: IndentLevel, +) -> SyntaxNode { let ret_ty = fun.return_type(ctx); let name = fun.name.clone(); - let args = fun.params.iter().map(|param| param.to_arg(ctx, fun.mods.edition)); + let args = fun.params.iter().map(|param| param.to_arg(make, ctx, fun.mods.edition)); let mut call_expr = if fun.make_this_param().is_some() { - let self_arg = make::expr_path(make::ext::ident_path("self")); - let func = make::expr_path(make::path_unqualified(make::path_segment(name))); - make::expr_call(func, make::arg_list(Some(self_arg).into_iter().chain(args))).into() + let self_arg = make.expr_path(make.ident_path("self")); + let func = make.expr_path(make.path_unqualified(make.path_segment(name))); + make.expr_call(func, make.arg_list(Some(self_arg).into_iter().chain(args))).into() } else if fun.self_param.is_some() { - let self_arg = make::expr_path(make::ext::ident_path("self")); - make::expr_method_call(self_arg, name, make::arg_list(args)).into() + let self_arg = make.expr_path(make.ident_path("self")); + make.expr_method_call(self_arg, name, make.arg_list(args)).into() } else { - let func = make::expr_path(make::path_unqualified(make::path_segment(name))); - make::expr_call(func, make::arg_list(args)).into() + let func = make.expr_path(make.path_unqualified(make.path_segment(name))); + make.expr_call(func, make.arg_list(args)).into() }; let handler = FlowHandler::from_ret_ty(fun, &ret_ty); if fun.control_flow.is_async { - call_expr = make::expr_await(call_expr); + call_expr = make.expr_await(call_expr).into(); } - let expr = handler.make_call_expr(call_expr).clone_for_update(); - expr.indent(indent); + let expr = handler.make_call_expr(make, call_expr).indent_with_mapping(indent, make); let outliving_bindings = match fun.outliving_locals.as_slice() { [] => None, [var] => { let name = var.local.name(ctx.db()); - let name = make::name(&name.display(ctx.db(), fun.mods.edition).to_string()); - Some(ast::Pat::IdentPat(make::ident_pat(false, var.mut_usage_outside_body, name))) + let name = make.name(&name.display(ctx.db(), fun.mods.edition).to_string()); + Some(ast::Pat::IdentPat(make.ident_pat(false, var.mut_usage_outside_body, name))) } vars => { let binding_pats = vars.iter().map(|var| { let name = var.local.name(ctx.db()); - let name = make::name(&name.display(ctx.db(), fun.mods.edition).to_string()); - make::ident_pat(false, var.mut_usage_outside_body, name).into() + let name = make.name(&name.display(ctx.db(), fun.mods.edition).to_string()); + make.ident_pat(false, var.mut_usage_outside_body, name).into() }); - Some(ast::Pat::TuplePat(make::tuple_pat(binding_pats))) + Some(ast::Pat::TuplePat(make.tuple_pat(binding_pats))) } }; @@ -1472,7 +1468,7 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function<'_>, indent: IndentLevel) - if let Some(bindings) = outliving_bindings { // with bindings that outlive it - make::let_stmt(bindings, None, Some(expr)).syntax().clone_for_update() + make.let_stmt(bindings, None, Some(expr)).syntax().clone() } else if parent_match_arm.as_ref().is_some() { // as a tail expr for a match arm expr.syntax().clone() @@ -1481,7 +1477,7 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function<'_>, indent: IndentLevel) - && (!fun.outliving_locals.is_empty() || !expr.is_block_like()) { // as an expr stmt - make::expr_stmt(expr).syntax().clone_for_update() + make.expr_stmt(expr).syntax().clone() } else { // as a tail expr, or a block expr.syntax().clone() @@ -1527,82 +1523,87 @@ impl<'db> FlowHandler<'db> { } } - fn make_call_expr(&self, call_expr: ast::Expr) -> ast::Expr { + fn make_call_expr(&self, make: &SyntaxFactory, call_expr: ast::Expr) -> ast::Expr { match self { FlowHandler::None => call_expr, - FlowHandler::Try { kind: _ } => make::expr_try(call_expr), + FlowHandler::Try { kind: _ } => make.expr_try(call_expr), FlowHandler::If { action } => { - let action = action.make_result_handler(None); - let stmt = make::expr_stmt(action); - let block = make::block_expr(iter::once(stmt.into()), None); - let controlflow_break_path = make::path_from_text("ControlFlow::Break"); - let condition = make::expr_let( - make::tuple_struct_pat( + let action = action.make_result_handler(make, None); + let stmt = make.expr_stmt(action); + let block = make.block_expr(iter::once(stmt.into()), None); + let controlflow_break_path = make.path_from_text("ControlFlow::Break"); + let condition = make.expr_let( + make.tuple_struct_pat( controlflow_break_path, - iter::once(make::wildcard_pat().into()), + iter::once(make.wildcard_pat().into()), ) .into(), call_expr, ); - make::expr_if(condition.into(), block, None).into() + make.expr_if(condition.into(), block, None).into() } FlowHandler::IfOption { action } => { - let path = make::ext::ident_path("Some"); - let value_pat = make::ext::simple_ident_pat(make::name("value")); - let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into())); - let cond = make::expr_let(pattern.into(), call_expr); - let value = make::expr_path(make::ext::ident_path("value")); - let action_expr = action.make_result_handler(Some(value)); - let action_stmt = make::expr_stmt(action_expr); - let then = make::block_expr(iter::once(action_stmt.into()), None); - make::expr_if(cond.into(), then, None).into() + let path = make.ident_path("Some"); + let value_pat = make.simple_ident_pat(make.name("value")); + let pattern = make.tuple_struct_pat(path, iter::once(value_pat.into())); + let cond = make.expr_let(pattern.into(), call_expr); + let value = make.expr_path(make.ident_path("value")); + let action_expr = action.make_result_handler(make, Some(value)); + let action_stmt = make.expr_stmt(action_expr); + let then = make.block_expr(iter::once(action_stmt.into()), None); + make.expr_if(cond.into(), then, None).into() } FlowHandler::MatchOption { none } => { let some_name = "value"; let some_arm = { - let path = make::ext::ident_path("Some"); - let value_pat = make::ext::simple_ident_pat(make::name(some_name)); - let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); - let value = make::expr_path(make::ext::ident_path(some_name)); - make::match_arm(pat.into(), None, value) + let path = make.ident_path("Some"); + let value_pat = make.simple_ident_pat(make.name(some_name)); + let pat = make.tuple_struct_pat(path, iter::once(value_pat.into())); + let value = make.expr_path(make.ident_path(some_name)); + make.match_arm(pat.into(), None, value) }; let none_arm = { - let path = make::ext::ident_path("None"); - let pat = make::path_pat(path); - make::match_arm(pat, None, none.make_result_handler(None)) + let path = make.ident_path("None"); + let pat = make.path_pat(path); + make.match_arm(pat, None, none.make_result_handler(make, None)) }; - let arms = make::match_arm_list(vec![some_arm, none_arm]); - make::expr_match(call_expr, arms).into() + let arms = make.match_arm_list(vec![some_arm, none_arm]); + make.expr_match(call_expr, arms).into() } FlowHandler::MatchResult { err } => { let ok_name = "value"; let err_name = "value"; let ok_arm = { - let path = make::ext::ident_path("Ok"); - let value_pat = make::ext::simple_ident_pat(make::name(ok_name)); - let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); - let value = make::expr_path(make::ext::ident_path(ok_name)); - make::match_arm(pat.into(), None, value) + let path = make.ident_path("Ok"); + let value_pat = make.simple_ident_pat(make.name(ok_name)); + let pat = make.tuple_struct_pat(path, iter::once(value_pat.into())); + let value = make.expr_path(make.ident_path(ok_name)); + make.match_arm(pat.into(), None, value) }; let err_arm = { - let path = make::ext::ident_path("Err"); - let value_pat = make::ext::simple_ident_pat(make::name(err_name)); - let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); - let value = make::expr_path(make::ext::ident_path(err_name)); - make::match_arm(pat.into(), None, err.make_result_handler(Some(value))) + let path = make.ident_path("Err"); + let value_pat = make.simple_ident_pat(make.name(err_name)); + let pat = make.tuple_struct_pat(path, iter::once(value_pat.into())); + let value = make.expr_path(make.ident_path(err_name)); + make.match_arm(pat.into(), None, err.make_result_handler(make, Some(value))) }; - let arms = make::match_arm_list(vec![ok_arm, err_arm]); - make::expr_match(call_expr, arms).into() + let arms = make.match_arm_list(vec![ok_arm, err_arm]); + make.expr_match(call_expr, arms).into() } } } } -fn path_expr_from_local(ctx: &AssistContext<'_>, var: Local, edition: Edition) -> ast::Expr { +fn path_expr_from_local( + make: &SyntaxFactory, + ctx: &AssistContext<'_>, + var: Local, + edition: Edition, +) -> ast::Expr { let name = var.name(ctx.db()).display(ctx.db(), edition).to_string(); - make::expr_path(make::ext::ident_path(&name)) + make.expr_path(make.ident_path(&name)) } fn format_function( @@ -1610,17 +1611,18 @@ fn format_function( module: hir::Module, fun: &Function<'_>, old_indent: IndentLevel, + make: &SyntaxFactory, ) -> ast::Fn { - let fun_name = make::name(&fun.name.text()); - let params = fun.make_param_list(ctx, module, fun.mods.edition); - let ret_ty = fun.make_ret_ty(ctx, module); - let body = make_body(ctx, old_indent, fun); - let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun); + let fun_name = make.name(&fun.name.text()); + let params = fun.make_param_list(make, ctx, module, fun.mods.edition); + let ret_ty = fun.make_ret_ty(make, ctx, module); + let body = make_body(make, ctx, old_indent, fun); + let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, make, fun); - make::fn_( + make.fn_( fun.mods.attrs.clone(), None, - fun_name, + fun_name.clone(), generic_params, where_clause, params, @@ -1635,18 +1637,20 @@ fn format_function( fn make_generic_params_and_where_clause( ctx: &AssistContext<'_>, + make: &SyntaxFactory, fun: &Function<'_>, ) -> (Option, Option) { let used_type_params = fun.type_params(ctx); - let generic_param_list = make_generic_param_list(ctx, fun, &used_type_params); - let where_clause = make_where_clause(ctx, fun, &used_type_params); + let generic_param_list = make_generic_param_list(ctx, make, fun, &used_type_params); + let where_clause = make_where_clause(ctx, make, fun, &used_type_params); (generic_param_list, where_clause) } fn make_generic_param_list( ctx: &AssistContext<'_>, + make: &SyntaxFactory, fun: &Function<'_>, used_type_params: &[TypeParam], ) -> Option { @@ -1662,7 +1666,7 @@ fn make_generic_param_list( .peekable(); if generic_params.peek().is_some() { - Some(make::generic_param_list(generic_params)) + Some(make.generic_param_list(generic_params)) } else { None } @@ -1684,6 +1688,7 @@ fn param_is_required( fn make_where_clause( ctx: &AssistContext<'_>, + make: &SyntaxFactory, fun: &Function<'_>, used_type_params: &[TypeParam], ) -> Option { @@ -1698,7 +1703,7 @@ fn make_where_clause( }) .peekable(); - if predicates.peek().is_some() { Some(make::where_clause(predicates)) } else { None } + if predicates.peek().is_some() { Some(make.where_clause(predicates)) } else { None } } fn pred_is_required( @@ -1738,35 +1743,41 @@ impl<'db> Function<'db> { fn make_param_list( &self, + make: &SyntaxFactory, ctx: &AssistContext<'_>, module: hir::Module, edition: Edition, ) -> ast::ParamList { - let this_param = self.make_this_param().map(|f| f()); + let this_param = self.make_this_param().map(|f| f(make)); let self_param = self.self_param.clone().filter(|_| this_param.is_none()); - let params = self.params.iter().map(|param| param.to_param(ctx, module, edition)); - make::param_list(self_param, this_param.into_iter().chain(params)) + let params = self.params.iter().map(|param| param.to_param(make, ctx, module, edition)); + make.param_list(self_param, this_param.into_iter().chain(params)) } - fn make_this_param(&self) -> Option ast::Param> { + fn make_this_param(&self) -> Option ast::Param> { if let Some(name) = self.mods.trait_name.clone() && let Some(self_param) = &self.self_param { - Some(|| { - let bounds = make::type_bound_list([make::type_bound(name)]); - let pat = make::path_pat(make::ext::ident_path("this")); - let mut ty = make::impl_trait_type(bounds.unwrap()).into(); + Some(move |make: &SyntaxFactory| { + let bounds = make.type_bound_list([make.type_bound(name)]); + let pat = make.path_pat(make.ident_path("this")); + let mut ty = make.impl_trait_type(bounds.unwrap()).into(); if self_param.amp_token().is_some() { - ty = make::ty_ref(ty, self_param.mut_token().is_some()); + ty = make.ty_ref(ty, self_param.mut_token().is_some()); } - make::param(pat, ty) + make.param(pat, ty) }) } else { None } } - fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option { + fn make_ret_ty( + &self, + make: &SyntaxFactory, + ctx: &AssistContext<'_>, + module: hir::Module, + ) -> Option { let fun_ty = self.return_type(ctx); let handler = FlowHandler::from_ret_ty(self, &fun_ty); let ret_ty = match &handler { @@ -1774,57 +1785,64 @@ impl<'db> Function<'db> { if matches!(fun_ty, FunType::Unit) { return None; } - fun_ty.make_ty(ctx, module) + fun_ty.make_ty(make, ctx, module) } FlowHandler::Try { kind: TryKind::Option } => { - make::ext::ty_option(fun_ty.make_ty(ctx, module)) + make.ty_option(fun_ty.make_ty(make, ctx, module)).into() } FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { let handler_ty = parent_ret_ty .type_arguments() .nth(1) - .map(|ty| make_ty(&ty, ctx, module)) - .unwrap_or_else(make::ty_placeholder); - make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty) + .map(|ty| make_ty(make, &ty, ctx, module)) + .unwrap_or_else(|| make.ty_placeholder()); + make.ty_result(fun_ty.make_ty(make, ctx, module), handler_ty).into() } - FlowHandler::If { .. } => make::ty("ControlFlow<()>"), + FlowHandler::If { .. } => make.ty("ControlFlow<()>"), FlowHandler::IfOption { action } => { let handler_ty = action .expr_ty(ctx) - .map(|ty| make_ty(&ty, ctx, module)) - .unwrap_or_else(make::ty_placeholder); - make::ext::ty_option(handler_ty) + .map(|ty| make_ty(make, &ty, ctx, module)) + .unwrap_or_else(|| make.ty_placeholder()); + make.ty_option(handler_ty).into() + } + FlowHandler::MatchOption { .. } => { + make.ty_option(fun_ty.make_ty(make, ctx, module)).into() } - FlowHandler::MatchOption { .. } => make::ext::ty_option(fun_ty.make_ty(ctx, module)), FlowHandler::MatchResult { err } => { let handler_ty = err .expr_ty(ctx) - .map(|ty| make_ty(&ty, ctx, module)) - .unwrap_or_else(make::ty_placeholder); - make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty) + .map(|ty| make_ty(make, &ty, ctx, module)) + .unwrap_or_else(|| make.ty_placeholder()); + make.ty_result(fun_ty.make_ty(make, ctx, module), handler_ty).into() } }; - Some(make::ret_type(ret_ty)) + Some(make.ret_type(ret_ty)) } } impl<'db> FunType<'db> { - fn make_ty(&self, ctx: &AssistContext<'db>, module: hir::Module) -> ast::Type { + fn make_ty( + &self, + make: &SyntaxFactory, + ctx: &AssistContext<'db>, + module: hir::Module, + ) -> ast::Type { match self { - FunType::Unit => make::ty_unit(), - FunType::Single(ty) => make_ty(ty, ctx, module), + FunType::Unit => make.ty_unit(), + FunType::Single(ty) => make_ty(make, ty, ctx, module), FunType::Tuple(types) => match types.as_slice() { [] => { stdx::never!("tuple type with 0 elements"); - make::ty_unit() + make.ty_unit() } [ty] => { stdx::never!("tuple type with 1 element"); - make_ty(ty, ctx, module) + make_ty(make, ty, ctx, module) } types => { - let types = types.iter().map(|ty| make_ty(ty, ctx, module)); - make::ty_tuple(types) + let types = types.iter().map(|ty| make_ty(make, ty, ctx, module)); + make.ty_tuple(types) } }, } @@ -1832,6 +1850,7 @@ impl<'db> FunType<'db> { } fn make_body( + make: &SyntaxFactory, ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function<'_>, @@ -1848,7 +1867,7 @@ fn make_body( match expr { ast::Expr::BlockExpr(block) => { // If the extracted expression is itself a block, there is no need to wrap it inside another block. - block.dedent(old_indent); + let block = block.dedent(old_indent); let elements = block.stmt_list().map_or_else( || Either::Left(iter::empty()), |stmt_list| { @@ -1865,12 +1884,12 @@ fn make_body( Either::Right(elements) }, ); - make::hacky_block_expr(elements, block.tail_expr()) + make.hacky_block_expr(elements, block.tail_expr()) } _ => { - expr.reindent_to(1.into()); + let expr = expr.dedent(old_indent).indent(1.into()); - make::block_expr(Vec::new(), Some(expr)) + make.block_expr(Vec::new(), Some(expr)) } } } @@ -1901,13 +1920,14 @@ fn make_body( None => match fun.outliving_locals.as_slice() { [] => {} [var] => { - tail_expr = Some(path_expr_from_local(ctx, var.local, fun.mods.edition)); + tail_expr = + Some(path_expr_from_local(make, ctx, var.local, fun.mods.edition)); } vars => { - let exprs = vars - .iter() - .map(|var| path_expr_from_local(ctx, var.local, fun.mods.edition)); - let expr = make::expr_tuple(exprs); + let exprs = vars.iter().map(|var| { + path_expr_from_local(make, ctx, var.local, fun.mods.edition) + }); + let expr = make.expr_tuple(exprs); tail_expr = Some(expr.into()); } }, @@ -1919,80 +1939,90 @@ fn make_body( .map(|node_or_token| match &node_or_token { syntax::NodeOrToken::Node(node) => match ast::Stmt::cast(node.clone()) { Some(stmt) => { - stmt.reindent_to(body_indent); - let ast_node = stmt.syntax().clone_subtree(); - syntax::NodeOrToken::Node(ast_node) + let stmt = stmt.dedent(old_indent).indent(body_indent); + syntax::NodeOrToken::Node(stmt.syntax().clone()) } _ => node_or_token, }, _ => node_or_token, }) .collect::>(); - if let Some(tail_expr) = &mut tail_expr { - tail_expr.reindent_to(body_indent); - } + tail_expr = tail_expr.map(|expr| expr.dedent(old_indent).indent(body_indent)); - make::hacky_block_expr(elements, tail_expr) + make.hacky_block_expr(elements, tail_expr) } }; match &handler { FlowHandler::None => block, FlowHandler::Try { kind } => { - let block = with_default_tail_expr(block, make::ext::expr_unit()); - map_tail_expr(block, |tail_expr| { + let block = with_default_tail_expr(make, block, make.expr_unit()); + map_tail_expr(make, block, |tail_expr| { let constructor = match kind { TryKind::Option => "Some", TryKind::Result { .. } => "Ok", }; - let func = make::expr_path(make::ext::ident_path(constructor)); - let args = make::arg_list(iter::once(tail_expr)); - make::expr_call(func, args).into() + let func = make.expr_path(make.ident_path(constructor)); + let args = make.arg_list(iter::once(tail_expr)); + make.expr_call(func, args).into() }) } FlowHandler::If { .. } => { - let controlflow_continue = make::expr_call( - make::expr_path(make::path_from_text("ControlFlow::Continue")), - make::arg_list([make::ext::expr_unit()]), - ) - .into(); - with_tail_expr(block, controlflow_continue) + let controlflow_continue = make + .expr_call( + make.expr_path(make.path_from_text("ControlFlow::Continue")), + make.arg_list([make.expr_unit()]), + ) + .into(); + with_tail_expr(make, block, controlflow_continue) } FlowHandler::IfOption { .. } => { - let none = make::expr_path(make::ext::ident_path("None")); - with_tail_expr(block, none) + let none = make.expr_path(make.ident_path("None")); + with_tail_expr(make, block, none) } - FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| { - let some = make::expr_path(make::ext::ident_path("Some")); - let args = make::arg_list(iter::once(tail_expr)); - make::expr_call(some, args).into() + FlowHandler::MatchOption { .. } => map_tail_expr(make, block, |tail_expr| { + let some = make.expr_path(make.ident_path("Some")); + let args = make.arg_list(iter::once(tail_expr)); + make.expr_call(some, args).into() }), - FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| { - let ok = make::expr_path(make::ext::ident_path("Ok")); - let args = make::arg_list(iter::once(tail_expr)); - make::expr_call(ok, args).into() + FlowHandler::MatchResult { .. } => map_tail_expr(make, block, |tail_expr| { + let ok = make.expr_path(make.ident_path("Ok")); + let args = make.arg_list(iter::once(tail_expr)); + make.expr_call(ok, args).into() }), } } -fn map_tail_expr(block: ast::BlockExpr, f: impl FnOnce(ast::Expr) -> ast::Expr) -> ast::BlockExpr { +fn map_tail_expr( + make: &SyntaxFactory, + block: ast::BlockExpr, + f: impl FnOnce(ast::Expr) -> ast::Expr, +) -> ast::BlockExpr { let tail_expr = match block.tail_expr() { Some(tail_expr) => tail_expr, None => return block, }; - make::block_expr(block.statements(), Some(f(tail_expr))) + make.block_expr(block.statements(), Some(f(tail_expr))) } -fn with_default_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr { +fn with_default_tail_expr( + make: &SyntaxFactory, + block: ast::BlockExpr, + tail_expr: ast::Expr, +) -> ast::BlockExpr { match block.tail_expr() { Some(_) => block, - None => make::block_expr(block.statements(), Some(tail_expr)), + None => make.block_expr(block.statements(), Some(tail_expr)), } } -fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr { +fn with_tail_expr( + make: &SyntaxFactory, + block: ast::BlockExpr, + tail_expr: ast::Expr, +) -> ast::BlockExpr { let stmt_tail_opt: Option = - block.tail_expr().map(|expr| make::expr_stmt(expr).into()); + block.tail_expr().map(|expr| make.expr_stmt(expr).into()); let mut elements: Vec = vec![]; @@ -2012,7 +2042,7 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr elements.push(syntax::NodeOrToken::Node(stmt_tail.syntax().clone())); } - make::hacky_block_expr(elements, Some(tail_expr)) + make.hacky_block_expr(elements, Some(tail_expr)) } fn format_type(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> String { @@ -2024,9 +2054,14 @@ fn is_inherit_attr(attr: &ast::Attr) -> bool { matches!(name.as_str(), "track_caller" | "cfg") } -fn make_ty(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type { +fn make_ty( + make: &SyntaxFactory, + ty: &hir::Type<'_>, + ctx: &AssistContext<'_>, + module: hir::Module, +) -> ast::Type { let ty_str = format_type(ty, ctx, module); - make::ty(&ty_str) + make.ty(&ty_str) } fn rewrite_body_segment( @@ -2037,29 +2072,33 @@ fn rewrite_body_segment( syntax: &SyntaxNode, ) -> SyntaxNode { let to_this_param = to_this_param.and_then(|it| ctx.sema.to_def(&it)); - let syntax = fix_param_usages(ctx, to_this_param, params, syntax); - update_external_control_flow(handler, &syntax); - syntax + let (param_editor, param_root) = SyntaxEditor::new(syntax.clone()); + fix_param_usages(¶m_editor, syntax, ¶m_root, ctx, to_this_param, params); + let syntax = param_editor.finish().new_root().clone(); + + let (flow_editor, flow_root) = SyntaxEditor::new(syntax); + update_external_control_flow(&flow_editor, &flow_root, handler); + flow_editor.finish().new_root().clone() } /// change all usages to account for added `&`/`&mut` for some params fn fix_param_usages( + editor: &SyntaxEditor, + source_syntax: &SyntaxNode, + syntax: &SyntaxNode, ctx: &AssistContext<'_>, to_this_param: Option, params: &[Param<'_>], - syntax: &SyntaxNode, -) -> SyntaxNode { +) { let mut usages_for_param: Vec<(&Param<'_>, Vec)> = Vec::new(); let mut usages_for_self_param: Vec = Vec::new(); + let source_range = source_syntax.text_range(); + let source_start = source_range.start(); - let tm = TreeMutator::new(syntax); let reference_filter = |reference: &FileReference| { - syntax - .text_range() - .contains_range(reference.range) - .then_some(()) - .and_then(|_| path_element_of_reference(syntax, reference)) - .map(|expr| tm.make_mut(&expr)) + source_range.contains_range(reference.range).then_some(())?; + let local_range = reference.range - source_start; + path_element_of_reference(syntax, local_range) }; if let Some(self_param) = to_this_param { @@ -2079,11 +2118,11 @@ fn fix_param_usages( usages_for_param.push((param, usages.unique().collect())); } - let res = tm.make_syntax_mut(syntax); + let make = editor.make(); for self_usage in usages_for_self_param { - let this_expr = make::expr_path(make::ext::ident_path("this")).clone_for_update(); - ted::replace(self_usage.syntax(), this_expr.syntax()); + let this_expr = make.expr_path(make.ident_path("this")); + editor.replace(self_usage.syntax(), this_expr.syntax()); } for (param, usages) in usages_for_param { for usage in usages { @@ -2098,7 +2137,7 @@ fn fix_param_usages( Some(ast::Expr::RefExpr(node)) if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => { - ted::replace( + editor.replace( node.syntax(), node.expr().expect("RefExpr::expr() cannot be None").syntax(), ); @@ -2106,23 +2145,25 @@ fn fix_param_usages( Some(ast::Expr::RefExpr(node)) if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => { - ted::replace( + editor.replace( node.syntax(), node.expr().expect("RefExpr::expr() cannot be None").syntax(), ); } Some(_) | None => { - let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update(); - ted::replace(usage.syntax(), p.syntax()) + let p = make.expr_prefix(T![*], usage.clone()); + editor.replace(usage.syntax(), p.syntax()) } } } } - - res } -fn update_external_control_flow(handler: &FlowHandler<'_>, syntax: &SyntaxNode) { +fn update_external_control_flow( + editor: &SyntaxEditor, + syntax: &SyntaxNode, + handler: &FlowHandler<'_>, +) { let mut nested_loop = None; let mut nested_scope = None; for event in syntax.preorder() { @@ -2151,19 +2192,25 @@ fn update_external_control_flow(handler: &FlowHandler<'_>, syntax: &SyntaxNode) match expr { ast::Expr::ReturnExpr(return_expr) => { let expr = return_expr.expr(); - if let Some(replacement) = make_rewritten_flow(handler, expr) { - ted::replace(return_expr.syntax(), replacement.syntax()) + if let Some(replacement) = + make_rewritten_flow(handler, expr, editor.make()) + { + editor.replace(return_expr.syntax(), replacement.syntax()) } } ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => { let expr = break_expr.expr(); - if let Some(replacement) = make_rewritten_flow(handler, expr) { - ted::replace(break_expr.syntax(), replacement.syntax()) + if let Some(replacement) = + make_rewritten_flow(handler, expr, editor.make()) + { + editor.replace(break_expr.syntax(), replacement.syntax()) } } ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => { - if let Some(replacement) = make_rewritten_flow(handler, None) { - ted::replace(continue_expr.syntax(), replacement.syntax()) + if let Some(replacement) = + make_rewritten_flow(handler, None, editor.make()) + { + editor.replace(continue_expr.syntax(), replacement.syntax()) } } _ => { @@ -2186,27 +2233,29 @@ fn update_external_control_flow(handler: &FlowHandler<'_>, syntax: &SyntaxNode) fn make_rewritten_flow( handler: &FlowHandler<'_>, arg_expr: Option, + make: &SyntaxFactory, ) -> Option { let value = match handler { FlowHandler::None | FlowHandler::Try { .. } => return None, - FlowHandler::If { .. } => make::expr_call( - make::expr_path(make::path_from_text("ControlFlow::Break")), - make::arg_list([make::ext::expr_unit()]), - ) - .into(), + FlowHandler::If { .. } => make + .expr_call( + make.expr_path(make.path_from_text("ControlFlow::Break")), + make.arg_list([make.expr_unit()]), + ) + .into(), FlowHandler::IfOption { .. } => { - let expr = arg_expr.unwrap_or_else(make::ext::expr_unit); - let args = make::arg_list([expr]); - make::expr_call(make::expr_path(make::ext::ident_path("Some")), args).into() + let expr = arg_expr.unwrap_or_else(|| make.expr_unit()); + let args = make.arg_list([expr]); + make.expr_call(make.expr_path(make.ident_path("Some")), args).into() } - FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")), + FlowHandler::MatchOption { .. } => make.expr_path(make.ident_path("None")), FlowHandler::MatchResult { .. } => { - let expr = arg_expr.unwrap_or_else(make::ext::expr_unit); - let args = make::arg_list([expr]); - make::expr_call(make::expr_path(make::ext::ident_path("Err")), args).into() + let expr = arg_expr.unwrap_or_else(|| make.expr_unit()); + let args = make.arg_list([expr]); + make.expr_call(make.expr_path(make.ident_path("Err")), args).into() } }; - Some(make::expr_return(Some(value)).clone_for_update()) + Some(make.expr_return(Some(value)).into()) } #[cfg(test)] From f463459b9614e8e20b7678b40e9a79844dddfb1f Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 09:20:03 +0530 Subject: [PATCH 027/289] add mappings to ty_tuple and hacky_block_expr --- .../src/ast/syntax_factory/constructors.rs | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) 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 cb05c41ade9d6..bc4a825987138 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 @@ -568,7 +568,18 @@ impl SyntaxFactory { } pub fn ty_tuple(&self, types: impl IntoIterator) -> ast::Type { - make::ty_tuple(types).clone_for_update() + let (types, input) = iterator_input(types); + let ast = make::ty_tuple(types).clone_for_update(); + + if let Some(mut mapping) = self.mappings() + && let ast::Type::TupleType(tuple_ty) = &ast + { + let mut builder = SyntaxMappingBuilder::new(tuple_ty.syntax().clone()); + builder.map_children(input, tuple_ty.fields().map(|ty| ty.syntax().clone())); + builder.finish(&mut mapping); + } + + ast } pub fn path_segment_generics( @@ -2180,7 +2191,30 @@ impl SyntaxFactory { elements: impl IntoIterator, tail_expr: Option, ) -> ast::BlockExpr { - make::hacky_block_expr(elements, tail_expr).clone_for_update() + let elements = elements.into_iter().collect::>(); + let ast = + make::hacky_block_expr(elements.iter().cloned(), tail_expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() + && let Some(stmt_list) = ast.stmt_list() + { + let mut builder = SyntaxMappingBuilder::new(stmt_list.syntax().clone()); + builder.map_children( + elements.into_iter().filter_map(|node_or_token| match node_or_token { + NodeOrToken::Node(node) => Some(node), + NodeOrToken::Token(_) => None, + }), + stmt_list.syntax().children(), + ); + if let Some(tail_expr) = tail_expr + && let Some(output_tail_expr) = stmt_list.tail_expr() + { + builder.map_node(tail_expr.syntax().clone(), output_tail_expr.syntax().clone()); + } + builder.finish(&mut mapping); + } + + ast } pub fn expr_break(&self, label: Option, expr: Option) -> ast::BreakExpr { From a556f3a04019a310623cb6fe2460530fa62ae2d2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 09:49:16 +0300 Subject: [PATCH 028/289] Suppress infer vars in monomorphization Having infer vars can cause panics down the road, when we try to resolve them on a different `InferCtxt`. Generally infer vars must not cross the pass they initiated in. --- .../rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index 41044f00c2e96..dfce8ca2a5cfe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -65,6 +65,9 @@ impl<'db> FallibleTypeFolder> for Filler<'db> { ty, ) .map_err(|_| MirLowerError::NotSupported("can't normalize alias".to_owned()))?; + // Normalization could introduce infer vars (for example, if the alias cannot be normalized), + // and we must not have infer vars in the body. + let ty = ty.replace_infer_with_error(self.infcx.interner); ty.try_super_fold_with(self) } TyKind::Param(param) => Ok(self From e78c1a2431cb3a7280364a9b10513b091934be3d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 08:38:03 +0300 Subject: [PATCH 029/289] Port pattern inference from rustc Some additional changes: - Have `Path` instead of `Option` for paths in hir. This means when desugaring some constructs that require lang item and the lang item is not present we might miss expressions/patterns, which requires adding minicore items in some tests, but it simplifies the code, so worth it IMO. - Port pattern adjustments from rustc (allowing overloaded deref adjustments for deref patterns) - Have the infrastructure in place for deref patterns, although they aren't even parsed yet but why remove them when we port from rustc. On the other hand, I removed support for pin patterns, since the feature is more experimental and it was impacting the code more. --- .../crates/hir-def/src/expr_store/lower.rs | 68 +- .../src/expr_store/lower/format_args.rs | 13 +- .../crates/hir-def/src/expr_store/pretty.rs | 15 +- .../rust-analyzer/crates/hir-def/src/hir.rs | 6 +- .../crates/hir-def/src/lang_item.rs | 3 +- .../crates/hir-def/src/unstable_features.rs | 4 + .../crates/hir-ty/src/diagnostics/expr.rs | 4 +- .../hir-ty/src/diagnostics/match_check.rs | 8 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 132 +- .../crates/hir-ty/src/infer/cast.rs | 6 +- .../crates/hir-ty/src/infer/closure.rs | 4 +- .../hir-ty/src/infer/closure/analysis.rs | 6 +- .../closure/analysis/expr_use_visitor.rs | 56 +- .../crates/hir-ty/src/infer/coerce.rs | 23 +- .../crates/hir-ty/src/infer/expr.rs | 64 +- .../crates/hir-ty/src/infer/op.rs | 13 +- .../crates/hir-ty/src/infer/pat.rs | 2052 ++++++++++++----- .../crates/hir-ty/src/infer/path.rs | 27 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 19 +- .../crates/hir-ty/src/mir/lower.rs | 9 +- .../hir-ty/src/mir/lower/pattern_matching.rs | 12 +- .../crates/hir-ty/src/next_solver.rs | 8 + .../hir-ty/src/next_solver/infer/mod.rs | 53 +- .../hir-ty/src/next_solver/infer/select.rs | 2 +- .../crates/hir-ty/src/next_solver/ty.rs | 29 + .../crates/hir-ty/src/tests/coercion.rs | 4 +- .../crates/hir-ty/src/tests/patterns.rs | 52 +- .../crates/hir-ty/src/tests/regression.rs | 2 +- .../crates/hir-ty/src/tests/simple.rs | 6 +- .../crates/hir/src/diagnostics.rs | 14 +- .../crates/hir/src/source_analyzer.rs | 12 +- .../src/handlers/destructure_tuple_binding.rs | 2 +- .../src/handlers/extract_function.rs | 3 +- .../src/handlers/inline_local_variable.rs | 1 + .../ide-completion/src/completions/postfix.rs | 4 +- .../src/handlers/missing_match_arms.rs | 18 +- .../src/handlers/type_mismatch.rs | 4 +- .../crates/ide/src/expand_macro.rs | 1 + .../ide/src/inlay_hints/binding_mode.rs | 14 +- .../crates/intern/src/symbol/symbols.rs | 5 + 40 files changed, 1963 insertions(+), 815 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 4ace4aef168d5..3440fbee6d78c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -1348,8 +1348,10 @@ impl<'db> ExprCollector<'db> { ast::Expr::RecordExpr(e) => { let path = e .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return Some(self.missing_expr()); + }; let record_lit = if let Some(nfl) = e.record_expr_field_list() { let fields = nfl .fields() @@ -1702,8 +1704,10 @@ impl<'db> ExprCollector<'db> { let path = collect_path(self, e.expr()?)?; let path = path .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return Some(self.missing_pat()); + }; let (ellipsis, args) = collect_tuple(self, e.arg_list()?.args()); self.alloc_pat_from_expr(Pat::TupleStruct { path, args, ellipsis }, syntax_ptr) } @@ -1733,8 +1737,10 @@ impl<'db> ExprCollector<'db> { ast::Expr::RecordExpr(e) => { let path = e .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return Some(self.missing_pat()); + }; let record_field_list = e.record_expr_field_list()?; let ellipsis = record_field_list.dotdot_token().is_some(); // FIXME: Report an error here if `record_field_list.spread().is_some()`. @@ -1988,24 +1994,27 @@ impl<'db> ExprCollector<'db> { /// ``` fn collect_for_loop(&mut self, syntax_ptr: AstPtr, e: ast::ForExpr) -> ExprId { let lang_items = self.lang_items(); - let into_iter_fn = self.lang_path(lang_items.IntoIterIntoIter); - let iter_next_fn = self.lang_path(lang_items.IteratorNext); - let option_some = self.lang_path(lang_items.OptionSome); - let option_none = self.lang_path(lang_items.OptionNone); + let (Some(into_iter_fn), Some(iter_next_fn), Some(option_some), Some(option_none)) = ( + self.lang_path(lang_items.IntoIterIntoIter), + self.lang_path(lang_items.IteratorNext), + self.lang_path(lang_items.OptionSome), + self.lang_path(lang_items.OptionNone), + ) else { + return self.missing_expr(); + }; let head = self.collect_expr_opt(e.iterable()); - let into_iter_fn_expr = - self.alloc_expr(into_iter_fn.map_or(Expr::Missing, Expr::Path), syntax_ptr); + let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr); let iterator = self.alloc_expr( Expr::Call { callee: into_iter_fn_expr, args: Box::new([head]) }, syntax_ptr, ); let none_arm = MatchArm { - pat: self.alloc_pat_desugared(option_none.map_or(Pat::Missing, Pat::Path)), + pat: self.alloc_pat_desugared(Pat::Path(option_none)), guard: None, expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr), }; let some_pat = Pat::TupleStruct { - path: option_some.map(Box::new), + path: option_some, args: Box::new([self.collect_pat_top(e.pat())]), ellipsis: None, }; @@ -2025,8 +2034,7 @@ impl<'db> ExprCollector<'db> { Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut }, syntax_ptr, ); - let iter_next_fn_expr = - self.alloc_expr(iter_next_fn.map_or(Expr::Missing, Expr::Path), syntax_ptr); + let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr); let iter_next_expr = self.alloc_expr( Expr::Call { callee: iter_next_fn_expr, args: Box::new([iter_expr_mut]) }, syntax_ptr, @@ -2074,11 +2082,15 @@ impl<'db> ExprCollector<'db> { /// ``` fn collect_try_operator(&mut self, syntax_ptr: AstPtr, e: ast::TryExpr) -> ExprId { let lang_items = self.lang_items(); - let try_branch = self.lang_path(lang_items.TryTraitBranch); - let cf_continue = self.lang_path(lang_items.ControlFlowContinue); - let cf_break = self.lang_path(lang_items.ControlFlowBreak); + let (Some(try_branch), Some(cf_continue), Some(cf_break)) = ( + self.lang_path(lang_items.TryTraitBranch), + self.lang_path(lang_items.ControlFlowContinue), + self.lang_path(lang_items.ControlFlowBreak), + ) else { + return self.missing_expr(); + }; let operand = self.collect_expr_opt(e.expr()); - let try_branch = self.alloc_expr(try_branch.map_or(Expr::Missing, Expr::Path), syntax_ptr); + let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr); let expr = self .alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr); let continue_name = self.generate_new_name(); @@ -2092,7 +2104,7 @@ impl<'db> ExprCollector<'db> { self.add_definition_to_binding(continue_binding, continue_bpat); let continue_arm = MatchArm { pat: self.alloc_pat_desugared(Pat::TupleStruct { - path: cf_continue.map(Box::new), + path: cf_continue, args: Box::new([continue_bpat]), ellipsis: None, }), @@ -2106,7 +2118,7 @@ impl<'db> ExprCollector<'db> { self.add_definition_to_binding(break_binding, break_bpat); let break_arm = MatchArm { pat: self.alloc_pat_desugared(Pat::TupleStruct { - path: cf_break.map(Box::new), + path: cf_break, args: Box::new([break_bpat]), ellipsis: None, }), @@ -2499,8 +2511,10 @@ impl<'db> ExprCollector<'db> { ast::Pat::TupleStructPat(p) => { let path = p .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return self.missing_pat(); + }; let (args, ellipsis) = self.collect_tuple_pat( p.fields(), comma_follows_token(p.l_paren_token()), @@ -2565,8 +2579,10 @@ impl<'db> ExprCollector<'db> { ast::Pat::RecordPat(p) => { let path = p .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return self.missing_pat(); + }; let record_pat_field_list = &p.record_pat_field_list().expect("every struct should have a field list"); let args = record_pat_field_list diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs index 51616afb3892c..beb126717314f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs @@ -867,11 +867,14 @@ impl<'db> ExprCollector<'db> { }; let width = RecordLitField { name: Name::new_symbol_root(sym::width), expr: width_expr }; - self.alloc_expr_desugared(Expr::RecordLit { - path: self.lang_path(lang_items.FormatPlaceholder).map(Box::new), - fields: Box::new([position, flags, precision, width]), - spread: RecordSpread::None, - }) + match self.lang_path(lang_items.FormatPlaceholder) { + Some(path) => self.alloc_expr_desugared(Expr::RecordLit { + path, + fields: Box::new([position, flags, precision, width]), + spread: RecordSpread::None, + }), + None => self.missing_expr(), + } } else { let format_placeholder_new = self.ty_rel_lang_path_desugared_expr(lang_items.FormatPlaceholder, sym::new); 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 70ea54c734cad..fdd0654508474 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 @@ -668,10 +668,7 @@ impl Printer<'_> { } } Expr::RecordLit { path, fields, spread } => { - match path { - Some(path) => self.print_path(path), - None => w!(self, "�"), - } + self.print_path(path); w!(self, "{{"); let edition = self.edition; @@ -923,10 +920,7 @@ impl Printer<'_> { w!(self, ")"); } Pat::Record { path, args, ellipsis } => { - match path { - Some(path) => self.print_path(path), - None => w!(self, "�"), - } + self.print_path(path); w!(self, " {{"); let edition = self.edition; @@ -1004,10 +998,7 @@ impl Printer<'_> { } } Pat::TupleStruct { path, args, ellipsis } => { - match path { - Some(path) => self.print_path(path), - None => w!(self, "�"), - } + self.print_path(path); w!(self, "("); for (i, arg) in args.iter().enumerate() { if i != 0 { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 031280b6e92ef..a1a346cabc307 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -259,7 +259,7 @@ pub enum Expr { expr: Option, }, RecordLit { - path: Option>, + path: Path, fields: Box<[RecordLitField]>, spread: RecordSpread, }, @@ -673,7 +673,7 @@ pub enum Pat { }, Or(Box<[PatId]>), Record { - path: Option>, + path: Path, args: Box<[RecordFieldPat]>, ellipsis: bool, }, @@ -694,7 +694,7 @@ pub enum Pat { subpat: Option, }, TupleStruct { - path: Option>, + path: Path, args: Box<[PatId]>, ellipsis: Option, }, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 5b6ebe2a0b2da..6071ed2981809 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -365,9 +365,10 @@ language_item_table! { LangItems => Deref, sym::deref, TraitId; DerefMut, sym::deref_mut, TraitId; + DerefPure, sym::deref_pure, TraitId; DerefTarget, sym::deref_target, TypeAliasId; Receiver, sym::receiver, TraitId; - ReceiverTarget, sym::receiver_target, TypeAliasId; + ReceiverTarget, sym::receiver_target, TypeAliasId; Fn, sym::fn_, TraitId; FnMut, sym::fn_mut, TraitId; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/unstable_features.rs b/src/tools/rust-analyzer/crates/hir-def/src/unstable_features.rs index 3b7f83081f8ea..559726fe9b65b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/unstable_features.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/unstable_features.rs @@ -88,4 +88,8 @@ define_unstable_features! { never_type_fallback, specialization, min_specialization, + ref_pat_eat_one_layer_2024, + ref_pat_eat_one_layer_2024_structural, + deref_patterns, + mut_ref, } 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 068118c7053d8..99dddf0bf4cde 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 @@ -218,9 +218,7 @@ impl<'db> ExprValidator<'db> { // Note: Skipping the entire diagnostic rather than just not including a faulty match arm is // preferred to avoid the chance of false positives. for arm in arms { - let Some(pat_ty) = self.infer.type_of_pat_with_adjust(arm.pat) else { - return; - }; + let pat_ty = self.infer.type_of_pat_with_adjust(arm.pat); if pat_ty.references_non_lt_error() { return; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs index f559c26bf57ea..8356329d96ae3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs @@ -22,7 +22,7 @@ use span::Edition; use stdx::{always, never, variance::PhantomCovariantLifetime}; use crate::{ - InferenceResult, + ByRef, InferenceResult, db::HirDatabase, display::{HirDisplay, HirDisplayError, HirFormatter}, infer::BindingMode, @@ -121,7 +121,7 @@ impl<'a, 'db> PatCtxt<'a, 'db> { self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold( unadjusted_pat, |subpattern, ref_ty| Pat { - ty: ref_ty.as_ref(), + ty: ref_ty.source.as_ref(), kind: Box::new(PatKind::Deref { subpattern }), }, ) @@ -158,8 +158,8 @@ impl<'a, 'db> PatCtxt<'a, 'db> { ty = self.infer.binding_ty(id); let name = &self.body[id].name; match (bm, ty.kind()) { - (BindingMode::Ref(_), TyKind::Ref(_, rty, _)) => ty = rty, - (BindingMode::Ref(_), _) => { + (BindingMode(ByRef::Yes(_), _), TyKind::Ref(_, rty, _)) => ty = rty, + (BindingMode(ByRef::Yes(_), _), _) => { never!( "`ref {}` has wrong type {:?}", name.display(self.db, Edition::LATEST), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 9acd5c0e0fc34..54f334b66d2b1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -34,11 +34,12 @@ use std::{cell::OnceCell, convert::identity, fmt, iter, ops::Deref}; use base_db::{Crate, FxIndexMap}; use either::Either; use hir_def::{ - AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, ExpressionStoreOwnerId, FieldId, - FunctionId, GenericDefId, GenericParamId, ItemContainerId, LocalFieldId, Lookup, TraitId, - TupleFieldId, TupleId, TypeAliasId, TypeOrConstParamId, VariantId, + AdtId, AssocItemId, AttrDefId, ConstId, ConstParamId, DefWithBodyId, ExpressionStoreOwnerId, + FieldId, FunctionId, GenericDefId, GenericParamId, HasModule, ItemContainerId, LocalFieldId, + Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, TypeOrConstParamId, VariantId, + attrs::AttrFlags, expr_store::{Body, ExpressionStore, HygieneId, RootExprOrigin, path::Path}, - hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId}, + hir::{BindingId, ExprId, ExprOrPatId, LabelId, PatId}, lang_item::LangItems, layout::Integer, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, @@ -75,6 +76,7 @@ use crate::{ coerce::{CoerceMany, DynamicCoerceMany}, diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext}, expr::ExprIsRead, + pat::PatOrigin, }, lower::{ ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic, @@ -283,25 +285,21 @@ fn infer_finalize(mut ctx: InferenceContext<'_, '_>) -> InferenceResult { ctx.resolve_all() } -/// Binding modes inferred for patterns. -/// -#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] -pub enum BindingMode { - #[default] - Move, - Ref(Mutability), -} -impl BindingMode { - fn convert(annotation: BindingAnnotation) -> BindingMode { - match annotation { - BindingAnnotation::Unannotated | BindingAnnotation::Mutable => BindingMode::Move, - BindingAnnotation::Ref => BindingMode::Ref(Mutability::Not), - BindingAnnotation::RefMut => BindingMode::Ref(Mutability::Mut), - } - } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ByRef { + Yes(Mutability), + No, } +/// The mode of a binding (`mut`, `ref mut`, etc). +/// Used for both the explicit binding annotations given in the HIR for a binding +/// and the final binding mode that we infer after type inference/match ergonomics. +/// `.0` is the by-reference mode (`ref`, `ref mut`, or by value), +/// `.1` is the mutability of the binding. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct BindingMode(pub ByRef, pub Mutability); + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum InferenceTyDiagnosticSource { /// Diagnostics that come from types in the body. @@ -357,7 +355,7 @@ pub enum InferenceDiagnostic { found: usize, }, MismatchedTupleStructPatArgCount { - pat: ExprOrPatId, + pat: PatId, expected: usize, found: usize, }, @@ -576,6 +574,27 @@ pub enum PointerCast { Unsize, } +/// Represents an implicit coercion applied to the scrutinee of a match before testing a pattern +/// against it. Currently, this is used only for implicit dereferences. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PatAdjustment { + pub kind: PatAdjust, + /// The type of the scrutinee before the adjustment is applied, or the "adjusted type" of the + /// pattern. + pub source: StoredTy, +} + +/// Represents implicit coercions of patterns' types, rather than values' types. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum PatAdjust { + /// An implicit dereference before matching, such as when matching the pattern `0` against a + /// scrutinee of type `&u8` or `&mut u8`. + BuiltinDeref, + /// An implicit call to `Deref(Mut)::deref(_mut)` before matching, such as when matching the + /// pattern `[..]` against a scrutinee of type `Vec`. + OverloadedDeref, +} + /// The result of type inference: A mapping from expressions and patterns to types. /// /// When you add a field that stores types (including `Substitution` and the like), don't forget @@ -621,7 +640,7 @@ pub struct InferenceResult { pub(crate) expr_adjustments: FxHashMap>, /// Stores the types which were implicitly dereferenced in pattern binding modes. - pub(crate) pat_adjustments: FxHashMap>, + pub(crate) pat_adjustments: FxHashMap>, /// Stores the binding mode (`ref` in `let ref x = 2`) of bindings. /// /// This one is tied to the `PatId` instead of `BindingId`, because in some rare cases, a binding in an @@ -637,6 +656,10 @@ pub struct InferenceResult { /// the first `rest` has implicit `ref` binding mode, but the second `rest` binding mode is `move`. pub(crate) binding_modes: ArenaMap, + /// Set of reference patterns that match against a match-ergonomics inserted reference + /// (as opposed to against a reference in the scrutinee type). + skipped_ref_pats: FxHashSet, + pub(crate) coercion_casts: FxHashSet, pub closures_data: FxHashMap, @@ -909,6 +932,7 @@ impl InferenceResult { type_of_type_placeholder: Default::default(), type_of_opaque: Default::default(), type_mismatches: Default::default(), + skipped_ref_pats: Default::default(), has_errors: Default::default(), error_ty: error_ty.store(), pat_adjustments: Default::default(), @@ -1008,10 +1032,10 @@ impl InferenceResult { None => self.type_of_expr.get(id).map(|it| it.as_ref()), } } - pub fn type_of_pat_with_adjust<'db>(&self, id: PatId) -> Option> { + pub fn type_of_pat_with_adjust<'db>(&self, id: PatId) -> Ty<'db> { match self.pat_adjustments.get(&id).and_then(|adjustments| adjustments.last()) { - Some(adjusted) => Some(adjusted.as_ref()), - None => self.type_of_pat.get(id).map(|it| it.as_ref()), + Some(adjusted) => adjusted.source.as_ref(), + None => self.pat_ty(id), } } pub fn is_erroneous(&self) -> bool { @@ -1026,7 +1050,7 @@ impl InferenceResult { self.tuple_field_access_types[id.0 as usize].as_ref() } - pub fn pat_adjustment(&self, id: PatId) -> Option<&[StoredTy]> { + pub fn pat_adjustment(&self, id: PatId) -> Option<&[PatAdjustment]> { self.pat_adjustments.get(&id).map(|it| &**it) } @@ -1034,8 +1058,8 @@ impl InferenceResult { self.expr_adjustments.get(&id).map(|it| &**it) } - pub fn binding_mode(&self, id: PatId) -> Option { - self.binding_modes.get(id).copied() + pub fn binding_mode(&self, id: PatId) -> BindingMode { + self.binding_modes[id] } // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please. @@ -1101,6 +1125,10 @@ impl InferenceResult { .values() .flat_map(|captures| captures.iter().map(|capture| capture.captured_ty(db))) } + + pub fn is_skipped_ref_pat(&self, pat: PatId) -> bool { + self.skipped_ref_pats.contains(&pat) + } } /// The inference context contains all information needed during type inference. @@ -1316,6 +1344,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { type_of_binding, type_of_type_placeholder, type_of_opaque, + skipped_ref_pats, type_mismatches, closures_data, has_errors, @@ -1328,6 +1357,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { diagnostics: _, } = &mut result; + skipped_ref_pats.shrink_to_fit(); for ty in type_of_expr.values_mut() { *ty = table.resolve_completely(ty.as_ref()).store(); *has_errors = *has_errors || ty.as_ref().references_non_lt_error(); @@ -1404,9 +1434,12 @@ impl<'body, 'db> InferenceContext<'body, 'db> { *has_errors = *has_errors || adjustment.target.as_ref().references_non_lt_error(); } expr_adjustments.shrink_to_fit(); - for adjustment in pat_adjustments.values_mut().flatten() { - *adjustment = table.resolve_completely(adjustment.as_ref()).store(); - *has_errors = *has_errors || adjustment.as_ref().references_non_lt_error(); + for adjustments in pat_adjustments.values_mut() { + for adjustment in &mut *adjustments { + adjustment.source = table.resolve_completely(adjustment.source.as_ref()).store(); + *has_errors = *has_errors || adjustment.source.as_ref().references_non_lt_error(); + } + adjustments.shrink_to_fit(); } pat_adjustments.shrink_to_fit(); for closure_data in closures_data.values_mut() { @@ -1515,7 +1548,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { for (ty, pat) in param_tys.zip(params) { let ty = self.process_user_written_ty(ty); - self.infer_top_pat(*pat, ty, None); + self.infer_top_pat(*pat, ty, PatOrigin::Param); } self.return_ty = match data.ret_type { Some(return_ty) => { @@ -1589,13 +1622,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } } - fn write_pat_adj(&mut self, pat: PatId, adjustments: Box<[StoredTy]>) { - if adjustments.is_empty() { - return; - } - self.result.pat_adjustments.entry(pat).or_default().extend(adjustments); - } - pub(crate) fn write_method_resolution( &mut self, expr: ExprId, @@ -1882,15 +1908,24 @@ impl<'body, 'db> InferenceContext<'body, 'db> { result.map_err(drop) } - fn demand_suptype(&mut self, expected: Ty<'db>, actual: Ty<'db>) { + fn demand_suptype( + &mut self, + id: ExprOrPatId, + expected: Ty<'db>, + actual: Ty<'db>, + ) -> Result<(), ()> { let result = self .table .at(&ObligationCause::new()) .sup(expected, actual) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); - if let Err(_err) = result { - // FIXME: Emit diagnostic. + if result.is_err() { + self.result + .type_mismatches + .get_or_insert_default() + .insert(id, TypeMismatch { expected: expected.store(), actual: actual.store() }); } + result.map_err(drop) } fn demand_coerce( @@ -1901,7 +1936,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { allow_two_phase: AllowTwoPhase, expr_is_read: ExprIsRead, ) -> Ty<'db> { - let result = self.coerce(expr.into(), checked_ty, expected, allow_two_phase, expr_is_read); + let result = self.coerce(expr, checked_ty, expected, allow_two_phase, expr_is_read); if let Err(_err) = result { // FIXME: Emit diagnostic. } @@ -1966,13 +2001,9 @@ impl<'body, 'db> InferenceContext<'body, 'db> { fn resolve_variant( &mut self, node: ExprOrPatId, - path: Option<&Path>, + path: &Path, value_ns: bool, ) -> (Ty<'db>, Option) { - let path = match path { - Some(path) => path, - None => return (self.err_ty(), None), - }; let mut ctx = TyLoweringContext::new( self.db, &self.resolver, @@ -2373,6 +2404,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { Either::Right(&self.traits_in_scope) } } + + fn has_applicable_non_exhaustive(&self, def: AttrDefId) -> bool { + AttrFlags::query(self.db, def).contains(AttrFlags::NON_EXHAUSTIVE) + && def.krate(self.db) != self.krate() + } } /// When inferring an expression, we propagate downward whatever type hint we diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index e5ee73447471d..09855766d8bc4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -131,7 +131,7 @@ impl<'db> CastCheck<'db> { // This should always come first so that we apply the coercion, which impacts infer vars. if ctx .coerce( - self.source_expr.into(), + self.source_expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No, @@ -167,7 +167,7 @@ impl<'db> CastCheck<'db> { let sig = self.expr_ty.fn_sig(ctx.interner()); let fn_ptr = Ty::new_fn_ptr(ctx.interner(), sig); match ctx.coerce( - self.source_expr.into(), + self.source_expr, self.expr_ty, fn_ptr, AllowTwoPhase::No, @@ -275,7 +275,7 @@ impl<'db> CastCheck<'db> { let array_ptr_type = Ty::new_ptr(ctx.interner(), t_expr, m_expr); if ctx .coerce( - self.source_expr.into(), + self.source_expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 2207bc37e8be7..0d2de9b9846e6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -20,7 +20,7 @@ use tracing::debug; use crate::{ FnAbi, db::{InternedClosure, InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId}, - infer::{BreakableKind, Diverges, coerce::CoerceMany}, + infer::{BreakableKind, Diverges, coerce::CoerceMany, pat::PatOrigin}, next_solver::{ AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig, PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, Ty, TyKind, @@ -263,7 +263,7 @@ impl<'db> InferenceContext<'_, 'db> { // Now go through the argument patterns for (arg_pat, arg_ty) in args.iter().zip(bound_sig.skip_binder().inputs()) { - self.infer_top_pat(*arg_pat, *arg_ty, None); + self.infer_top_pat(*arg_pat, *arg_ty, PatOrigin::Param); } // FIXME: lift these out into a struct diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs index 668d7496cd1b2..b0f5533b3b902 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -506,7 +506,11 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // Build a tuple (U0..Un) of the final upvar types U0..Un // and unify the upvar tuple type in the closure with it: let final_tupled_upvars_type = Ty::new_tup(self.interner(), &final_upvar_tys); - self.demand_suptype(args.tupled_upvars_ty(), final_tupled_upvars_type); + _ = self.demand_suptype( + closure_expr_id.into(), + args.tupled_upvars_ty(), + final_tupled_upvars_type, + ); let fake_reads = delegate.fake_reads; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index 45696c402a662..bddb01ff99d25 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -21,11 +21,13 @@ use rustc_type_ir::{ }; use smallvec::{SmallVec, smallvec}; use syntax::ast::{BinaryOp, UnaryOp}; -use tracing::{debug, instrument}; +use tracing::{debug, instrument, trace}; use crate::{ - Adjust, Adjustment, AutoBorrow, BindingMode, - infer::{CaptureSourceStack, InferenceContext, UpvarCapture, closure::analysis::BorrowKind}, + Adjust, Adjustment, AutoBorrow, + infer::{ + ByRef, CaptureSourceStack, InferenceContext, UpvarCapture, closure::analysis::BorrowKind, + }, method_resolution::CandidateId, next_solver::{DbInterner, ErrorGuaranteed, StoredTy, Ty, TyKind}, upvars::UpvarsRef, @@ -947,12 +949,12 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { // In a cases of pattern like `let pat = upvar`, don't use the span // of the pattern, as this just looks confusing, instead use the span // of the discriminant. - match this.cx.result.binding_mode(pat) { - Some(BindingMode::Ref(m)) => { + match this.cx.result.binding_mode(pat).0 { + ByRef::Yes(m) => { let bk = BorrowKind::from_mutbl(m); this.delegate.borrow(place, bk, this.cx); } - None | Some(BindingMode::Move) => { + ByRef::No => { debug!("walk_pat binding consuming pat"); this.consume_or_copy(place); } @@ -1194,18 +1196,48 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { // that these are never attached to binding patterns, so // actually this is somewhat "disjoint" from the code below // that aims to account for `ref x`. - if let Some(vec) = self.cx.result.pat_adjustments.get(&pat) - && let Some(first_adjust) = vec.first() + if let Some(vec) = self.cx.result.pat_adjustment(pat) { + if let Some(first_adjust) = vec.first() { + debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust); + return Ok(first_adjust.source.as_ref()); + } + } else if let Pat::Ref { pat: subpat, .. } = self.cx.store[pat] + && self.cx.result.is_skipped_ref_pat(pat) { - debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust); - return Ok(first_adjust.as_ref()); + return self.pat_ty_adjusted(subpat); } + self.pat_ty_unadjusted(pat) } /// Like [`Self::pat_ty_adjusted`], but ignores implicit `&` patterns. fn pat_ty_unadjusted(&mut self, pat: PatId) -> Result> { - Ok(self.cx.result.pat_ty(pat)) + let base_ty = self.node_ty(pat.into())?; + trace!(?base_ty); + + // This code detects whether we are looking at a `ref x`, + // and if so, figures out what the type *being borrowed* is. + match self.cx.store[pat] { + Pat::Bind { .. } => { + let bm = self.cx.result.binding_mode(pat); + + if let ByRef::Yes(_) = bm.0 { + // a bind-by-ref means that the base_ty will be the type of the ident itself, + // but what we want here is the type of the underlying value being borrowed. + // So peel off one-level, turning the &T into T. + match self.cx.table.structurally_resolve_type(base_ty).builtin_deref(false) { + Some(ty) => Ok(ty), + None => { + debug!("By-ref binding of non-derefable type: {base_ty:?}"); + Err(ErrorGuaranteed) + } + } + } else { + Ok(base_ty) + } + } + _ => Ok(base_ty), + } } fn cat_expr(&mut self, expr: ExprId) -> Result { @@ -1602,7 +1634,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { .builtin_index() else { debug!("explicit index of non-indexable type {:?}", place_with_id); - panic!("explicit index of non-indexable type"); + return Err(ErrorGuaranteed); }; let elt_place = self.cat_projection( pat.into(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 732a583047494..db912c0eb6c78 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -38,10 +38,7 @@ use std::ops::ControlFlow; use hir_def::{ - CallableDefId, TraitId, - attrs::AttrFlags, - hir::{ExprId, ExprOrPatId}, - signatures::FunctionSignature, + CallableDefId, TraitId, attrs::AttrFlags, hir::ExprId, signatures::FunctionSignature, }; use rustc_ast_ir::Mutability; use rustc_type_ir::{ @@ -894,7 +891,7 @@ impl<'db> InferenceContext<'_, 'db> { /// The expressions *must not* have any preexisting adjustments. pub(crate) fn coerce( &mut self, - expr: ExprOrPatId, + expr: ExprId, expr_ty: Ty<'db>, mut target: Ty<'db>, allow_two_phase: AllowTwoPhase, @@ -905,13 +902,7 @@ impl<'db> InferenceContext<'_, 'db> { debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target); let cause = ObligationCause::new(); - let coerce_never = match expr { - ExprOrPatId::ExprId(idx) => { - self.expr_guaranteed_to_constitute_read_for_never(idx, expr_is_read) - } - // `PatId` is passed for `PatKind::Path`. - ExprOrPatId::PatId(_) => false, - }; + let coerce_never = self.expr_guaranteed_to_constitute_read_for_never(expr, expr_is_read); let mut coerce = Coerce { delegate: InferenceCoercionDelegate(self), cause, @@ -922,11 +913,7 @@ impl<'db> InferenceContext<'_, 'db> { let ok = coerce.commit_if_ok(|coerce| coerce.coerce(source, target))?; let (adjustments, _) = self.table.register_infer_ok(ok); - match expr { - ExprOrPatId::ExprId(expr) => self.write_expr_adj(expr, adjustments.into_boxed_slice()), - ExprOrPatId::PatId(pat) => self - .write_pat_adj(pat, adjustments.into_iter().map(|adjust| adjust.target).collect()), - } + self.write_expr_adj(expr, adjustments.into_boxed_slice()); Ok(target) } @@ -1335,7 +1322,7 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { // To be honest, I'm not entirely sure why we do this. // We don't allow two-phase borrows, see comment in try_find_coercion_lub for why icx.coerce( - expression.into(), + expression, expression_ty, self.expected_ty, AllowTwoPhase::No, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index f26be638067f0..e84a03a2e72a9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -24,12 +24,9 @@ use syntax::ast::RangeOp; use tracing::debug; use crate::{ - Adjust, Adjustment, CallableDefId, DeclContext, DeclOrigin, Rawness, consteval, + Adjust, Adjustment, CallableDefId, Rawness, consteval, generics::generics, - infer::{ - AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable, - pat::contains_explicit_ref_binding, - }, + infer::{AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable, pat::PatOrigin}, lower::{GenericPredicates, lower_mutability}, method_resolution::{self, CandidateId, MethodCallee, MethodError}, next_solver::{ @@ -92,7 +89,7 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { let ty = self.infer_expr_inner(expr, expected, is_read); if let Some(target) = expected.only_has_type(&mut self.table) { - match self.coerce(expr.into(), ty, target, AllowTwoPhase::No, is_read) { + match self.coerce(expr, ty, target, AllowTwoPhase::No, is_read) { Ok(res) => res, Err(_) => { self.result.type_mismatches.get_or_insert_default().insert( @@ -279,7 +276,7 @@ impl<'db> InferenceContext<'_, 'db> { } if let Some(target) = expected.only_has_type(&mut self.table) { - self.coerce(expr.into(), ty, target, AllowTwoPhase::No, ExprIsRead::Yes) + self.coerce(expr, ty, target, AllowTwoPhase::No, ExprIsRead::Yes) .expect("never-to-any coercion should always succeed") } else { ty @@ -366,11 +363,7 @@ impl<'db> InferenceContext<'_, 'db> { ExprIsRead::No }; let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read); - self.infer_top_pat( - pat, - input_ty, - Some(DeclContext { origin: DeclOrigin::LetExpr }), - ); + self.infer_top_pat(pat, input_ty, PatOrigin::LetExpr); self.types.types.bool } Expr::Block { statements, tail, label, id: _ } => { @@ -449,7 +442,7 @@ impl<'db> InferenceContext<'_, 'db> { let matchee_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let mut all_arms_diverge = Diverges::Always; for arm in arms.iter() { - self.infer_top_pat(arm.pat, input_ty, None); + self.infer_top_pat(arm.pat, input_ty, PatOrigin::MatchArm); } let expected = expected.adjust_for_branches(&mut self.table); @@ -566,7 +559,7 @@ impl<'db> InferenceContext<'_, 'db> { } else { let unit = self.types.types.unit; let _ = self.coerce( - tgt_expr.into(), + tgt_expr, unit, yield_ty, AllowTwoPhase::No, @@ -586,7 +579,7 @@ impl<'db> InferenceContext<'_, 'db> { self.types.types.never } Expr::RecordLit { path, fields, spread, .. } => { - let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path.as_deref(), false); + let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path, false); if let Some(t) = expected.only_has_type(&mut self.table) { self.unify(ty, t); @@ -727,7 +720,7 @@ impl<'db> InferenceContext<'_, 'db> { let resolver_guard = self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); self.inside_assignment = true; - self.infer_top_pat(target, rhs_ty, None); + self.infer_top_pat(target, rhs_ty, PatOrigin::DestructuringAssignment); self.inside_assignment = false; self.resolver.reset_to_guard(resolver_guard); } @@ -959,7 +952,7 @@ impl<'db> InferenceContext<'_, 'db> { .instantiate(this.interner(), parameters), ); _ = this.coerce( - expr.into(), + expr, ty, fnptr_ty, AllowTwoPhase::No, @@ -969,7 +962,7 @@ impl<'db> InferenceContext<'_, 'db> { TyKind::Ref(_, base_ty, mutbl) => { let ptr_ty = Ty::new_ptr(this.interner(), base_ty, mutbl); _ = this.coerce( - expr.into(), + expr, ty, ptr_ty, AllowTwoPhase::No, @@ -1100,7 +1093,7 @@ impl<'db> InferenceContext<'_, 'db> { fn infer_expr_path(&mut self, path: &Path, id: ExprOrPatId, scope_id: ExprId) -> Ty<'db> { let g = self.resolver.update_to_inner_scope(self.db, self.owner, scope_id); let ty = match self.infer_path(path, id) { - Some(ty) => ty, + Some((_, ty)) => ty, None => { if path.mod_path().is_some_and(|mod_path| mod_path.is_ident() || mod_path.is_self()) { @@ -1288,16 +1281,7 @@ impl<'db> InferenceContext<'_, 'db> { .unwrap_or_else(Expectation::none); let inner_ty = self.infer_expr_inner(inner_expr, &inner_exp, ExprIsRead::Yes); - Ty::new_adt( - self.interner(), - box_id, - GenericArgs::fill_with_defaults( - self.interner(), - box_id.into(), - [inner_ty.into()], - |_, id, _| self.table.next_var_for_param(id), - ), - ) + Ty::new_box(self.interner(), inner_ty) } else { self.err_ty() } @@ -1333,7 +1317,7 @@ impl<'db> InferenceContext<'_, 'db> { } else { ExprIsRead::No }; - let ty = if contains_explicit_ref_binding(this.store, *pat) { + let ty = if this.contains_explicit_ref_binding(*pat) { this.infer_expr( *expr, &Expectation::has_type(decl_ty), @@ -1351,11 +1335,11 @@ impl<'db> InferenceContext<'_, 'db> { decl_ty }; - let decl = DeclContext { - origin: DeclOrigin::LocalDecl { has_else: else_branch.is_some() }, - }; - - this.infer_top_pat(*pat, ty, Some(decl)); + this.infer_top_pat( + *pat, + ty, + PatOrigin::LetStmt { has_else: else_branch.is_some() }, + ); if let Some(expr) = else_branch { let previous_diverges = mem::replace(&mut this.diverges, Diverges::Maybe); @@ -1399,7 +1383,7 @@ impl<'db> InferenceContext<'_, 'db> { } else if let Some(t) = expected.only_has_type(&mut this.table) { if this .coerce( - expr.into(), + expr, this.types.types.unit, t, AllowTwoPhase::No, @@ -1893,13 +1877,7 @@ impl<'db> InferenceContext<'_, 'db> { let coerced_ty = this.table.resolve_vars_with_obligations(coerced_ty); let coerce_error = this - .coerce( - provided_arg.into(), - checked_ty, - coerced_ty, - AllowTwoPhase::Yes, - ExprIsRead::Yes, - ) + .coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, ExprIsRead::Yes) .err(); if coerce_error.is_some() { return Err((coerce_error, coerced_ty, checked_ty)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs index 95d63ffb508f6..2916a46ca3302 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs @@ -38,7 +38,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, category) { - self.enforce_builtin_binop_types(lhs_ty, rhs_ty, category); + self.enforce_builtin_binop_types(expr, lhs_ty, rhs_ty, category); self.types.types.unit } else { return_ty @@ -107,7 +107,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { && is_builtin_binop(lhs_ty, rhs_ty, category) { let builtin_return_ty = - self.enforce_builtin_binop_types(lhs_ty, rhs_ty, category); + self.enforce_builtin_binop_types(expr, lhs_ty, rhs_ty, category); _ = self.demand_eqtype(expr.into(), builtin_return_ty, return_ty); builtin_return_ty } else { @@ -119,6 +119,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { fn enforce_builtin_binop_types( &mut self, + expr: ExprId, lhs_ty: Ty<'db>, rhs_ty: Ty<'db>, category: BinOpCategory, @@ -131,8 +132,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { match category { BinOpCategory::Shortcircuit => { - self.demand_suptype(self.types.types.bool, lhs_ty); - self.demand_suptype(self.types.types.bool, rhs_ty); + _ = self.demand_suptype(expr.into(), self.types.types.bool, lhs_ty); + _ = self.demand_suptype(expr.into(), self.types.types.bool, rhs_ty); self.types.types.bool } @@ -143,13 +144,13 @@ impl<'a, 'db> InferenceContext<'a, 'db> { BinOpCategory::Math | BinOpCategory::Bitwise => { // both LHS and RHS and result will have the same type - self.demand_suptype(lhs_ty, rhs_ty); + _ = self.demand_suptype(expr.into(), lhs_ty, rhs_ty); lhs_ty } BinOpCategory::Comparison => { // both LHS and RHS and result will have the same type - self.demand_suptype(lhs_ty, rhs_ty); + _ = self.demand_suptype(expr.into(), lhs_ty, rhs_ty); self.types.types.bool } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index a437253672014..bba69c758a685 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -1,645 +1,1516 @@ //! Type inference for patterns. -use std::{cmp, iter}; +use std::{ + cmp, + collections::hash_map::Entry::{Occupied, Vacant}, + iter, +}; use hir_def::{ - HasModule as _, - expr_store::{ExpressionStore, path::Path}, - hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId}, + AdtId, LocalFieldId, VariantId, + expr_store::path::Path, + hir::{ + BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId, + RecordFieldPat, + }, + resolver::ValueNs, signatures::VariantFields, }; -use hir_expand::name::Name; use rustc_ast_ir::Mutability; -use rustc_type_ir::inherent::{GenericArg as _, GenericArgs as _, IntoKind, Ty as _}; -use stdx::TupleExt; +use rustc_hash::FxHashMap; +use rustc_type_ir::{ + TypeVisitableExt as _, + inherent::{AdtDef as _, IntoKind as _, Ty as _}, +}; +use span::Edition; +use tracing::{debug, instrument, trace}; use crate::{ - DeclContext, DeclOrigin, InferenceDiagnostic, - consteval::{self, try_const_usize, usize_const}, + BindingMode, InferenceDiagnostic, infer::{ - AllowTwoPhase, BindingMode, Expectation, InferenceContext, TypeMismatch, expr::ExprIsRead, + AllowTwoPhase, ByRef, Expectation, InferenceContext, PatAdjust, PatAdjustment, + TypeMismatch, expr::ExprIsRead, + }, + next_solver::{ + Const, TraitRef, Ty, TyKind, Tys, + infer::{ + InferOk, + traits::{Obligation, ObligationCause}, + }, }, - lower::lower_mutability, - next_solver::{GenericArgs, Ty, TyKind, Tys, infer::traits::ObligationCause}, + utils::EnumerateAndAdjustIterator, }; -impl<'db> InferenceContext<'_, 'db> { - /// Infers type for tuple struct pattern or its corresponding assignee expression. - /// - /// Ellipses found in the original pattern or expression must be filtered out. - pub(super) fn infer_tuple_struct_pat_like( - &mut self, - path: Option<&Path>, - expected: Ty<'db>, - default_bm: BindingMode, - id: PatId, - ellipsis: Option, - subs: &[PatId], - decl: Option, - ) -> Ty<'db> { - let (ty, def) = self.resolve_variant(id.into(), path, true); - let var_data = def.map(|it| it.fields(self.db)); - if let Some(variant) = def { - self.write_variant_resolution(id.into(), variant); - } - if let Some(var) = &var_data { - let cmp = if ellipsis.is_some() { usize::gt } else { usize::ne }; - - if cmp(&subs.len(), &var.fields().len()) { - self.push_diagnostic(InferenceDiagnostic::MismatchedTupleStructPatArgCount { - pat: id.into(), - expected: var.fields().len(), - found: subs.len(), - }); - } +impl ByRef { + #[must_use] + fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self { + if let ByRef::Yes(old_mutbl) = &mut self { + *old_mutbl = cmp::min(*old_mutbl, mutbl); } + self + } +} - self.unify(ty, expected); +impl BindingMode { + fn from_annotation(annotation: BindingAnnotation) -> BindingMode { + match annotation { + BindingAnnotation::Unannotated => BindingMode(ByRef::No, Mutability::Not), + BindingAnnotation::Mutable => BindingMode(ByRef::No, Mutability::Mut), + BindingAnnotation::Ref => BindingMode(ByRef::Yes(Mutability::Not), Mutability::Not), + BindingAnnotation::RefMut => BindingMode(ByRef::Yes(Mutability::Mut), Mutability::Not), + } + } +} - match def { - _ if subs.is_empty() => {} - Some(def) => { - let field_types = self.db.field_types(def); - let variant_data = def.fields(self.db); - let visibilities = VariantFields::field_visibilities(self.db, def); +#[derive(Clone, Copy, PartialEq, Eq)] +pub(super) enum PatOrigin { + LetExpr, + LetStmt { has_else: bool }, + Param, + MatchArm, + DestructuringAssignment, +} - let (pre, post) = match ellipsis { - Some(idx) => subs.split_at(idx as usize), - None => (subs, &[][..]), - }; - let post_idx_offset = field_types.iter().count().saturating_sub(post.len()); - - let pre_iter = pre.iter().enumerate(); - let post_iter = (post_idx_offset..).zip(post.iter()); - - let substs = ty.as_adt().map(TupleExt::tail); - - for (i, &subpat) in pre_iter.chain(post_iter) { - let expected_ty = { - match variant_data.field(&Name::new_tuple_field(i)) { - Some(local_id) => { - if !visibilities[local_id] - .is_visible_from(self.db, self.resolver.module()) - { - // FIXME(DIAGNOSE): private tuple field - } - let f = field_types[local_id].get(); - let expected_ty = match substs { - Some(substs) => f.instantiate(self.interner(), substs), - None => f.instantiate(self.interner(), &[]), - }; - self.process_remote_user_written_ty(expected_ty) - } - None => self.err_ty(), - } - }; +impl PatOrigin { + fn default_binding_modes(self) -> bool { + self != PatOrigin::DestructuringAssignment + } +} - self.infer_pat(subpat, expected_ty, default_bm, decl); - } - } - None => { - let err_ty = self.err_ty(); - for &inner in subs { - self.infer_pat(inner, err_ty, default_bm, decl); - } - } +#[derive(Copy, Clone)] +struct PatInfo { + binding_mode: ByRef, + max_ref_mutbl: MutblCap, + pat_origin: PatOrigin, +} + +/// Mode for adjusting the expected type and binding mode. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum AdjustMode { + /// Peel off all immediate reference types. If the `deref_patterns` feature is enabled, this + /// also peels smart pointer ADTs. + Peel { kind: PeelKind }, + /// Pass on the input binding mode and expected type. + Pass, +} + +/// Restrictions on what types to peel when adjusting the expected type and binding mode. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum PeelKind { + /// Only peel reference types. This is used for explicit `deref!(_)` patterns, which dereference + /// any number of `&`/`&mut` references, plus a single smart pointer. + ExplicitDerefPat, + /// Implicitly peel references, and if `deref_patterns` is enabled, smart pointer ADTs. + Implicit { + /// The ADT the pattern is a constructor for, if applicable, so that we don't peel it. See + /// [`ResolvedPat`] for more information. + until_adt: Option, + /// The number of references at the head of the pattern's type, so we can leave that many + /// untouched. This is `1` for string literals, and `0` for most patterns. + pat_ref_layers: usize, + }, +} + +impl AdjustMode { + const fn peel_until_adt(opt_adt_def: Option) -> AdjustMode { + AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def, pat_ref_layers: 0 } } + } + const fn peel_all() -> AdjustMode { + AdjustMode::peel_until_adt(None) + } +} + +/// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference. +/// Normally, the borrow checker enforces this, but for (currently experimental) match ergonomics, +/// we track this when typing patterns for two purposes: +/// +/// - For RFC 3627's Rule 3, when this would prevent us from binding with `ref mut`, we limit the +/// default binding mode to be by shared `ref` when it would otherwise be `ref mut`. +/// +/// - For RFC 3627's Rule 5, we allow `&` patterns to match against `&mut` references, treating them +/// as if they were shared references. Since the scrutinee is mutable in this case, the borrow +/// checker won't catch if we bind with `ref mut`, so we need to throw an error ourselves. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum MutblCap { + /// Mutability restricted to immutable. + Not, + + /// Mutability restricted to immutable, but only because of the pattern + /// (not the scrutinee type). + /// + /// The contained span, if present, points to an `&` pattern + /// that is the reason for the restriction, + /// and which will be reported in a diagnostic. + WeaklyNot, + + /// No restriction on mutability + Mut, +} + +impl MutblCap { + #[must_use] + fn cap_to_weakly_not(self) -> Self { + match self { + MutblCap::Not => MutblCap::Not, + _ => MutblCap::WeaklyNot, } + } - ty + #[must_use] + fn as_mutbl(self) -> Mutability { + match self { + MutblCap::Not | MutblCap::WeaklyNot => Mutability::Not, + MutblCap::Mut => Mutability::Mut, + } } +} - /// Infers type for record pattern or its corresponding assignee expression. - pub(super) fn infer_record_pat_like( - &mut self, - path: Option<&Path>, - expected: Ty<'db>, - default_bm: BindingMode, - id: PatId, - subs: impl ExactSizeIterator, - decl: Option, - ) -> Ty<'db> { - let (ty, def) = self.resolve_variant(id.into(), path, false); - if let Some(variant) = def { - self.write_variant_resolution(id.into(), variant); - } - - self.unify(ty, expected); - - match def { - _ if subs.len() == 0 => {} - Some(def) => { - let field_types = self.db.field_types(def); - let variant_data = def.fields(self.db); - let visibilities = VariantFields::field_visibilities(self.db, def); - - let substs = ty.as_adt().map(TupleExt::tail); - - for (name, inner) in subs { - let expected_ty = { - match variant_data.field(&name) { - Some(local_id) => { - if !visibilities[local_id] - .is_visible_from(self.db, self.resolver.module()) - { - self.push_diagnostic(InferenceDiagnostic::NoSuchField { - field: inner.into(), - private: Some(local_id), - variant: def, - }); - } - let f = field_types[local_id].get(); - let expected_ty = match substs { - Some(substs) => f.instantiate(self.interner(), substs), - None => f.instantiate(self.interner(), &[]), - }; - self.process_remote_user_written_ty(expected_ty) - } - None => { - self.push_diagnostic(InferenceDiagnostic::NoSuchField { - field: inner.into(), - private: None, - variant: def, - }); - self.err_ty() - } - } - }; +/// Variations on RFC 3627's Rule 4: when do reference patterns match against inherited references? +/// +/// "Inherited reference" designates the `&`/`&mut` types that arise from using match ergonomics, i.e. +/// from matching a reference type with a non-reference pattern. E.g. when `Some(x)` matches on +/// `&mut Option<&T>`, `x` gets type `&mut &T` and the outer `&mut` is considered "inherited". +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum InheritedRefMatchRule { + /// Reference patterns consume only the inherited reference if possible, regardless of whether + /// the underlying type being matched against is a reference type. If there is no inherited + /// reference, a reference will be consumed from the underlying type. + EatOuter, + /// Reference patterns consume only a reference from the underlying type if possible. If the + /// underlying type is not a reference type, the inherited reference will be consumed. + EatInner, + /// When the underlying type is a reference type, reference patterns consume both layers of + /// reference, i.e. they both reset the binding mode and consume the reference type. + EatBoth { + /// If `true`, an inherited reference will be considered when determining whether a reference + /// pattern matches a given type: + /// - If the underlying type is not a reference, a reference pattern may eat the inherited reference; + /// - If the underlying type is a reference, a reference pattern matches if it can eat either one + /// of the underlying and inherited references. E.g. a `&mut` pattern is allowed if either the + /// underlying type is `&mut` or the inherited reference is `&mut`. + /// + /// If `false`, a reference pattern is only matched against the underlying type. + /// This is `false` for stable Rust and `true` for both the `ref_pat_eat_one_layer_2024` and + /// `ref_pat_eat_one_layer_2024_structural` feature gates. + consider_inherited_ref: bool, + }, +} - self.infer_pat(inner, expected_ty, default_bm, decl); - } +/// When checking patterns containing paths, we need to know the path's resolution to determine +/// whether to apply match ergonomics and implicitly dereference the scrutinee. For instance, when +/// the `deref_patterns` feature is enabled and we're matching against a scrutinee of type +/// `Cow<'a, Option>`, we insert an implicit dereference to allow the pattern `Some(_)` to type, +/// but we must not dereference it when checking the pattern `Cow::Borrowed(_)`. +/// +/// `ResolvedPat` contains the information from resolution needed to determine match ergonomics +/// adjustments, and to finish checking the pattern once we know its adjusted type. +#[derive(Clone, Copy, Debug)] +struct ResolvedPat<'db> { + /// The type of the pattern, to be checked against the type of the scrutinee after peeling. This + /// is also used to avoid peeling the scrutinee's constructors (see the `Cow` example above). + ty: Ty<'db>, + kind: ResolvedPatKind, +} + +#[derive(Clone, Copy, Debug)] +enum ResolvedPatKind { + Path { res: ValueNs }, + Struct { variant: VariantId }, + TupleStruct { variant: VariantId }, +} + +impl<'db> ResolvedPat<'db> { + fn adjust_mode(&self) -> AdjustMode { + if let ResolvedPatKind::Path { res, .. } = self.kind + && matches!(res, ValueNs::ConstId(_)) + { + // These constants can be of a reference type, e.g. `const X: &u8 = &0;`. + // Peeling the reference types too early will cause type checking failures. + // Although it would be possible to *also* peel the types of the constants too. + AdjustMode::Pass + } else { + // The remaining possible resolutions for path, struct, and tuple struct patterns are + // ADT constructors. As such, we may peel references freely, but we must not peel the + // ADT itself from the scrutinee if it's a smart pointer. + AdjustMode::peel_until_adt(self.ty.as_adt().map(|(adt, _)| adt)) + } + } +} + +impl<'a, 'db> InferenceContext<'a, 'db> { + /// Experimental pattern feature: after matching against a shared reference, do we limit the + /// default binding mode in subpatterns to be `ref` when it would otherwise be `ref mut`? + /// This corresponds to Rule 3 of RFC 3627. + fn downgrade_mut_inside_shared(&self) -> bool { + // NB: RFC 3627 proposes stabilizing Rule 3 in all editions. If we adopt the same behavior + // across all editions, this may be removed. + self.features.ref_pat_eat_one_layer_2024_structural + } + + /// Experimental pattern feature: when do reference patterns match against inherited references? + /// This corresponds to variations on Rule 4 of RFC 3627. + fn ref_pat_matches_inherited_ref(&self, edition: Edition) -> InheritedRefMatchRule { + // NB: The particular rule used here is likely to differ across editions, so calls to this + // may need to become edition checks after match ergonomics stabilize. + if edition.at_least_2024() { + if self.features.ref_pat_eat_one_layer_2024 { + InheritedRefMatchRule::EatOuter + } else if self.features.ref_pat_eat_one_layer_2024_structural { + InheritedRefMatchRule::EatInner + } else { + // Currently, matching against an inherited ref on edition 2024 is an error. + // Use `EatBoth` as a fallback to be similar to stable Rust. + InheritedRefMatchRule::EatBoth { consider_inherited_ref: false } } - None => { - let err_ty = self.err_ty(); - for (_, inner) in subs { - self.infer_pat(inner, err_ty, default_bm, decl); - } + } else { + InheritedRefMatchRule::EatBoth { + consider_inherited_ref: self.features.ref_pat_eat_one_layer_2024 + || self.features.ref_pat_eat_one_layer_2024_structural, } } + } - ty + /// Experimental pattern feature: do `&` patterns match against `&mut` references, treating them + /// as if they were shared references? This corresponds to Rule 5 of RFC 3627. + fn ref_pat_matches_mut_ref(&self) -> bool { + // NB: RFC 3627 proposes stabilizing Rule 5 in all editions. If we adopt the same behavior + // across all editions, this may be removed. + self.features.ref_pat_eat_one_layer_2024 + || self.features.ref_pat_eat_one_layer_2024_structural } - /// Infers type for tuple pattern or its corresponding assignee expression. + /// Type check the given top level pattern against the `expected` type. + /// + /// If a `Some(span)` is provided and `origin_expr` holds, + /// then the `span` represents the scrutinee's span. + /// The scrutinee is found in e.g. `match scrutinee { ... }` and `let pat = scrutinee;`. /// - /// Ellipses found in the original pattern or expression must be filtered out. - pub(super) fn infer_tuple_pat_like( + /// Otherwise, `Some(span)` represents the span of a type expression + /// which originated the `expected` type. + pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: Ty<'db>, pat_origin: PatOrigin) { + let pat_info = + PatInfo { binding_mode: ByRef::No, max_ref_mutbl: MutblCap::Mut, pat_origin }; + self.infer_pat(pat, expected, pat_info); + } + + /// Type check the given `pat` against the `expected` type + /// with the provided `binding_mode` (default binding mode). + /// + /// Outside of this module, `check_pat_top` should always be used. + /// Conversely, inside this module, `check_pat_top` should never be used. + #[instrument(level = "debug", skip(self, pat_info))] + fn infer_pat(&mut self, pat_id: PatId, expected: Ty<'db>, pat_info: PatInfo) { + // For patterns containing paths, we need the path's resolution to determine whether to + // implicitly dereference the scrutinee before matching. + let pat = &self.store[pat_id]; + let opt_path_res = match pat { + Pat::Path(path) => Some(self.resolve_pat_path(pat_id, path)), + Pat::Record { path, .. } => Some(self.resolve_record_pat(pat_id, path)), + Pat::TupleStruct { path, .. } => Some(self.resolve_tuple_struct_pat(pat_id, path)), + _ => None, + }; + let adjust_mode = self.calc_adjust_mode(pat, opt_path_res); + let ty = self.infer_pat_inner(pat_id, opt_path_res, adjust_mode, expected, pat_info); + self.write_pat_ty(pat_id, ty); + + // If we implicitly inserted overloaded dereferences before matching check the pattern to + // see if the dereferenced types need `DerefMut` bounds. + if let Some(derefed_tys) = self.result.pat_adjustment(pat_id) + && derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref) + { + let infer_ok = self.register_deref_mut_bounds_if_needed( + pat_id, + derefed_tys.iter().filter_map(|adjust| match adjust.kind { + PatAdjust::OverloadedDeref => Some(adjust.source.as_ref()), + PatAdjust::BuiltinDeref => None, + }), + ); + self.table.register_infer_ok(infer_ok); + } + + // (note_1): In most of the cases where (note_1) is referenced + // (literals and constants being the exception), we relate types + // using strict equality, even though subtyping would be sufficient. + // There are a few reasons for this, some of which are fairly subtle + // and which cost me (nmatsakis) an hour or two debugging to remember, + // so I thought I'd write them down this time. + // + // 1. There is no loss of expressiveness here, though it does + // cause some inconvenience. What we are saying is that the type + // of `x` becomes *exactly* what is expected. This can cause unnecessary + // errors in some cases, such as this one: + // + // ``` + // fn foo<'x>(x: &'x i32) { + // let a = 1; + // let mut z = x; + // z = &a; + // } + // ``` + // + // The reason we might get an error is that `z` might be + // assigned a type like `&'x i32`, and then we would have + // a problem when we try to assign `&a` to `z`, because + // the lifetime of `&a` (i.e., the enclosing block) is + // shorter than `'x`. + // + // HOWEVER, this code works fine. The reason is that the + // expected type here is whatever type the user wrote, not + // the initializer's type. In this case the user wrote + // nothing, so we are going to create a type variable `Z`. + // Then we will assign the type of the initializer (`&'x i32`) + // as a subtype of `Z`: `&'x i32 <: Z`. And hence we + // will instantiate `Z` as a type `&'0 i32` where `'0` is + // a fresh region variable, with the constraint that `'x : '0`. + // So basically we're all set. + // + // Note that there are two tests to check that this remains true + // (`regions-reassign-{match,let}-bound-pointer.rs`). + // + // 2. An outdated issue related to the old HIR borrowck. See the test + // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`, + } + + // Helper to avoid resolving the same path pattern several times. + fn infer_pat_inner( &mut self, pat: PatId, + opt_path_res: Option, ()>>, + adjust_mode: AdjustMode, expected: Ty<'db>, - default_bm: BindingMode, - ellipsis: Option, - elements: &[PatId], - decl: Option, + pat_info: PatInfo, ) -> Ty<'db> { - let mut expected_len = elements.len(); - if ellipsis.is_some() { - // Require known type only when `..` is present. - if let TyKind::Tuple(tys) = self.table.structurally_resolve_type(expected).kind() { - expected_len = tys.len(); - } + #[cfg(debug_assertions)] + if matches!(pat_info.binding_mode, ByRef::Yes(Mutability::Mut)) + && pat_info.max_ref_mutbl != MutblCap::Mut + && self.downgrade_mut_inside_shared() + { + panic!("Pattern mutability cap violated!"); } - let max_len = cmp::max(expected_len, elements.len()); - let element_tys_iter = (0..max_len).map(|_| self.table.next_ty_var()); - let element_tys = Tys::new_from_iter(self.interner(), element_tys_iter); - let pat_ty = Ty::new(self.interner(), TyKind::Tuple(element_tys)); - if self.demand_eqtype(pat.into(), expected, pat_ty).is_err() - && let TyKind::Tuple(expected) = expected.kind() + // Resolve type if needed. + let expected = if let AdjustMode::Peel { .. } = adjust_mode + && pat_info.pat_origin.default_binding_modes() { - // Equate expected type with the infer vars, for better diagnostics. - for (expected, elem_ty) in iter::zip(expected, element_tys) { - _ = self - .table - .at(&ObligationCause::dummy()) - .eq(expected, elem_ty) - .map(|infer_ok| self.table.register_infer_ok(infer_ok)); - } - } - let (before_ellipsis, after_ellipsis) = match ellipsis { - Some(ellipsis) => { - let element_tys = element_tys.as_slice(); - // Don't check patterns twice. - let from_end_start = cmp::max( - element_tys.len().saturating_sub(elements.len() - ellipsis as usize), - ellipsis as usize, - ); - ( - element_tys.get(..ellipsis as usize).unwrap_or(element_tys), - element_tys.get(from_end_start..).unwrap_or_default(), + self.table.try_structurally_resolve_type(expected) + } else { + expected + }; + + match self.store[pat] { + // Peel off a `&` or `&mut`from the scrutinee type. See the examples in + // `tests/ui/rfcs/rfc-2005-default-binding-mode`. + _ if let AdjustMode::Peel { kind: peel_kind } = adjust_mode + && pat_info.pat_origin.default_binding_modes() + && let TyKind::Ref(_, inner_ty, inner_mutability) = expected.kind() + && self.should_peel_ref(peel_kind, expected) => + { + debug!("inspecting {:?}", expected); + + debug!("current discriminant is Ref, inserting implicit deref"); + // Preserve the reference type. We'll need it later during THIR lowering. + self.result.pat_adjustments.entry(pat).or_default().push(PatAdjustment { + kind: PatAdjust::BuiltinDeref, + source: expected.store(), + }); + + // Use the old pat info to keep `current_depth` to its old value. + let new_pat_info = self.adjust_pat_info(inner_mutability, pat_info); + + // Recurse with the new expected type. + self.infer_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info) + } + // If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the + // examples in `tests/ui/pattern/deref_patterns/`. + _ if self.features.deref_patterns + && let AdjustMode::Peel { kind: peel_kind } = adjust_mode + && pat_info.pat_origin.default_binding_modes() + && self.should_peel_smart_pointer(peel_kind, expected) => + { + debug!("scrutinee ty {expected:?} is a smart pointer, inserting pin deref"); + + // The scrutinee is a smart pointer; implicitly dereference it. This adds a + // requirement that `expected: DerefPure`. + let inner_ty = self.deref_pat_target(expected); + // Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any + // `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`. + + self.check_deref_pattern( + pat, + opt_path_res, + adjust_mode, + expected, + inner_ty, + PatAdjust::OverloadedDeref, + pat_info, ) } - None => (element_tys.as_slice(), &[][..]), - }; - for (&elem, &elem_ty) in iter::zip(elements, before_ellipsis.iter().chain(after_ellipsis)) { - self.infer_pat(elem, elem_ty, default_bm, decl); - } - if let Some(uncovered) = elements.get(element_tys.len()..) { - for &elem in uncovered { - self.infer_pat(elem, self.types.types.error, default_bm, decl); + Pat::Missing | Pat::Wild => expected, + // We allow any type here; we ensure that the type is uninhabited during match checking. + // Pat::Never => expected, + Pat::Path(_) => { + let ty = match opt_path_res.unwrap() { + Ok(ref pr) => self.infer_pat_path(pat, pr, expected), + Err(()) => self.types.types.error, + }; + self.write_pat_ty(pat, ty); + ty + } + Pat::Lit(expr) => self.infer_lit_pat(expr, expected), + Pat::Range { start: lhs, end: rhs, .. } => self.infer_range_pat(lhs, rhs, expected), + Pat::Bind { id: var_id, subpat } => { + self.infer_bind_pat(pat, var_id, subpat, expected, pat_info) + } + Pat::TupleStruct { args: ref subpats, ellipsis: ddpos, .. } => match opt_path_res + .unwrap() + { + Ok(ResolvedPat { ty, kind: ResolvedPatKind::TupleStruct { variant } }) => self + .infer_tuple_struct_pat(pat, subpats, ddpos, ty, variant, expected, pat_info), + Err(()) => { + let ty_err = self.types.types.error; + for &subpat in subpats { + self.infer_pat(subpat, ty_err, pat_info); + } + ty_err + } + Ok(pr) => panic!("tuple struct pattern resolved to {pr:?}"), + }, + Pat::Record { args: ref fields, ellipsis: has_rest_pat, .. } => { + match opt_path_res.unwrap() { + Ok(ResolvedPat { ty, kind: ResolvedPatKind::Struct { variant } }) => self + .infer_record_pat( + pat, + fields, + has_rest_pat, + ty, + variant, + expected, + pat_info, + ), + Err(()) => { + let ty_err = self.types.types.error; + for field in fields { + self.infer_pat(field.pat, ty_err, pat_info); + } + ty_err + } + Ok(pr) => panic!("struct pattern resolved to {pr:?}"), + } + } + // Pat::Guard(pat, cond) => { + // self.infer_pat(pat, expected, pat_info); + // self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {}); + // expected + // } + Pat::Or(ref pats) => { + for &pat in pats { + self.infer_pat(pat, expected, pat_info); + } + expected + } + Pat::Tuple { args: ref elements, ellipsis: ddpos } => { + self.infer_tuple_pat(pat, elements, ddpos, expected, pat_info) + } + Pat::Box { inner } => self.infer_box_pat(pat, inner, expected, pat_info), + // Pat::Deref(inner) => self.infer_deref_pat(pat.span, inner, expected, pat_info), + Pat::Ref { pat: inner, mutability: mutbl } => self.infer_ref_pat( + pat, + inner, + if mutbl.is_mut() { Mutability::Mut } else { Mutability::Not }, + expected, + pat_info, + ), + Pat::Slice { prefix: ref before, slice, suffix: ref after } => { + self.infer_slice_pat(pat, before, slice, after, expected, pat_info) + } + Pat::Expr(expr) => self.infer_destructuring_assignment_expr(expr, expected), + Pat::ConstBlock(expr) => { + self.infer_expr(expr, &Expectation::has_type(expected), ExprIsRead::Yes) } } - pat_ty } - pub(super) fn infer_top_pat( - &mut self, - pat: PatId, - expected: Ty<'db>, - decl: Option, - ) { - self.infer_pat(pat, expected, BindingMode::default(), decl); + fn adjust_pat_info(&self, inner_mutability: Mutability, pat_info: PatInfo) -> PatInfo { + let mut binding_mode = match pat_info.binding_mode { + // If default binding mode is by value, make it `ref`, `ref mut`, `ref pin const` + // or `ref pin mut` (depending on whether we observe `&`, `&mut`, `&pin const` or + // `&pin mut`). + ByRef::No => ByRef::Yes(inner_mutability), + ByRef::Yes(mutability) => { + let mutability = match mutability { + // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). + Mutability::Mut => inner_mutability, + // Once a `ref`, always a `ref`. + // This is because a `& &mut` cannot mutate the underlying value. + Mutability::Not => Mutability::Not, + }; + ByRef::Yes(mutability) + } + }; + + let PatInfo { mut max_ref_mutbl, .. } = pat_info; + if self.downgrade_mut_inside_shared() { + binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl()); + } + match binding_mode { + ByRef::Yes(Mutability::Not) => max_ref_mutbl = MutblCap::Not, + _ => {} + } + debug!("default binding mode is now {:?}", binding_mode); + PatInfo { binding_mode, max_ref_mutbl, ..pat_info } } - fn infer_pat( + fn check_deref_pattern( &mut self, pat: PatId, + opt_path_res: Option, ()>>, + adjust_mode: AdjustMode, expected: Ty<'db>, - mut default_bm: BindingMode, - decl: Option, + mut inner_ty: Ty<'db>, + pat_adjust_kind: PatAdjust, + pat_info: PatInfo, ) -> Ty<'db> { - let mut expected = self.table.structurally_resolve_type(expected); - - if matches!(&self.store[pat], Pat::Ref { .. }) || self.inside_assignment { - cov_mark::hit!(match_ergonomics_ref); - // When you encounter a `&pat` pattern, reset to Move. - // This is so that `w` is by value: `let (_, &w) = &(1, &2);` - // Destructuring assignments also reset the binding mode and - // don't do match ergonomics. - default_bm = BindingMode::Move; - } else if self.is_non_ref_pat(self.store, pat) { - let mut pat_adjustments = Vec::new(); - while let TyKind::Ref(_lifetime, inner, mutability) = expected.kind() { - pat_adjustments.push(expected.store()); - expected = self.table.try_structurally_resolve_type(inner); - default_bm = match default_bm { - BindingMode::Move => BindingMode::Ref(mutability), - BindingMode::Ref(Mutability::Not) => BindingMode::Ref(Mutability::Not), - BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability), - } - } + debug_assert!( + !matches!(pat_adjust_kind, PatAdjust::BuiltinDeref), + "unexpected deref pattern for builtin reference type {expected:?}", + ); - if !pat_adjustments.is_empty() { - pat_adjustments.shrink_to_fit(); - self.result.pat_adjustments.insert(pat, pat_adjustments); - } + let pat_adjustments = self.result.pat_adjustments.entry(pat).or_default(); + // We may reach the recursion limit if a user matches on a type `T` satisfying + // `T: Deref`; error gracefully in this case. + // FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move + // this check out of this branch. Alternatively, this loop could be implemented with + // autoderef and this check removed. For now though, don't break code compiling on + // stable with lots of `&`s and a low recursion limit, if anyone's done that. + if pat_adjustments.len() < self.resolver.top_level_def_map().recursion_limit() as usize { + // Preserve the smart pointer type for THIR lowering and closure upvar analysis. + pat_adjustments.push(PatAdjustment { kind: pat_adjust_kind, source: expected.store() }); + } else { + // FIXME: Emit an error. + inner_ty = self.types.types.error; } - // Lose mutability. - let default_bm = default_bm; - let expected = expected; + // Recurse, using the old pat info to keep `current_depth` to its old value. + // Peeling smart pointers does not update the default binding mode. + self.infer_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, pat_info) + } - let ty = match &self.store[pat] { - Pat::Tuple { args, ellipsis } => { - self.infer_tuple_pat_like(pat, expected, default_bm, *ellipsis, args, decl) - } - Pat::Or(pats) => { - for pat in pats.iter() { - self.infer_pat(*pat, expected, default_bm, decl); - } - expected + /// How should the binding mode and expected type be adjusted? + /// + /// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`. + fn calc_adjust_mode( + &mut self, + pat: &Pat, + opt_path_res: Option, ()>>, + ) -> AdjustMode { + match pat { + // Type checking these product-like types successfully always require + // that the expected type be of those types and not reference types. + Pat::Tuple { .. } | Pat::Range { .. } | Pat::Slice { .. } => AdjustMode::peel_all(), + // When checking an explicit deref pattern, only peel reference types. + // FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box + // patterns may want `PeelKind::Implicit`, stopping on encountering a box. + Pat::Box { .. } /* | Pat::Deref(_) */ => { + AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat } } - &Pat::Ref { pat, mutability } => { - self.infer_ref_pat(pat, lower_mutability(mutability), expected, default_bm, decl) + // A never pattern behaves somewhat like a literal or unit variant. + // Pat::Never => AdjustMode::peel_all(), + // For patterns with paths, how we peel the scrutinee depends on the path's resolution. + Pat::Record { .. } + | Pat::TupleStruct { .. } + | Pat::Path(_) => { + // If there was an error resolving the path, default to peeling everything. + opt_path_res.unwrap().map_or(AdjustMode::peel_all(), |pr| pr.adjust_mode()) } - Pat::TupleStruct { path: p, args: subpats, ellipsis } => self - .infer_tuple_struct_pat_like( - p.as_deref(), - expected, - default_bm, - pat, - *ellipsis, - subpats, - decl, - ), - Pat::Record { path: p, args: fields, ellipsis: _ } => { - let subs = fields.iter().map(|f| (f.name.clone(), f.pat)); - self.infer_record_pat_like(p.as_deref(), expected, default_bm, pat, subs, decl) - } - Pat::Path(path) => { - let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty()); - let ty_inserted_vars = self.insert_type_vars_shallow(ty); - match self.coerce( - pat.into(), - expected, - ty_inserted_vars, - AllowTwoPhase::No, - ExprIsRead::No, - ) { - Ok(coerced_ty) => { - self.write_pat_ty(pat, coerced_ty); - return self.pat_ty_after_adjustment(pat); + + // String and byte-string literals result in types `&str` and `&[u8]` respectively. + // All other literals result in non-reference types. + // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}` unless + // `deref_patterns` is enabled. + &Pat::Lit(expr) | &Pat::ConstBlock(expr) => { + let lit_ty = self.infer_expr_pat_unadjusted(expr); + // Call `resolve_vars_if_possible` here for inline const blocks. + let lit_ty = self.infcx().resolve_vars_if_possible(lit_ty); + // If `deref_patterns` is enabled, allow `if let "foo" = &&"foo" {}`. + if self.features.deref_patterns { + let mut peeled_ty = lit_ty; + let mut pat_ref_layers = 0; + while let TyKind::Ref(_, inner_ty, mutbl) = + self.table.try_structurally_resolve_type(peeled_ty).kind() + { + // We rely on references at the head of constants being immutable. + debug_assert!(mutbl.is_not()); + pat_ref_layers += 1; + peeled_ty = inner_ty; } - Err(_) => { - self.result.type_mismatches.get_or_insert_default().insert( - pat.into(), - TypeMismatch { - expected: expected.store(), - actual: ty_inserted_vars.store(), - }, - ); - self.write_pat_ty(pat, ty); - // We return `expected` to prevent cascading errors. I guess an alternative is to - // not emit type mismatches for error types and emit an error type here. - return expected; + AdjustMode::Peel { + kind: PeelKind::Implicit { until_adt: None, pat_ref_layers }, } + } else { + if lit_ty.is_ref() { AdjustMode::Pass } else { AdjustMode::peel_all() } } } - Pat::Bind { id, subpat } => { - return self.infer_bind_pat(pat, *id, default_bm, *subpat, expected, decl); - } - Pat::Slice { prefix, slice, suffix } => { - self.infer_slice_pat(expected, prefix, *slice, suffix, default_bm, decl) + + // Ref patterns are complicated, we handle them in `check_pat_ref`. + Pat::Ref { .. } + // No need to do anything on a missing pattern. + | Pat::Missing + // A `_` pattern works with any expected type, so there's no need to do anything. + | Pat::Wild + // Bindings also work with whatever the expected type is, + // and moreover if we peel references off, that will give us the wrong binding type. + // Also, we can have a subpattern `binding @ pat`. + // Each side of the `@` should be treated independently (like with OR-patterns). + | Pat::Bind { .. } + // `Pat::Expr(_)` inside assignments becomes a binding in rustc, therefore should be + // the same as `Pat::Bind`. + | Pat::Expr(_) + // An OR-pattern just propagates to each individual alternative. + // This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`. + // In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`. + | Pat::Or(_) + // Like or-patterns, guard patterns just propagate to their subpatterns. + /* | Pat::Guard(..) */ => AdjustMode::Pass, + } + } + + /// Assuming `expected` is a reference type, determine whether to peel it before matching. + fn should_peel_ref(&self, peel_kind: PeelKind, mut expected: Ty<'db>) -> bool { + debug_assert!(expected.is_ref()); + let pat_ref_layers = match peel_kind { + PeelKind::ExplicitDerefPat => 0, + PeelKind::Implicit { pat_ref_layers, .. } => pat_ref_layers, + }; + + // Most patterns don't have reference types, so we'll want to peel all references from the + // scrutinee before matching. To optimize for the common case, return early. + if pat_ref_layers == 0 { + return true; + } + debug_assert!( + self.features.deref_patterns, + "Peeling for patterns with reference types is gated by `deref_patterns`." + ); + + // If the pattern has as many or more layers of reference as the expected type, we can match + // without peeling more, unless we find a smart pointer or `&mut` that we also need to peel. + // We don't treat `&` and `&mut` as interchangeable, but by peeling `&mut`s before matching, + // we can still, e.g., match on a `&mut str` with a string literal pattern. This is because + // string literal patterns may be used where `str` is expected. + let mut expected_ref_layers = 0; + while let TyKind::Ref(_, inner_ty, mutbl) = expected.kind() { + if mutbl.is_mut() { + // Mutable references can't be in the final value of constants, thus they can't be + // at the head of their types, thus we should always peel `&mut`. + return true; } - Pat::Wild => expected, - Pat::Range { start, end, range_type: _ } => { - if let Some(start) = *start { - let start_ty = self.infer_expr(start, &Expectation::None, ExprIsRead::Yes); - _ = self.demand_eqtype(start.into(), expected, start_ty); + expected_ref_layers += 1; + expected = inner_ty; + } + pat_ref_layers < expected_ref_layers || self.should_peel_smart_pointer(peel_kind, expected) + } + + /// Determine whether `expected` is a smart pointer type that should be peeled before matching. + fn should_peel_smart_pointer(&self, peel_kind: PeelKind, expected: Ty<'db>) -> bool { + // Explicit `deref!(_)` patterns match against smart pointers; don't peel in that case. + if let PeelKind::Implicit { until_adt, .. } = peel_kind + // For simplicity, only apply overloaded derefs if `expected` is a known ADT. + // FIXME(deref_patterns): we'll get better diagnostics for users trying to + // implicitly deref generics if we allow them here, but primitives, tuples, and + // inference vars definitely should be stopped. Figure out what makes most sense. + && let TyKind::Adt(scrutinee_adt, _) = expected.kind() + // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if + // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern. + && until_adt != Some(scrutinee_adt.def_id().0) + // At this point, the pattern isn't able to match `expected` without peeling. Check + // that it implements `Deref` before assuming it's a smart pointer, to get a normal + // type error instead of a missing impl error if not. This only checks for `Deref`, + // not `DerefPure`: we require that too, but we want a trait error if it's missing. + && let Some(deref_trait) = self.lang_items.Deref + && self.infcx().type_implements_trait(deref_trait, [expected], self.table.param_env).may_apply() + { + true + } else { + false + } + } + + fn infer_expr_pat_unadjusted(&mut self, expr: ExprId) -> Ty<'db> { + self.infer_expr_no_expect(expr, ExprIsRead::Yes) + } + + fn infer_lit_pat(&mut self, expr: ExprId, expected: Ty<'db>) -> Ty<'db> { + let literal = match &self.store[expr] { + Expr::Literal(literal) => literal, + _ => panic!("expected a literal"), + }; + + // We've already computed the type above (when checking for a non-ref pat), + // so avoid computing it again. + let ty = self.expr_ty(expr); + + // Byte string patterns behave the same way as array patterns + // They can denote both statically and dynamically-sized byte arrays. + // Additionally, when `deref_patterns` is enabled, byte string literal patterns may have + // types `[u8]` or `[u8; N]`, in order to type, e.g., `deref!(b"..."): Vec`. + let mut pat_ty = ty; + if matches!(literal, Literal::ByteString(_)) { + let expected = self.table.structurally_resolve_type(expected); + match expected.kind() { + // Allow `b"...": &[u8]` + TyKind::Ref(_, inner_ty, _) + if self.table.try_structurally_resolve_type(inner_ty).is_slice() => + { + trace!(?expr, "polymorphic byte string lit"); + pat_ty = self.types.types.static_u8_slice; } - if let Some(end) = *end { - let end_ty = self.infer_expr(end, &Expectation::None, ExprIsRead::Yes); - _ = self.demand_eqtype(end.into(), expected, end_ty); + // Allow `b"...": [u8; 3]` for `deref_patterns` + TyKind::Array(..) if self.features.deref_patterns => { + pat_ty = match ty.kind() { + TyKind::Ref(_, inner_ty, _) => inner_ty, + _ => panic!("found byte string literal with non-ref type {ty:?}"), + } } - expected - } - &Pat::Lit(expr) => { - // Don't emit type mismatches again, the expression lowering already did that. - let ty = self.infer_lit_pat(expr, expected); - self.write_pat_ty(pat, ty); - return self.pat_ty_after_adjustment(pat); - } - Pat::Box { inner } => match self.resolve_boxed_box() { - Some(box_adt) => { - let (inner_ty, alloc_ty) = match expected.as_adt() { - Some((adt, subst)) if adt == box_adt => { - (subst.type_at(0), subst.as_slice().get(1).and_then(|a| a.as_type())) - } - _ => (self.types.types.error, None), - }; - - let inner_ty = self.infer_pat(*inner, inner_ty, default_bm, decl); - Ty::new_adt( - self.interner(), - box_adt, - GenericArgs::fill_with_defaults( - self.interner(), - box_adt.into(), - iter::once(inner_ty.into()).chain(alloc_ty.map(Into::into)), - |_, id, _| self.table.next_var_for_param(id), - ), - ) + // Allow `b"...": [u8]` for `deref_patterns` + TyKind::Slice(..) if self.features.deref_patterns => { + pat_ty = self.types.types.u8_slice; } - None => self.err_ty(), - }, - Pat::ConstBlock(expr) => { - let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false); - let result = - self.infer_expr(*expr, &Expectation::has_type(expected), ExprIsRead::Yes); - self.inside_assignment = old_inside_assign; - result - } - Pat::Expr(expr) => { - let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false); - // LHS of assignment doesn't constitute reads. - let expr_is_read = ExprIsRead::No; - let result = - self.infer_expr_inner(*expr, &Expectation::has_type(expected), expr_is_read); - // We are returning early to avoid the unifiability check below. - let lhs_ty = self.insert_type_vars_shallow(result); - let ty = match self.coerce( - (*expr).into(), - expected, - lhs_ty, - AllowTwoPhase::No, - expr_is_read, - ) { - Ok(ty) => ty, - Err(_) => { - self.result.type_mismatches.get_or_insert_default().insert( - pat.into(), - TypeMismatch { expected: expected.store(), actual: lhs_ty.store() }, - ); - // `rhs_ty` is returned so no further type mismatches are - // reported because of this mismatch. - expected - } - }; - self.write_pat_ty(pat, ty); - self.inside_assignment = old_inside_assign; - return ty; + // Otherwise, `b"...": &[u8; 3]` + _ => {} } - Pat::Missing => self.err_ty(), - }; - // use a new type variable if we got error type here - let ty = self.insert_type_vars_shallow(ty); - // FIXME: This never check is odd, but required with out we do inference right now - if !expected.is_never() && !self.unify(ty, expected) { - self.result.type_mismatches.get_or_insert_default().insert( - pat.into(), - TypeMismatch { expected: expected.store(), actual: ty.store() }, - ); } - self.write_pat_ty(pat, ty); - self.pat_ty_after_adjustment(pat) - } - fn pat_ty_after_adjustment(&self, pat: PatId) -> Ty<'db> { - self.result - .pat_adjustments - .get(&pat) - .and_then(|it| it.last()) - .unwrap_or_else(|| &self.result.type_of_pat[pat]) - .as_ref() + // When `deref_patterns` is enabled, in order to allow `deref!("..."): String`, we allow + // string literal patterns to have type `str`. This is accounted for when lowering to MIR. + if self.features.deref_patterns + && matches!(literal, Literal::String(_)) + && self.table.try_structurally_resolve_type(expected).is_str() + { + pat_ty = self.types.types.str; + } + + // Somewhat surprising: in this case, the subtyping relation goes the + // opposite way as the other cases. Actually what we really want is not + // a subtyping relation at all but rather that there exists a LUB + // (so that they can be compared). However, in practice, constants are + // always scalars or strings. For scalars subtyping is irrelevant, + // and for strings `ty` is type is `&'static str`, so if we say that + // + // &'static str <: expected + // + // then that's equivalent to there existing a LUB. + _ = self.demand_suptype(expr.into(), expected, pat_ty); + + pat_ty } - fn infer_ref_pat( + fn infer_range_pat( &mut self, - inner_pat: PatId, - mutability: Mutability, + lhs: Option, + rhs: Option, expected: Ty<'db>, - default_bm: BindingMode, - decl: Option, ) -> Ty<'db> { - let (expectation_type, expectation_lt) = match expected.kind() { - TyKind::Ref(lifetime, inner_ty, _exp_mut) => (inner_ty, lifetime), - _ => { - let inner_ty = self.table.next_ty_var(); - let inner_lt = self.table.next_region_var(); - let ref_ty = Ty::new_ref(self.interner(), inner_lt, inner_ty, mutability); - // Unification failure will be reported by the caller. - self.unify(ref_ty, expected); - (inner_ty, inner_lt) + let mut calc_side = |opt_expr: Option| match opt_expr { + None => None, + Some(expr) => { + let ty = self.infer_expr_pat_unadjusted(expr); + // Check that the end-point is possibly of numeric or char type. + // The early check here is not for correctness, but rather better + // diagnostics (e.g. when `&str` is being matched, `expected` will + // be peeled to `str` while ty here is still `&str`, if we don't + // err early here, a rather confusing unification error will be + // emitted instead). + let ty = self.table.try_structurally_resolve_type(ty); + let fail = + !(ty.is_numeric() || ty.is_char() || ty.is_ty_var() || ty.references_error()); + Some((fail, ty, expr)) } }; - let subty = self.infer_pat(inner_pat, expectation_type, default_bm, decl); - Ty::new_ref(self.interner(), expectation_lt, subty, mutability) + let mut lhs = calc_side(lhs); + let mut rhs = calc_side(rhs); + + if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { + // There exists a side that didn't meet our criteria that the end-point + // be of a numeric or char type, as checked in `calc_side` above. + // FIXME: Emit an error. + return self.types.types.error; + } + + // Unify each side with `expected`. + // Subtyping doesn't matter here, as the value is some kind of scalar. + let mut demand_eqtype = |x: &mut _| { + if let Some((_, x_ty, x_expr)) = *x { + _ = self.demand_eqtype(ExprOrPatId::from(x_expr), expected, x_ty); + } + }; + demand_eqtype(&mut lhs); + demand_eqtype(&mut rhs); + + if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { + return self.types.types.error; + } + + // Find the unified type and check if it's of numeric or char type again. + // This check is needed if both sides are inference variables. + // We require types to be resolved here so that we emit inference failure + // rather than "_ is not a char or numeric". + let ty = self.table.structurally_resolve_type(expected); + if !(ty.is_numeric() || ty.is_char() || ty.references_error()) { + // FIXME: Emit an error. + return self.types.types.error; + } + ty } fn infer_bind_pat( &mut self, pat: PatId, - binding: BindingId, - default_bm: BindingMode, - subpat: Option, + var_id: BindingId, + sub: Option, expected: Ty<'db>, - decl: Option, + pat_info: PatInfo, ) -> Ty<'db> { - let Binding { mode, .. } = self.store[binding]; - let mode = if mode == BindingAnnotation::Unannotated { - default_bm - } else { - BindingMode::convert(mode) + let PatInfo { binding_mode: def_br, .. } = pat_info; + let binding_data = &self.store[var_id]; + + // Determine the binding mode... + let user_bind_annot = BindingMode::from_annotation(binding_data.mode); + let bm = match user_bind_annot { + BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(_) = def_br => { + // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and + // using other experimental matching features compatible with it. + if self.edition.at_least_2024() + && (self.features.ref_pat_eat_one_layer_2024 + || self.features.ref_pat_eat_one_layer_2024_structural) + { + if !self.features.mut_ref { + // FIXME: Emit an error: binding cannot be both mutable and by-reference. + } + + BindingMode(def_br, Mutability::Mut) + } else { + // `mut` resets the binding mode on edition <= 2021 + BindingMode(ByRef::No, Mutability::Mut) + } + } + BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl), + BindingMode(ByRef::Yes(_), _) => user_bind_annot, }; - self.result.binding_modes.insert(pat, mode); - let inner_ty = match subpat { - Some(subpat) => self.infer_pat(subpat, expected, default_bm, decl), - None => expected, + if matches!(bm.0, ByRef::Yes(Mutability::Mut)) + && let MutblCap::WeaklyNot = pat_info.max_ref_mutbl + { + // FIXME: Emit an error: cannot borrow as mutable inside an `&` pattern. + } + + // ...and store it in a side table: + self.result.binding_modes.insert(pat, bm); + + debug!("check_pat_ident: pat.hir_id={:?} bm={:?}", pat, bm); + + let local_ty = match bm.0 { + ByRef::Yes(mutbl) => { + // If the binding is like `ref x | ref mut x`, + // then `x` is assigned a value of type `&M T` where M is the + // mutability and T is the expected type. + // + // Under pin ergonomics, if the binding is like `ref pin const|mut x`, + // then `x` is assigned a value of type `&pin M T` where M is the + // mutability and T is the expected type. + // + // `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` + // is required. However, we use equality, which is stronger. + // See (note_1) for an explanation. + self.new_ref_ty(mutbl, expected) + } + // Otherwise, the type of x is the expected type `T`. + ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). }; - let inner_ty = self.insert_type_vars_shallow(inner_ty); - let bound_ty = match mode { - BindingMode::Ref(mutability) => { - let inner_lt = self.table.next_region_var(); - Ty::new_ref(self.interner(), inner_lt, expected, mutability) + // We have a concrete type for the local, so we do not need to taint it and hide follow up errors *using* the local. + if let Some(existing_local_ty) = self.result.type_of_binding.get(var_id) { + // If there are multiple arms, make sure they all agree on + // what the type of the binding `x` ought to be. + _ = self.demand_eqtype(pat.into(), existing_local_ty.as_ref(), local_ty); + } else { + self.write_binding_ty(var_id, local_ty); + } + + if let Some(p) = sub { + self.infer_pat(p, expected, pat_info); + } + + local_ty + } + + fn check_dereferenceable(&self, expected: Ty<'db>, inner: PatId) -> Result<(), ()> { + if let Pat::Bind { .. } = self.store[inner] + && let Some(pointee_ty) = self.shallow_resolve(expected).builtin_deref(true) + && let TyKind::Dynamic(..) = pointee_ty.kind() + { + // This is "x = dyn SomeTrait" being reduced from + // "let &x = &dyn SomeTrait" or "let box x = Box", an error. + // FIXME: Emit an error. rustc emits this message: + const _CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\ +This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \ +pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \ +this type has no compile-time size. Therefore, all accesses to trait types must be through \ +pointers. If you encounter this error you should try to avoid dereferencing the pointer. + +You can read more about trait objects in the Trait Objects section of the Reference: \ +https://doc.rust-lang.org/reference/types.html#trait-objects"; + } + Ok(()) + } + + fn resolve_record_pat(&mut self, pat: PatId, path: &Path) -> Result, ()> { + // Resolve the path and check the definition for errors. + let (pat_ty, Some(variant)) = self.resolve_variant(pat.into(), path, false) else { + return Err(()); + }; + self.write_variant_resolution(pat.into(), variant); + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Struct { variant } }) + } + + fn infer_record_pat( + &mut self, + pat: PatId, + fields: &[RecordFieldPat], + has_rest_pat: bool, + pat_ty: Ty<'db>, + variant: VariantId, + expected: Ty<'db>, + pat_info: PatInfo, + ) -> Ty<'db> { + // Type-check the path. + let _ = self.demand_eqtype(pat.into(), expected, pat_ty); + + // Type-check subpatterns. + self.check_record_pat_fields(pat_ty, pat, variant, fields, has_rest_pat, pat_info); + pat_ty + } + + fn resolve_pat_path(&mut self, pat: PatId, path: &Path) -> Result, ()> { + let (res, pat_ty) = self.infer_path(path, pat.into()).ok_or(())?; + match res { + ValueNs::FunctionId(_) + | ValueNs::GenericParam(_) + | ValueNs::ImplSelf(_) + | ValueNs::LocalBinding(_) + | ValueNs::StaticId(_) => { + // FIXME: Emit an error. + return Err(()); } - BindingMode::Move => expected, + ValueNs::ConstId(_) | ValueNs::EnumVariantId(_) | ValueNs::StructId(_) => {} // OK + } + + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Path { res } }) + } + + fn infer_pat_path( + &mut self, + pat: PatId, + resolved: &ResolvedPat<'db>, + expected: Ty<'db>, + ) -> Ty<'db> { + _ = self.demand_suptype(pat.into(), expected, resolved.ty); + resolved.ty + } + + fn resolve_tuple_struct_pat( + &mut self, + pat: PatId, + path: &Path, + ) -> Result, ()> { + // Resolve the path and check the definition for errors. + let (pat_ty, Some(variant)) = self.resolve_variant(pat.into(), path, true) else { + return Err(()); }; - self.write_pat_ty(pat, inner_ty); - self.write_binding_ty(binding, bound_ty); - inner_ty + self.write_variant_resolution(pat.into(), variant); + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::TupleStruct { variant } }) } - fn infer_slice_pat( + fn infer_tuple_struct_pat( &mut self, + pat: PatId, + subpats: &[PatId], + ddpos: Option, + pat_ty: Ty<'db>, + variant: VariantId, expected: Ty<'db>, - prefix: &[PatId], - slice: Option, - suffix: &[PatId], - default_bm: BindingMode, - decl: Option, + pat_info: PatInfo, ) -> Ty<'db> { - let expected = self.table.structurally_resolve_type(expected); + let interner = self.interner(); - // If `expected` is an infer ty, we try to equate it to an array if the given pattern - // allows it. See issue #16609 - if self.pat_is_irrefutable(decl) - && expected.is_ty_var() - && let Some(resolved_array_ty) = - self.try_resolve_slice_ty_to_array_ty(prefix, suffix, slice) + // Type-check the tuple struct pattern against the expected type. + let had_err = self.demand_eqtype(pat.into(), expected, pat_ty); + + let variant_fields = variant.fields(self.db); + let variant_field_tys = self.db.field_types(variant); + let TyKind::Adt(_, args) = pat_ty.kind() else { + panic!("unexpected pattern type {:?}", pat_ty); + }; + // Type-check subpatterns. + if subpats.len() == variant_fields.len() + || subpats.len() < variant_fields.len() && ddpos.is_some() { - self.unify(expected, resolved_array_ty); + for (i, &subpat) in subpats.iter().enumerate_and_adjust(variant_fields.len(), ddpos) { + let field_id = LocalFieldId::from_raw(la_arena::RawIdx::from_u32(i as u32)); + let field_ty = variant_field_tys[field_id].get().instantiate(interner, args); + self.infer_pat(subpat, field_ty, pat_info); + } + if let Err(()) = had_err { + for &pat in subpats { + self.infer_pat(pat, self.types.types.error, pat_info); + } + return self.types.types.error; + } + } else { + self.push_diagnostic(InferenceDiagnostic::MismatchedTupleStructPatArgCount { + pat, + expected: variant_fields.len(), + found: subpats.len(), + }); + + for (i, &pat) in subpats.iter().enumerate() { + let field_id = LocalFieldId::from_raw(la_arena::RawIdx::from_u32(i as u32)); + let expected = match variant_field_tys.get(field_id) { + Some(field_ty) => field_ty.get().instantiate(interner, args), + None => self.types.types.error, + }; + self.infer_pat(pat, expected, pat_info); + } } + pat_ty + } - let expected = self.table.try_structurally_resolve_type(expected); - let elem_ty = match expected.kind() { - TyKind::Array(st, _) | TyKind::Slice(st) => st, - _ => self.err_ty(), + fn infer_tuple_pat( + &mut self, + pat: PatId, + elements: &[PatId], + ddpos: Option, + expected: Ty<'db>, + pat_info: PatInfo, + ) -> Ty<'db> { + let interner = self.interner(); + let mut expected_len = elements.len(); + if ddpos.is_some() { + // Require known type only when `..` is present. + if let TyKind::Tuple(tys) = self.table.structurally_resolve_type(expected).kind() { + expected_len = tys.len(); + } + } + let max_len = cmp::max(expected_len, elements.len()); + + let element_tys_iter = (0..max_len).map(|_| self.table.next_ty_var()); + let element_tys = Tys::new_from_iter(interner, element_tys_iter); + let pat_ty = Ty::new(interner, TyKind::Tuple(element_tys)); + if self.demand_eqtype(pat.into(), expected, pat_ty).is_err() { + let expected = if let TyKind::Tuple(tys) = + self.table.try_structurally_resolve_type(expected).kind() + { + for (expected_var, found) in iter::zip(element_tys, tys) { + // Constrain the infer var so that the type mismatch error message, which contains it, + // will be better. + _ = self.demand_eqtype(pat.into(), expected_var, found); + } + tys + } else { + self.types.empty.tys + }; + let expected = expected.iter().chain(iter::repeat(self.types.types.error)); + Ty::new_tup_from_iter( + interner, + iter::zip(expected, elements).map(|(expected, &elem)| { + self.infer_pat(elem, expected, pat_info); + self.result.type_of_pat_with_adjust(elem) + }), + ) + } else { + for (i, &elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { + self.infer_pat(elem, element_tys[i], pat_info); + } + pat_ty + } + } + + fn check_record_pat_fields( + &mut self, + adt_ty: Ty<'db>, + _pat: PatId, + variant: VariantId, + fields: &[RecordFieldPat], + has_rest_pat: bool, + pat_info: PatInfo, + ) { + let interner = self.interner(); + + let TyKind::Adt(_, args) = adt_ty.kind() else { + panic!("struct pattern is not an ADT"); }; - for &pat_id in prefix.iter().chain(suffix.iter()) { - self.infer_pat(pat_id, elem_ty, default_bm, decl); - } - - if let Some(slice_pat_id) = slice { - let rest_pat_ty = match expected.kind() { - TyKind::Array(_, length) => { - let len = try_const_usize(self.db, length); - let len = - len.and_then(|len| len.checked_sub((prefix.len() + suffix.len()) as u128)); - Ty::new_array_with_const_len( - self.interner(), - elem_ty, - usize_const(self.db, len, self.resolver.krate()), - ) + // Index the struct fields' types. + let variant_fields = variant.fields(self.db); + let field_map = variant_fields + .fields() + .iter() + .map(|(i, field)| (field.name.clone(), i)) + .collect::>(); + let variant_field_tys = self.db.field_types(variant); + let variant_fields_vis = VariantFields::field_visibilities(self.db, variant); + + // Keep track of which fields have already appeared in the pattern. + let mut used_fields = FxHashMap::default(); + + let mut inexistent_fields = vec![]; + // Typecheck each field. + for (field_idx, field) in fields.iter().enumerate() { + match used_fields.entry(field.name.clone()) { + Occupied(_occupied) => { + // FIXME: Emit an error, field specified twice. + } + Vacant(vacant) => { + vacant.insert(field_idx); + } + }; + let field_idx = field_map.get(&field.name).copied(); + let field_ty = match field_idx { + Some(field_idx) => { + if !self.resolver.is_visible(self.db, variant_fields_vis[field_idx]) { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: field.pat.into(), + private: Some(field_idx), + variant, + }); + } + + variant_field_tys[field_idx].get().instantiate(interner, args) + } + None => { + inexistent_fields.push(field); + self.types.types.error } - _ => Ty::new_slice(self.interner(), elem_ty), }; - self.infer_pat(slice_pat_id, rest_pat_ty, default_bm, decl); + + self.infer_pat(field.pat, field_ty, pat_info); } - match expected.kind() { - TyKind::Array(_, const_) => { - Ty::new_array_with_const_len(self.interner(), elem_ty, const_) + let unmentioned_fields = variant_fields + .fields() + .iter() + .filter(|(_, field)| !used_fields.contains_key(&field.name)) + .collect::>(); + + for inexistent_field in inexistent_fields { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: inexistent_field.pat.into(), + private: None, + variant, + }); + } + + // Require `..` if struct has non_exhaustive attribute. + let non_exhaustive = self.has_applicable_non_exhaustive(variant.into()); + if non_exhaustive && !has_rest_pat { + // FIXME: Emit an error. + } + + // Report an error if an incorrect number of fields was specified. + if matches!(variant, VariantId::UnionId(_)) { + if fields.len() != 1 { + // FIXME: Emit an error, unions can't have more than one field. + } + if has_rest_pat { + // FIXME: Emit an error, unions can't have a rest pat. } - _ => Ty::new_slice(self.interner(), elem_ty), + } else if !unmentioned_fields.is_empty() && !has_rest_pat { + // FIXME: Emit an error. } } - fn infer_lit_pat(&mut self, expr: ExprId, expected: Ty<'db>) -> Ty<'db> { - // Like slice patterns, byte string patterns can denote both `&[u8; N]` and `&[u8]`. - if let Expr::Literal(Literal::ByteString(_)) = self.store[expr] - && let TyKind::Ref(_, inner, _) = expected.kind() - { - let inner = self.table.try_structurally_resolve_type(inner); - if matches!(inner.kind(), TyKind::Slice(_)) { - let elem_ty = self.types.types.u8; - let slice_ty = Ty::new_slice(self.interner(), elem_ty); - let ty = Ty::new_ref( - self.interner(), - self.types.regions.statik, - slice_ty, - Mutability::Not, - ); - self.write_expr_ty(expr, ty); - return ty; + fn infer_box_pat( + &mut self, + pat: PatId, + inner: PatId, + expected: Ty<'db>, + pat_info: PatInfo, + ) -> Ty<'db> { + let interner = self.interner(); + let (box_ty, inner_ty) = self + .check_dereferenceable(expected, inner) + .map(|()| { + // Here, `demand::subtype` is good enough, but I don't + // think any errors can be introduced by using `demand::eqtype`. + let inner_ty = self.table.next_ty_var(); + let box_ty = Ty::new_box(interner, inner_ty); + _ = self.demand_eqtype(pat.into(), expected, box_ty); + (box_ty, inner_ty) + }) + .unwrap_or_else(|()| { + let err = self.types.types.error; + (err, err) + }); + self.infer_pat(inner, inner_ty, pat_info); + box_ty + } + + fn _infer_deref_pat(&mut self, inner: PatId, expected: Ty<'db>, pat_info: PatInfo) -> Ty<'db> { + let target_ty = self.deref_pat_target(expected); + self.infer_pat(inner, target_ty, pat_info); + let infer_ok = self.register_deref_mut_bounds_if_needed(inner, [expected]); + self.table.register_infer_ok(infer_ok); + expected + } + + fn deref_pat_target(&mut self, source_ty: Ty<'db>) -> Ty<'db> { + let (Some(deref_pure), Some(deref_target)) = + (self.lang_items.DerefPure, self.lang_items.DerefTarget) + else { + return self.types.types.error; + }; + // Register a `DerefPure` bound, which is required by all `deref!()` pats. + let interner = self.interner(); + self.table.register_bound(source_ty, deref_pure, ObligationCause::new()); + // The expected type for the deref pat's inner pattern is `::Target`. + let target_ty = Ty::new_projection(interner, deref_target.into(), [source_ty]); + self.table.try_structurally_resolve_type(target_ty) + } + + /// Check if the interior of a deref pattern (either explicit or implicit) has any `ref mut` + /// bindings, which would require `DerefMut` to be emitted in MIR building instead of just + /// `Deref`. We do this *after* checking the inner pattern, since we want to make sure to + /// account for `ref mut` binding modes inherited from implicitly dereferencing `&mut` refs. + fn register_deref_mut_bounds_if_needed( + &self, + inner: PatId, + derefed_tys: impl IntoIterator>, + ) -> InferOk<'db, ()> { + let mut infer_ok = InferOk { value: (), obligations: Vec::new() }; + if self.pat_has_ref_mut_binding(inner) { + let Some(deref_mut) = self.lang_items.DerefMut else { return infer_ok }; + let interner = self.interner(); + for mutably_derefed_ty in derefed_tys { + infer_ok.obligations.push(Obligation::new( + interner, + ObligationCause::new(), + self.table.param_env, + TraitRef::new(interner, deref_mut.into(), [mutably_derefed_ty]), + )); } } + infer_ok + } - self.infer_expr(expr, &Expectation::has_type(expected), ExprIsRead::Yes) + /// Does the pattern recursively contain a `ref mut` binding in it? + /// + /// This is used to determined whether a `deref` pattern should emit a `Deref` + /// or `DerefMut` call for its pattern scrutinee. + /// + /// This is computed from the typeck results since we want to make + /// sure to apply any match-ergonomics adjustments, which we cannot + /// determine from the HIR alone. + fn pat_has_ref_mut_binding(&self, pat: PatId) -> bool { + let mut has_ref_mut = false; + self.store.walk_pats(pat, &mut |pat| { + if let Some(BindingMode(ByRef::Yes(Mutability::Mut), _)) = + self.result.binding_modes.get(pat) + { + has_ref_mut = true; + } + }); + has_ref_mut } - fn is_non_ref_pat(&mut self, store: &hir_def::expr_store::ExpressionStore, pat: PatId) -> bool { - match &store[pat] { - Pat::Tuple { .. } - | Pat::TupleStruct { .. } - | Pat::Record { .. } - | Pat::Range { .. } - | Pat::Slice { .. } => true, - Pat::Or(pats) => pats.iter().all(|p| self.is_non_ref_pat(store, *p)), - Pat::Path(path) => { - // A const is a reference pattern, but other value ns things aren't (see #16131). - let resolved = self.resolve_value_path_inner(path, pat.into(), true); - resolved.is_some_and(|it| !matches!(it.0, hir_def::resolver::ValueNs::ConstId(_))) - } - Pat::ConstBlock(..) => false, - Pat::Lit(expr) => !matches!( - store[*expr], - Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..)) - ), - Pat::Wild - | Pat::Bind { .. } - | Pat::Ref { .. } - | Pat::Box { .. } - | Pat::Missing - | Pat::Expr(_) => false, + // Precondition: Pat is Ref(inner) + fn infer_ref_pat( + &mut self, + pat: PatId, + inner: PatId, + pat_mutbl: Mutability, + mut expected: Ty<'db>, + mut pat_info: PatInfo, + ) -> Ty<'db> { + let ref_pat_matches_mut_ref = self.ref_pat_matches_mut_ref(); + if ref_pat_matches_mut_ref && pat_mutbl == Mutability::Not { + // If `&` patterns can match against mutable reference types (RFC 3627, Rule 5), we need + // to prevent subpatterns from binding with `ref mut`. Subpatterns of a shared reference + // pattern should have read-only access to the scrutinee, and the borrow checker won't + // catch it in this case. + pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not(); + } + + expected = self.table.try_structurally_resolve_type(expected); + // Determine whether we're consuming an inherited reference and resetting the default + // binding mode, based on edition and enabled experimental features. + if let ByRef::Yes(inh_mut) = pat_info.binding_mode { + match self.ref_pat_matches_inherited_ref(self.edition) { + InheritedRefMatchRule::EatOuter => { + // ref pattern attempts to consume inherited reference + if pat_mutbl > inh_mut { + // Tried to match inherited `ref` with `&mut` + // NB: This assumes that `&` patterns can match against mutable references + // (RFC 3627, Rule 5). If we implement a pattern typing ruleset with Rule 4E + // but not Rule 5, we'll need to check that here. + debug_assert!(ref_pat_matches_mut_ref); + // FIXME: Emit an error. + } + + pat_info.binding_mode = ByRef::No; + self.result.skipped_ref_pats.insert(pat); + self.infer_pat(inner, expected, pat_info); + return expected; + } + InheritedRefMatchRule::EatInner => { + if let TyKind::Ref(_, _, r_mutbl) = expected.kind() + && pat_mutbl <= r_mutbl + { + // Match against the reference type; don't consume the inherited ref. + // NB: The check for compatible pattern and ref type mutability assumes that + // `&` patterns can match against mutable references (RFC 3627, Rule 5). If + // we implement a pattern typing ruleset with Rule 4 (including the fallback + // to matching the inherited ref when the inner ref can't match) but not + // Rule 5, we'll need to check that here. + debug_assert!(ref_pat_matches_mut_ref); + // NB: For RFC 3627's Rule 3, we limit the default binding mode's ref + // mutability to `pat_info.max_ref_mutbl`. If we implement a pattern typing + // ruleset with Rule 4 but not Rule 3, we'll need to check that here. + debug_assert!(self.downgrade_mut_inside_shared()); + let mutbl_cap = cmp::min(r_mutbl, pat_info.max_ref_mutbl.as_mutbl()); + pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(mutbl_cap); + } else { + // The reference pattern can't match against the expected type, so try + // matching against the inherited ref instead. + if pat_mutbl > inh_mut { + // We can't match an inherited shared reference with `&mut`. + // NB: This assumes that `&` patterns can match against mutable + // references (RFC 3627, Rule 5). If we implement a pattern typing + // ruleset with Rule 4 but not Rule 5, we'll need to check that here. + // FIXME(ref_pat_eat_one_layer_2024_structural): If we already tried + // matching the real reference, the error message should explain that + // falling back to the inherited reference didn't work. This should be + // the same error as the old-Edition version below. + debug_assert!(ref_pat_matches_mut_ref); + // FIXME: Emit an error. + } + + pat_info.binding_mode = ByRef::No; + self.result.skipped_ref_pats.insert(pat); + self.infer_pat(inner, expected, pat_info); + return expected; + } + } + InheritedRefMatchRule::EatBoth { consider_inherited_ref: true } => { + // Reset binding mode on old editions + pat_info.binding_mode = ByRef::No; + + if let TyKind::Ref(_, inner_ty, _) = expected.kind() { + // Consume both the inherited and inner references. + if pat_mutbl.is_mut() && inh_mut.is_mut() { + // As a special case, a `&mut` reference pattern will be able to match + // against a reference type of any mutability if the inherited ref is + // mutable. Since this allows us to match against a shared reference + // type, we refer to this as "falling back" to matching the inherited + // reference, though we consume the real reference as well. We handle + // this here to avoid adding this case to the common logic below. + self.infer_pat(inner, inner_ty, pat_info); + return expected; + } else { + // Otherwise, use the common logic below for matching the inner + // reference type. + // FIXME(ref_pat_eat_one_layer_2024_structural): If this results in a + // mutability mismatch, the error message should explain that falling + // back to the inherited reference didn't work. This should be the same + // error as the Edition 2024 version above. + } + } else { + // The expected type isn't a reference type, so only match against the + // inherited reference. + if pat_mutbl > inh_mut { + // We can't match a lone inherited shared reference with `&mut`. + // FIXME: Emit an error. + } + + self.result.skipped_ref_pats.insert(pat); + self.infer_pat(inner, expected, pat_info); + return expected; + } + } + InheritedRefMatchRule::EatBoth { consider_inherited_ref: false } => { + // Reset binding mode on stable Rust. This will be a type error below if + // `expected` is not a reference type. + pat_info.binding_mode = ByRef::No; + } + } } + + let (ref_ty, inner_ty) = match self.check_dereferenceable(expected, inner) { + Ok(()) => { + // `demand::subtype` would be good enough, but using `eqtype` turns + // out to be equally general. See (note_1) for details. + + // Take region, inner-type from expected type if we can, + // to avoid creating needless variables. This also helps with + // the bad interactions of the given hack detailed in (note_1). + debug!("check_pat_ref: expected={:?}", expected); + match expected.as_reference() { + Some((r_ty, _, r_mutbl)) + if ((ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl) + || r_mutbl == pat_mutbl) => + { + if r_mutbl == Mutability::Not { + pat_info.max_ref_mutbl = MutblCap::Not; + } + + (expected, r_ty) + } + _ => { + let inner_ty = self.table.next_ty_var(); + let ref_ty = self.new_ref_ty(pat_mutbl, inner_ty); + debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); + _ = self.demand_eqtype(pat.into(), expected, ref_ty); + + (ref_ty, inner_ty) + } + } + } + Err(()) => { + let err = self.types.types.error; + (err, err) + } + }; + + self.infer_pat(inner, inner_ty, pat_info); + ref_ty + } + + /// Create a reference or pinned reference type with a fresh region variable. + fn new_ref_ty(&self, mutbl: Mutability, ty: Ty<'db>) -> Ty<'db> { + let region = self.table.next_region_var(); + Ty::new_ref(self.interner(), region, ty, mutbl) } fn try_resolve_slice_ty_to_array_ty( - &mut self, + &self, before: &[PatId], - suffix: &[PatId], slice: Option, ) -> Option> { if slice.is_some() { return None; } - let len = before.len() + suffix.len(); - let size = consteval::usize_const(self.db, Some(len as u128), self.owner.krate(self.db)); + let interner = self.interner(); + let len = before.len(); + let inner_ty = self.table.next_ty_var(); - let elem_ty = self.table.next_ty_var(); - let array_ty = Ty::new_array_with_const_len(self.interner(), elem_ty, size); - Some(array_ty) + Some(Ty::new_array(interner, inner_ty, len.try_into().unwrap())) } - /// Used to determine whether we can infer the expected type in the slice pattern to be of type array. + /// Used to determines whether we can infer the expected type in the slice pattern to be of type array. /// This is only possible if we're in an irrefutable pattern. If we were to allow this in refutable /// patterns we wouldn't e.g. report ambiguity in the following situation: /// /// ```ignore(rust) - /// struct Zeroes; + /// struct Zeroes; /// const ARR: [usize; 2] = [0; 2]; /// const ARR2: [usize; 2] = [2; 2]; /// @@ -664,15 +1535,150 @@ impl<'db> InferenceContext<'_, 'db> { /// /// If we're in an irrefutable pattern we prefer the array impl candidate given that /// the slice impl candidate would be rejected anyway (if no ambiguity existed). - fn pat_is_irrefutable(&self, decl_ctxt: Option) -> bool { - matches!(decl_ctxt, Some(DeclContext { origin: DeclOrigin::LocalDecl { has_else: false } })) + fn pat_is_irrefutable(&self, pat_origin: PatOrigin) -> bool { + match pat_origin { + PatOrigin::LetExpr | PatOrigin::MatchArm => false, + PatOrigin::LetStmt { has_else } => !has_else, + PatOrigin::DestructuringAssignment | PatOrigin::Param => true, + } } -} -pub(super) fn contains_explicit_ref_binding(store: &ExpressionStore, pat_id: PatId) -> bool { - let mut res = false; - store.walk_pats(pat_id, &mut |pat| { - res |= matches!(store[pat], Pat::Bind { id, .. } if matches!(store[id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)); - }); - res + /// Type check a slice pattern. + /// + /// Syntactically, these look like `[pat_0, ..., pat_n]`. + /// Semantically, we are type checking a pattern with structure: + /// ```ignore (not-rust) + /// [before_0, ..., before_n, (slice, after_0, ... after_n)?] + /// ``` + /// The type of `slice`, if it is present, depends on the `expected` type. + /// If `slice` is missing, then so is `after_i`. + /// If `slice` is present, it can still represent 0 elements. + fn infer_slice_pat( + &mut self, + pat: PatId, + before: &[PatId], + slice: Option, + after: &[PatId], + expected: Ty<'db>, + pat_info: PatInfo, + ) -> Ty<'db> { + let expected = self.table.try_structurally_resolve_type(expected); + + // If the pattern is irrefutable and `expected` is an infer ty, we try to equate it + // to an array if the given pattern allows it. See issue #76342 + if self.pat_is_irrefutable(pat_info.pat_origin) + && expected.is_ty_var() + && let Some(resolved_arr_ty) = self.try_resolve_slice_ty_to_array_ty(before, slice) + { + debug!(?resolved_arr_ty); + let _ = self.demand_eqtype(pat.into(), expected, resolved_arr_ty); + } + + let expected = self.table.structurally_resolve_type(expected); + debug!(?expected); + + let (element_ty, opt_slice_ty, inferred) = match expected.kind() { + // An array, so we might have something like `let [a, b, c] = [0, 1, 2];`. + TyKind::Array(element_ty, len) => { + let min = before.len() as u64 + after.len() as u64; + let (opt_slice_ty, expected) = + self.check_array_pat_len(pat, element_ty, expected, slice, len, min.into()); + // `opt_slice_ty.is_none()` => `slice.is_none()`. + // Note, though, that opt_slice_ty could be `Some(error_ty)`. + assert!(opt_slice_ty.is_some() || slice.is_none()); + (element_ty, opt_slice_ty, expected) + } + TyKind::Slice(element_ty) => (element_ty, Some(expected), expected), + // The expected type must be an array or slice, but was neither, so error. + _ => { + // FIXME: Emit an error: expected an array or a slice. + let err = self.types.types.error; + (err, Some(err), err) + } + }; + + // Type check all the patterns before `slice`. + for &elt in before { + self.infer_pat(elt, element_ty, pat_info); + } + // Type check the `slice`, if present, against its expected type. + if let Some(slice) = slice { + self.infer_pat(slice, opt_slice_ty.unwrap(), pat_info); + } + // Type check the elements after `slice`, if present. + for &elt in after { + self.infer_pat(elt, element_ty, pat_info); + } + inferred + } + + /// Type check the length of an array pattern. + /// + /// Returns both the type of the variable length pattern (or `None`), and the potentially + /// inferred array type. We only return `None` for the slice type if `slice.is_none()`. + fn check_array_pat_len( + &mut self, + pat: PatId, + element_ty: Ty<'db>, + arr_ty: Ty<'db>, + slice: Option, + len: Const<'db>, + min_len: u128, + ) -> (Option>, Ty<'db>) { + let len = crate::consteval::try_const_usize(self.db, len); + + if let Some(len) = len { + // Now we know the length... + if slice.is_none() { + // ...and since there is no variable-length pattern, + // we require an exact match between the number of elements + // in the array pattern and as provided by the matched type. + if min_len == len { + return (None, arr_ty); + } + + // FIXME: Emit an error: incorrect length. + } else if let Some(pat_len) = len.checked_sub(min_len) { + // The variable-length pattern was there, + // so it has an array type with the remaining elements left as its size... + return (Some(Ty::new_array(self.interner(), element_ty, pat_len)), arr_ty); + } else { + // ...however, in this case, there were no remaining elements. + // That is, the slice pattern requires more than the array type offers. + // FIXME: Emit an error. + } + } else if slice.is_none() { + // We have a pattern with a fixed length, + // which we can use to infer the length of the array. + let updated_arr_ty = Ty::new_array(self.interner(), element_ty, min_len); + _ = self.demand_eqtype(pat.into(), updated_arr_ty, arr_ty); + return (None, updated_arr_ty); + } else { + // We have a variable-length pattern and don't know the array length. + // This happens if we have e.g., + // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`. + // FIXME: Emit an error: cannot pattern-match on an array without a fixed length. + }; + + // If we get here, we must have emitted an error. + (Some(self.types.types.error), arr_ty) + } + + fn infer_destructuring_assignment_expr(&mut self, expr: ExprId, expected: Ty<'db>) -> Ty<'db> { + // LHS of assignment doesn't constitute reads. + let expr_is_read = ExprIsRead::No; + let lhs_ty = self.infer_expr_inner(expr, &Expectation::has_type(expected), expr_is_read); + match self.coerce(expr, expected, lhs_ty, AllowTwoPhase::No, expr_is_read) { + Ok(ty) => ty, + Err(_) => { + self.result.type_mismatches.get_or_insert_default().insert( + expr.into(), + TypeMismatch { expected: expected.store(), actual: lhs_ty.store() }, + ); + // `rhs_ty` is returned so no further type mismatches are + // reported because of this mismatch. + expected + } + } + } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 3cadc8e93359e..835721942a1cc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -26,29 +26,36 @@ use crate::{ use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource}; impl<'db> InferenceContext<'_, 'db> { - pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option> { - let (value_def, generic_def, substs) = match self.resolve_value_path(path, id)? { - ValuePathResolution::GenericDef(value_def, generic_def, substs) => { - (value_def, generic_def, substs) - } - ValuePathResolution::NonGeneric(ty) => return Some(ty), - }; + pub(super) fn infer_path( + &mut self, + path: &Path, + id: ExprOrPatId, + ) -> Option<(ValueNs, Ty<'db>)> { + let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?; + + let (value_def, generic_def, substs) = + match self.resolve_value_path(path, id, value, self_subst)? { + ValuePathResolution::GenericDef(value_def, generic_def, substs) => { + (value_def, generic_def, substs) + } + ValuePathResolution::NonGeneric(ty) => return Some((value, ty)), + }; let args = self.insert_type_vars(substs); self.add_required_obligations_for_value_path(generic_def, args); let ty = self.db.value_ty(value_def)?.instantiate(self.interner(), args); let ty = self.process_remote_user_written_ty(ty); - Some(ty) + Some((value, ty)) } fn resolve_value_path( &mut self, path: &Path, id: ExprOrPatId, + value: ValueNs, + self_subst: Option>, ) -> Option> { - let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?; - let value_def: ValueTyDefId = match value { ValueNs::FunctionId(it) => it.into(), ValueNs::ConstId(it) => it.into(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index d004b5e3ef1d6..c6c7856c8c3f3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -91,7 +91,7 @@ use crate::{ pub use autoderef::autoderef; pub use infer::{ - Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, InferenceResult, + Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref, PointerCast, cast::CastError, could_coerce, could_unify, could_unify_deeply, infer_query_with_inspect, }; @@ -660,23 +660,6 @@ pub fn known_const_to_ast<'db>( Some(make::expr_const_value(konst.display(db, display_target).to_string().as_str())) } -#[derive(Debug, Copy, Clone)] -pub(crate) enum DeclOrigin { - LetExpr, - /// from `let x = ..` - LocalDecl { - has_else: bool, - }, -} - -/// Provides context for checking patterns in declarations. More specifically this -/// allows us to infer array types if the pattern is irrefutable and allows us to infer -/// the size of the array. See issue rust-lang/rust#76342. -#[derive(Debug, Copy, Clone)] -pub(crate) struct DeclContext { - pub(crate) origin: DeclOrigin, -} - pub fn setup_tracing() -> Option { use std::env; use std::sync::LazyLock; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 0f0ed729c930a..b935a6ed32619 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -886,13 +886,12 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { RecordSpread::FieldDefaults => not_supported!("empty record spread"), }; let variant_id = - self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path { - Some(p) => MirLowerError::UnresolvedName( - hir_display_with_store(&**p, self.store) + self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| { + MirLowerError::UnresolvedName( + hir_display_with_store(path, self.store) .display(self.db, self.display_target()) .to_string(), - ), - None => MirLowerError::RecordLiteralWithoutPath, + ) })?; let subst = match self.expr_ty_without_adjust(expr_id).kind() { TyKind::Adt(_, s) => s, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 99c5f0fc653f5..68b1c8b3b6271 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -4,7 +4,7 @@ use hir_def::{hir::ExprId, signatures::VariantFields}; use rustc_type_ir::inherent::{IntoKind, Ty as _}; use crate::{ - BindingMode, + BindingMode, ByRef, mir::{ LocalId, MutBorrowKind, Operand, OperandKind, lower::{ @@ -104,7 +104,7 @@ impl<'db> MirLowerCtx<'_, 'db> { ) -> Result<'db, (BasicBlockId, Option)> { self.pattern_match_binding( id, - BindingMode::Move, + BindingMode(ByRef::No, rustc_ast_ir::Mutability::Not), local.into(), MirSpan::SelfParam, current, @@ -385,7 +385,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.push_match_assignment( current, local, - BindingMode::Move, + BindingMode(ByRef::No, rustc_ast_ir::Mutability::Not), cond_place, pattern.into(), ); @@ -535,13 +535,13 @@ impl<'db> MirLowerCtx<'_, 'db> { current, target_place.into(), match mode { - BindingMode::Move => { + BindingMode(ByRef::No, _) => { Operand { kind: OperandKind::Copy(cond_place), span: None }.into() } - BindingMode::Ref(rustc_ast_ir::Mutability::Not) => { + BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Not), _) => { Rvalue::Ref(BorrowKind::Shared, cond_place) } - BindingMode::Ref(rustc_ast_ir::Mutability::Mut) => { + BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Mut), _) => { Rvalue::Ref(BorrowKind::Mut { kind: MutBorrowKind::Default }, cond_place) } }, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 161a3142df556..aa118149e2c80 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -85,6 +85,10 @@ pub struct DefaultTypes<'db> { pub error: Ty<'db>, /// `&'static str` pub static_str_ref: Ty<'db>, + /// `[u8]` + pub u8_slice: Ty<'db>, + /// `&'static [u8]` + pub static_u8_slice: Ty<'db>, /// `*mut ()` pub mut_unit_ptr: Ty<'db>, } @@ -236,6 +240,8 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { let empty_tys = create_tys(&[]); let unit = create_ty(TyKind::Tuple(empty_tys)); let u8 = create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U8)); + let u8_slice = create_ty(TyKind::Slice(u8)); + let static_u8_slice = create_ty(TyKind::Ref(statik, u8_slice, Mutability::Not)); DefaultAny { types: DefaultTypes { usize: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::Usize)), @@ -261,6 +267,8 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { never: create_ty(TyKind::Never), error: create_ty(TyKind::Error(ErrorGuaranteed)), static_str_ref: create_ty(TyKind::Ref(statik, str, rustc_ast_ir::Mutability::Not)), + u8_slice, + static_u8_slice, mut_unit_ptr: create_ty(TyKind::RawPtr(unit, rustc_ast_ir::Mutability::Mut)), }, consts: DefaultConsts { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index a6352c7899fff..70d3366675692 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -7,12 +7,11 @@ use std::sync::Arc; pub use BoundRegionConversionTime::*; use ena::unify as ut; -use hir_def::GenericParamId; +use hir_def::{GenericParamId, TraitId}; use opaque_types::{OpaqueHiddenType, OpaqueTypeStorage}; use region_constraints::{RegionConstraintCollector, RegionConstraintStorage}; use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt}; use rustc_pattern_analysis::Captures; -use rustc_type_ir::solve::{NoSolution, inspect}; use rustc_type_ir::{ ClosureKind, ConstVid, FloatVarValue, FloatVid, GenericArgKind, InferConst, InferTy, IntVarValue, IntVid, OutlivesPredicate, RegionVid, TermKind, TyVid, TypeFoldable, TypeFolder, @@ -22,6 +21,10 @@ use rustc_type_ir::{ Const as _, GenericArg as _, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _, }, }; +use rustc_type_ir::{ + Upcast, + solve::{NoSolution, inspect}, +}; use snapshot::undo_log::InferCtxtUndoLogs; use tracing::{debug, instrument}; use traits::{ObligationCause, PredicateObligations}; @@ -509,6 +512,52 @@ impl<'db> InferCtxt<'db> { self.evaluate_obligation(obligation).must_apply_modulo_regions() } + /// Check whether a `ty` implements given trait(trait_def_id) without side-effects. + /// + /// The inputs are: + /// + /// - the def-id of the trait + /// - the type parameters of the trait, including the self-type + /// - the parameter environment + /// + /// Invokes `evaluate_obligation`, so in the event that evaluating + /// `Ty: Trait` causes overflow, EvaluatedToAmbigStackDependent will be returned. + /// + /// `type_implements_trait` is a convenience function for simple cases like + /// + /// ```ignore (illustrative) + /// let copy_trait = infcx.tcx.require_lang_item(LangItem::Copy, span); + /// let implements_copy = infcx.type_implements_trait(copy_trait, [ty], param_env) + /// .must_apply_modulo_regions(); + /// ``` + /// + /// In most cases you should instead create an [Obligation] and check whether + /// it holds via [`evaluate_obligation`] or one of its helper functions like + /// [`predicate_must_hold_modulo_regions`], because it properly handles higher ranked traits + /// and it is more convenient and safer when your `params` are inside a [`Binder`]. + /// + /// [Obligation]: traits::Obligation + /// [`evaluate_obligation`]: crate::traits::query::evaluate_obligation::InferCtxtExt::evaluate_obligation + /// [`predicate_must_hold_modulo_regions`]: crate::traits::query::evaluate_obligation::InferCtxtExt::predicate_must_hold_modulo_regions + /// [`Binder`]: ty::Binder + #[instrument(level = "debug", skip(self, params), ret)] + pub fn type_implements_trait( + &self, + trait_def_id: TraitId, + params: impl IntoIterator>>, + param_env: ParamEnv<'db>, + ) -> EvaluationResult { + let trait_ref = TraitRef::new(self.interner, trait_def_id.into(), params); + + let obligation = traits::Obligation { + cause: traits::ObligationCause::dummy(), + param_env, + recursion_depth: 0, + predicate: trait_ref.upcast(self.interner), + }; + self.evaluate_obligation(&obligation) + } + /// Evaluate a given predicate, capturing overflow and propagating it back. fn evaluate_obligation(&self, obligation: &PredicateObligation<'db>) -> EvaluationResult { self.probe(|snapshot| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs index bd407fd15718a..bbfc8a4757818 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs @@ -63,7 +63,7 @@ pub enum NotConstEvaluatable { /// so they are noops when unioned with a definite error, and within /// the categories it's easy to see that the unions are correct. #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] -pub(crate) enum EvaluationResult { +pub enum EvaluationResult { /// Evaluation successful. EvaluatedToOk, /// Evaluation successful, but there were unevaluated region obligations. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 39abdaf079b63..c953e79602d51 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -183,6 +183,35 @@ impl<'db> Ty<'db> { ) } + /// Note: this needs an interner with crate. + pub fn new_array(interner: DbInterner<'db>, ty: Ty<'db>, n: u128) -> Ty<'db> { + Ty::new( + interner, + TyKind::Array( + ty, + crate::consteval::usize_const(interner.db, Some(n), interner.expect_crate()), + ), + ) + } + + fn new_generic_adt(interner: DbInterner<'db>, adt_id: AdtId, ty_param: Ty<'db>) -> Ty<'db> { + let args = GenericArgs::fill_with_defaults( + interner, + adt_id.into(), + [ty_param.into()], + |_, _, _| panic!("all params except the first should have defaults"), + ); + Ty::new_adt(interner, adt_id, args) + } + + /// Note: Unlike most other constructors, this require the interner to have a crate, because this needs lang items. + pub fn new_box(interner: DbInterner<'db>, ty: Ty<'db>) -> Ty<'db> { + let Some(def_id) = interner.lang_items().OwnedBox else { + return Ty::new_error(interner, ErrorGuaranteed); + }; + Ty::new_generic_adt(interner, def_id.into(), ty) + } + /// Returns the `Size` for primitive types (bool, uint, int, char, float). pub fn primitive_size(self, interner: DbInterner<'db>) -> Size { match self.kind() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index a80ce5002deab..cc3464cd7fa9f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -878,11 +878,11 @@ struct V { t: T } fn main() { let a: V<&dyn Tr>; (a,) = V { t: &S }; - //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + '?)>,) + //^^^^expected V<&'? S>, got ({unknown},) let mut a: V<&dyn Tr> = V { t: &S }; (a,) = V { t: &S }; - //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + '?)>,) + //^^^^expected V<&'? S>, got ({unknown},) } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index d6bc03f57dee0..9449f531a6d29 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -133,8 +133,8 @@ fn infer_literal_pattern() { 55..72 'let "f... any()': bool 59..64 '"foo"': &'static str 59..64 '"foo"': &'static str - 67..70 'any': fn any<&'static str>() -> &'static str - 67..72 'any()': &'static str + 67..70 'any': fn any<&'? str>() -> &'? str + 67..72 'any()': &'? str 73..75 '{}': () 80..99 'if let...y() {}': () 83..96 'let 1 = any()': bool @@ -193,31 +193,27 @@ fn infer_literal_pattern() { fn infer_range_pattern() { check_infer_with_mismatches( r#" -//- minicore: range -fn test(x..y: &core::ops::Range) { +fn test() { if let 1..76 = 2u32 {} if let 1..=76 = 2u32 {} } "#, expect![[r#" - 8..9 'x': Range - 8..12 'x..y': Range - 11..12 'y': Range - 38..96 '{ ...2 {} }': () - 44..66 'if let...u32 {}': () - 47..63 'let 1....= 2u32': bool - 51..52 '1': u32 - 51..56 '1..76': u32 + 10..68 '{ ...2 {} }': () + 16..38 'if let...u32 {}': () + 19..35 'let 1....= 2u32': bool + 23..24 '1': u32 + 23..28 '1..76': u32 + 26..28 '76': u32 + 31..35 '2u32': u32 + 36..38 '{}': () + 43..66 'if let...u32 {}': () + 46..63 'let 1....= 2u32': bool + 50..51 '1': u32 + 50..56 '1..=76': u32 54..56 '76': u32 59..63 '2u32': u32 64..66 '{}': () - 71..94 'if let...u32 {}': () - 74..91 'let 1....= 2u32': bool - 78..79 '1': u32 - 78..84 '1..=76': u32 - 82..84 '76': u32 - 87..91 '2u32': u32 - 92..94 '{}': () "#]], ); check_no_mismatches( @@ -265,7 +261,6 @@ fn infer_pattern_match_ergonomics() { #[test] fn infer_pattern_match_ergonomics_ref() { - cov_mark::check!(match_ergonomics_ref); check_infer( r#" fn test() { @@ -409,7 +404,7 @@ fn infer_pattern_match_byte_string_literal() { 209..233 'if let...[S] {}': () 212..230 'let b"... &v[S]': bool 216..222 'b"foo"': &'static [u8] - 216..222 'b"foo"': &'static [u8] + 216..222 'b"foo"': &'static [u8; 3] 225..230 '&v[S]': &'? [u8] 226..227 'v': [u8; 3] 226..230 'v[S]': [u8] @@ -825,6 +820,8 @@ fn box_pattern() { ); check_infer( r#" + #![feature(lang_items)] + #[lang = "owned_box"] pub struct Box(T); @@ -835,12 +832,13 @@ fn box_pattern() { } "#, expect![[r#" - 52..58 'params': Box - 70..124 '{ ... } }': () - 76..122 'match ... }': () - 82..88 'params': Box - 99..110 'box integer': Box - 114..116 '{}': () + 77..83 'params': Box + 95..149 '{ ... } }': () + 101..147 'match ... }': () + 107..113 'params': Box + 124..135 'box integer': Box + 128..135 'integer': i32 + 139..141 '{}': () "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index e30fa779dac25..1b63a4a2c0bb4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -1956,7 +1956,7 @@ fn main() { Alias::Braced; //^^^^^^^^^^^^^ {unknown} let Alias::Braced = loop {}; - //^^^^^^^^^^^^^ ! + //^^^^^^^^^^^^^ {unknown} let Alias::Braced(..) = loop {}; //^^^^^^^^^^^^^^^^^ Enum diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index c30b24cd27d37..fb46e4b58b8fc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -3460,15 +3460,13 @@ struct TS(usize); fn main() { let x; [x,] = &[1,]; - //^^^^expected &'? [i32; 1], got [{unknown}] let x; [(x,),] = &[(1,),]; - //^^^^^^^expected &'? [(i32,); 1], got [{unknown}] let x; ((x,),) = &((1,),); - //^^^^^^^expected &'? ((i32,),), got (({unknown},),) + //^^^^^^^expected &'? ((i32,),), got ({unknown},) let x; (x,) = &(1,); @@ -3476,7 +3474,7 @@ fn main() { let x; (S { a: x },) = &(S { a: 42 },); - //^^^^^^^^^^^^^expected &'? (S,), got (S,) + //^^^^^^^^^^^^^expected &'? (S,), got ({unknown},) let x; S { a: x } = &S { a: 42 }; diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 6cfb79d5a1f4b..b14bf64266adb 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -716,16 +716,10 @@ impl<'db> AnyDiagnostic<'db> { TypedHole { expr, expected: Type::new(db, def, expected.as_ref()) }.into() } &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => { - let expr_or_pat = match pat { - ExprOrPatId::ExprId(expr) => expr_syntax(expr)?, - ExprOrPatId::PatId(pat) => { - let InFile { file_id, value } = pat_syntax(pat)?; - - // cast from Either -> Either<_, Pat> - let ptr = AstPtr::try_from_raw(value.syntax_node_ptr())?; - InFile { file_id, value: ptr } - } - }; + let InFile { file_id, value } = pat_syntax(pat)?; + // cast from Either -> Either<_, Pat> + let ptr = AstPtr::try_from_raw(value.syntax_node_ptr())?; + let expr_or_pat = InFile { file_id, value: ptr }; MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into() } InferenceDiagnostic::CastToUnsized { expr, cast_ty } => { 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 bbe1e670dee2b..d2658e06dc834 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -409,7 +409,7 @@ impl<'db> SourceAnalyzer<'db> { ExprOrPatId::PatId(idx) => infer .pat_adjustment(idx) .and_then(|adjusts| adjusts.last()) - .map(|adjust| adjust.as_ref()), + .map(|adjust| adjust.source.as_ref()), }; let ty = infer.expr_or_pat_ty(expr_or_pat_id); @@ -449,12 +449,12 @@ impl<'db> SourceAnalyzer<'db> { ) -> Option { let id = self.pat_id(&pat.clone().into())?; let infer = self.infer()?; - infer.binding_mode(id.as_pat()?).map(|bm| match bm { - hir_ty::BindingMode::Move => BindingMode::Move, - hir_ty::BindingMode::Ref(hir_ty::next_solver::Mutability::Mut) => { + Some(match infer.binding_mode(id.as_pat()?) { + hir_ty::BindingMode(hir_ty::ByRef::No, _) => BindingMode::Move, + hir_ty::BindingMode(hir_ty::ByRef::Yes(hir_ty::next_solver::Mutability::Mut), _) => { BindingMode::Ref(Mutability::Mut) } - hir_ty::BindingMode::Ref(hir_ty::next_solver::Mutability::Not) => { + hir_ty::BindingMode(hir_ty::ByRef::Yes(hir_ty::next_solver::Mutability::Not), _) => { BindingMode::Ref(Mutability::Shared) } }) @@ -470,7 +470,7 @@ impl<'db> SourceAnalyzer<'db> { infer .pat_adjustment(pat_id.as_pat()?)? .iter() - .map(|ty| Type::new_with_resolver(db, &self.resolver, ty.as_ref())) + .map(|adjust| Type::new_with_resolver(db, &self.resolver, adjust.source.as_ref())) .collect(), ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index 291605056b3c6..09a554234a356 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -1787,7 +1787,7 @@ fn main() { // * `?` check_in_place_assist( r#" -//- minicore: option +//- minicore: try, option fn f1(v: i32) {} fn f2(v: &i32) {} trait T { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 99dd2ea237746..c5fd0201e959a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -2499,6 +2499,7 @@ fn $0fun_name() { check_assist( extract_function, r#" +//- minicore: iterator fn foo() { $0for v in &[0, 1] { }$0 } @@ -4746,7 +4747,7 @@ async fn some_function() { check_assist( extract_function, r#" -//- minicore: future, result +//- minicore: future, result, try async fn foo() -> Result<(), ()> { $0async {}.await; Err(())?$0 diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs index 2af074f1fcdfe..bf9bc394b399c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -724,6 +724,7 @@ fn foo() { check_assist( inline_local_variable, r" +//- minicore: iterator fn foo() { let a$0 = vec![10, 20]; for i in a {} 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 82baf885ddc68..1e7f9891100fa 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 @@ -1186,7 +1186,9 @@ fn main() { ); check_edit( kind, - r#"fn main() { for i in 0..10 {}.$0 }"#, + r#" +//- minicore: iterator +fn main() { for i in 0..10 {}.$0 }"#, &format!("fn main() {{ {kind} {{ for i in 0..10 {{}} }} }}"), ); check_edit( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index d52fc73870530..8cd41f7aed90b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -300,7 +300,7 @@ fn main() { } match (true, false) { (true, false, true) => (), - //^^^^^^^^^^^^^^^^^^^ error: expected (bool, bool), found (bool, bool, bool) + //^^^^^^^^^^^^^^^^^^^ error: expected (bool, bool), found (bool, bool, {unknown}) (true) => (), // ^^^^ error: expected (bool, bool), found bool } @@ -1198,4 +1198,20 @@ fn main() { ); } } + + #[test] + fn no_overloaded_deref_is_not_projection() { + check_diagnostics( + r#" +const FOO: &str = ""; + +fn foo() { + match "" { + FOO => {} + _ => {} + } +} + "#, + ); + } } 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 98a4474ef1e94..355617a2b11d6 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 @@ -739,7 +739,7 @@ fn foo() -> Result<(), ()> { check_fix( r#" -//- minicore: result +//- minicore: result, iterator fn foo() -> Result<(), ()> { for _ in 0..5 {}$0 } @@ -1190,9 +1190,7 @@ fn f() { let &() = &mut (); //^^^ error: expected &mut (), found &() match &() { - // FIXME: we should only show the deep one. &9 => () - //^^ error: expected &(), found &i32 //^ error: expected (), found i32 } } 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 fb885c2ad11f5..6ac4fa1fba8ef 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -448,6 +448,7 @@ fn main() { fn macro_expand_match_ast_inside_let_statement() { check( r#" +//- minicore: try macro_rules! match_ast { (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; (match ($node:expr) {}) => {{}}; diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs index e8d305afb3b96..63a83ea33528f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs @@ -169,13 +169,14 @@ fn __( } match &(0,) { (x,) | (x,) => (), - //^^^^^^^^^^^) - //^^^^^^^^^^^&( + //^^^^& //^ ref //^ ref + //^^^^& ((x,) | (x,)) => (), - //^^^^^^^^^^^^^& + //^^^^& //^ ref + //^^^^& //^ ref } match &mut (0,) { @@ -183,7 +184,8 @@ fn __( //^^^^ &mut //^ ref mut } -}"#, +} +"#, ); } @@ -217,8 +219,8 @@ fn main() { expect![[r#" fn main() { match &(0,) { - &(&((ref x,) | (ref x,))) => (), - &((ref x,) | (ref x,)) => (), + &(ref x,) | &(ref x,) => (), + (&(ref x,) | &(ref x,)) => (), } } "#]], diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 614411598b2fd..8dcc73d81f58a 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -194,6 +194,7 @@ define_symbols! { Default, deprecated, deref_mut, + deref_pure, deref_target, deref, derive_const, @@ -578,4 +579,8 @@ define_symbols! { field, field_base, field_type, + ref_pat_eat_one_layer_2024, + ref_pat_eat_one_layer_2024_structural, + deref_patterns, + mut_ref, } From 3411dda01e9e789affbfd1308894d49b3dad1cfb Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 08:42:47 +0300 Subject: [PATCH 030/289] Add "inh" to typos rustc uses this as a short for "inherited". --- src/tools/rust-analyzer/.typos.toml | 1 + src/tools/rust-analyzer/bench_data/glorious_old_parser | 2 +- .../crates/hir-ty/src/next_solver/infer/mod.rs | 6 +++--- .../crates/ide/src/syntax_highlighting/tests.rs | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/.typos.toml b/src/tools/rust-analyzer/.typos.toml index e954b08fb1e1b..873daa3bf3b83 100644 --- a/src/tools/rust-analyzer/.typos.toml +++ b/src/tools/rust-analyzer/.typos.toml @@ -34,6 +34,7 @@ thir = "thir" jod = "jod" tructure = "tructure" taits = "taits" +inh = "inh" [default.extend-identifiers] anc = "anc" diff --git a/src/tools/rust-analyzer/bench_data/glorious_old_parser b/src/tools/rust-analyzer/bench_data/glorious_old_parser index 5022514924687..a6de9daa86dee 100644 --- a/src/tools/rust-analyzer/bench_data/glorious_old_parser +++ b/src/tools/rust-analyzer/bench_data/glorious_old_parser @@ -1,4 +1,4 @@ -//- minicore: fn +//- minicore: fn, iterator, try, panic use crate::ast::{AngleBracketedArgs, ParenthesizedArgs, AttrStyle, BareFnTy}; use crate::ast::{GenericBound, TraitBoundModifier}; use crate::ast::Unsafety; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 70d3366675692..de21c5442bd77 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -537,9 +537,9 @@ impl<'db> InferCtxt<'db> { /// and it is more convenient and safer when your `params` are inside a [`Binder`]. /// /// [Obligation]: traits::Obligation - /// [`evaluate_obligation`]: crate::traits::query::evaluate_obligation::InferCtxtExt::evaluate_obligation - /// [`predicate_must_hold_modulo_regions`]: crate::traits::query::evaluate_obligation::InferCtxtExt::predicate_must_hold_modulo_regions - /// [`Binder`]: ty::Binder + /// [`evaluate_obligation`]: InferCtxt::evaluate_obligation + /// [`predicate_must_hold_modulo_regions`]: InferCtxt::predicate_must_hold_modulo_regions + /// [`Binder`]: rustc_type_ir::Binder #[instrument(level = "debug", skip(self, params), ret)] pub fn type_implements_trait( &self, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index d687cb40a9697..b1094872ffa05 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -1348,7 +1348,7 @@ fn benchmark_syntax_highlighting_parser() { }) .count() }; - assert_eq!(hash, 1631); + assert_eq!(hash, 1644); } #[test] From f006b0db79dbe8c100fa729c9417f0448a616bf7 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 14:50:48 +0530 Subject: [PATCH 031/289] migrate path transform to use mod_path_to_ast_with_factory --- .../crates/ide-db/src/path_transform.rs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 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 2d4a6b8b5b1ba..e66f645c00567 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 @@ -1,6 +1,6 @@ //! See [`PathTransform`]. -use crate::helpers::mod_path_to_ast; +use crate::helpers::mod_path_to_ast_with_factory; use either::Either; use hir::{ AsAssocItem, FindPathConfig, HirDisplay, HirFileId, ModuleDef, SemanticsScope, @@ -354,6 +354,7 @@ impl Ctx<'_> { } fn transform_path_(&self, editor: &SyntaxEditor, path: &ast::Path) -> Option<()> { + let make = editor.make(); if path.qualifier().is_some() { return None; } @@ -397,7 +398,14 @@ impl Ctx<'_> { hir::ModuleDef::Trait(trait_ref), cfg, )?; - match make::ty_path(mod_path_to_ast(&found_path, self.target_edition)) { + match make + .ty_path(mod_path_to_ast_with_factory( + make, + &found_path, + self.target_edition, + )) + .into() + { ast::Type::PathType(path_ty) => Some(path_ty), _ => None, } @@ -447,7 +455,7 @@ impl Ctx<'_> { allow_unstable: true, }; let found_path = self.target_module.find_path(self.source_scope.db, def, cfg)?; - let res = mod_path_to_ast(&found_path, self.target_edition); + let res = mod_path_to_ast_with_factory(make, &found_path, self.target_edition); let (res_editor, res) = SyntaxEditor::with_ast_node(&res); if let Some(args) = path.segment().and_then(|it| it.generic_arg_list()) && let Some(segment) = res.segment() @@ -501,7 +509,8 @@ impl Ctx<'_> { )?; if let Some(qual) = - mod_path_to_ast(&found_path, self.target_edition).qualifier() + mod_path_to_ast_with_factory(make, &found_path, self.target_edition) + .qualifier() { editor.replace( path.syntax(), @@ -524,8 +533,9 @@ impl Ctx<'_> { fn transform_ident_pat(&self, editor: &SyntaxEditor, ident_pat: &ast::IdentPat) -> Option<()> { let name = ident_pat.name()?; + let make = editor.make(); - let temp_path = make::path_from_text(&name.text()); + let temp_path = make.path_from_text(&name.text()); let resolution = self.source_scope.speculative_resolve(&temp_path)?; @@ -580,7 +590,7 @@ impl Ctx<'_> { let found_path = self.target_module.find_path(self.source_scope.db, def, cfg)?; editor.replace( ident_pat.syntax(), - mod_path_to_ast(&found_path, self.target_edition).syntax(), + mod_path_to_ast_with_factory(make, &found_path, self.target_edition).syntax(), ); Some(()) } From 567570e133e53ef00ce2209f7a4331013001acae Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 14:51:12 +0530 Subject: [PATCH 032/289] migrate add_missing_match_arm to use mod_path_to_ast_with_factory --- .../ide-assists/src/handlers/add_missing_match_arms.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 3c33ddec31624..f2d71a1dfee47 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -4,7 +4,7 @@ use either::Either; use hir::{Adt, AsAssocItem, Crate, FindPathConfig, HasAttrs, ModuleDef, Semantics}; use ide_db::RootDatabase; use ide_db::syntax_helpers::suggest_name; -use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast}; +use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast_with_factory}; use itertools::Itertools; use syntax::ast::edit::IndentLevel; use syntax::ast::syntax_factory::SyntaxFactory; @@ -602,7 +602,11 @@ fn build_pat( false, ) } else { - mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition) + mod_path_to_ast_with_factory( + make, + &module.find_path(db, ModuleDef::from(var), cfg)?, + edition, + ) }; let fields = var.fields(db); let pat: ast::Pat = match var.kind(db) { From 3d1c65ab0d8242afc3df0f7aa31056b326606426 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 14:51:46 +0530 Subject: [PATCH 033/289] migrate convert_bool_to_enum to use mod_path_to_ast_with_factory --- .../crates/ide-assists/src/handlers/convert_bool_to_enum.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs index e88778a62e193..231c447ec3816 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs @@ -5,7 +5,7 @@ use ide_db::{ FxHashSet, assists::AssistId, defs::Definition, - helpers::mod_path_to_ast, + helpers::mod_path_to_ast_with_factory, imports::insert_use::{ImportScope, insert_use}, search::{FileReference, UsageSearchResult}, source_change::SourceChangeBuilder, @@ -373,7 +373,7 @@ fn augment_references_with_imports( ) .map(|mod_path| { make.path_concat( - mod_path_to_ast(&mod_path, edition), + mod_path_to_ast_with_factory(make, &mod_path, edition), make.path_from_text("Bool"), ) })?; From cbf3bc3e5d2caf0a9ac176d1eb3ad4fe9f4056b1 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 14:53:28 +0530 Subject: [PATCH 034/289] migrate convert_tuple_return_type_to_struct to use mod_path_to_ast_with_factory --- .../handlers/convert_tuple_return_type_to_struct.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 0af0cbc32a988..254ff7280f0d6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -4,7 +4,7 @@ use ide_db::{ FxHashSet, assists::AssistId, defs::Definition, - helpers::mod_path_to_ast, + helpers::mod_path_to_ast_with_factory, imports::insert_use::{ImportScope, insert_use_with_editor}, search::{FileReference, UsageSearchResult}, source_change::SourceChangeBuilder, @@ -176,7 +176,7 @@ fn node_to_pats(node: SyntaxNode) -> Option> { } fn augment_references_with_imports( - syntax_factory: &SyntaxFactory, + make: &SyntaxFactory, ctx: &AssistContext<'_>, references: &[FileReference], struct_name: &str, @@ -208,12 +208,13 @@ fn augment_references_with_imports( cfg, ) .map(|mod_path| { - syntax_factory.path_concat( - mod_path_to_ast( + make.path_concat( + mod_path_to_ast_with_factory( + make, &mod_path, target_module.krate(ctx.db()).edition(ctx.db()), ), - syntax_factory.path_from_text(struct_name), + make.path_from_text(struct_name), ) }); From d0c62104f83ce426b05f5bd8d8635b4d7287d66d Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 14:53:51 +0530 Subject: [PATCH 035/289] migrate destructure_struct_binding to use mod_path_to_ast_with_factory --- .../ide-assists/src/handlers/destructure_struct_binding.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index 9ffce445d1a1e..6e6c7fcbfbedc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -3,7 +3,7 @@ use ide_db::{ FxHashMap, FxHashSet, RootDatabase, assists::AssistId, defs::Definition, - helpers::mod_path_to_ast, + helpers::mod_path_to_ast_with_factory, search::{FileReference, SearchScope}, }; use itertools::Itertools; @@ -276,7 +276,7 @@ fn destructure_pat( field_names: &[(SmolStr, SmolStr)], ) { let make = editor.make(); - let struct_path = mod_path_to_ast(&data.struct_def_path, data.edition); + let struct_path = mod_path_to_ast_with_factory(make, &data.struct_def_path, data.edition); let is_ref = data.target.is_ref(); let is_mut = data.target.is_mut(); From d457b65c57dbb07a1237d95dd2afffa59ed67bd1 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 14:54:10 +0530 Subject: [PATCH 036/289] migrate extract_struct_from_enum_variant to use mod_path_to_ast_with_factory --- .../src/handlers/extract_struct_from_enum_variant.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 21013e2e614c7..9a884bc1daedf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -5,7 +5,7 @@ use hir::{EnumVariant, HasCrate, Module, ModuleDef, Name}; use ide_db::{ FxHashSet, RootDatabase, defs::Definition, - helpers::mod_path_to_ast, + helpers::mod_path_to_ast_with_factory, imports::insert_use::{ImportScope, InsertUseConfig, insert_use_with_editor}, path_transform::PathTransform, search::FileReference, @@ -401,7 +401,12 @@ fn apply_references( ) { let make = editor.make(); if let Some((scope, path)) = import { - insert_use_with_editor(&scope, mod_path_to_ast(&path, edition), &insert_use_cfg, editor); + insert_use_with_editor( + &scope, + mod_path_to_ast_with_factory(make, &path, edition), + &insert_use_cfg, + editor, + ); } // deep clone to prevent cycle let path = make.path_from_segments(iter::once(segment.clone()), false); From b6a7361169367c4b9d4f7a61cccd31d1c790d9e5 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 14:54:26 +0530 Subject: [PATCH 037/289] migrate generate_new to use mod_path_to_ast_with_factory --- .../crates/ide-assists/src/handlers/generate_new.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 520709adc5e29..7f1eafb98d3ee 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -92,7 +92,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let expr = use_trivial_constructor( ctx.sema.db, - ide_db::helpers::mod_path_to_ast(&type_path, edition), + ide_db::helpers::mod_path_to_ast_with_factory(make, &type_path, edition), &ty, edition, )?; From 0f48c8a48df4d44161c7d499505779102529ff8e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Apr 2026 11:28:45 +0200 Subject: [PATCH 038/289] misc improvements --- .../src/completions/attribute.rs | 16 ++--- .../src/completions/attribute/cfg.rs | 7 +- .../src/completions/attribute/diagnostic.rs | 70 +++++++++---------- .../src/completions/attribute/lint.rs | 18 ++--- .../src/completions/attribute/macro_use.rs | 6 +- .../src/completions/attribute/repr.rs | 56 +++++++-------- .../crates/ide-completion/src/lib.rs | 2 +- 7 files changed, 79 insertions(+), 96 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index da1e664f961c7..a518e70861de7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -37,7 +37,7 @@ pub(crate) use self::derive::complete_derive_path; pub(crate) fn complete_known_attribute_input( acc: &mut Completions, ctx: &CompletionContext<'_>, - &colon_prefix: &bool, + colon_prefix: bool, fake_attribute_under_caret: &ast::TokenTreeMeta, extern_crate: Option<&ast::ExternCrate>, ) -> Option<()> { @@ -49,7 +49,7 @@ pub(crate) fn complete_known_attribute_input( let tt = attribute.token_tree()?; match segments.as_slice() { - ["repr"] => repr::complete_repr(acc, ctx, tt), + ["repr"] => repr::complete_repr(acc, ctx, &parse_comma_sep_expr(tt)?), ["feature"] => lint::complete_lint( acc, ctx, @@ -57,12 +57,10 @@ pub(crate) fn complete_known_attribute_input( &parse_tt_as_comma_sep_paths(tt, ctx.edition)?, FEATURES, ), - ["allow"] | ["expect"] | ["deny"] | ["forbid"] | ["warn"] => { + ["allow" | "expect" | "deny" | "forbid" | "warn"] => { let existing_lints = parse_tt_as_comma_sep_paths(tt, ctx.edition)?; - let lints: Vec = CLIPPY_LINT_GROUPS - .iter() - .map(|g| &g.lint) + let lints: Vec = (CLIPPY_LINT_GROUPS.iter().map(|g| &g.lint)) .chain(DEFAULT_LINTS) .chain(CLIPPY_LINTS) .chain(RUSTDOC_LINTS) @@ -77,7 +75,9 @@ pub(crate) fn complete_known_attribute_input( extern_crate, &parse_tt_as_comma_sep_paths(tt, ctx.edition)?, ), - ["diagnostic", "on_unimplemented"] => diagnostic::complete_on_unimplemented(acc, ctx, tt), + ["diagnostic", "on_unimplemented"] => { + diagnostic::complete_on_unimplemented(acc, ctx, &parse_comma_sep_expr(tt)?) + } _ => (), } Some(()) @@ -190,7 +190,7 @@ pub(crate) fn complete_attribute_path( match attributes { Some(applicable) => applicable .iter() - .flat_map(|name| ATTRIBUTES.binary_search_by(|attr| attr.key().cmp(name)).ok()) + .flat_map(|name| ATTRIBUTES.binary_search_by_key(name, |attr| attr.key()).ok()) .flat_map(|idx| ATTRIBUTES.get(idx)) .for_each(add_completion), None if is_inner => ATTRIBUTES.iter().for_each(add_completion), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index 0d36fb7a405b8..1bd6e6d9bfdf2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs @@ -11,7 +11,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { let mut completion = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), item, ctx.edition); completion.insert_text(format!(r#""{item}""#)); - acc.add(completion.build(ctx.db)); + completion.add_to(acc, ctx.db); }; // FIXME: Move this into context/analysis.rs @@ -49,8 +49,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { ctx.edition, ); item.insert_text(insert_text); - - acc.add(item.build(ctx.db)); + item.add_to(acc, ctx.db); }), }, None => ctx @@ -82,7 +81,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { { item.insert_snippet(cap, snippet); } - acc.add(item.build(ctx.db)); + item.add_to(acc, ctx.db); }), } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs index 8adc97423909f..0899bbff14641 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs @@ -10,46 +10,44 @@ use super::AttrCompletion; pub(super) fn complete_on_unimplemented( acc: &mut Completions, ctx: &CompletionContext<'_>, - input: ast::TokenTree, + existing_keys: &[ast::Expr], ) { - if let Some(existing_keys) = super::parse_comma_sep_expr(input) { - for attr in ATTRIBUTE_ARGS { - let already_annotated = existing_keys - .iter() - .filter_map(|expr| match expr { - ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(), - ast::Expr::BinExpr(bin) - if bin.op_kind() == Some(ast::BinaryOp::Assignment { op: None }) => - { - match bin.lhs()? { - ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(), - _ => None, - } + for attr in ATTRIBUTE_ARGS { + let already_annotated = existing_keys + .iter() + .filter_map(|expr| match expr { + ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(), + ast::Expr::BinExpr(bin) + if bin.op_kind() == Some(ast::BinaryOp::Assignment { op: None }) => + { + match bin.lhs()? { + ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(), + _ => None, } - _ => None, - }) - .any(|it| { - let text = it.text(); - attr.key() == text && text != "note" - }); - if already_annotated { - continue; - } + } + _ => None, + }) + .any(|it| { + let text = it.text(); + attr.key() == text && text != "note" + }); + if already_annotated { + continue; + } - let mut item = CompletionItem::new( - SymbolKind::BuiltinAttr, - ctx.source_range(), - attr.label, - ctx.edition, - ); - if let Some(lookup) = attr.lookup { - item.lookup_by(lookup); - } - if let Some((snippet, cap)) = attr.snippet.zip(ctx.config.snippet_cap) { - item.insert_snippet(cap, snippet); - } - item.add_to(acc, ctx.db); + let mut item = CompletionItem::new( + SymbolKind::BuiltinAttr, + ctx.source_range(), + attr.label, + ctx.edition, + ); + if let Some(lookup) = attr.lookup { + item.lookup_by(lookup); + } + if let Some((snippet, cap)) = attr.snippet.zip(ctx.config.snippet_cap) { + item.insert_snippet(cap, snippet); } + item.add_to(acc, ctx.db); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs index df577b8ed02eb..d662f5d543c13 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs @@ -12,18 +12,10 @@ pub(super) fn complete_lint( lints_completions: &[Lint], ) { for &Lint { label, description, .. } in lints_completions { - let (qual, name) = { - // FIXME: change `Lint`'s label to not store a path in it but split the prefix off instead? - let mut parts = label.split("::"); - let ns_or_label = match parts.next() { - Some(it) => it, - None => continue, - }; - let label = parts.next(); - match label { - Some(label) => (Some(ns_or_label), label), - None => (None, ns_or_label), - } + // FIXME: change `Lint`'s label to not store a path in it but split the prefix off instead? + let (qual, name) = match label.split_once("::") { + Some((qual, name)) => (Some(qual), name), + None => (None, label), }; if qual.is_none() && is_qualified { // qualified completion requested, but this lint is unqualified @@ -56,7 +48,7 @@ pub(super) fn complete_lint( }; let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label, ctx.edition); - item.documentation(Documentation::new_owned(description.to_owned())); + item.documentation(Documentation::new_borrowed(description)); item.add_to(acc, ctx.db) } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs index 136315c61f585..9c0fe0055dd4f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs @@ -20,11 +20,11 @@ pub(super) fn complete_macro_use( let mac_name = mac.name(ctx.db); let mac_name = mac_name.as_str(); - let existing_import = existing_imports + let already_imported = existing_imports .iter() .filter_map(|p| p.as_single_name_ref()) - .find(|n| n.text() == mac_name); - if existing_import.is_some() { + .any(|n| n.text() == mac_name); + if already_imported { continue; } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs index cb7ccf7373123..b04358963ae80 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs @@ -8,42 +8,36 @@ use crate::{Completions, context::CompletionContext, item::CompletionItem}; pub(super) fn complete_repr( acc: &mut Completions, ctx: &CompletionContext<'_>, - input: ast::TokenTree, + existing_reprs: &[ast::Expr], ) { - if let Some(existing_reprs) = super::parse_comma_sep_expr(input) { - for &ReprCompletion { label, snippet, lookup, collides } in REPR_COMPLETIONS { - let repr_already_annotated = existing_reprs - .iter() - .filter_map(|expr| match expr { + for &ReprCompletion { label, snippet, lookup, collides } in REPR_COMPLETIONS { + let repr_already_annotated = existing_reprs + .iter() + .filter_map(|expr| match expr { + ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(), + ast::Expr::CallExpr(call) => match call.expr()? { ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(), - ast::Expr::CallExpr(call) => match call.expr()? { - ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(), - _ => None, - }, _ => None, - }) - .any(|it| { - let text = it.text(); - lookup.unwrap_or(label) == text || collides.contains(&text.as_str()) - }); - if repr_already_annotated { - continue; - } + }, + _ => None, + }) + .any(|it| { + let text = it.text(); + lookup.unwrap_or(label) == text || collides.contains(&text.as_str()) + }); + if repr_already_annotated { + continue; + } - let mut item = CompletionItem::new( - SymbolKind::BuiltinAttr, - ctx.source_range(), - label, - ctx.edition, - ); - if let Some(lookup) = lookup { - item.lookup_by(lookup); - } - if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) { - item.insert_snippet(cap, snippet); - } - item.add_to(acc, ctx.db); + let mut item = + CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), label, ctx.edition); + if let Some(lookup) = lookup { + item.lookup_by(lookup); + } + if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) { + item.insert_snippet(cap, snippet); } + item.add_to(acc, ctx.db); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 3df511a5ad0f3..66ecb790a0aaa 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -258,7 +258,7 @@ pub fn completions( completions::attribute::complete_known_attribute_input( acc, ctx, - colon_prefix, + *colon_prefix, attr, extern_crate.as_ref(), ); From 4debf6f43ddd3bf5d239200cbfa2e852eabb99a9 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Apr 2026 19:55:59 +0200 Subject: [PATCH 039/289] copy `attribute/lint.rs` to `attribute/feature.rs` this is to make next commit's diff clearer --- .../src/completions/attribute.rs | 3 +- .../src/completions/attribute/feature.rs | 54 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/feature.rs diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index a518e70861de7..766352aad99a3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -26,6 +26,7 @@ use crate::{ mod cfg; mod derive; mod diagnostic; +mod feature; mod lint; mod macro_use; mod repr; @@ -50,7 +51,7 @@ pub(crate) fn complete_known_attribute_input( match segments.as_slice() { ["repr"] => repr::complete_repr(acc, ctx, &parse_comma_sep_expr(tt)?), - ["feature"] => lint::complete_lint( + ["feature"] => feature::complete_lint( acc, ctx, colon_prefix, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/feature.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/feature.rs new file mode 100644 index 0000000000000..d662f5d543c13 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/feature.rs @@ -0,0 +1,54 @@ +//! Completion for lints +use ide_db::{SymbolKind, documentation::Documentation, generated::lints::Lint}; +use syntax::ast; + +use crate::{Completions, context::CompletionContext, item::CompletionItem}; + +pub(super) fn complete_lint( + acc: &mut Completions, + ctx: &CompletionContext<'_>, + is_qualified: bool, + existing_lints: &[ast::Path], + lints_completions: &[Lint], +) { + for &Lint { label, description, .. } in lints_completions { + // FIXME: change `Lint`'s label to not store a path in it but split the prefix off instead? + let (qual, name) = match label.split_once("::") { + Some((qual, name)) => (Some(qual), name), + None => (None, label), + }; + if qual.is_none() && is_qualified { + // qualified completion requested, but this lint is unqualified + continue; + } + let lint_already_annotated = existing_lints + .iter() + .filter_map(|path| { + let q = path.qualifier(); + if q.as_ref().and_then(|it| it.qualifier()).is_some() { + return None; + } + Some((q.and_then(|it| it.as_single_name_ref()), path.segment()?.name_ref()?)) + }) + .any(|(q, name_ref)| { + let qualifier_matches = match (q, qual) { + (None, None) => true, + (None, Some(_)) => false, + (Some(_), None) => false, + (Some(q), Some(ns)) => q.text() == ns, + }; + qualifier_matches && name_ref.text() == name + }); + if lint_already_annotated { + continue; + } + let label = match qual { + Some(qual) if !is_qualified => format!("{qual}::{name}"), + _ => name.to_owned(), + }; + let mut item = + CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label, ctx.edition); + item.documentation(Documentation::new_borrowed(description)); + item.add_to(acc, ctx.db) + } +} From 1ffdc04dac842f6f068f06d234882ae4e6ba6a0e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 21 Apr 2026 19:54:29 +0200 Subject: [PATCH 040/289] simplify `feature.rs` and `lint.rs` - don't need to pass `lints_completions` -- create in the function - remove qualifier handling from `feature.rs`, as feature names don't have qualifiers --- .../src/completions/attribute.rs | 26 ++-------- .../src/completions/attribute/feature.rs | 52 +++++-------------- .../src/completions/attribute/lint.rs | 14 +++-- 3 files changed, 30 insertions(+), 62 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index 766352aad99a3..0ce0dde2e25c9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -4,13 +4,7 @@ use std::sync::LazyLock; -use ide_db::{ - FxHashMap, SymbolKind, - generated::lints::{ - CLIPPY_LINT_GROUPS, CLIPPY_LINTS, DEFAULT_LINTS, FEATURES, Lint, RUSTDOC_LINTS, - }, - syntax_helpers::node_ext::parse_tt_as_comma_sep_paths, -}; +use ide_db::{FxHashMap, SymbolKind, syntax_helpers::node_ext::parse_tt_as_comma_sep_paths}; use itertools::Itertools; use syntax::{ AstNode, Edition, SyntaxKind, T, @@ -51,25 +45,15 @@ pub(crate) fn complete_known_attribute_input( match segments.as_slice() { ["repr"] => repr::complete_repr(acc, ctx, &parse_comma_sep_expr(tt)?), - ["feature"] => feature::complete_lint( + ["feature"] => { + feature::complete_feature(acc, ctx, &parse_tt_as_comma_sep_paths(tt, ctx.edition)?) + } + ["allow" | "expect" | "deny" | "forbid" | "warn"] => lint::complete_lint( acc, ctx, colon_prefix, &parse_tt_as_comma_sep_paths(tt, ctx.edition)?, - FEATURES, ), - ["allow" | "expect" | "deny" | "forbid" | "warn"] => { - let existing_lints = parse_tt_as_comma_sep_paths(tt, ctx.edition)?; - - let lints: Vec = (CLIPPY_LINT_GROUPS.iter().map(|g| &g.lint)) - .chain(DEFAULT_LINTS) - .chain(CLIPPY_LINTS) - .chain(RUSTDOC_LINTS) - .cloned() - .collect(); - - lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints); - } ["macro_use"] => macro_use::complete_macro_use( acc, ctx, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/feature.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/feature.rs index d662f5d543c13..1e6baca864c78 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/feature.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/feature.rs @@ -1,51 +1,27 @@ -//! Completion for lints -use ide_db::{SymbolKind, documentation::Documentation, generated::lints::Lint}; +//! Completion for features +use ide_db::{ + SymbolKind, + documentation::Documentation, + generated::lints::{FEATURES, Lint}, +}; use syntax::ast; use crate::{Completions, context::CompletionContext, item::CompletionItem}; -pub(super) fn complete_lint( +pub(super) fn complete_feature( acc: &mut Completions, ctx: &CompletionContext<'_>, - is_qualified: bool, - existing_lints: &[ast::Path], - lints_completions: &[Lint], + existing_features: &[ast::Path], ) { - for &Lint { label, description, .. } in lints_completions { - // FIXME: change `Lint`'s label to not store a path in it but split the prefix off instead? - let (qual, name) = match label.split_once("::") { - Some((qual, name)) => (Some(qual), name), - None => (None, label), - }; - if qual.is_none() && is_qualified { - // qualified completion requested, but this lint is unqualified - continue; - } - let lint_already_annotated = existing_lints + for &Lint { label, description, .. } in FEATURES { + let feature_already_annotated = existing_features .iter() - .filter_map(|path| { - let q = path.qualifier(); - if q.as_ref().and_then(|it| it.qualifier()).is_some() { - return None; - } - Some((q.and_then(|it| it.as_single_name_ref()), path.segment()?.name_ref()?)) - }) - .any(|(q, name_ref)| { - let qualifier_matches = match (q, qual) { - (None, None) => true, - (None, Some(_)) => false, - (Some(_), None) => false, - (Some(q), Some(ns)) => q.text() == ns, - }; - qualifier_matches && name_ref.text() == name - }); - if lint_already_annotated { + .filter_map(|p| p.as_single_name_ref()) + .any(|n| n.text() == label); + if feature_already_annotated { continue; } - let label = match qual { - Some(qual) if !is_qualified => format!("{qual}::{name}"), - _ => name.to_owned(), - }; + let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label, ctx.edition); item.documentation(Documentation::new_borrowed(description)); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs index d662f5d543c13..f810196bfe3f8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs @@ -1,5 +1,9 @@ //! Completion for lints -use ide_db::{SymbolKind, documentation::Documentation, generated::lints::Lint}; +use ide_db::{ + SymbolKind, + documentation::Documentation, + generated::lints::{CLIPPY_LINT_GROUPS, CLIPPY_LINTS, DEFAULT_LINTS, Lint, RUSTDOC_LINTS}, +}; use syntax::ast; use crate::{Completions, context::CompletionContext, item::CompletionItem}; @@ -9,9 +13,13 @@ pub(super) fn complete_lint( ctx: &CompletionContext<'_>, is_qualified: bool, existing_lints: &[ast::Path], - lints_completions: &[Lint], ) { - for &Lint { label, description, .. } in lints_completions { + let lints = (CLIPPY_LINT_GROUPS.iter().map(|g| &g.lint)) + .chain(DEFAULT_LINTS) + .chain(CLIPPY_LINTS) + .chain(RUSTDOC_LINTS); + + for &Lint { label, description, .. } in lints { // FIXME: change `Lint`'s label to not store a path in it but split the prefix off instead? let (qual, name) = match label.split_once("::") { Some((qual, name)) => (Some(qual), name), From 1195dfd8d3463930dbbd071a4f4c7e689965b3b2 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 15:11:06 +0530 Subject: [PATCH 041/289] migrate qualify_path to use mod_path_to_ast_with_factory --- .../crates/ide-assists/src/handlers/qualify_path.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index e3dd77360cca3..18a31a2b42c10 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -4,7 +4,7 @@ use std::iter; use hir::AsAssocItem; use ide_db::RootDatabase; use ide_db::{ - helpers::mod_path_to_ast, + helpers::mod_path_to_ast_with_factory, imports::import_assets::{ImportCandidate, LocatedImport}, }; use syntax::Edition; @@ -129,7 +129,7 @@ impl QualifyCandidate<'_> { item: hir::ItemInNs, edition: Edition, ) { - let import = mod_path_to_ast(import, edition); + let import = mod_path_to_ast_with_factory(editor.make(), import, edition); match self { QualifyCandidate::QualifierStart(segment, generics) => { let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); From 4fa409bd75d280947403bbc5356a8613fa1a3207 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 15:12:04 +0530 Subject: [PATCH 042/289] migrate replace_derive_with_manual_impl to use mod_path_to_ast_with_factory and also restructured it --- .../replace_derive_with_manual_impl.rs | 49 ++++++++++++++----- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 751cd42f6e8df..da02d9db404dd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -1,10 +1,13 @@ use hir::{InFile, ModuleDef}; -use ide_db::{helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator}; +use ide_db::{ + helpers::mod_path_to_ast_with_factory, imports::import_assets::NameToImport, items_locator, +}; use itertools::Itertools; use syntax::{ + Edition, SyntaxKind::WHITESPACE, T, - ast::{self, AstNode, HasName}, + ast::{self, AstNode, HasName, syntax_factory::SyntaxFactory}, syntax_editor::{Position, SyntaxEditor}, }; @@ -87,13 +90,12 @@ pub(crate) fn replace_derive_with_manual_impl( .flat_map(|trait_| { current_module .find_path(ctx.sema.db, hir::ModuleDef::Trait(trait_), cfg) - .as_ref() - .map(|path| mod_path_to_ast(path, current_edition)) - .zip(Some(trait_)) + .map(|path| (path, trait_)) }); - let mut no_traits_found = true; - for (replace_trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { + let found_traits = found_traits.collect::>(); + let no_traits_found = found_traits.is_empty(); + for (replace_trait_mod_path, trait_) in found_traits { add_assist( acc, ctx, @@ -101,13 +103,25 @@ pub(crate) fn replace_derive_with_manual_impl( ¤t_derives, &args, &path, - &replace_trait_path, + Some(replace_trait_mod_path), Some(trait_), &adt, + current_edition, )?; } if no_traits_found { - add_assist(acc, ctx, &attr, ¤t_derives, &args, &path, &path, None, &adt)?; + add_assist( + acc, + ctx, + &attr, + ¤t_derives, + &args, + &path, + None, + None, + &adt, + current_edition, + )?; } Some(()) } @@ -119,17 +133,28 @@ fn add_assist( old_derives: &[ast::Path], old_tree: &ast::TokenTree, old_trait_path: &ast::Path, - replace_trait_path: &ast::Path, + replace_trait_mod_path: Option, trait_: Option, adt: &ast::Adt, + current_edition: Edition, ) -> Option<()> { let target = attr.syntax().text_range(); let annotated_name = adt.name()?; - let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`"); + let label_trait_path = match replace_trait_mod_path.as_ref() { + Some(path) => { + mod_path_to_ast_with_factory(&SyntaxFactory::without_mappings(), path, current_edition) + } + None => old_trait_path.clone(), + }; + let label = format!("Convert to manual `impl {label_trait_path} for {annotated_name}`"); acc.add(AssistId::refactor("replace_derive_with_manual_impl"), label, target, |builder| { let editor = builder.make_editor(attr.syntax()); let make = editor.make(); + let replace_trait_path = match replace_trait_mod_path.as_ref() { + Some(path) => mod_path_to_ast_with_factory(make, path, current_edition), + None => old_trait_path.clone(), + }; let insert_after = Position::after(adt.syntax()); let impl_is_unsafe = trait_.map(|s| s.is_unsafe(ctx.db())).unwrap_or(false); let impl_def = impl_def_from_trait( @@ -139,7 +164,7 @@ fn add_assist( adt, &annotated_name, trait_, - replace_trait_path, + &replace_trait_path, impl_is_unsafe, ); update_attribute(&editor, old_derives, old_tree, old_trait_path, attr); From 6930436615c09f4852b15fc1f74301fba300435a Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 15:14:59 +0530 Subject: [PATCH 043/289] add use_trivial_constructor_with_factory --- .../ide-db/src/use_trivial_constructor.rs | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs index a91d436afcfbb..0b94a1fa5aad9 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs @@ -4,7 +4,7 @@ use hir::StructKind; use span::Edition; use syntax::{ ToSmolStr, - ast::{Expr, Path, make}, + ast::{Expr, Path, make, syntax_factory::SyntaxFactory}, }; /// given a type return the trivial constructor (if one exists) @@ -37,3 +37,34 @@ pub fn use_trivial_constructor( None } + +pub fn use_trivial_constructor_with_factory( + make: &SyntaxFactory, + db: &crate::RootDatabase, + path: Path, + ty: &hir::Type<'_>, + edition: Edition, +) -> Option { + match ty.as_adt() { + Some(hir::Adt::Enum(x)) => { + if let &[variant] = &*x.variants(db) + && variant.kind(db) == hir::StructKind::Unit + { + let path = make.path_qualified( + path, + make.path_segment( + make.name_ref(&variant.name(db).display_no_db(edition).to_smolstr()), + ), + ); + + return Some(make.expr_path(path)); + } + } + Some(hir::Adt::Struct(x)) if x.kind(db) == StructKind::Unit => { + return Some(make.expr_path(path)); + } + _ => {} + } + + None +} From cb01af0b291059857137e5a433bb7c8829802e82 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 15:15:21 +0530 Subject: [PATCH 044/289] add path qualified in SyntaxFactory --- .../src/ast/syntax_factory/constructors.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) 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 bc4a825987138..b9106408b3fbf 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 @@ -671,6 +671,23 @@ impl SyntaxFactory { ast } + pub fn path_qualified(&self, qual: ast::Path, segment: ast::PathSegment) -> ast::Path { + let ast = make::path_qualified(qual.clone(), segment.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + if let Some(out_qual) = ast.qualifier() { + builder.map_node(qual.syntax().clone(), out_qual.syntax().clone()); + } + if let Some(out_segment) = ast.segment() { + builder.map_node(segment.syntax().clone(), out_segment.syntax().clone()); + } + builder.finish(&mut mapping); + } + + ast + } + pub fn path_from_segments( &self, segments: impl IntoIterator, From 14bd085b16ca6018f275dfb0f118ea1534eba0bf Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 15:16:31 +0530 Subject: [PATCH 045/289] migrate generate_single_field_struct_from to use use_trivial_constructor_with_factory and also restructured it --- .../handlers/generate_single_field_struct_from.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 10c009a2ea440..265a632a8cb9b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -1,8 +1,9 @@ use hir::next_solver::{DbInterner, TypingMode}; use hir::{HasCrate, ModuleDef, Semantics}; +use ide_db::use_trivial_constructor::use_trivial_constructor_with_factory; use ide_db::{ - RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast, - imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor, + RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast_with_factory, + imports::import_assets::item_for_path_search, }; use syntax::syntax_editor::{Position, SyntaxEditor}; use syntax::{ @@ -197,7 +198,13 @@ fn make_constructors( let ty_path = module.find_path(db, item_for_path_search(db, item_in_ns)?, cfg)?; - use_trivial_constructor(db, mod_path_to_ast(&ty_path, edition), &ty, edition) + use_trivial_constructor_with_factory( + &make, + db, + mod_path_to_ast_with_factory(&make, &ty_path, edition), + &ty, + edition, + ) }) .collect() } From 0f4076ff8fe33f038fde29a6a6b73f4265e7f2e2 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 15:16:49 +0530 Subject: [PATCH 046/289] migrate generate_new to use use_trivial_constructor_with_factory and also restructured it --- .../crates/ide-assists/src/handlers/generate_new.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 7f1eafb98d3ee..756dc3d0fa073 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -1,6 +1,6 @@ use ide_db::{ imports::import_assets::item_for_path_search, syntax_helpers::suggest_name::NameGenerator, - use_trivial_constructor::use_trivial_constructor, + use_trivial_constructor::use_trivial_constructor_with_factory, }; use syntax::{ ast::{self, AstNode, HasName, HasVisibility, StructKind, edit::AstNodeEdit}, @@ -90,7 +90,8 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let edition = current_module.krate(ctx.db()).edition(ctx.db()); - let expr = use_trivial_constructor( + let expr = use_trivial_constructor_with_factory( + make, ctx.sema.db, ide_db::helpers::mod_path_to_ast_with_factory(make, &type_path, edition), &ty, From 78cc6c3c4d25a08004b1395079dfd49fc9a49f5d Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 22 Apr 2026 15:17:31 +0530 Subject: [PATCH 047/289] migrate convert_into_to_from to SyntaxEditor --- .../src/handlers/convert_into_to_from.rs | 52 ++++++++++++++----- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs index e330102423517..2fcde15874367 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs @@ -1,4 +1,6 @@ -use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast, traits::resolve_target_trait}; +use ide_db::{ + famous_defs::FamousDefs, helpers::mod_path_to_ast_with_factory, traits::resolve_target_trait, +}; use syntax::ast::{self, AstNode, HasGenericArgs, HasName}; use crate::{AssistContext, AssistId, Assists}; @@ -44,17 +46,15 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) - } let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate(ctx.sema.db))); + let current_edition = module.krate(ctx.db()).edition(ctx.db()); - let src_type_path = { + let src_type_mod_path = { let src_type_path = src_type.syntax().descendants().find_map(ast::Path::cast)?; let src_type_def = match ctx.sema.resolve_path(&src_type_path) { Some(hir::PathResolution::Def(module_def)) => module_def, _ => return None, }; - mod_path_to_ast( - &module.find_path(ctx.db(), src_type_def, cfg)?, - module.krate(ctx.db()).edition(ctx.db()), - ) + module.find_path(ctx.db(), src_type_def, cfg)? }; let dest_type = match &ast_trait { @@ -89,19 +89,45 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) - "Convert Into to From", impl_.syntax().text_range(), |builder| { - builder.replace(src_type.syntax().text_range(), dest_type.to_string()); - builder.replace(ast_trait.syntax().text_range(), format!("From<{src_type}>")); - builder.replace(into_fn_return.syntax().text_range(), "-> Self"); - builder.replace(into_fn_params.syntax().text_range(), format!("(val: {src_type})")); - builder.replace(into_fn_name.syntax().text_range(), "from"); + let editor = builder.make_editor(impl_.syntax()); + let make = editor.make(); + let src_type_path = + mod_path_to_ast_with_factory(make, &src_type_mod_path, current_edition); + + editor.replace(src_type.syntax(), make.ty(&dest_type.to_string()).syntax()); + editor.replace(ast_trait.syntax(), make.ty(&format!("From<{src_type}>")).syntax()); + editor.replace(into_fn_return.syntax(), make.ret_type(make.ty("Self")).syntax()); + + editor.replace( + into_fn_params.syntax(), + make.param_list( + None, + [make.param(make.simple_ident_pat(make.name("val")).into(), src_type.clone())], + ) + .syntax(), + ); + editor.replace(into_fn_name.syntax(), make.name("from").syntax()); for s in selfs { match s.text().as_ref() { - "self" => builder.replace(s.syntax().text_range(), "val"), - "Self" => builder.replace(s.syntax().text_range(), src_type_path.to_string()), + "self" => editor.replace(s.syntax(), make.name_ref("val").syntax()), + "Self" => { + if let Some(path_segment) = + s.syntax().parent().and_then(ast::PathSegment::cast) + { + let self_path = path_segment.parent_path(); + if self_path.qualifier().is_none() + && path_segment.generic_arg_list().is_none() + { + editor.replace(self_path.syntax(), src_type_path.syntax()); + } + } + } _ => {} } } + + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } From 0d9a6deb13e8a653839c0d301577f8ef5b2c7575 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 20 Apr 2026 18:42:43 +0100 Subject: [PATCH 048/289] fix: Pass proc_macro_cwd to Analysis::from_single_file() If the directory that rust-analyzer was started in no longer exists, all semantic highlighting requests crash and the user sees a lot of errors in their editor. [Error - 2:31:20 PM] Request textDocument/semanticTokens/full failed. Message: request handler panicked: called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" } Code: -32603 This is because Analysis::from_single_file() used std::env::current_dir().unwrap() for proc_maco_cwd, and current_dir() returns Err if the working directory no longer exists. I've encountered this when the user's project is a filesystem that is unmounted and remounted. I believe it can also happen when the user has multiple workspaces open in their editor and they delete the directory that rust-analyzer happened to be started in. Instead, pass proc_macro_cwd explicitly to from_single_file. This is more correct, and removes a FIXME. I've also checked the other usages of std::env::current_dir().unwrap() but all the other cases look like unit tests. AI: Code was partially written with Claude. --- src/tools/rust-analyzer/crates/base-db/src/lib.rs | 2 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 9 ++------- .../crates/ide/src/syntax_highlighting/inject.rs | 13 ++++++++++++- .../crates/rust-analyzer/src/cli/highlight.rs | 5 ++++- .../crates/rust-analyzer/src/cli/symbols.rs | 5 ++++- .../crates/rust-analyzer/src/lsp/to_proto.rs | 11 +++++++++-- 6 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index e438505c07e4b..a209a0e631e0b 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -37,7 +37,7 @@ use rustc_hash::{FxHashSet, FxHasher}; use salsa::{Durability, Setter}; pub use semver::{BuildMetadata, Prerelease, Version, VersionReq}; use triomphe::Arc; -pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet}; +pub use vfs::{AbsPathBuf, AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet}; pub type FxIndexSet = indexmap::IndexSet; pub type FxIndexMap = diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 0af2a1f82039e..e131e7bdd17dd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -70,7 +70,7 @@ use ide_db::ra_fixture::RaFixtureAnalysis; use ide_db::{ FxHashMap, FxIndexSet, base_db::{ - CrateOrigin, CrateWorkspaceData, Env, FileSet, SourceDatabase, VfsPath, + AbsPathBuf, CrateOrigin, CrateWorkspaceData, Env, FileSet, SourceDatabase, VfsPath, salsa::{Cancelled, Database}, }, prime_caches, symbol_index, @@ -253,7 +253,7 @@ impl Analysis { // Creates an analysis instance for a single file, without any external // dependencies, stdlib support or ability to apply changes. See // `AnalysisHost` for creating a fully-featured analysis. - pub fn from_single_file(text: String) -> (Analysis, FileId) { + pub fn from_single_file(text: String, proc_macro_cwd: Arc) -> (Analysis, FileId) { let mut host = AnalysisHost::default(); let file_id = FileId::from_raw(0); let mut file_set = FileSet::default(); @@ -267,11 +267,6 @@ impl Analysis { // Default to enable test for single file. let mut cfg_options = CfgOptions::default(); - // FIXME: This is less than ideal - let proc_macro_cwd = Arc::new( - TryFrom::try_from(&*std::env::current_dir().unwrap().as_path().to_string_lossy()) - .unwrap(), - ); let crate_attrs = Vec::new(); cfg_options.insert_atom(sym::test); crate_graph.add_crate_root( diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 6afe5681a9b5c..76bb06328b7cf 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -9,6 +9,7 @@ use syntax::{ SyntaxNode, TextRange, TextSize, ast::{self, IsString}, }; +use triomphe::Arc; use crate::{ Analysis, HlMod, HlRange, HlTag, RootDatabase, @@ -92,6 +93,7 @@ pub(super) fn doc_comment( Some(it) => it, None => return, }; + let vfs_file_id = src_file_id.file_id(sema.db); let src_file_id: HirFileId = src_file_id.into(); let Some(docs) = attributes.hir_docs(sema.db) else { return }; @@ -171,7 +173,16 @@ pub(super) fn doc_comment( inj.add_unmapped("\n}"); - let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text()); + let proc_macro_cwd = { + match sema.first_crate(vfs_file_id) { + Some(krate) => krate.base().data(sema.db).proc_macro_cwd.clone(), + None => { + // Arbitrarily pick /, since from_single_file treats this file as as /main.rs anyway. + Arc::new(ide_db::base_db::AbsPathBuf::try_from("/").unwrap()) + } + } + }; + let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text(), proc_macro_cwd); if let Ok(ranges) = analysis.with_db(|db| { super::highlight( diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/highlight.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/highlight.rs index 84607b9fd5d52..5aba532f576ee 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/highlight.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/highlight.rs @@ -1,12 +1,15 @@ //! Read Rust code on stdin, print HTML highlighted version to stdout. use ide::Analysis; +use ide_db::base_db::AbsPathBuf; +use triomphe::Arc; use crate::cli::{flags, read_stdin}; impl flags::Highlight { pub fn run(self) -> anyhow::Result<()> { - let (analysis, file_id) = Analysis::from_single_file(read_stdin()?); + let cwd = AbsPathBuf::assert_utf8(std::env::current_dir()?); + let (analysis, file_id) = Analysis::from_single_file(read_stdin()?, Arc::new(cwd)); let html = analysis.highlight_as_html(file_id, self.rainbow).unwrap(); println!("{html}"); Ok(()) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs index d7af56d3e15be..3d7d712326a48 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs @@ -1,12 +1,15 @@ //! Read Rust code on stdin, print syntax tree on stdout. use ide::{Analysis, FileStructureConfig}; +use ide_db::base_db::AbsPathBuf; +use triomphe::Arc; use crate::cli::{flags, read_stdin}; impl flags::Symbols { pub fn run(self) -> anyhow::Result<()> { let text = read_stdin()?; - let (analysis, file_id) = Analysis::from_single_file(text); + let cwd = AbsPathBuf::assert_utf8(std::env::current_dir()?); + let (analysis, file_id) = Analysis::from_single_file(text, Arc::new(cwd)); let structure = analysis // The default setting in config.rs (document_symbol_search_excludeLocals) is to exclude // locals because it is unlikely that users want document search to return the names of diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index d857f23b6703c..d59e9d8434c73 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -2028,6 +2028,7 @@ pub(crate) fn rename_error(err: RenameError) -> LspError { mod tests { use expect_test::{Expect, expect}; use ide::{Analysis, FilePosition}; + use ide_db::base_db::AbsPathBuf; use ide_db::source_change::Snippet; use test_utils::extract_offset; use triomphe::Arc; @@ -2048,7 +2049,10 @@ fn main() { } }"#; - let (analysis, file_id) = Analysis::from_single_file(text.to_owned()); + let (analysis, file_id) = Analysis::from_single_file( + text.to_owned(), + Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())), + ); let folds = analysis.folding_ranges(file_id, true).unwrap(); assert_eq!(folds.len(), 5); @@ -2084,7 +2088,10 @@ fn bar(_: usize) {} "#; let (offset, text) = extract_offset(text); - let (analysis, file_id) = Analysis::from_single_file(text); + let (analysis, file_id) = Analysis::from_single_file( + text, + Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())), + ); let help = signature_help( analysis.signature_help(FilePosition { file_id, offset }).unwrap().unwrap(), CallInfoConfig { params_only: false, docs: true }, From 02072c45ffefdd38bc8f6502f8c60c0fbc084077 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 22 Apr 2026 19:14:43 +0800 Subject: [PATCH 049/289] minor: Rename unwrap_block to unwrap_branch --- .../{unwrap_block.rs => unwrap_branch.rs} | 74 +++++++++---------- .../crates/ide-assists/src/lib.rs | 4 +- .../crates/ide-assists/src/tests/generated.rs | 4 +- 3 files changed, 41 insertions(+), 41 deletions(-) rename src/tools/rust-analyzer/crates/ide-assists/src/handlers/{unwrap_block.rs => unwrap_branch.rs} (92%) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs similarity index 92% rename from src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs rename to src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs index 77941bcfb2bdb..1915292e39f75 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs @@ -10,7 +10,7 @@ use syntax::{ use crate::{AssistContext, AssistId, Assists}; -// Assist: unwrap_block +// Assist: unwrap_branch // // This assist removes if...else, for, while and loop control statements to just keep the body. // @@ -27,7 +27,7 @@ use crate::{AssistContext, AssistId, Assists}; // println!("foo"); // } // ``` -pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unwrap_branch(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?; let target = block.syntax().text_range(); @@ -70,7 +70,7 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option }; let replacement = replacement.stmt_list()?; - acc.add(AssistId::refactor_rewrite("unwrap_block"), "Unwrap block", target, |builder| { + acc.add(AssistId::refactor_rewrite("unwrap_branch"), "Unwrap branch", target, |builder| { let editor = builder.make_editor(block.syntax()); let replacement = replacement.dedent(from_indent).indent(into_indent); let container = prefer_container.unwrap_or(container); @@ -141,7 +141,7 @@ mod tests { #[test] fn unwrap_tail_expr_block() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { $0{ @@ -160,7 +160,7 @@ fn main() { #[test] fn unwrap_stmt_expr_block() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { $0{ @@ -178,7 +178,7 @@ fn main() { ); // Pedantically, we should add an `;` here... check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { $0{ @@ -199,7 +199,7 @@ fn main() { #[test] fn simple_if() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { bar(); @@ -228,7 +228,7 @@ fn main() { #[test] fn simple_if_else() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { bar(); @@ -260,7 +260,7 @@ fn main() { #[test] fn simple_if_else_if() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { // bar(); @@ -294,7 +294,7 @@ fn main() { #[test] fn simple_if_else_if_nested() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { // bar(); @@ -330,7 +330,7 @@ fn main() { #[test] fn simple_if_else_if_nested_else() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { // bar(); @@ -370,7 +370,7 @@ fn main() { #[test] fn simple_if_else_if_nested_middle() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { // bar(); @@ -408,7 +408,7 @@ fn main() { #[test] fn simple_if_bad_cursor_position() { check_assist_not_applicable( - unwrap_block, + unwrap_branch, r#" fn main() { bar();$0 @@ -428,7 +428,7 @@ fn main() { #[test] fn simple_for() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { for i in 0..5 {$0 @@ -461,7 +461,7 @@ fn main() { #[test] fn simple_if_in_for() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { for i in 0..5 { @@ -492,7 +492,7 @@ fn main() { #[test] fn simple_loop() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { loop {$0 @@ -525,7 +525,7 @@ fn main() { #[test] fn simple_while() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { while true {$0 @@ -558,7 +558,7 @@ fn main() { #[test] fn simple_let_else() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { let Some(2) = None else {$0 @@ -573,7 +573,7 @@ fn main() { "#, ); check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { let Some(2) = None else {$0 @@ -592,7 +592,7 @@ fn main() { #[test] fn unwrap_match_arm() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { match rel_path { @@ -616,7 +616,7 @@ fn main() { #[test] fn unwrap_match_arm_in_let() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { let value = match rel_path { @@ -640,7 +640,7 @@ fn main() { #[test] fn simple_if_in_while_bad_cursor_position() { check_assist_not_applicable( - unwrap_block, + unwrap_branch, r#" fn main() { while true { @@ -661,7 +661,7 @@ fn main() { #[test] fn simple_single_line() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { {$0 0 } @@ -678,7 +678,7 @@ fn main() { #[test] fn simple_nested_block() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { $0{ @@ -701,7 +701,7 @@ fn main() { #[test] fn nested_single_line() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { {$0 { println!("foo"); } } @@ -715,7 +715,7 @@ fn main() { ); check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { {$0 { 0 } } @@ -732,7 +732,7 @@ fn main() { #[test] fn simple_if_single_line() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { if true {$0 /* foo */ foo() } else { bar() /* bar */} @@ -749,7 +749,7 @@ fn main() { #[test] fn if_single_statement() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { if true {$0 @@ -768,7 +768,7 @@ fn main() { #[test] fn multiple_statements() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() -> i32 { if 2 > 1 {$0 @@ -792,7 +792,7 @@ fn main() -> i32 { fn unwrap_block_in_let_initializers() { // https://github.com/rust-lang/rust-analyzer/issues/13679 check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { let x = {$0 @@ -807,7 +807,7 @@ fn main() { "#, ); check_assist( - unwrap_block, + unwrap_branch, r#" fn main() -> i32 { let _ = {$01; 2}; @@ -820,7 +820,7 @@ fn main() -> i32 { "#, ); check_assist( - unwrap_block, + unwrap_branch, r#" fn main() -> i32 { let mut a = {$01; 2}; @@ -833,7 +833,7 @@ fn main() -> i32 { "#, ); check_assist( - unwrap_block, + unwrap_branch, r#" fn main() -> i32 { let mut a = {$0 @@ -857,7 +857,7 @@ fn main() -> i32 { fn unwrap_if_in_let_initializers() { // https://github.com/rust-lang/rust-analyzer/issues/13679 check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { let a = 1; @@ -881,7 +881,7 @@ fn main() { fn unwrap_block_with_modifiers() { // https://github.com/rust-lang/rust-analyzer/issues/17964 check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { unsafe $0{ @@ -896,7 +896,7 @@ fn main() { "#, ); check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { async move $0{ @@ -911,7 +911,7 @@ fn main() { "#, ); check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { try $0{ diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 80f05caf4e0dc..1fe385e95bd48 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -232,7 +232,7 @@ mod handlers { mod unmerge_match_arm; mod unnecessary_async; mod unqualify_method_call; - mod unwrap_block; + mod unwrap_branch; mod unwrap_return_type; mod unwrap_tuple; mod unwrap_type_to_generic_arg; @@ -380,7 +380,7 @@ mod handlers { unmerge_imports::unmerge_imports, unnecessary_async::unnecessary_async, unqualify_method_call::unqualify_method_call, - unwrap_block::unwrap_block, + unwrap_branch::unwrap_branch, unwrap_return_type::unwrap_return_type, unwrap_tuple::unwrap_tuple, unwrap_type_to_generic_arg::unwrap_type_to_generic_arg, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 048f3d7ce8f3e..7e66f02475fa0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -3731,9 +3731,9 @@ mod std { pub mod ops { pub trait Add { fn add(self, _: Self) {} } impl Add for } #[test] -fn doctest_unwrap_block() { +fn doctest_unwrap_branch() { check_doc_test( - "unwrap_block", + "unwrap_branch", r#####" fn foo() { if true {$0 From a229a3153e3e959ceaf2c6c82ee9b327b5a9567d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 22 Apr 2026 12:52:12 +0200 Subject: [PATCH 050/289] docs: add some intradoc links --- src/tools/rust-analyzer/crates/ide-db/src/source_change.rs | 2 +- src/tools/rust-analyzer/crates/syntax/src/lib.rs | 4 ++-- src/tools/rust-analyzer/crates/syntax/src/ptr.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) 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 81b679ead233c..eade9eb0a440d 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 @@ -1,7 +1,7 @@ //! This modules defines type to represent changes to the source code, that flow //! from the server to the client. //! -//! It can be viewed as a dual for `Change`. +//! It can be viewed as a dual for [`Change`][vfs::Change]. use std::{collections::hash_map::Entry, fmt, iter, mem}; diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index c510b2831e12b..cda3e69b7c625 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -10,8 +10,8 @@ //! the [Swift] one. //! //! The most interesting modules here are `syntax_node` (which defines concrete -//! syntax tree) and `ast` (which defines abstract syntax tree on top of the -//! CST). The actual parser live in a separate `parser` crate, though the +//! syntax tree) and [`ast`] (which defines abstract syntax tree on top of the +//! CST). The actual parser live in a separate [`parser`] crate, though the //! lexer lives in this crate. //! //! See `api_walkthrough` test in this file for a quick API tour! diff --git a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs index c4979b8e3ae80..ed8a68fb8afd5 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs @@ -21,7 +21,7 @@ use crate::{AstNode, SyntaxNode, syntax_node::RustLanguage}; /// A "pointer" to a [`SyntaxNode`], via location in the source code. pub type SyntaxNodePtr = rowan::ast::SyntaxNodePtr; -/// Like `SyntaxNodePtr`, but remembers the type of node. +/// Like [`SyntaxNodePtr`], but remembers the type of node. pub struct AstPtr { raw: SyntaxNodePtr, _ty: PhantomData N>, @@ -90,7 +90,7 @@ impl AstPtr { AstPtr { raw: self.raw, _ty: PhantomData } } - /// Like `SyntaxNodePtr::cast` but the trait bounds work out. + /// Like [`SyntaxNodePtr::cast`] but the trait bounds work out. pub fn try_from_raw(raw: SyntaxNodePtr) -> Option> { N::can_cast(raw.kind()).then_some(AstPtr { raw, _ty: PhantomData }) } From f00ddaab760e858ffd23725596e7ef49e557b956 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 22 Apr 2026 20:42:38 +0800 Subject: [PATCH 051/289] To smart label for unwrap_branch --- .../ide-assists/src/handlers/unwrap_branch.rs | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs index 1915292e39f75..a71ba555d013f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs @@ -68,9 +68,12 @@ pub(crate) fn unwrap_branch(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio } }; }; + let is_branch = + !block.is_standalone() || block.syntax().parent().and_then(ast::MatchArm::cast).is_some(); + let label = if is_branch { "Unwrap branch" } else { "Unwrap block" }; let replacement = replacement.stmt_list()?; - acc.add(AssistId::refactor_rewrite("unwrap_branch"), "Unwrap branch", target, |builder| { + acc.add(AssistId::refactor_rewrite("unwrap_branch"), label, target, |builder| { let editor = builder.make_editor(block.syntax()); let replacement = replacement.dedent(from_indent).indent(into_indent); let container = prefer_container.unwrap_or(container); @@ -134,7 +137,7 @@ fn extract_statements(stmt_list: ast::StmtList) -> Vec { #[cfg(test)] mod tests { - use crate::tests::{check_assist, check_assist_not_applicable}; + use crate::tests::{check_assist, check_assist_not_applicable, check_assist_with_label}; use super::*; @@ -926,4 +929,67 @@ fn main() { "#, ); } + + #[test] + fn unwrap_block_labels() { + check_assist_with_label( + unwrap_branch, + r#" +fn main() { + $0{ + bar; + } +} +"#, + "Unwrap block", + ); + check_assist_with_label( + unwrap_branch, + r#" +fn main() { + let x = $0{ + bar() + }; +} +"#, + "Unwrap block", + ); + check_assist_with_label( + unwrap_branch, + r#" +fn main() { + let x = if true $0{ + bar() + }; +} +"#, + "Unwrap branch", + ); + check_assist_with_label( + unwrap_branch, + r#" +fn main() { + let x = match () { + () => $0{ + bar(), + } + }; +} +"#, + "Unwrap branch", + ); + check_assist_with_label( + unwrap_branch, + r#" +fn main() { + match () { + () => $0{ + bar(), + } + } +} +"#, + "Unwrap branch", + ); + } } From 5fc5dd61a4198488fd7766b8d177329b1de84f32 Mon Sep 17 00:00:00 2001 From: Kao-Wei Yeh Date: Wed, 22 Apr 2026 21:26:18 +0800 Subject: [PATCH 052/289] Fix trait auto import appearing again when trait already been imported as _ --- .../crates/ide-completion/src/render.rs | 27 +++++++++++++++++++ .../ide-db/src/imports/import_assets.rs | 1 + 2 files changed, 28 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 608dec1285dc4..6b00bc16118ca 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -877,6 +877,33 @@ mod tests { } } + #[test] + fn trait_imported_as_underscore_should_not_appear_auto_import_again() { + // make sure there has no `requires_import` + // see https://github.com/rust-lang/rust-analyzer/issues/19767 + check_relevance( + r#" +//- /dep.rs crate:dep +pub trait MyTrait { + fn my_method(&self); +} + +//- /main.rs crate:main deps:dep +use dep::MyTrait as _; +struct MyStruct; +impl dep::MyTrait for MyStruct { + fn my_method(&self) {} +} +fn main() { + MyStruct::my_method$0 +} +"#, + expect![[r#" + me my_method(…) fn(&self) [] + "#]], + ); + } + #[test] fn set_struct_type_completion_info() { check_relevance( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 4f05714a556a4..e0501b5e44ea3 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -454,6 +454,7 @@ impl<'db> ImportAssets<'db> { |trait_to_import| { !scope_definitions .contains(&ScopeDef::ModuleDef(ModuleDef::Trait(trait_to_import))) + && !scope.can_use_trait_methods(trait_to_import) }, ), } From 357b238ce60c3bdb3d484769cabeb1b7941561e2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 22 Apr 2026 18:59:38 +0200 Subject: [PATCH 053/289] misc improvements --- src/tools/rust-analyzer/crates/hir-ty/src/tests.rs | 11 ++++------- .../crates/hir-ty/src/tests/diagnostics.rs | 4 +--- 2 files changed, 5 insertions(+), 10 deletions(-) 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 430a570444d8a..83767c42ea7c3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -88,15 +88,12 @@ fn check_impl( let file_range = FileRange { file_id, range }; if only_types { types.insert(file_range, expected); - } else if expected.starts_with("type: ") { - types.insert(file_range, expected.trim_start_matches("type: ").to_owned()); + } else if let Some(ty) = expected.strip_prefix("type: ") { + types.insert(file_range, ty.to_owned()); } else if expected.starts_with("expected") { mismatches.insert(file_range, expected); - } else if expected.starts_with("adjustments:") { - adjustments.insert( - file_range, - expected.trim_start_matches("adjustments:").trim().to_owned(), - ); + } else if let Some(adjs) = expected.strip_prefix("adjustments:") { + adjustments.insert(file_range, adjs.trim().to_owned()); } else { panic!("unexpected annotation: {expected} @ {range:?}"); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs index f257aa1b6e602..94ca88088ddc5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs @@ -1,6 +1,4 @@ -use crate::tests::check_no_mismatches; - -use super::check; +use super::{check, check_no_mismatches}; #[test] fn function_return_type_mismatch_1() { From 0e72c8fd5020c483c80fdf48ba4c2e279c7e3472 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 22 Apr 2026 13:44:11 +0200 Subject: [PATCH 054/289] feat: add diagnostic for E0735 --- .../crates/hir-ty/src/lower/diagnostics.rs | 5 +++ .../crates/hir-ty/src/lower/path.rs | 6 ++- .../crates/hir/src/diagnostics.rs | 12 ++++++ .../generic_default_refers_to_self.rs | 43 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 5 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs index 009f047109dfb..2565fb46ce8ce 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs @@ -81,6 +81,11 @@ pub enum PathLoweringDiagnostic { def: GenericDefId, expected_count: u32, }, + /// Generic defaults are not allowed to refer to `Self`. + GenericDefaultRefersToSelf { + /// Index of the `Self` segment. + segment: u32, + }, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index 4f707321782a2..a3648945399eb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -300,8 +300,10 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy); if self.ctx.lowering_param_default.is_some() { - // Generic defaults are not allowed to refer to `Self`. - // FIXME: Emit an error. + let segment = self.current_segment_u32(); + self.on_diagnostic(PathLoweringDiagnostic::GenericDefaultRefersToSelf { + segment, + }); return false; } } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 6cfb79d5a1f4b..e0cab2d82c62a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -57,6 +57,7 @@ diagnostics![AnyDiagnostic<'db> -> BreakOutsideOfLoop, CastToUnsized<'db>, ExpectedFunction<'db>, + GenericDefaultRefersToSelf, InactiveCode, IncoherentImpl, IncorrectCase, @@ -465,6 +466,12 @@ pub struct IncorrectGenericsOrder { pub expected_kind: GenericArgKind, } +#[derive(Debug)] +pub struct GenericDefaultRefersToSelf { + /// The `Self` segment. + pub segment: InFile>, +} + impl<'db> AnyDiagnostic<'db> { pub(crate) fn body_validation_diagnostic( db: &'db dyn HirDatabase, @@ -894,6 +901,11 @@ impl<'db> AnyDiagnostic<'db> { } .into() } + PathLoweringDiagnostic::GenericDefaultRefersToSelf { segment } => { + let segment = hir_segment_to_ast_segment(&path.value, segment)?; + let segment = path.with_value(AstPtr::new(&segment)); + GenericDefaultRefersToSelf { segment }.into() + } }) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs new file mode 100644 index 0000000000000..3d38159c4f195 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs @@ -0,0 +1,43 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: generic-default-refers-to-self +// +// This diagnostic is shown when a generic default refers to `Self` +pub(crate) fn generic_default_refers_to_self( + ctx: &DiagnosticsContext<'_>, + d: &hir::GenericDefaultRefersToSelf, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0735"), + "generic parameters cannot use `Self` in their defaults", + d.segment.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn plain_self() { + check_diagnostics( + r#" +struct Foo(T); + // ^^^^ error: generic parameters cannot use `Self` in their defaults +"#, + ); + } + + #[test] + fn self_as_generic() { + check_diagnostics( + r#" +struct Wrapper(T); +struct Foo>(T); + // ^^^^ error: generic parameters cannot use `Self` in their defaults +"#, + ); + } +} 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 09c9f8eab0a0d..54af3a35f46cf 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -35,6 +35,7 @@ mod handlers { pub(crate) mod elided_lifetimes_in_path; pub(crate) mod expected_function; pub(crate) mod generic_args_prohibited; + pub(crate) mod generic_default_refers_to_self; pub(crate) mod inactive_code; pub(crate) mod incoherent_impl; pub(crate) mod incorrect_case; @@ -477,6 +478,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::IncorrectGenericsOrder(d) => handlers::incorrect_generics_order::incorrect_generics_order(&ctx, &d), AnyDiagnostic::MissingLifetime(d) => handlers::missing_lifetime::missing_lifetime(&ctx, &d), AnyDiagnostic::ElidedLifetimesInPath(d) => handlers::elided_lifetimes_in_path::elided_lifetimes_in_path(&ctx, &d), + AnyDiagnostic::GenericDefaultRefersToSelf(d) => handlers::generic_default_refers_to_self::generic_default_refers_to_self(&ctx, &d), }; res.push(d) } From d712a52cd36e6032a5f67beb9db8996b020069bd Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 11:06:30 +0300 Subject: [PATCH 055/289] Remove methods that do not have a correspondence in rustc And make `InferenceTable` used by inference only. --- .../crates/hir-ty/src/dyn_compatibility.rs | 14 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 2 +- .../crates/hir-ty/src/infer/cast.rs | 7 +- .../crates/hir-ty/src/infer/unify.rs | 141 ++---------------- .../rust-analyzer/crates/hir-ty/src/lib.rs | 115 +++++++------- .../hir-ty/src/next_solver/infer/mod.rs | 18 ++- .../hir-ty/src/next_solver/infer/traits.rs | 8 +- .../crates/hir-ty/src/next_solver/ty.rs | 4 + .../rust-analyzer/crates/hir-ty/src/traits.rs | 97 +----------- src/tools/rust-analyzer/crates/hir/src/lib.rs | 18 +-- 10 files changed, 111 insertions(+), 313 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index ee7527446a952..0cf7f650f0d9c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -21,11 +21,14 @@ use crate::{ db::{HirDatabase, InternedOpaqueTyId}, lower::{GenericPredicates, associated_ty_item_bounds}, next_solver::{ - AliasTy, Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, Goal, ParamEnv, - ParamTy, SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, infer::DbInternerInferExt, + AliasTy, Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, ParamTy, + SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, + infer::{ + DbInternerInferExt, + traits::{Obligation, ObligationCause}, + }, mk_param, }, - traits::next_trait_solve_in_ctxt, }; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -474,12 +477,11 @@ fn receiver_is_dispatchable<'db>( // Receiver: DispatchFromDyn U]> let predicate = TraitRef::new(interner, dispatch_from_dyn_did.into(), [receiver_ty, unsized_receiver_ty]); - let goal = Goal::new(interner, param_env, predicate); + let obligation = Obligation::new(interner, ObligationCause::dummy(), param_env, predicate); let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); // the receiver is dispatchable iff the obligation holds - let res = next_trait_solve_in_ctxt(&infcx, goal); - res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes)) + infcx.predicate_must_hold_modulo_regions(&obligation) } fn receiver_for_self_ty<'db>( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 54f334b66d2b1..9a6941f8a3889 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1240,7 +1240,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { db.trait_environment(ExpressionStoreOwnerId::VariantFields(variant_id)) } }; - let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), Some(owner)); + let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), owner); let types = crate::next_solver::default_types(db); InferenceContext { result: InferenceResult::new(types.types.error), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index 09855766d8bc4..d23a32d81b64f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -147,7 +147,8 @@ impl<'db> CastCheck<'db> { return Ok(()); } - if !self.cast_ty.has_infer_types() && !ctx.table.is_sized(self.cast_ty) { + if !self.cast_ty.has_infer_types() && !ctx.table.type_is_sized_modulo_regions(self.cast_ty) + { return Err(InferenceDiagnostic::CastToUnsized { expr: self.expr, cast_ty: self.cast_ty.store(), @@ -199,7 +200,7 @@ impl<'db> CastCheck<'db> { // array-ptr-cast CastTy::Ptr(t, m) => { let t = ctx.table.try_structurally_resolve_type(t); - if !ctx.table.is_sized(t) { + if !ctx.table.type_is_sized_modulo_regions(t) { return Err(CastError::IllegalCast); } self.check_ref_cast(ctx, inner_ty, mutbl, t, m) @@ -520,7 +521,7 @@ fn pointer_kind<'db>( ) -> Result>, ()> { let ty = ctx.table.try_structurally_resolve_type(ty); - if ctx.table.is_sized(ty) { + if ctx.table.type_is_sized_modulo_regions(ty) { return Ok(Some(PointerKind::Thin)); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index b0f916b8c0763..1d6a87a157b50 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -3,10 +3,10 @@ use std::fmt; use base_db::Crate; -use hir_def::{AdtId, ExpressionStoreOwnerId, GenericParamId, TraitId}; +use hir_def::{ExpressionStoreOwnerId, GenericParamId, TraitId}; use rustc_hash::FxHashSet; use rustc_type_ir::{ - TyVid, TypeFoldable, TypeVisitableExt, UpcastFrom, + TyVid, TypeFoldable, TypeVisitableExt, inherent::{Const as _, GenericArg as _, IntoKind, Ty as _}, solve::Certainty, }; @@ -15,7 +15,7 @@ use smallvec::SmallVec; use crate::{ db::HirDatabase, next_solver::{ - Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Goal, + Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, TyKind, TypingMode, fulfill::{FulfillmentCtxt, NextSolverError}, @@ -28,10 +28,7 @@ use crate::{ inspect::{InspectConfig, InspectGoal, ProofTreeVisitor}, obligation_ctxt::ObligationCtxt, }, - traits::{ - NextTraitSolveResult, ParamEnvAndCrate, next_trait_solve_canonical_in_ctxt, - next_trait_solve_in_ctxt, - }, + traits::ParamEnvAndCrate, }; struct NestedObligationsForSelfTy<'a, 'db> { @@ -145,14 +142,10 @@ impl<'db> InferenceTable<'db> { db: &'db dyn HirDatabase, trait_env: ParamEnv<'db>, krate: Crate, - owner: Option, + owner: ExpressionStoreOwnerId, ) -> Self { let interner = DbInterner::new_with(db, krate); - let typing_mode = match owner { - Some(owner) => TypingMode::typeck_for_body(interner, owner.into()), - // IDE things wants to reveal opaque types. - None => TypingMode::PostAnalysis, - }; + let typing_mode = TypingMode::typeck_for_body(interner, owner.into()); let infer_ctxt = interner.infer_ctxt().build(typing_mode); InferenceTable { db, @@ -172,6 +165,10 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.type_is_copy_modulo_regions(self.param_env, ty) } + pub(crate) fn type_is_sized_modulo_regions(&self, ty: Ty<'db>) -> bool { + self.infer_ctxt.type_is_sized_modulo_regions(self.param_env, ty) + } + pub(crate) fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'db>) -> bool { self.infer_ctxt.type_is_use_cloned_modulo_regions(self.param_env, ty) } @@ -253,23 +250,6 @@ impl<'db> InferenceTable<'db> { self.diverging_type_vars.insert(ty); } - pub(crate) fn canonicalize(&mut self, t: T) -> rustc_type_ir::Canonical, T> - where - T: TypeFoldable>, - { - // try to resolve obligations before canonicalizing, since this might - // result in new knowledge about variables - self.select_obligations_where_possible(); - self.infer_ctxt.canonicalize_response(t) - } - - pub(crate) fn normalize_alias_ty(&mut self, alias: Ty<'db>) -> Ty<'db> { - self.infer_ctxt - .at(&ObligationCause::new(), self.param_env) - .structurally_normalize_ty(alias, &mut self.fulfillment_cx) - .unwrap_or(alias) - } - pub(crate) fn next_ty_var(&self) -> Ty<'db> { self.infer_ctxt.next_ty_var() } @@ -407,43 +387,6 @@ impl<'db> InferenceTable<'db> { result } - /// Checks an obligation without registering it. Useful mostly to check - /// whether a trait *might* be implemented before deciding to 'lock in' the - /// choice (during e.g. method resolution or deref). - #[tracing::instrument(level = "debug", skip(self))] - pub(crate) fn try_obligation(&mut self, predicate: Predicate<'db>) -> NextTraitSolveResult { - let goal = Goal { param_env: self.param_env, predicate }; - let canonicalized = self.canonicalize(goal); - - next_trait_solve_canonical_in_ctxt(&self.infer_ctxt, canonicalized) - } - - pub(crate) fn register_obligation(&mut self, predicate: Predicate<'db>) { - let goal = Goal { param_env: self.param_env, predicate }; - self.register_obligation_in_env(goal) - } - - #[tracing::instrument(level = "debug", skip(self))] - fn register_obligation_in_env(&mut self, goal: Goal<'db, Predicate<'db>>) { - let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); - tracing::debug!(?result); - match result { - Ok((_, Certainty::Yes)) => {} - Err(rustc_type_ir::solve::NoSolution) => {} - Ok((_, Certainty::Maybe { .. })) => { - self.fulfillment_cx.register_predicate_obligation( - &self.infer_ctxt, - Obligation::new( - self.interner(), - ObligationCause::new(), - goal.param_env, - goal.predicate, - ), - ); - } - } - } - pub(crate) fn register_bound(&mut self, ty: Ty<'db>, def_id: TraitId, cause: ObligationCause) { if !ty.references_non_lt_error() { let trait_ref = TraitRef::new(self.interner(), def_id.into(), [ty]); @@ -533,70 +476,6 @@ impl<'db> InferenceTable<'db> { pub(super) fn insert_const_vars_shallow(&mut self, c: Const<'db>) -> Const<'db> { if c.is_ct_error() { self.next_const_var() } else { c } } - - /// Check if given type is `Sized` or not - pub(crate) fn is_sized(&mut self, ty: Ty<'db>) -> bool { - fn short_circuit_trivial_tys(ty: Ty<'_>) -> Option { - match ty.kind() { - TyKind::Bool - | TyKind::Char - | TyKind::Int(_) - | TyKind::Uint(_) - | TyKind::Float(_) - | TyKind::Ref(..) - | TyKind::RawPtr(..) - | TyKind::Never - | TyKind::FnDef(..) - | TyKind::Array(..) - | TyKind::FnPtr(..) => Some(true), - TyKind::Slice(..) | TyKind::Str | TyKind::Dynamic(..) => Some(false), - _ => None, - } - } - - let mut ty = ty; - ty = self.try_structurally_resolve_type(ty); - if let Some(sized) = short_circuit_trivial_tys(ty) { - return sized; - } - - { - let mut structs = SmallVec::<[_; 8]>::new(); - // Must use a loop here and not recursion because otherwise users will conduct completely - // artificial examples of structs that have themselves as the tail field and complain r-a crashes. - while let Some((AdtId::StructId(id), subst)) = ty.as_adt() { - let struct_data = id.fields(self.db); - if let Some((last_field, _)) = struct_data.fields().iter().next_back() { - let last_field_ty = self.db.field_types(id.into())[last_field] - .get() - .instantiate(self.interner(), subst); - if structs.contains(&ty) { - // A struct recursively contains itself as a tail field somewhere. - return true; // Don't overload the users with too many errors. - } - structs.push(ty); - // Structs can have DST as its last field and such cases are not handled - // as unsized by the chalk, so we do this manually. - ty = last_field_ty; - ty = self.try_structurally_resolve_type(ty); - if let Some(sized) = short_circuit_trivial_tys(ty) { - return sized; - } - } else { - break; - }; - } - } - - let Some(sized) = self.interner().lang_items().Sized else { - return false; - }; - let sized_pred = Predicate::upcast_from( - TraitRef::new(self.interner(), sized.into(), [ty]), - self.interner(), - ); - self.try_obligation(sized_pred).certain() - } } impl fmt::Debug for InferenceTable<'_> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index c6c7856c8c3f3..2973b970f3015 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -71,7 +71,7 @@ use macros::GenericTypeVisitable; use mir::{MirEvalError, VTableMap}; use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; use rustc_type_ir::{ - BoundVarIndexKind, TypeSuperVisitable, TypeVisitableExt, UpcastFrom, + BoundVarIndexKind, TypeSuperVisitable, TypeVisitableExt, inherent::{IntoKind, Ty as _}, }; use syntax::ast::{ConstArg, make}; @@ -80,12 +80,17 @@ use traits::FnTrait; use crate::{ db::HirDatabase, display::{DisplayTarget, HirDisplay}, - infer::unify::InferenceTable, lower::SupertraitsInfo, next_solver::{ AliasTy, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, Canonical, - CanonicalVarKind, CanonicalVarKinds, ClauseKind, Const, ConstKind, DbInterner, FnSig, - GenericArgs, PolyFnSig, Predicate, Region, RegionKind, TraitRef, Ty, TyKind, Tys, abi, + CanonicalVarKind, CanonicalVarKinds, ClauseKind, Const, ConstKind, DbInterner, GenericArgs, + PolyFnSig, Region, RegionKind, TraitRef, Ty, TyKind, TypingMode, + abi::Safety, + infer::{ + DbInternerInferExt, + traits::{Obligation, ObligationCause}, + }, + obligation_ctxt::ObligationCtxt, }, }; @@ -525,68 +530,64 @@ pub fn associated_type_shorthand_candidates( /// To be used from `hir` only. pub fn callable_sig_from_fn_trait<'db>( self_ty: Ty<'db>, - trait_env: ParamEnvAndCrate<'db>, + param_env: ParamEnvAndCrate<'db>, db: &'db dyn HirDatabase, ) -> Option<(FnTrait, PolyFnSig<'db>)> { - let mut table = InferenceTable::new(db, trait_env.param_env, trait_env.krate, None); - let lang_items = table.interner().lang_items(); + let ParamEnvAndCrate { param_env, krate } = param_env; + let interner = DbInterner::new_with(db, krate); + let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); + let lang_items = interner.lang_items(); + let cause = ObligationCause::dummy(); + + let impls_trait = |trait_: FnTrait| { + let mut ocx = ObligationCtxt::new(&infcx); + let tupled_args = infcx.next_ty_var(); + let args = GenericArgs::new_from_slice(&[self_ty.into(), tupled_args.into()]); + let trait_id = trait_.get_id(lang_items)?; + let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); + let obligation = Obligation::new(interner, cause.clone(), param_env, trait_ref); + ocx.register_obligation(obligation); + if !ocx.try_evaluate_obligations().is_empty() { + return None; + } + let tupled_args = + infcx.resolve_vars_if_possible(tupled_args).replace_infer_with_error(interner); + if tupled_args.is_tuple() { Some(tupled_args) } else { None } + }; - let fn_once_trait = FnTrait::FnOnce.get_id(lang_items)?; + let (trait_, args) = 'find_trait: { + for trait_ in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { + if let Some(args) = impls_trait(trait_) { + break 'find_trait (trait_, args); + } + } + return None; + }; + + let fn_once_trait = lang_items.FnOnce?; let output_assoc_type = fn_once_trait .trait_items(db) .associated_type_by_name(&Name::new_symbol_root(sym::Output))?; - - // Register two obligations: - // - Self: FnOnce - // - >::Output == ?ret_ty - let args_ty = table.next_ty_var(); - let args = GenericArgs::new_from_slice(&[self_ty.into(), args_ty.into()]); - let trait_ref = TraitRef::new_from_args(table.interner(), fn_once_trait.into(), args); - let projection = Ty::new_alias( - table.interner(), - AliasTy::new_from_args( - table.interner(), + let output_projection = Ty::new_alias( + interner, + AliasTy::new( + interner, rustc_type_ir::Projection { def_id: output_assoc_type.into() }, - args, + [self_ty, args], ), ); - - let pred = Predicate::upcast_from(trait_ref, table.interner()); - if !table.try_obligation(pred).no_solution() { - table.register_obligation(pred); - let return_ty = table.normalize_alias_ty(projection); - for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { - let fn_x_trait = fn_x.get_id(lang_items)?; - let trait_ref = TraitRef::new_from_args(table.interner(), fn_x_trait.into(), args); - if !table - .try_obligation(Predicate::upcast_from(trait_ref, table.interner())) - .no_solution() - { - let ret_ty = table.resolve_completely(return_ty); - let args_ty = table.resolve_completely(args_ty); - let TyKind::Tuple(params) = args_ty.kind() else { - return None; - }; - let inputs_and_output = Tys::new_from_iter( - table.interner(), - params.iter().chain(std::iter::once(ret_ty)), - ); - - return Some(( - fn_x, - Binder::dummy(FnSig { - inputs_and_output, - c_variadic: false, - safety: abi::Safety::Safe, - abi: FnAbi::RustCall, - }), - )); - } - } - unreachable!("It should at least implement FnOnce at this point"); - } else { - None - } + let mut ocx = ObligationCtxt::new(&infcx); + let ret = ocx.structurally_normalize_ty(&cause, param_env, output_projection).ok()?; + let ret = ret.replace_infer_with_error(interner); + + let sig = Binder::dummy(interner.mk_fn_sig( + args.tuple_fields(), + ret, + false, + Safety::Safe, + FnAbi::Rust, + )); + Some((trait_, sig)) } struct ParamCollector { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index de21c5442bd77..1eacc295c91c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -58,7 +58,7 @@ pub mod relate; pub mod resolve; pub mod select; pub(crate) mod snapshot; -pub(crate) mod traits; +pub mod traits; mod type_variable; mod unify_key; @@ -494,8 +494,7 @@ impl<'db> InferCtxt<'db> { /// check::<&'_ T>(); /// } /// ``` - #[expect(dead_code, reason = "this is used in rustc")] - fn predicate_must_hold_considering_regions( + pub fn predicate_must_hold_considering_regions( &self, obligation: &PredicateObligation<'db>, ) -> bool { @@ -507,8 +506,10 @@ impl<'db> InferCtxt<'db> { /// not entirely accurate if inference variables are involved. /// /// This version ignores all outlives constraints. - #[expect(dead_code, reason = "this is used in rustc")] - fn predicate_must_hold_modulo_regions(&self, obligation: &PredicateObligation<'db>) -> bool { + pub fn predicate_must_hold_modulo_regions( + &self, + obligation: &PredicateObligation<'db>, + ) -> bool { self.evaluate_obligation(obligation).must_apply_modulo_regions() } @@ -610,6 +611,13 @@ impl<'db> InferCtxt<'db> { traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id) } + pub fn type_is_sized_modulo_regions(&self, param_env: ParamEnv<'db>, ty: Ty<'db>) -> bool { + let Some(sized_def_id) = self.interner.lang_items().Sized else { + return true; + }; + traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, sized_def_id) + } + pub fn type_is_use_cloned_modulo_regions(&self, param_env: ParamEnv<'db>, ty: Ty<'db>) -> bool { let ty = self.resolve_vars_if_possible(ty); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index dde623483642f..5b875d29608a3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -185,12 +185,12 @@ impl<'db> PredicateObligation<'db> { impl<'db, O> Obligation<'db, O> { pub fn new( - tcx: DbInterner<'db>, + interner: DbInterner<'db>, cause: ObligationCause, param_env: ParamEnv<'db>, predicate: impl Upcast, O>, ) -> Obligation<'db, O> { - Self::with_depth(tcx, cause, 0, param_env, predicate) + Self::with_depth(interner, cause, 0, param_env, predicate) } /// We often create nested obligations without setting the correct depth. @@ -202,13 +202,13 @@ impl<'db, O> Obligation<'db, O> { } pub fn with_depth( - tcx: DbInterner<'db>, + interner: DbInterner<'db>, cause: ObligationCause, recursion_depth: usize, param_env: ParamEnv<'db>, predicate: impl Upcast, O>, ) -> Obligation<'db, O> { - let predicate = predicate.upcast(tcx); + let predicate = predicate.upcast(interner); Obligation { cause, param_env, recursion_depth, predicate } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index c953e79602d51..0fd02eb8ca960 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -626,6 +626,10 @@ impl<'db> Ty<'db> { } } + pub fn is_tuple(self) -> bool { + matches!(self.kind(), TyKind::Tuple(_)) + } + pub fn as_tuple(self) -> Option> { match self.kind() { TyKind::Tuple(tys) => Some(tys), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 878696c721295..0dc834ddcc71e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -15,18 +15,15 @@ use hir_def::{ }; use hir_expand::name::Name; use intern::sym; -use rustc_next_trait_solver::solve::{HasChanged, SolverDelegateEvalExt}; use rustc_type_ir::{ TypingMode, - inherent::{AdtDef, BoundExistentialPredicates, IntoKind, Span as _}, - solve::Certainty, + inherent::{AdtDef, BoundExistentialPredicates, IntoKind}, }; use crate::{ db::HirDatabase, next_solver::{ - Canonical, DbInterner, GenericArgs, Goal, ParamEnv, Predicate, SolverContext, Span, - StoredClauses, Ty, TyKind, + DbInterner, GenericArgs, ParamEnv, StoredClauses, Ty, TyKind, infer::{ DbInternerInferExt, InferCtxt, traits::{Obligation, ObligationCause}, @@ -79,91 +76,6 @@ pub fn structurally_normalize_ty<'db>( ty.replace_infer_with_error(infcx.interner) } -#[derive(Clone, Debug, PartialEq)] -pub enum NextTraitSolveResult { - Certain, - Uncertain, - NoSolution, -} - -impl NextTraitSolveResult { - pub fn no_solution(&self) -> bool { - matches!(self, NextTraitSolveResult::NoSolution) - } - - pub fn certain(&self) -> bool { - matches!(self, NextTraitSolveResult::Certain) - } - - pub fn uncertain(&self) -> bool { - matches!(self, NextTraitSolveResult::Uncertain) - } -} - -pub fn next_trait_solve_canonical_in_ctxt<'db>( - infer_ctxt: &InferCtxt<'db>, - goal: Canonical<'db, Goal<'db, Predicate<'db>>>, -) -> NextTraitSolveResult { - infer_ctxt.probe(|_| { - let context = <&SolverContext<'db>>::from(infer_ctxt); - - tracing::info!(?goal); - - let (goal, var_values) = context.instantiate_canonical(&goal); - tracing::info!(?var_values); - - let res = context.evaluate_root_goal(goal, Span::dummy(), None); - - let obligation = Obligation { - cause: ObligationCause::dummy(), - param_env: goal.param_env, - recursion_depth: 0, - predicate: goal.predicate, - }; - infer_ctxt.inspect_evaluated_obligation(&obligation, &res, || { - Some(context.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1) - }); - - let res = res.map(|r| (r.has_changed, r.certainty)); - - tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); - - match res { - Err(_) => NextTraitSolveResult::NoSolution, - Ok((_, Certainty::Yes)) => NextTraitSolveResult::Certain, - Ok((_, Certainty::Maybe { .. })) => NextTraitSolveResult::Uncertain, - } - }) -} - -/// Solve a trait goal using next trait solver. -pub fn next_trait_solve_in_ctxt<'db, 'a>( - infer_ctxt: &'a InferCtxt<'db>, - goal: Goal<'db, Predicate<'db>>, -) -> Result<(HasChanged, Certainty), rustc_type_ir::solve::NoSolution> { - tracing::info!(?goal); - - let context = <&SolverContext<'db>>::from(infer_ctxt); - - let res = context.evaluate_root_goal(goal, Span::dummy(), None); - - let obligation = Obligation { - cause: ObligationCause::dummy(), - param_env: goal.param_env, - recursion_depth: 0, - predicate: goal.predicate, - }; - infer_ctxt.inspect_evaluated_obligation(&obligation, &res, || { - Some(context.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1) - }); - - let res = res.map(|r| (r.has_changed, r.certainty)); - - tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); - - res -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Update)] pub enum FnTrait { // Warning: Order is important. If something implements `x` it should also implement @@ -235,10 +147,9 @@ fn implements_trait_unique_impl<'db>( let args = create_args(&infcx); let trait_ref = rustc_type_ir::TraitRef::new_from_args(interner, trait_.into(), args); - let goal = Goal::new(interner, env.param_env, trait_ref); - let result = crate::traits::next_trait_solve_in_ctxt(&infcx, goal); - matches!(result, Ok((_, Certainty::Yes))) + let obligation = Obligation::new(interner, ObligationCause::dummy(), env.param_env, trait_ref); + infcx.predicate_must_hold_modulo_regions(&obligation) } pub fn is_inherent_impl_coherent(db: &dyn HirDatabase, def_map: &DefMap, impl_id: ImplId) -> bool { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index ecd11fb5d767b..11598f2a103af 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -6561,21 +6561,13 @@ impl<'db> TypeNs<'db> { ); let trait_ref = hir_ty::next_solver::TraitRef::new_from_args(infcx.interner, trait_.id.into(), args); - - let pred_kind = rustc_type_ir::Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(rustc_type_ir::TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )); - let predicate = hir_ty::next_solver::Predicate::new(infcx.interner, pred_kind); - let goal = hir_ty::next_solver::Goal::new( + let obligation = hir_ty::next_solver::infer::traits::Obligation::new( infcx.interner, - hir_ty::next_solver::ParamEnv::empty(), - predicate, + hir_ty::next_solver::infer::traits::ObligationCause::dummy(), + self.env.param_env, + trait_ref, ); - let res = hir_ty::traits::next_trait_solve_in_ctxt(&infcx, goal); - res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes)) + infcx.predicate_must_hold_modulo_regions(&obligation) } pub fn is_bool(&self) -> bool { From 962e4216617a160551ab2d40716b9f52dcf25543 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 12:39:05 +0300 Subject: [PATCH 056/289] Port record struct literal handling from rustc --- .../crates/hir-def/src/unstable_features.rs | 1 + .../rust-analyzer/crates/hir-ty/src/infer.rs | 6 +- .../crates/hir-ty/src/infer/closure.rs | 14 +- .../crates/hir-ty/src/infer/expr.rs | 347 +++++++++++++----- .../crates/hir-ty/src/infer/opaques.rs | 2 +- .../crates/hir-ty/src/infer/unify.rs | 4 + .../crates/intern/src/symbol/symbols.rs | 1 + 7 files changed, 280 insertions(+), 95 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/unstable_features.rs b/src/tools/rust-analyzer/crates/hir-def/src/unstable_features.rs index 559726fe9b65b..f581f02617883 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/unstable_features.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/unstable_features.rs @@ -92,4 +92,5 @@ define_unstable_features! { ref_pat_eat_one_layer_2024_structural, deref_patterns, mut_ref, + type_changing_struct_update, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 9a6941f8a3889..50809f46219e9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1871,6 +1871,10 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.table.shallow_resolve(ty) } + pub(crate) fn resolve_vars_if_possible>>(&self, t: T) -> T { + self.table.resolve_vars_if_possible(t) + } + fn resolve_associated_type( &mut self, inner_ty: Ty<'db>, @@ -2489,7 +2493,7 @@ impl<'db> Expectation<'db> { fn only_has_type(&self, table: &mut unify::InferenceTable<'db>) -> Option> { match self { - Expectation::HasType(t) => Some(table.shallow_resolve(*t)), + Expectation::HasType(t) => Some(table.resolve_vars_if_possible(*t)), Expectation::Castable(_) | Expectation::RValueLikeUnsized(_) | Expectation::None => { None } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 0d2de9b9846e6..a53ab7daaaa34 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -440,8 +440,7 @@ impl<'db> InferenceContext<'_, 'db> { .eq(inferred_fnptr_sig, generalized_fnptr_sig) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); - let resolved_sig = - self.table.infer_ctxt.resolve_vars_if_possible(generalized_fnptr_sig); + let resolved_sig = self.resolve_vars_if_possible(generalized_fnptr_sig); if resolved_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { expected_sig = Some(resolved_sig.fn_sig(self.interner())); @@ -531,7 +530,7 @@ impl<'db> InferenceContext<'_, 'db> { &self, projection: PolyProjectionPredicate<'db>, ) -> Option> { - let projection = self.table.infer_ctxt.resolve_vars_if_possible(projection); + let projection = self.resolve_vars_if_possible(projection); let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1); debug!(?arg_param_ty); @@ -576,7 +575,7 @@ impl<'db> InferenceContext<'_, 'db> { &mut self, projection: PolyProjectionPredicate<'db>, ) -> Option> { - let projection = self.table.infer_ctxt.resolve_vars_if_possible(projection); + let projection = self.resolve_vars_if_possible(projection); let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1); debug!(?arg_param_ty); @@ -822,11 +821,8 @@ impl<'db> InferenceContext<'_, 'db> { .eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?; all_obligations.extend(obligations); - let inputs = supplied_sig - .inputs() - .iter() - .copied() - .map(|ty| table.infer_ctxt.resolve_vars_if_possible(ty)); + let inputs = + supplied_sig.inputs().iter().copied().map(|ty| table.resolve_vars_if_possible(ty)); expected_sigs.liberated_sig = table.interner().mk_fn_sig( inputs, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index e84a03a2e72a9..db8ab67d2bdc5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -4,11 +4,12 @@ use std::{iter::repeat_with, mem}; use either::Either; use hir_def::{ - FieldId, GenericDefId, ItemContainerId, Lookup, TupleFieldId, TupleId, + AdtId, FieldId, GenericDefId, ItemContainerId, Lookup, TupleFieldId, TupleId, VariantId, expr_store::path::{GenericArgs as HirGenericArgs, Path}, hir::{ Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId, - InlineAsmKind, LabelId, Literal, Pat, PatId, RecordSpread, Statement, UnaryOp, + InlineAsmKind, LabelId, Literal, Pat, PatId, RecordLitField, RecordSpread, Statement, + UnaryOp, }, resolver::ValueNs, signatures::VariantFields, @@ -16,10 +17,12 @@ use hir_def::{ use hir_def::{FunctionId, hir::ClosureKind}; use hir_expand::name::Name; use rustc_ast_ir::Mutability; +use rustc_hash::FxHashMap; use rustc_type_ir::{ InferTy, Interner, inherent::{AdtDef, GenericArgs as _, IntoKind, Ty as _}, }; +use stdx::never; use syntax::ast::RangeOp; use tracing::debug; @@ -60,17 +63,41 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { let ty = self.infer_expr_inner(tgt_expr, expected, is_read); if let Some(expected_ty) = expected.only_has_type(&mut self.table) { - let could_unify = self.unify(ty, expected_ty); - if !could_unify { - self.result.type_mismatches.get_or_insert_default().insert( - tgt_expr.into(), - TypeMismatch { expected: expected_ty.store(), actual: ty.store() }, - ); - } + _ = self.demand_eqtype(tgt_expr.into(), expected_ty, ty); } ty } + pub(crate) fn infer_expr_suptype_coerce_never( + &mut self, + expr: ExprId, + expected: &Expectation<'db>, + is_read: ExprIsRead, + ) -> Ty<'db> { + let ty = self.infer_expr_inner(expr, expected, is_read); + if ty.is_never() { + if let Some(adjustments) = self.result.expr_adjustments.get(&expr) { + return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments { + target.as_ref() + } else { + self.err_ty() + }; + } + + if let Some(target) = expected.only_has_type(&mut self.table) { + self.coerce(expr, ty, target, AllowTwoPhase::No, ExprIsRead::Yes) + .expect("never-to-any coercion should always succeed") + } else { + ty + } + } else { + if let Some(expected_ty) = expected.only_has_type(&mut self.table) { + _ = self.demand_suptype(expr.into(), expected_ty, ty); + } + ty + } + } + pub(crate) fn infer_expr_no_expect( &mut self, tgt_expr: ExprId, @@ -283,13 +310,7 @@ impl<'db> InferenceContext<'_, 'db> { } } else { if let Some(expected_ty) = expected.only_has_type(&mut self.table) { - let could_unify = self.unify(ty, expected_ty); - if !could_unify { - self.result.type_mismatches.get_or_insert_default().insert( - expr.into(), - TypeMismatch { expected: expected_ty.store(), actual: ty.store() }, - ); - } + _ = self.demand_eqtype(expr.into(), ty, expected_ty); } ty } @@ -579,74 +600,7 @@ impl<'db> InferenceContext<'_, 'db> { self.types.types.never } Expr::RecordLit { path, fields, spread, .. } => { - let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path, false); - - if let Some(t) = expected.only_has_type(&mut self.table) { - self.unify(ty, t); - } - - let substs = ty.as_adt().map(|(_, s)| s).unwrap_or(self.types.empty.generic_args); - if let Some(variant) = def_id { - self.write_variant_resolution(tgt_expr.into(), variant); - } - match def_id { - _ if fields.is_empty() => {} - Some(def) => { - let field_types = self.db.field_types(def); - let variant_data = def.fields(self.db); - let visibilities = VariantFields::field_visibilities(self.db, def); - for field in fields.iter() { - let field_def = { - match variant_data.field(&field.name) { - Some(local_id) => { - if !visibilities[local_id] - .is_visible_from(self.db, self.resolver.module()) - { - self.push_diagnostic( - InferenceDiagnostic::NoSuchField { - field: field.expr.into(), - private: Some(local_id), - variant: def, - }, - ); - } - Some(local_id) - } - None => { - self.push_diagnostic(InferenceDiagnostic::NoSuchField { - field: field.expr.into(), - private: None, - variant: def, - }); - None - } - } - }; - let field_ty = field_def.map_or(self.err_ty(), |it| { - field_types[it].get().instantiate(self.interner(), &substs) - }); - - // Field type might have some unknown types - // FIXME: we may want to emit a single type variable for all instance of type fields? - let field_ty = self.insert_type_vars(field_ty); - self.infer_expr_coerce( - field.expr, - &Expectation::has_type(field_ty), - ExprIsRead::Yes, - ); - } - } - None => { - for field in fields.iter() { - // Field projections don't constitute reads. - self.infer_expr_coerce(field.expr, &Expectation::None, ExprIsRead::No); - } - } - } - if let RecordSpread::Expr(expr) = *spread { - self.infer_expr_coerce_never(expr, &Expectation::has_type(ty), ExprIsRead::Yes); - } - ty + self.infer_record_expr(tgt_expr, expected, path, fields, *spread) } Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected), Expr::Await { expr } => { @@ -1019,6 +973,231 @@ impl<'db> InferenceContext<'_, 'db> { ty } + fn infer_record_expr( + &mut self, + expr: ExprId, + expected: &Expectation<'db>, + path: &Path, + fields: &[RecordLitField], + base_expr: RecordSpread, + ) -> Ty<'db> { + // Find the relevant variant + let (adt_ty, Some(variant)) = self.resolve_variant(expr.into(), path, false) else { + // FIXME: Emit an error. + for field in fields { + self.infer_expr_no_expect(field.expr, ExprIsRead::Yes); + } + + return self.types.types.error; + }; + self.write_variant_resolution(expr.into(), variant); + + // Prohibit struct expressions when non-exhaustive flag is set. + if self.has_applicable_non_exhaustive(variant.into()) { + // FIXME: Emit an error. + } + + self.check_record_expr_fields(adt_ty, expected, expr, variant, fields, base_expr); + + self.require_type_is_sized(adt_ty); + adt_ty + } + + fn check_record_expr_fields( + &mut self, + adt_ty: Ty<'db>, + expected: &Expectation<'db>, + expr: ExprId, + variant: VariantId, + hir_fields: &[RecordLitField], + base_expr: RecordSpread, + ) { + let interner = self.interner(); + + let adt_ty = self.table.try_structurally_resolve_type(adt_ty); + let adt_ty_hint = expected.only_has_type(&mut self.table).and_then(|expected| { + self.infcx() + .fudge_inference_if_ok(|| { + let mut ocx = ObligationCtxt::new(self.infcx()); + ocx.sup(&ObligationCause::new(), self.table.param_env, expected, adt_ty)?; + if !ocx.try_evaluate_obligations().is_empty() { + return Err(TypeError::Mismatch); + } + Ok(self.resolve_vars_if_possible(adt_ty)) + }) + .ok() + }); + if let Some(adt_ty_hint) = adt_ty_hint { + // re-link the variables that the fudging above can create. + _ = self.demand_eqtype(expr.into(), adt_ty_hint, adt_ty); + } + + let TyKind::Adt(adt, args) = adt_ty.kind() else { + never!("non-ADT passed to check_struct_expr_fields"); + return; + }; + let adt_id = adt.def_id().0; + + let variant_fields = variant.fields(self.db); + let variant_field_tys = self.db.field_types(variant); + let variant_field_vis = VariantFields::field_visibilities(self.db, variant); + let mut remaining_fields = variant_fields + .fields() + .iter() + .map(|(i, field)| (field.name.clone(), i)) + .collect::>(); + + let mut seen_fields = FxHashMap::default(); + + // Type-check each field. + for field in hir_fields { + let name = &field.name; + let field_type = if let Some(i) = remaining_fields.remove(name) { + seen_fields.insert(name, i); + + if !self.resolver.is_visible(self.db, variant_field_vis[i]) { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: field.expr.into(), + private: Some(i), + variant, + }); + } + + variant_field_tys[i].get().instantiate(interner, args) + } else { + if let Some(field_idx) = seen_fields.get(&name) { + // FIXME: Emit an error: duplicate field. + variant_field_tys[*field_idx].get().instantiate(interner, args) + } else { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: field.expr.into(), + private: None, + variant, + }); + self.types.types.error + } + }; + + // Check that the expected field type is WF. Otherwise, we emit no use-site error + // in the case of coercions for non-WF fields, which leads to incorrect error + // tainting. See issue #126272. + self.table.register_wf_obligation(field_type.into(), ObligationCause::new()); + + // Make sure to give a type to the field even if there's + // an error, so we can continue type-checking. + self.infer_expr_coerce(field.expr, &Expectation::has_type(field_type), ExprIsRead::Yes); + } + + // Make sure the programmer specified correct number of fields. + if matches!(adt_id, AdtId::UnionId(_)) && hir_fields.len() != 1 { + // FIXME: Emit an error: unions must specify exactly one field. + } + + match base_expr { + RecordSpread::FieldDefaults => { + let mut missing_mandatory_fields = Vec::new(); + let mut missing_optional_fields = Vec::new(); + for (field_idx, field) in variant_fields.fields().iter() { + if remaining_fields.remove(&field.name).is_some() { + if field.default_value.is_none() { + missing_mandatory_fields.push(field_idx); + } else { + missing_optional_fields.push(field_idx); + } + } + } + if !missing_mandatory_fields.is_empty() { + // FIXME: Emit an error: missing fields. + } + } + RecordSpread::Expr(base_expr) => { + // FIXME: We are currently creating two branches here in order to maintain + // consistency. But they should be merged as much as possible. + if self.features.type_changing_struct_update { + if matches!(adt_id, AdtId::StructId(_)) { + // Make some fresh generic parameters for our ADT type. + let fresh_args = self.table.fresh_args_for_item(adt_id.into()); + // We do subtyping on the FRU fields first, so we can + // learn exactly what types we expect the base expr + // needs constrained to be compatible with the struct + // type we expect from the expectation value. + for (field_idx, field) in variant_fields.fields().iter() { + let fru_ty = variant_field_tys[field_idx] + .get() + .instantiate(interner, fresh_args); + if remaining_fields.remove(&field.name).is_some() { + let target_ty = + variant_field_tys[field_idx].get().instantiate(interner, args); + let cause = ObligationCause::new(); + match self.table.at(&cause).sup(target_ty, fru_ty) { + Ok(InferOk { obligations, value: () }) => { + self.table.register_predicates(obligations) + } + Err(_) => { + never!( + "subtyping remaining fields of type changing FRU \ + failed: {target_ty:?} != {fru_ty:?}: {:?}", + field.name, + ); + } + } + } + } + // The use of fresh args that we have subtyped against + // our base ADT type's fields allows us to guide inference + // along so that, e.g. + // ``` + // MyStruct<'a, F1, F2, const C: usize> { + // f: F1, + // // Other fields that reference `'a`, `F2`, and `C` + // } + // + // let x = MyStruct { + // f: 1usize, + // ..other_struct + // }; + // ``` + // will have the `other_struct` expression constrained to + // `MyStruct<'a, _, F2, C>`, as opposed to just `_`... + // This is important to allow coercions to happen in + // `other_struct` itself. See `coerce-in-base-expr.rs`. + let fresh_base_ty = Ty::new_adt(self.interner(), adt_id, fresh_args); + self.infer_expr_suptype_coerce_never( + base_expr, + &Expectation::has_type(self.resolve_vars_if_possible(fresh_base_ty)), + ExprIsRead::Yes, + ); + } else { + // Check the base_expr, regardless of a bad expected adt_ty, so we can get + // type errors on that expression, too. + self.infer_expr_no_expect(base_expr, ExprIsRead::Yes); + // FIXME: Emit an error: functional update syntax on non-struct. + } + } else { + self.infer_expr_suptype_coerce_never( + base_expr, + &Expectation::has_type(adt_ty), + ExprIsRead::Yes, + ); + if !matches!(adt_id, AdtId::StructId(_)) { + // FIXME: Emit an error: functional update syntax on non-struct. + } + } + } + RecordSpread::None => { + if !matches!(adt_id, AdtId::UnionId(_)) + && !remaining_fields.is_empty() + //~ non_exhaustive already reported, which will only happen for extern modules + && !self.has_applicable_non_exhaustive(adt_id.into()) + { + debug!(?remaining_fields); + + // FIXME: Emit an error: missing fields. + } + } + } + } + fn demand_scrutinee_type( &mut self, scrut: ExprId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/opaques.rs index a39288721b3e1..02bce22d6d4cb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/opaques.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/opaques.rs @@ -68,7 +68,7 @@ impl<'db> InferenceContext<'_, 'db> { mut opaque_types: Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)>, ) { for entry in opaque_types.iter_mut() { - *entry = self.table.infer_ctxt.resolve_vars_if_possible(*entry); + *entry = self.resolve_vars_if_possible(*entry); } debug!(?opaque_types); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 1d6a87a157b50..38b1388b9eb1e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -312,6 +312,10 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.shallow_resolve(ty) } + pub(crate) fn resolve_vars_if_possible>>(&self, t: T) -> T { + self.infer_ctxt.resolve_vars_if_possible(t) + } + pub(crate) fn resolve_vars_with_obligations(&mut self, t: T) -> T where T: rustc_type_ir::TypeFoldable>, diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 8dcc73d81f58a..25c2e3f733f75 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -583,4 +583,5 @@ define_symbols! { ref_pat_eat_one_layer_2024_structural, deref_patterns, mut_ref, + type_changing_struct_update, } From e1fb4a6cc378d282942a19240e33e9e58ec2254a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 12:43:06 +0300 Subject: [PATCH 057/289] Remove `unify()` Some usages were converted into `demand_eqtype()` which is the name in rustc and also reports diagnostics, and others should use a different method. --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 ---- .../rust-analyzer/crates/hir-ty/src/infer/expr.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/infer/path.rs | 2 +- .../crates/hir-ty/src/infer/unify.rs | 15 ++------------- 4 files changed, 4 insertions(+), 19 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 50809f46219e9..467a1a92ddc26 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1791,10 +1791,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.table.insert_type_vars(ty) } - fn unify(&mut self, ty1: Ty<'db>, ty2: Ty<'db>) -> bool { - self.table.unify(ty1, ty2) - } - /// Attempts to returns the deeply last field of nested structures, but /// does not apply any normalization in its search. Returns the same type /// if input `ty` is not a structure at all. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index db8ab67d2bdc5..90daa13576212 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1434,7 +1434,7 @@ impl<'db> InferenceContext<'_, 'db> { // NB: this should *not* coerce. // tail calls don't support any coercions except lifetimes ones (like `&'static u8 -> &'a u8`). - self.unify(call_expr_ty, ret_ty); + _ = self.demand_eqtype(expr.into(), call_expr_ty, ret_ty); } None => { // FIXME: diagnose `become` outside of functions diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 835721942a1cc..10a561215fb1b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -334,7 +334,7 @@ impl<'db> InferenceContext<'_, 'db> { let impl_substs = self.table.fresh_args_for_item(impl_id.into()); let impl_self_ty = self.db.impl_self_ty(impl_id).instantiate(self.interner(), impl_substs); - self.unify(impl_self_ty, ty); + _ = self.demand_eqtype(id, impl_self_ty, ty); impl_substs } ItemContainerId::TraitId(trait_) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 38b1388b9eb1e..4df198e002d74 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -20,8 +20,8 @@ use crate::{ TypingMode, fulfill::{FulfillmentCtxt, NextSolverError}, infer::{ - DbInternerInferExt, InferCtxt, InferOk, InferResult, - at::{At, ToTrace}, + DbInternerInferExt, InferCtxt, InferOk, + at::At, snapshot::CombinedSnapshot, traits::{Obligation, ObligationCause, PredicateObligation}, }, @@ -293,17 +293,6 @@ impl<'db> InferenceTable<'db> { value.fold_with(&mut resolve_completely::Resolver::new(self, true, &mut goals)) } - /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. - pub(crate) fn unify>(&mut self, ty1: T, ty2: T) -> bool { - self.try_unify(ty1, ty2).map(|infer_ok| self.register_infer_ok(infer_ok)).is_ok() - } - - /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the - /// caller needs to deal with them. - pub(crate) fn try_unify>(&mut self, t1: T, t2: T) -> InferResult<'db, ()> { - self.at(&ObligationCause::new()).eq(t1, t2) - } - pub(crate) fn at<'a>(&'a self, cause: &'a ObligationCause) -> At<'a, 'db> { self.infer_ctxt.at(cause, self.param_env) } From cc0376bb4b28add307e9d43ac8d6c381eb9de924 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 12:56:37 +0300 Subject: [PATCH 058/289] Remove unnecessary code --- .../crates/hir-ty/src/infer/expr.rs | 55 +++++-------------- .../crates/hir-ty/src/infer/path.rs | 2 +- 2 files changed, 14 insertions(+), 43 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 90daa13576212..f35dd8ae0be90 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -4,7 +4,7 @@ use std::{iter::repeat_with, mem}; use either::Either; use hir_def::{ - AdtId, FieldId, GenericDefId, ItemContainerId, Lookup, TupleFieldId, TupleId, VariantId, + AdtId, FieldId, TupleFieldId, TupleId, VariantId, expr_store::path::{GenericArgs as HirGenericArgs, Path}, hir::{ Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId, @@ -28,18 +28,16 @@ use tracing::debug; use crate::{ Adjust, Adjustment, CallableDefId, Rawness, consteval, - generics::generics, infer::{AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable, pat::PatOrigin}, - lower::{GenericPredicates, lower_mutability}, + lower::lower_mutability, method_resolution::{self, CandidateId, MethodCallee, MethodError}, next_solver::{ - ClauseKind, FnSig, GenericArg, GenericArgs, TraitRef, Ty, TyKind, TypeError, + ClauseKind, FnSig, GenericArg, GenericArgs, Ty, TyKind, TypeError, infer::{ BoundRegionConversionTime, InferOk, traits::{Obligation, ObligationCause}, }, obligation_ctxt::ObligationCtxt, - util::clauses_as_obligations, }, }; @@ -1732,7 +1730,7 @@ impl<'db> InferenceContext<'_, 'db> { MethodCallee { def_id, args, sig } } - fn check_call( + fn infer_method_call_as_call( &mut self, tgt_expr: ExprId, args: &[ExprId], @@ -1743,7 +1741,14 @@ impl<'db> InferenceContext<'_, 'db> { is_varargs: bool, expected: &Expectation<'db>, ) -> Ty<'db> { - self.register_obligations_for_call(callee_ty); + if let TyKind::FnDef(def_id, args) = callee_ty.kind() { + let def_id = match def_id.0 { + CallableDefId::FunctionId(it) => it.into(), + CallableDefId::StructId(it) => it.into(), + CallableDefId::EnumVariantId(it) => it.loc(self.db).parent.into(), + }; + self.add_required_obligations_for_value_path(def_id, args); + } self.check_call_arguments( tgt_expr, @@ -1849,7 +1854,7 @@ impl<'db> InferenceContext<'_, 'db> { }), }; match recovered { - Some((callee_ty, sig, strip_first)) => self.check_call( + Some((callee_ty, sig, strip_first)) => self.infer_method_call_as_call( tgt_expr, args, callee_ty, @@ -2137,40 +2142,6 @@ impl<'db> InferenceContext<'_, 'db> { if !args_count_matches {} } - fn register_obligations_for_call(&mut self, callable_ty: Ty<'db>) { - let callable_ty = self.table.try_structurally_resolve_type(callable_ty); - if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind() { - let generic_predicates = GenericPredicates::query_all( - self.db, - GenericDefId::from_callable(self.db, fn_def.0), - ); - let param_env = self.table.param_env; - self.table.register_predicates(clauses_as_obligations( - generic_predicates.iter_instantiated(self.interner(), parameters.as_slice()), - ObligationCause::new(), - param_env, - )); - // add obligation for trait implementation, if this is a trait method - match fn_def.0 { - CallableDefId::FunctionId(f) => { - if let ItemContainerId::TraitId(trait_) = f.lookup(self.db).container { - // construct a TraitRef - let trait_params_len = generics(self.db, trait_.into()).len(); - let substs = - GenericArgs::new_from_slice(¶meters.as_slice()[..trait_params_len]); - self.table.register_predicate(Obligation::new( - self.interner(), - ObligationCause::new(), - self.table.param_env, - TraitRef::new_from_args(self.interner(), trait_.into(), substs), - )); - } - } - CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {} - } - } - } - pub(super) fn with_breakable_ctx( &mut self, kind: BreakableKind, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 10a561215fb1b..176053b40240b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -226,7 +226,7 @@ impl<'db> InferenceContext<'_, 'db> { } } - fn add_required_obligations_for_value_path( + pub(super) fn add_required_obligations_for_value_path( &mut self, def: GenericDefId, subst: GenericArgs<'db>, From 7ec1e7366b1f03c5c8ecec700099601c58d842a5 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 13:04:33 +0300 Subject: [PATCH 059/289] Remove more unnecessary code The obligation `Type: ParentTrait` for a path used to be registered three(!) times. Leave it registered implicitly as one of the generic predicates of the item. --- .../crates/hir-ty/src/generics.rs | 5 --- .../crates/hir-ty/src/infer/path.rs | 40 ++----------------- 2 files changed, 4 insertions(+), 41 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index 822942eec37dd..b04128184413e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -131,11 +131,6 @@ impl<'db> Generics<'db> { self.parent_generics().map_or(0, Generics::len) } - /// Returns numbers of generic parameters excluding those from parent. - pub(crate) fn len_self(&self) -> usize { - self.params.len() - } - pub(crate) fn len_lifetimes_self(&self) -> usize { self.params.len_lifetimes() } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 176053b40240b..0687d56024d38 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -12,13 +12,11 @@ use stdx::never; use crate::{ InferenceDiagnostic, ValueTyDefId, - generics::generics, infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, lower::{GenericPredicates, LifetimeElisionKind}, method_resolution::{self, CandidateId, MethodError}, next_solver::{ - GenericArg, GenericArgs, TraitRef, Ty, - infer::traits::{Obligation, ObligationCause}, + GenericArg, GenericArgs, TraitRef, Ty, infer::traits::ObligationCause, util::clauses_as_obligations, }, }; @@ -239,25 +237,6 @@ impl<'db> InferenceContext<'_, 'db> { ObligationCause::new(), param_env, )); - - // We need to add `Self: Trait` obligation when `def` is a trait assoc item. - let container = match def { - GenericDefId::FunctionId(id) => id.lookup(self.db).container, - GenericDefId::ConstId(id) => id.lookup(self.db).container, - _ => return, - }; - - if let ItemContainerId::TraitId(trait_) = container { - let parent_len = generics(self.db, def).parent_generics().map_or(0, |g| g.len_self()); - let parent_subst = GenericArgs::new_from_slice(&subst.as_slice()[..parent_len]); - let trait_ref = TraitRef::new_from_args(interner, trait_.into(), parent_subst); - self.table.register_predicate(Obligation::new( - interner, - ObligationCause::new(), - param_env, - trait_ref, - )); - } } fn resolve_trait_assoc_item( @@ -339,20 +318,9 @@ impl<'db> InferenceContext<'_, 'db> { } ItemContainerId::TraitId(trait_) => { // we're picking this method - let args = GenericArgs::fill_rest( - self.interner(), - trait_.into(), - [ty.into()], - |_, id, _| self.table.next_var_for_param(id), - ); - let trait_ref = TraitRef::new_from_args(self.interner(), trait_.into(), args); - self.table.register_predicate(Obligation::new( - self.interner(), - ObligationCause::new(), - self.table.param_env, - trait_ref, - )); - args + GenericArgs::fill_rest(self.interner(), trait_.into(), [ty.into()], |_, id, _| { + self.table.next_var_for_param(id) + }) } ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => { never!("assoc item contained in module/extern block"); From a2db5c840672cf58f76106466b7aa9b80cf81616 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 14:16:46 +0300 Subject: [PATCH 060/289] Rewrite `await` inference - Do not eagerly normalize the `IntoFuture::Output` projection - Store `IntoFuture` and `IntoFuture::Output` in `LangItems`, to save computing them every time. They are not really lang items, but are similar in nature. --- .../crates/hir-def/src/lang_item.rs | 35 +++++++++++- .../rust-analyzer/crates/hir-ty/src/infer.rs | 54 ++----------------- .../crates/hir-ty/src/infer/expr.rs | 17 ++++-- 3 files changed, 50 insertions(+), 56 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 6071ed2981809..adc445c2a8083 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -7,8 +7,8 @@ use intern::{Symbol, sym}; use stdx::impl_from; use crate::{ - AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, MacroId, - ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, + AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, + ItemContainerId, MacroId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, attrs::AttrFlags, db::DefDatabase, nameres::{DefMap, assoc::TraitItems, crate_def_map, crate_local_def_map}, @@ -102,6 +102,8 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option Option> { let mut traits = Vec::new(); @@ -221,6 +240,10 @@ macro_rules! language_item_table { @non_lang_core_macros: $( core::$($non_lang_macro_module:ident)::*, $non_lang_macro:ident, $non_lang_macro_field:ident; )* + + @resolve_manually: + + $( $resolve_manually:ident, $resolve_manually_type:ident; )* ) => { #[allow(non_snake_case)] // FIXME: Should we remove this? #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] @@ -235,6 +258,9 @@ macro_rules! language_item_table { $( pub $non_lang_macro_field: Option, )* + $( + pub $resolve_manually: Option<$resolve_manually_type>, + )* } impl LangItems { @@ -247,6 +273,7 @@ macro_rules! language_item_table { $( self.$lang_item = self.$lang_item.or(other.$lang_item); )* $( self.$non_lang_trait = self.$non_lang_trait.or(other.$non_lang_trait); )* $( self.$non_lang_macro_field = self.$non_lang_macro_field.or(other.$non_lang_macro_field); )* + $( self.$resolve_manually = self.$resolve_manually.or(other.$resolve_manually); )* } fn assign_lang_item(&mut self, name: Symbol, target: LangItemTarget) { @@ -538,4 +565,8 @@ language_item_table! { LangItems => core::marker, CoercePointee, CoercePointeeDerive; core::marker, Copy, CopyDerive; core::clone, Clone, CloneDerive; + + @resolve_manually: + IntoFuture, TraitId; + IntoFutureOutput, TypeAliasId; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 467a1a92ddc26..fd0612e066497 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -35,8 +35,8 @@ use base_db::{Crate, FxIndexMap}; use either::Either; use hir_def::{ AdtId, AssocItemId, AttrDefId, ConstId, ConstParamId, DefWithBodyId, ExpressionStoreOwnerId, - FieldId, FunctionId, GenericDefId, GenericParamId, HasModule, ItemContainerId, LocalFieldId, - Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, TypeOrConstParamId, VariantId, + FieldId, FunctionId, GenericDefId, GenericParamId, HasModule, LocalFieldId, Lookup, TraitId, + TupleFieldId, TupleId, TypeOrConstParamId, VariantId, attrs::AttrFlags, expr_store::{Body, ExpressionStore, HygieneId, RootExprOrigin, path::Path}, hir::{BindingId, ExprId, ExprOrPatId, LabelId, PatId}, @@ -49,7 +49,6 @@ use hir_def::{ }; use hir_expand::{mod_path::ModPath, name::Name}; use indexmap::IndexSet; -use intern::sym; use la_arena::ArenaMap; use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; @@ -83,8 +82,8 @@ use crate::{ }, method_resolution::CandidateId, next_solver::{ - AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Region, - StoredGenericArgs, StoredTy, StoredTys, Ty, TyKind, Tys, + AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArgs, Region, StoredGenericArgs, + StoredTy, StoredTys, Ty, TyKind, Tys, abi::Safety, infer::{InferCtxt, ObligationInspector, traits::ObligationCause}, }, @@ -1871,14 +1870,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.table.resolve_vars_if_possible(t) } - fn resolve_associated_type( - &mut self, - inner_ty: Ty<'db>, - assoc_ty: Option, - ) -> Ty<'db> { - self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[]) - } - fn demand_eqtype( &mut self, id: ExprOrPatId, @@ -1974,30 +1965,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { ty.unwrap_or_else(|| self.expr_ty(e)) } - fn resolve_associated_type_with_params( - &mut self, - inner_ty: Ty<'db>, - assoc_ty: Option, - // FIXME(GATs): these are args for the trait ref, args for assoc type itself should be - // handled when we support them. - params: &[GenericArg<'db>], - ) -> Ty<'db> { - match assoc_ty { - Some(res_assoc_ty) => { - let alias = Ty::new_alias( - self.interner(), - AliasTy::new( - self.interner(), - AliasTyKind::Projection { def_id: res_assoc_ty.into() }, - iter::once(inner_ty.into()).chain(params.iter().copied()), - ), - ); - self.table.try_structurally_resolve_type(alias) - } - None => self.err_ty(), - } - } - fn resolve_variant( &mut self, node: ExprOrPatId, @@ -2323,19 +2290,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } } - fn resolve_output_on(&self, trait_: TraitId) -> Option { - trait_.trait_items(self.db).associated_type_by_name(&Name::new_symbol_root(sym::Output)) - } - - fn resolve_future_future_output(&self) -> Option { - let ItemContainerId::TraitId(trait_) = - self.lang_items.IntoFutureIntoFuture?.lookup(self.db).container - else { - return None; - }; - self.resolve_output_on(trait_) - } - fn resolve_boxed_box(&self) -> Option { let struct_ = self.lang_items.OwnedBox?; Some(struct_.into()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index f35dd8ae0be90..73d81ad16ec43 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -601,10 +601,7 @@ impl<'db> InferenceContext<'_, 'db> { self.infer_record_expr(tgt_expr, expected, path, fields, *spread) } Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected), - Expr::Await { expr } => { - let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes); - self.resolve_associated_type(inner_ty, self.resolve_future_future_output()) - } + Expr::Await { expr } => self.infer_await_expr(*expr), Expr::Cast { expr, type_ref } => { let cast_ty = self.make_body_ty(*type_ref); let expr_ty = @@ -971,6 +968,18 @@ impl<'db> InferenceContext<'_, 'db> { ty } + fn infer_await_expr(&mut self, awaitee: ExprId) -> Ty<'db> { + let awaitee_ty = self.infer_expr_no_expect(awaitee, ExprIsRead::Yes); + let (Some(into_future), Some(into_future_output)) = + (self.lang_items.IntoFuture, self.lang_items.IntoFutureOutput) + else { + return self.types.types.error; + }; + self.table.register_bound(awaitee_ty, into_future, ObligationCause::new()); + // Do not eagerly normalize. + Ty::new_projection(self.interner(), into_future_output.into(), [awaitee_ty]) + } + fn infer_record_expr( &mut self, expr: ExprId, From c96e72f0d02aea9f80f98dda412229fa3194c956 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 14:50:01 +0300 Subject: [PATCH 061/289] Do not save the `FulfillmentCtxt` in snapshots This is what rustc does. It's more performant, but we couldn't do that so far because old code relied on also restoring the obligations, i.e. it was registering maybe-incorrect obligations in the `InferenceTable` without first checking them in a separate `ObligationCtxt`. That code is now gone. --- .../crates/hir-ty/src/infer/coerce.rs | 6 ++--- .../crates/hir-ty/src/infer/unify.rs | 22 +++++-------------- .../crates/hir-ty/src/next_solver/fulfill.rs | 19 +++++----------- .../src/next_solver/infer/snapshot/mod.rs | 6 ++--- 4 files changed, 17 insertions(+), 36 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index db912c0eb6c78..962fc752a5eb3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -152,10 +152,8 @@ where let snapshot = self.infcx().start_snapshot(); let result = f(self); match result { - Ok(_) => {} - Err(_) => { - self.infcx().rollback_to(snapshot); - } + Ok(_) => self.infcx().commit_from(snapshot), + Err(_) => self.infcx().rollback_to(snapshot), } result } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 4df198e002d74..c28fea0ff17be 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -130,11 +130,6 @@ pub(crate) struct InferenceTable<'db> { pub(super) diverging_type_vars: FxHashSet>, } -pub(crate) struct InferenceTableSnapshot<'db> { - ctxt_snapshot: CombinedSnapshot, - obligations: FulfillmentCtxt<'db>, -} - impl<'db> InferenceTable<'db> { /// Inside hir-ty you should use this for inference only, and always pass `owner`. /// Outside it, always pass `owner = None`. @@ -353,16 +348,13 @@ impl<'db> InferenceTable<'db> { // FIXME: Err if it still contain infer vars. } - pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot<'db> { - let ctxt_snapshot = self.infer_ctxt.start_snapshot(); - let obligations = self.fulfillment_cx.clone(); - InferenceTableSnapshot { ctxt_snapshot, obligations } + pub(crate) fn snapshot(&mut self) -> CombinedSnapshot { + self.infer_ctxt.start_snapshot() } #[tracing::instrument(skip_all)] - pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot<'db>) { - self.infer_ctxt.rollback_to(snapshot.ctxt_snapshot); - self.fulfillment_cx = snapshot.obligations; + pub(crate) fn rollback_to(&mut self, snapshot: CombinedSnapshot) { + self.infer_ctxt.rollback_to(snapshot); } pub(crate) fn commit_if_ok( @@ -372,10 +364,8 @@ impl<'db> InferenceTable<'db> { let snapshot = self.snapshot(); let result = f(self); match result { - Ok(_) => {} - Err(_) => { - self.rollback_to(snapshot); - } + Ok(_) => self.infer_ctxt.commit_from(snapshot), + Err(_) => self.rollback_to(snapshot), } result } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index 6739795a0060d..cd0cb597602c3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -44,7 +44,6 @@ pub struct FulfillmentCtxt<'db> { /// outside of this snapshot leads to subtle bugs if the snapshot /// gets rolled back. Because of this we explicitly check that we only /// use the context in exactly this snapshot. - #[expect(unused)] usable_in_snapshot: usize, try_evaluate_obligations_scratch: PendingObligations<'db>, } @@ -120,24 +119,22 @@ impl<'db> FulfillmentCtxt<'db> { } impl<'db> FulfillmentCtxt<'db> { - #[tracing::instrument(level = "trace", skip(self, _infcx))] + #[tracing::instrument(level = "trace", skip(self, infcx))] pub(crate) fn register_predicate_obligation( &mut self, - _infcx: &InferCtxt<'db>, + infcx: &InferCtxt<'db>, obligation: PredicateObligation<'db>, ) { - // FIXME: See the comment in `try_evaluate_obligations()`. - // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); self.obligations.register(obligation, None); } pub(crate) fn register_predicate_obligations( &mut self, - _infcx: &InferCtxt<'db>, + infcx: &InferCtxt<'db>, obligations: impl IntoIterator>, ) { - // FIXME: See the comment in `try_evaluate_obligations()`. - // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); obligations.into_iter().for_each(|obligation| self.obligations.register(obligation, None)); } @@ -157,11 +154,7 @@ impl<'db> FulfillmentCtxt<'db> { &mut self, infcx: &InferCtxt<'db>, ) -> Vec> { - // FIXME(next-solver): We should bring this assertion back. Currently it panics because - // there are places which use `InferenceTable` and open a snapshot and register obligations - // and select. They should use a different `ObligationCtxt` instead. Then we'll be also able - // to not put the obligations queue in `InferenceTable`'s snapshots. - // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); self.try_evaluate_obligations_scratch.clear(); let mut errors = Vec::new(); loop { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs index 705aa43fb1199..39c8a37adbdbd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs @@ -47,7 +47,7 @@ impl<'db> InferCtxt<'db> { UndoLogs::>::num_open_snapshots(&self.inner.borrow_mut().undo_log) } - pub(crate) fn start_snapshot(&self) -> CombinedSnapshot { + pub fn start_snapshot(&self) -> CombinedSnapshot { debug!("start_snapshot()"); let mut inner = self.inner.borrow_mut(); @@ -60,7 +60,7 @@ impl<'db> InferCtxt<'db> { } #[instrument(skip(self, snapshot), level = "debug")] - pub(crate) fn rollback_to(&self, snapshot: CombinedSnapshot) { + pub fn rollback_to(&self, snapshot: CombinedSnapshot) { let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe } = snapshot; self.universe.set(universe); @@ -71,7 +71,7 @@ impl<'db> InferCtxt<'db> { } #[instrument(skip(self, snapshot), level = "debug")] - fn commit_from(&self, snapshot: CombinedSnapshot) { + pub fn commit_from(&self, snapshot: CombinedSnapshot) { let CombinedSnapshot { undo_snapshot, region_constraints_snapshot: _, universe: _ } = snapshot; From 2294347a5c4c36b230a8abf7eba5fc365748d6de Mon Sep 17 00:00:00 2001 From: Amit Singhmar Date: Wed, 22 Apr 2026 01:03:07 +0000 Subject: [PATCH 062/289] feat: add .new postfix completion based on expected type (rust-lang/rust-analyzer#20851) --- .../ide-completion/src/completions/postfix.rs | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) 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 82baf885ddc68..5fec7ac3f7089 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 @@ -3,7 +3,7 @@ mod format_like; use base_db::SourceDatabase; -use hir::{ItemInNs, Semantics}; +use hir::{HirDisplay, ItemInNs, Semantics}; use ide_db::{ RootDatabase, SnippetCap, documentation::{Documentation, HasDocs}, @@ -117,6 +117,34 @@ pub(crate) fn complete_postfix( postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")) .add_to(acc, ctx.db); + if let Some(expected_ty) = ctx.expected_type.as_ref() { + let is_valid_new = expected_ty + .iterate_assoc_items(ctx.db, |item| { + if let hir::AssocItem::Function(func) = item + && func.name(ctx.db).as_str() == "new" + && !func.has_self_param(ctx.db) + { + let params = func.params_without_self(ctx.db); + if params.len() == 1 { + return Some(()); + } + } + None + }) + .is_some(); + + if is_valid_new { + let ty_name = expected_ty.display(ctx.db, ctx.display_target).to_string(); + + postfix_snippet( + "new", + &format!("{}::new(expr)", ty_name), + &format!("{}::new({}$0)", ty_name, receiver_text), + ) + .add_to(acc, ctx.db); + } + } + let try_enum = TryEnum::from_ty(&ctx.sema, receiver_ty); let is_in_cond = is_in_condition(&dot_receiver_including_refs); if let Some(parent) = dot_receiver_including_refs.syntax().parent() { @@ -1558,6 +1586,37 @@ fn foo(x: Option, y: Option) { .map(|it| it+2); }; } +"#, + ); + } + + #[test] + fn postfix_new() { + check_edit( + "new", + r#" +struct OtherThing; +struct RefCell(T); +impl RefCell { + fn new(t: T) -> Self { RefCell(t) } +} + +fn main() { + let other_thing = OtherThing; + let thing: RefCell = other_thing.$0; +} +"#, + r#" +struct OtherThing; +struct RefCell(T); +impl RefCell { + fn new(t: T) -> Self { RefCell(t) } +} + +fn main() { + let other_thing = OtherThing; + let thing: RefCell = RefCell::new(other_thing$0); +} "#, ); } From 09895c8dfae85c0b5fbae82eb8341793cca017cf Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 23 Apr 2026 13:21:26 +0800 Subject: [PATCH 063/289] internal: remove redundant str::to_owned in postfix --- .../ide-completion/src/completions/postfix.rs | 89 +++++++++---------- .../src/completions/postfix/format_like.rs | 2 +- 2 files changed, 45 insertions(+), 46 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 ee529d888852a..0a2e15edae9d2 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 @@ -84,15 +84,15 @@ pub(crate) fn complete_postfix( let mut item = postfix_snippet( "drop", "fn drop(&mut self)", - &format!("{path}($0{receiver_text})", path = path.display(ctx.db, ctx.edition)), + format!("{path}($0{receiver_text})", path = path.display(ctx.db, ctx.edition)), ); item.set_documentation(drop_fn.docs(ctx.db)); item.add_to(acc, ctx.db); } - postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc, ctx.db); - postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc, ctx.db); - postfix_snippet("deref", "*expr", &format!("*{receiver_text}")).add_to(acc, ctx.db); + postfix_snippet("ref", "&expr", format!("&{receiver_text}")).add_to(acc, ctx.db); + postfix_snippet("refm", "&mut expr", format!("&mut {receiver_text}")).add_to(acc, ctx.db); + postfix_snippet("deref", "*expr", format!("*{receiver_text}")).add_to(acc, ctx.db); // The rest of the postfix completions create an expression that moves an argument, // so it's better to consider references now to avoid breaking the compilation @@ -110,11 +110,11 @@ pub(crate) fn complete_postfix( add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text); } - postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})")) + postfix_snippet("box", "Box::new(expr)", format!("Box::new({receiver_text})")) .add_to(acc, ctx.db); - postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme - postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db); - postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")) + postfix_snippet("dbg", "dbg!(expr)", format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme + postfix_snippet("dbgr", "dbg!(&expr)", format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db); + postfix_snippet("call", "function(expr)", format!("${{1}}({receiver_text})")) .add_to(acc, ctx.db); if let Some(expected_ty) = ctx.expected_type.as_ref() { @@ -134,12 +134,12 @@ pub(crate) fn complete_postfix( .is_some(); if is_valid_new { - let ty_name = expected_ty.display(ctx.db, ctx.display_target).to_string(); + let ty_name = expected_ty.display(ctx.db, ctx.display_target).to_smolstr(); postfix_snippet( "new", - &format!("{}::new(expr)", ty_name), - &format!("{}::new({}$0)", ty_name, receiver_text), + &format_smolstr!("{}::new(expr)", ty_name), + format!("{}::new({}$0)", ty_name, receiver_text), ) .add_to(acc, ctx.db); } @@ -155,13 +155,13 @@ pub(crate) fn complete_postfix( postfix_snippet( "let", "let Ok(_)", - &format!("let Ok({placeholder}) = {receiver_text}"), + format!("let Ok({placeholder}) = {receiver_text}"), ) .add_to(acc, ctx.db); postfix_snippet( "letm", "let Ok(mut _)", - &format!("let Ok(mut {placeholder}) = {receiver_text}"), + format!("let Ok(mut {placeholder}) = {receiver_text}"), ) .add_to(acc, ctx.db); } @@ -169,38 +169,38 @@ pub(crate) fn complete_postfix( postfix_snippet( "let", "let Some(_)", - &format!("let Some({placeholder}) = {receiver_text}"), + format!("let Some({placeholder}) = {receiver_text}"), ) .add_to(acc, ctx.db); postfix_snippet( "letm", "let Some(mut _)", - &format!("let Some(mut {placeholder}) = {receiver_text}"), + format!("let Some(mut {placeholder}) = {receiver_text}"), ) .add_to(acc, ctx.db); } }, _ if is_in_cond => { - postfix_snippet("let", "let", &format!("let $1 = {receiver_text}")) + postfix_snippet("let", "let", format!("let $1 = {receiver_text}")) .add_to(acc, ctx.db); } _ if matches!(parent.kind(), STMT_LIST | EXPR_STMT) => { - postfix_snippet("let", "let", &format!("let $0 = {receiver_text}{semi}")) + postfix_snippet("let", "let", format!("let $0 = {receiver_text}{semi}")) .add_to(acc, ctx.db); - postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text}{semi}")) + postfix_snippet("letm", "let mut", format!("let mut $0 = {receiver_text}{semi}")) .add_to(acc, ctx.db); } _ if matches!(parent.kind(), MATCH_ARM | CLOSURE_EXPR) => { postfix_snippet( "let", "let", - &format!("{{\n let $1 = {receiver_text};\n $0\n}}"), + format!("{{\n let $1 = {receiver_text};\n $0\n}}"), ) .add_to(acc, ctx.db); postfix_snippet( "letm", "let mut", - &format!("{{\n let mut $1 = {receiver_text};\n $0\n}}"), + format!("{{\n let mut $1 = {receiver_text};\n $0\n}}"), ) .add_to(acc, ctx.db); } @@ -215,7 +215,7 @@ pub(crate) fn complete_postfix( postfix_snippet( "match", "match expr {}", - &format!("match {receiver_text} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}"), + format!("match {receiver_text} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}"), ) .add_to(acc, ctx.db); } @@ -223,7 +223,7 @@ pub(crate) fn complete_postfix( postfix_snippet( "match", "match expr {}", - &format!( + format!( "match {receiver_text} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}" ), ) @@ -234,7 +234,7 @@ pub(crate) fn complete_postfix( postfix_snippet( "match", "match expr {}", - &format!("match {receiver_text} {{\n ${{1:_}} => {{$0}},\n}}"), + format!("match {receiver_text} {{\n ${{1:_}} => {{$0}},\n}}"), ) .add_to(acc, ctx.db); } @@ -246,23 +246,21 @@ pub(crate) fn complete_postfix( postfix_snippet( "ifl", "if let Ok {}", - &format!("if let Ok({placeholder}) = {receiver_text} {{\n $0\n}}"), + format!("if let Ok({placeholder}) = {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); postfix_snippet( "lete", "let Ok else {}", - &format!( - "let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0" - ), + format!("let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0"), ) .add_to(acc, ctx.db); postfix_snippet( "while", "while let Ok {}", - &format!("while let Ok({placeholder}) = {receiver_text} {{\n $0\n}}"), + format!("while let Ok({placeholder}) = {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); } @@ -270,14 +268,14 @@ pub(crate) fn complete_postfix( postfix_snippet( "ifl", "if let Some {}", - &format!("if let Some({placeholder}) = {receiver_text} {{\n $0\n}}"), + format!("if let Some({placeholder}) = {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); postfix_snippet( "lete", "let Some else {}", - &format!( + format!( "let Some({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0" ), ) @@ -286,18 +284,18 @@ pub(crate) fn complete_postfix( postfix_snippet( "while", "while let Some {}", - &format!("while let Some({placeholder}) = {receiver_text} {{\n $0\n}}"), + format!("while let Some({placeholder}) = {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); } } } else if receiver_ty.is_bool() || receiver_ty.is_unknown() { - postfix_snippet("if", "if expr {}", &format!("if {receiver_text} {{\n $0\n}}")) + postfix_snippet("if", "if expr {}", format!("if {receiver_text} {{\n $0\n}}")) .add_to(acc, ctx.db); postfix_snippet( "while", "while expr {}", - &format!("while {receiver_text} {{\n $0\n}}"), + format!("while {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() @@ -306,14 +304,14 @@ pub(crate) fn complete_postfix( postfix_snippet( "for", "for ele in expr {}", - &format!("for ele in {receiver_text} {{\n $0\n}}"), + format!("for ele in {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); } } if receiver_ty.is_bool() || receiver_ty.is_unknown() { - postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db); + postfix_snippet("not", "!expr", format!("!{receiver_text}")).add_to(acc, ctx.db); } let block_should_be_wrapped = if let ast::Expr::BlockExpr(block) = dot_receiver { @@ -328,11 +326,11 @@ pub(crate) fn complete_postfix( let (open_paren, close_paren) = if is_in_cond { ("(", ")") } else { ("", "") }; let unsafe_completion_string = format!("{open_paren}unsafe {open_brace}{receiver_text}{close_brace}{close_paren}"); - postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db); + postfix_snippet("unsafe", "unsafe {}", unsafe_completion_string).add_to(acc, ctx.db); let const_completion_string = format!("{open_paren}const {open_brace}{receiver_text}{close_brace}{close_paren}"); - postfix_snippet("const", "const {}", &const_completion_string).add_to(acc, ctx.db); + postfix_snippet("const", "const {}", const_completion_string).add_to(acc, ctx.db); } if let ast::Expr::Literal(literal) = dot_receiver.clone() @@ -341,11 +339,11 @@ pub(crate) fn complete_postfix( add_format_like_completions(acc, ctx, dot_receiver, cap, &literal_text, semi); } - postfix_snippet("return", "return expr", &format!("return {receiver_text}{semi}")) + postfix_snippet("return", "return expr", format!("return {receiver_text}{semi}")) .add_to(acc, ctx.db); if let Some(BreakableKind::Block | BreakableKind::Loop) = expr_ctx.in_breakable { - postfix_snippet("break", "break expr", &format!("break {receiver_text}{semi}")) + postfix_snippet("break", "break expr", format!("break {receiver_text}{semi}")) .add_to(acc, ctx.db); } } @@ -476,7 +474,7 @@ fn build_postfix_snippet_builder<'ctx>( ctx: &'ctx CompletionContext<'_>, cap: SnippetCap, receiver: &'ctx ast::Expr, -) -> Option Builder + 'ctx> { +) -> Option Builder + 'ctx> { let receiver_range = ctx.sema.original_range_opt(receiver.syntax())?.range; if ctx.source_range().end() < receiver_range.start() { // This shouldn't happen, yet it does. I assume this might be due to an incorrect token @@ -492,9 +490,9 @@ fn build_postfix_snippet_builder<'ctx>( ctx: &'ctx CompletionContext<'_>, cap: SnippetCap, delete_range: TextRange, - ) -> impl Fn(&str, &str, &str) -> Builder + 'ctx { + ) -> impl Fn(&str, &str, String) -> Builder + 'ctx { move |label, detail, snippet| { - let edit = TextEdit::replace(delete_range, snippet.to_owned()); + let edit = TextEdit::replace(delete_range, snippet); let mut item = CompletionItem::new( CompletionItemKind::Snippet, ctx.source_range(), @@ -520,7 +518,7 @@ fn build_postfix_snippet_builder<'ctx>( fn add_custom_postfix_completions( acc: &mut Completions, ctx: &CompletionContext<'_>, - postfix_snippet: impl Fn(&str, &str, &str) -> Builder, + postfix_snippet: impl Fn(&str, &str, String) -> Builder, receiver_text: &str, ) -> Option<()> { ImportScope::find_insert_use_container(&ctx.token.parent()?, &ctx.sema)?; @@ -531,9 +529,10 @@ fn add_custom_postfix_completions( None => return, }; let body = snippet.postfix_snippet(receiver_text); + let document = Documentation::new_owned(format!("```rust\n{body}\n```")); let mut builder = - postfix_snippet(trigger, snippet.description.as_deref().unwrap_or_default(), &body); - builder.documentation(Documentation::new_owned(format!("```rust\n{body}\n```"))); + postfix_snippet(trigger, snippet.description.as_deref().unwrap_or_default(), body); + builder.documentation(document); for import in imports.into_iter() { builder.add_import(import); } 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 85a8899fd10b0..6b0e9f31c4a47 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 @@ -73,7 +73,7 @@ pub(crate) fn add_format_like_completions( format!(r#"{}({}, {}){semi}"#, macro_name, out, exprs.join(", ")) }; - postfix_snippet(label, macro_name, &snippet).add_to(acc, ctx.db); + postfix_snippet(label, macro_name, snippet).add_to(acc, ctx.db); } } } From afb96b35ca8932b0fecc874e125bfce2f8f7d726 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 16 Apr 2026 09:59:03 +0800 Subject: [PATCH 064/289] fix: Improve prettify_macro_expansion() - Add basic tests for prettify_macro_expansion Example --- ```rust const _: () = { loop {break} foo() }; ``` **Before this PR** ```rust const _:() = { loop{ break }foo() }; ``` **After this PR** ```rust const _: () = { loop { break } foo() }; ``` --- ```rust const _: () = { match 2 { tmp => foo!(), }; }; ``` **Before this PR** ```rust const _:() = { match 2 { tmp => foo!(), }; }; ``` **After this PR** ```rust const _: () = { match 2 { tmp => foo!(), }; }; ``` --- src/tools/rust-analyzer/Cargo.lock | 1 + .../ide-assists/src/handlers/inline_macro.rs | 2 +- .../src/completions/item_list/trait_impl.rs | 2 +- .../crates/ide/src/expand_macro.rs | 15 +- .../rust-analyzer/crates/mbe/src/tests.rs | 2 +- .../crates/syntax-bridge/Cargo.toml | 1 + .../src/prettify_macro_expansion.rs | 328 ++++++++++++++++-- 7 files changed, 316 insertions(+), 35 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index e6575c28c1dd0..92acf544ee354 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2744,6 +2744,7 @@ dependencies = [ name = "syntax-bridge" version = "0.0.0" dependencies = [ + "expect-test", "intern", "parser", "rustc-hash 2.1.1", 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 280bd7f2ca9ab..1cf5402db8173 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 @@ -258,7 +258,7 @@ macro_rules! whitespace { if true {} }; } -fn f() { if true{}; } +fn f() { if true {}; } "#, ) } 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 4072f05a41f42..9bac34885c0cb 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 @@ -1356,7 +1356,7 @@ noop! { struct Test; impl Foo for Test { - fn foo(&mut self,bar:i64,baz: &mut u32) -> Result<(),u32> { + fn foo(&mut self,bar: i64,baz: &mut u32) -> Result<(),u32> { $0 } } 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 6ac4fa1fba8ef..fe58786991688 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -357,7 +357,7 @@ fn main() { "#, expect![[r#" bar! - for _ in 0..42{}"#]], + for _ in 0..42 {}"#]], ); } @@ -433,9 +433,7 @@ fn main() { expect![[r#" match_ast! { - if let Some(it) = ast::TraitDef::cast(container.clone()){} - else if let Some(it) = ast::ImplDef::cast(container.clone()){} - else { + if let Some(it) = ast::TraitDef::cast(container.clone()){}else if let Some(it) = ast::ImplDef::cast(container.clone()){}else { { continue } @@ -594,7 +592,7 @@ struct Foo {} "#, expect![[r#" proc_macros::DeriveIdentity - struct Foo{}"#]], + struct Foo {}"#]], ); } @@ -612,7 +610,7 @@ struct Foo {} expect![[r#" proc_macros::DeriveIdentity #[derive(proc_macros::DeriveIdentity)] - struct Foo{}"#]], + struct Foo {}"#]], ); } @@ -628,7 +626,7 @@ struct Foo {} "#, expect![[r#" proc_macros::DeriveIdentity - struct Foo{}"#]], + struct Foo {}"#]], ); check( r#" @@ -640,7 +638,7 @@ struct Foo {} "#, expect![[r#" proc_macros::DeriveIdentity - struct Foo{}"#]], + struct Foo {}"#]], ); } @@ -783,7 +781,6 @@ foo(); macro_rules! foo { () => { fn item(){} - }; } foo();"#]], diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs index 4a1af31656ad5..271dfc877b475 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/tests.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/tests.rs @@ -237,7 +237,7 @@ fn expr_2021() { PUNCH ; [alone] 0:Root[0000, 0]@39..40#ROOT2024 _; - (const { + (const { 1 });"#]], ); diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml index b0fd40ff59f4b..c928f23e02e66 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml @@ -25,6 +25,7 @@ span = { path = "../span", version = "0.0.0", default-features = false} intern.workspace = true [dev-dependencies] +expect-test = "1.5.1" test-utils.workspace = true [features] diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs index 2f932e0458324..43f270800d821 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs @@ -45,17 +45,23 @@ pub fn prettify_macro_expansion( for event in syn.preorder_with_tokens() { let token = match event { WalkEvent::Enter(NodeOrToken::Token(token)) => token, - WalkEvent::Leave(NodeOrToken::Node(node)) - if matches!( - node.kind(), - ATTR | MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL | MACRO_RULES - ) => - { - if indent > 0 { - mods.push((Position::after(node.clone()), PrettifyWsKind::Indent(indent))); - } - if node.parent().is_some() { - mods.push((Position::after(node), PrettifyWsKind::Newline)); + WalkEvent::Leave(NodeOrToken::Node(node)) => { + let is_last_child = + node.parent().is_some_and(|parent| parent.last_child().as_ref() == Some(&node)); + let is_always_newline = matches!(node.kind(), ATTR); + let is_non_last_newline = match node.kind() { + MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL | MACRO_RULES | EXTERN_BLOCK + | EXTERN_CRATE | MODULE => true, + EXPR_STMT if Some(R_CURLY) == node.last_token().map(|it| it.kind()) => true, + _ => false, + }; + if !is_last_child && is_non_last_newline || is_always_newline { + if indent > 0 { + mods.push((Position::after(node.clone()), PrettifyWsKind::Indent(indent))); + } + if node.parent().is_some() { + mods.push((Position::after(node), PrettifyWsKind::Newline)); + } } continue; } @@ -77,16 +83,12 @@ pub fn prettify_macro_expansion( match tok.kind() { k if is_text(k) - && is_next(|it| !it.is_punct() || matches!(it, T![_] | T![#]), false) => + && is_next(|it| !it.is_punct() || matches!(it, T![_] | T![#] | L_CURLY), false) => { mods.push(do_ws(after, tok)); } L_CURLY if is_next(|it| it != R_CURLY, true) => { indent += 1; - if is_last(is_text, false) { - mods.push(do_ws(before, tok)); - } - mods.push(do_indent(after, tok, indent)); mods.push(do_nl(after, tok)); } @@ -98,16 +100,10 @@ pub fn prettify_macro_expansion( } mods.push(do_nl(before, tok)); } - R_CURLY => { - if indent > 0 { - mods.push(do_indent(after, tok, indent)); - } - mods.push(do_nl(after, tok)); - } LIFETIME_IDENT if is_next(is_text, true) => { mods.push(do_ws(after, tok)); } - AS_KW | DYN_KW | IMPL_KW | CONST_KW | MUT_KW => { + AS_KW | DYN_KW | IMPL_KW | CONST_KW | MUT_KW | LET_KW | MATCH_KW => { mods.push(do_ws(after, tok)); } T![;] if is_next(|it| it != R_CURLY, true) => { @@ -126,6 +122,12 @@ pub fn prettify_macro_expansion( mods.push(do_ws(before, tok)); mods.push(do_ws(after, tok)); } + T![:] if is_next(|it| it != T![:], false) && is_last(|it| it != T![:], false) => { + // XXX: Why input included WHITESPACE? + if is_next(|it| it != SyntaxKind::WHITESPACE, false) { + mods.push(do_ws(after, tok)); + } + } T![!] if is_last(|it| it == MACRO_RULES_KW, false) && is_next(is_text, false) => { mods.push(do_ws(after, tok)); } @@ -161,3 +163,283 @@ fn is_text(k: SyntaxKind) -> bool { // Consider all keywords in all editions. k.is_any_identifier() || k.is_literal() || k == UNDERSCORE } + +#[cfg(test)] +mod tests { + use super::*; + use expect_test::{Expect, expect}; + + #[expect(deprecated)] + fn check_pretty(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + let ra_fixture = stdx::trim_indent(ra_fixture); + let source_file = syntax::ast::SourceFile::parse(&ra_fixture, span::Edition::CURRENT); + let syn = source_file.syntax_node().clone_for_update(); + + remove_whitespaces(&syn); + let pretty = prettify_macro_expansion(syn, &mut |_| None, |_| ()); + let mut pretty = pretty.to_string(); + if pretty.contains('\n') { + pretty.push('\n'); + } + expect.assert_eq(&pretty); + + fn remove_whitespaces(node: &SyntaxNode) { + let mut to_remove = vec![]; + node.preorder_with_tokens().for_each(|it| match it { + WalkEvent::Enter(NodeOrToken::Token(tok)) if tok.kind().is_trivia() => { + to_remove.push(tok); + } + _ => (), + }); + to_remove.iter().for_each(ted::remove) + } + } + + #[test] + fn test_colon() { + check_pretty( + r#" + const X: i32 = x::y::z; + macro_rules! foo { + () => { + $crate::foo::bar!() + }; + } + "#, + expect![[r#" + const X: i32 = x::y::z; + macro_rules! foo { + () => { + $crate::foo::bar!() + }; + } + "#]], + ); + } + + #[test] + fn test_curly_indent() { + check_pretty( + r#" + const _: () = { + { + 2; + 3 + } + }; + "#, + expect![[r#" + const _: () = { + { + 2; + 3 + } + }; + "#]], + ); + } + + #[test] + fn test_pats() { + check_pretty( + r#" + const _: () = { + let x = 2; + let mut y = 3; + let ref mut z @ 0..5 = 4; + let (x, ref y) = (5, 6); + let (Foo { x, y }, Bar(z, t)); + let (&mut x, (y | y)); + match () {} + }; + "#, + expect![[r#" + const _: () = { + let x = 2; + let mut y = 3; + let ref mut z@0..5 = 4; + let (x,ref y) = (5,6); + let (Foo { + x,y + },Bar(z,t)); + let (&mut x,(y|y)); + match (){} + }; + "#]], + ); + } + + #[test] + fn test_attrs() { + check_pretty( + r#" + #[attr1] + #[attr2] + const _: () = {}; + #[attr1] + const _: () = { + #[attr2] + {} + }; + "#, + expect![[r#" + #[attr1] + #[attr2] + const _: () = {}; + #[attr1] + const _: () = { + #[attr2] + {} + }; + "#]], + ); + } + + #[test] + fn test_items() { + check_pretty( + r#" + fn foo() {} + struct Foo {} + struct Foo; + enum Foo {} + impl Foo {} + const _: () = {}; + static S: () = {}; + extern {} + mod x {} + mod x; + type X = 2; + use a; + use b::{c, d}; + macro_rules! foo { () => {}; } + "#, + expect![[r#" + fn foo(){} + struct Foo {} + struct Foo; + + enum Foo {} + impl Foo {} + const _: () = {}; + static S: () = {}; + extern {} + mod x {} + mod x; + + type X = 2; + use a; + use b::{ + c,d + }; + macro_rules! foo { + () => {}; + } + "#]], + ); + } + + #[test] + fn test_exprs() { + check_pretty( + r#" + const _: () = { + let _ = 1+2; + let _ = !true && false; + let _ = foo() + !bar() + dbg!(2) + *x; + let _ = async move || {}; + let _ = async move {}; + let _ = x.await; + 'lab: for _ in 0..5 { + loop { } + break 'lab expr; + if let pat = expr { + foo() + } else if true { + bar() + } else {} + fun() + } + }; + "#, + expect![[r#" + const _: () = { + let _ = 1+2; + let _ = !true&&false; + let _ = foo()+!bar()+dbg!(2)+*x; + let _ = async move||{}; + let _ = async move {}; + let _ = x.await; + 'lab: for _ in 0..5 { + loop {} + break 'lab expr; + if let pat = expr { + foo() + }else if true { + bar() + }else {} + fun() + } + }; + "#]], + ); + } + + #[test] + fn test_match_arm() { + check_pretty( + r#" + const _: () = { + match 2 { + tmp => foo!(), + }; + }; + "#, + expect![[r#" + const _: () = { + match 2 { + tmp => foo!(), + }; + }; + "#]], + ); + + check_pretty( + r#" + const _: () = { + match 2 { + tmp => {} + }; + }; + "#, + expect![[r#" + const _: () = { + match 2 { + tmp => {} + }; + }; + "#]], + ); + + check_pretty( + r#" + const _: () = { + match 2 { + 1 => {} + 2 => foo(), + _ => {}, + }; + }; + "#, + expect![[r#" + const _: () = { + match 2 { + 1 => {} + 2 => foo(), + _ => {}, + }; + }; + "#]], + ); + } +} From a5218d91e31736e3a328f0b3f459cc8cc2ff70a1 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 17 Apr 2026 09:39:50 +0800 Subject: [PATCH 065/289] Migrate `inline_macro` assist to SyntaxEditor --- .../crates/ide-assists/src/handlers/inline_macro.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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 1cf5402db8173..81f1a8a73f950 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 @@ -46,12 +46,14 @@ pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option "Inline macro".to_owned(), text_range, |builder| { + let editor = builder.make_editor(unexpanded.syntax()); let expanded = ctx.sema.parse_or_expand(macro_call.into()); let span_map = ctx.sema.db.expansion_span_map(macro_call); // 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); - builder.replace(text_range, expanded.to_string()) + editor.replace(unexpanded.syntax(), expanded); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } @@ -207,15 +209,15 @@ macro_rules! num { inline_macro, r#" macro_rules! foo { - () => {foo!()} + ($t:tt) => {foo!(1)} } -fn f() { let result = foo$0!(); } +fn f() { let result = foo$0!(0); } "#, r#" macro_rules! foo { - () => {foo!()} + ($t:tt) => {foo!(1)} } -fn f() { let result = foo!(); } +fn f() { let result = foo!(1); } "#, ); } From 64b565fbabecb12dfdebc1dddfd33b0dc4910084 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 17 Apr 2026 09:55:17 +0800 Subject: [PATCH 066/289] Indent 'inline_macro' expanded code --- .../ide-assists/src/handlers/inline_macro.rs | 15 ++++++++------- .../rust-analyzer/crates/syntax/src/ast/edit.rs | 4 ++++ 2 files changed, 12 insertions(+), 7 deletions(-) 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 81f1a8a73f950..ef9b5d093f364 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,6 +1,6 @@ use hir::db::ExpandDatabase; use ide_db::syntax_helpers::prettify_macro_expansion; -use syntax::ast::{self, AstNode}; +use syntax::ast::{self, AstNode, edit::AstNodeEdit}; use crate::{AssistContext, AssistId, Assists}; @@ -52,6 +52,7 @@ pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option // 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); + let expanded = ast::edit::indent(&expanded, unexpanded.indent_level()); editor.replace(unexpanded.syntax(), expanded); builder.add_file_edits(ctx.vfs_file_id(), editor); }, @@ -299,12 +300,12 @@ macro_rules! foo { } fn main() { cfg_if!{ - if #[cfg(test)]{ - 1; - }else { - 1; - } -}; + if #[cfg(test)]{ + 1; + }else { + 1; + } + }; } "#, ); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs index b20aa90d06f23..0ffcbd212b3a5 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs @@ -252,6 +252,10 @@ impl ast::IdentPat { } } +pub fn indent(node: &SyntaxNode, level: IndentLevel) -> SyntaxNode { + level.clone_increase_indent(node) +} + #[test] fn test_increase_indent() { let arm_list = { From 0aed24875499703600bef444e5122cacf9bc177f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 23 Apr 2026 13:46:39 +0800 Subject: [PATCH 067/289] minor: offer without bound list for replace_named_generic_with_impl Example --- ```rust fn foo(input: T) {} ``` **Before this PR** Assist not applicable **After this PR** ```rust fn foo(input: impl Sized) {} ``` --- .../src/handlers/replace_named_generic_with_impl.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index 17ef7727eca39..43726830161e7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -34,7 +34,7 @@ pub(crate) fn replace_named_generic_with_impl( let type_param_name = type_param.name()?; // The list of type bounds / traits: `AsRef` - let type_bound_list = type_param.type_bound_list()?; + let type_bound_list = type_param.type_bound_list(); let fn_ = type_param.syntax().ancestors().find_map(ast::Fn::cast)?; let param_list_text_range = fn_.param_list()?.syntax().text_range(); @@ -89,6 +89,8 @@ pub(crate) fn replace_named_generic_with_impl( } } + let type_bound_list = type_bound_list + .unwrap_or_else(|| make.type_bound_list([make.type_bound_text("Sized")]).unwrap()); let new_bounds = make.impl_trait_type(type_bound_list); for path_type in path_types_to_replace.iter().rev() { editor.replace(path_type.syntax(), new_bounds.syntax()); @@ -312,6 +314,15 @@ mod tests { ); } + #[test] + fn replace_generic_without_bounds() { + check_assist( + replace_named_generic_with_impl, + r#"fn foo(input: T) {}"#, + r#"fn foo(input: impl Sized) {}"#, + ); + } + #[test] fn replace_generic_with_multiple_trait_bounds() { check_assist( From f0bb54517db6b4774c2ec271bd36a8fb52db7d14 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 23 Apr 2026 14:08:31 +0800 Subject: [PATCH 068/289] minor: filter default bounds for introduce_named_type_parameter Example --- ```rust fn foo(bar: $0impl Sized) {} ``` **Before this PR** ```rust fn foo<$0S: Sized>(bar: S) {} ``` **After this PR** ```rust fn foo<$0S>(bar: S) {} ``` --- .../handlers/introduce_named_type_parameter.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs index 95f223420b0b8..c8ae13a83328c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs @@ -48,7 +48,8 @@ pub(crate) fn introduce_named_type_parameter( ) .for_impl_trait_as_generic(&impl_trait_type); - let type_param = make.type_param(make.name(&type_param_name), Some(type_bound_list)); + let type_bound_list = non_default_bounds(&type_bound_list).then_some(type_bound_list); + let type_param = make.type_param(make.name(&type_param_name), type_bound_list); let new_ty = make.ty(&type_param_name); editor.replace(impl_trait_type.syntax(), new_ty.syntax()); @@ -63,6 +64,10 @@ pub(crate) fn introduce_named_type_parameter( ) } +fn non_default_bounds(bounds: &ast::TypeBoundList) -> bool { + bounds.bounds().collect_array().is_none_or(|[bound]| bound.syntax().text() != "Sized") +} + #[cfg(test)] mod tests { use super::*; @@ -168,6 +173,15 @@ fn foo< ); } + #[test] + fn replace_impl_default_bounds() { + check_assist( + introduce_named_type_parameter, + r#"fn foo(bar: $0impl Sized) {}"#, + r#"fn foo<$0S>(bar: S) {}"#, + ); + } + #[test] fn replace_impl_with_mut() { check_assist( From fc3568ddf6f11d315419453a82d2bb030b5af76e Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 23 Apr 2026 14:45:15 +0800 Subject: [PATCH 069/289] fix: offer on `is_some_and` etc for apply_demorgan_iterator Example --- ```rust fn main() { let cond = Some(2); if !cond.$0is_some_and(|num| num > 3) { println!("foo"); } } ``` **Before this PR** Assist not applicable **After this PR** ```rust fn main() { let cond = Some(2); if cond.is_none_or(|num| num <= 3) { println!("foo"); } } ``` --- .../src/handlers/apply_demorgan.rs | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index b87a757047ac5..9a414ba235203 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -210,6 +210,8 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_> let new_name = match name.text().as_str() { "all" => make.name_ref("any"), "any" => make.name_ref("all"), + "is_some_and" => make.name_ref("is_none_or"), + "is_none_or" => make.name_ref("is_some_and"), _ => unreachable!(), }; editor.replace(name.syntax(), new_name.syntax()); @@ -249,10 +251,13 @@ fn validate_method_call_expr( method_call: &ast::MethodCallExpr, ) -> Option<(ast::NameRef, ast::Expr)> { let name_ref = method_call.name_ref()?; + let arg_expr = method_call.arg_list()?.args().next()?; + if name_ref.text() == "is_some_and" || name_ref.text() == "is_none_or" { + return Some((name_ref, arg_expr)); + } if name_ref.text() != "all" && name_ref.text() != "any" { return None; } - let arg_expr = method_call.arg_list()?.args().next()?; let sema = &ctx.sema; @@ -643,6 +648,51 @@ fn main() { ); } + #[test] + fn demorgan_option_is_some_and() { + check_assist( + apply_demorgan_iterator, + r#" +//- minicore: option +fn main() { + let cond = Some(2); + if !cond.$0is_some_and(|num| num > 3) { + println!("foo"); + } +} +"#, + r#" +fn main() { + let cond = Some(2); + if cond.is_none_or(|num| num <= 3) { + println!("foo"); + } +} +"#, + ); + + check_assist( + apply_demorgan_iterator, + r#" +//- minicore: option +fn main() { + let cond = Some(2); + if !cond.$0is_none_or(|num| num > 3) { + println!("foo"); + } +} +"#, + r#" +fn main() { + let cond = Some(2); + if cond.is_some_and(|num| num <= 3) { + println!("foo"); + } +} +"#, + ); + } + #[test] fn demorgan_method_call_receiver() { check_assist( From 42bffca3cbdafde6ac7063048a8d78d9980ba329 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 23 Apr 2026 15:00:17 +0800 Subject: [PATCH 070/289] fix: offer on `!` for apply_demorgan_iterator Example --- ```rust fn main() { let arr = [1, 2, 3]; let cond = $0!arr.into_iter().all(|num| num != 4); } ``` **Before this PR** Assist not applicable **After this PR** ```rust fn main() { let arr = [1, 2, 3]; let cond = arr.into_iter().any(|num| num == 4); } ``` --- .../src/handlers/apply_demorgan.rs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index b87a757047ac5..03be28314be83 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -190,7 +190,13 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti // } // ``` pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?; + let method_call: ast::MethodCallExpr = ctx.find_node_at_offset().or_else(|| { + let parent = ctx.find_token_syntax_at_offset(T![!])?.parent()?; + match ast::PrefixExpr::cast(parent)?.expr()? { + ast::Expr::MethodCallExpr(method_call) => Some(method_call), + _ => None, + } + })?; let (name, arg_expr) = validate_method_call_expr(ctx, &method_call)?; let ast::Expr::ClosureExpr(closure_expr) = arg_expr else { return None }; @@ -393,6 +399,26 @@ fn f() { if let 1 = 1 &&$0 true { } } ) } + #[test] + fn demorgan_iterator_on_not() { + check_assist( + apply_demorgan_iterator, + r#" +//- minicore: iterator +fn main() { + let arr = [1, 2, 3]; + let cond = $0!arr.into_iter().all(|num| num != 4); +} +"#, + r#" +fn main() { + let arr = [1, 2, 3]; + let cond = arr.into_iter().any(|num| num == 4); +} +"#, + ); + } + #[test] fn demorgan_keep_pars_for_op_precedence() { check_assist( From 3ad87f60a359bdcd68398f0953fb6b7f52be4231 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 23 Apr 2026 10:45:38 +0200 Subject: [PATCH 071/289] misc improvements --- .../crates/hir/src/diagnostics.rs | 74 ++++++++----------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 110cc19cf1a95..473a25379a76a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -543,59 +543,47 @@ impl<'db> AnyDiagnostic<'db> { } } BodyValidationDiagnostic::MissingMatchArms { match_expr, uncovered_patterns } => { - match source_map.expr_syntax(match_expr) { - Ok(source_ptr) => { - let root = source_ptr.file_syntax(db); - if let Either::Left(ast::Expr::MatchExpr(match_expr)) = - &source_ptr.value.to_node(&root) - { - match match_expr.expr() { - Some(scrut_expr) if match_expr.match_arm_list().is_some() => { - return Some( - MissingMatchArms { - scrutinee_expr: InFile::new( - source_ptr.file_id, - AstPtr::new(&scrut_expr), - ), - uncovered_patterns, - } - .into(), - ); - } - _ => {} - } + if let Ok(source_ptr) = source_map.expr_syntax(match_expr) + && let root = source_ptr.file_syntax(db) + && let Either::Left(ast::Expr::MatchExpr(match_expr)) = + source_ptr.value.to_node(&root) + && let Some(scrut_expr) = match_expr.expr() + && match_expr.match_arm_list().is_some() + { + return Some( + MissingMatchArms { + scrutinee_expr: InFile::new( + source_ptr.file_id, + AstPtr::new(&scrut_expr), + ), + uncovered_patterns, } - } - Err(SyntheticSyntax) => (), + .into(), + ); } } BodyValidationDiagnostic::NonExhaustiveLet { pat, uncovered_patterns } => { - match source_map.pat_syntax(pat) { - Ok(source_ptr) => { - if let Some(ast_pat) = source_ptr.value.cast::() { - return Some( - NonExhaustiveLet { - pat: InFile::new(source_ptr.file_id, ast_pat), - uncovered_patterns, - } - .into(), - ); + if let Ok(source_ptr) = source_map.pat_syntax(pat) + && let Some(ast_pat) = source_ptr.value.cast::() + { + return Some( + NonExhaustiveLet { + pat: InFile::new(source_ptr.file_id, ast_pat), + uncovered_patterns, } - } - Err(SyntheticSyntax) => {} + .into(), + ); } } BodyValidationDiagnostic::RemoveTrailingReturn { return_expr } => { - if let Ok(source_ptr) = source_map.expr_syntax(return_expr) { + if let Ok(source_ptr) = source_map.expr_syntax(return_expr) // Filters out desugared return expressions (e.g. desugared try operators). - if let Some(ptr) = source_ptr.value.cast::() { - return Some( - RemoveTrailingReturn { - return_expr: InFile::new(source_ptr.file_id, ptr), - } + && let Some(ptr) = source_ptr.value.cast::() + { + return Some( + RemoveTrailingReturn { return_expr: InFile::new(source_ptr.file_id, ptr) } .into(), - ); - } + ); } } BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr } => { From f546577849408adfb3a564201ff91932b1f971ce Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 23 Apr 2026 16:38:14 +0800 Subject: [PATCH 072/289] fix: no complete where kw after qualified path Example --- ```rust mod foo { pub struct Bar; } fn x() foo::b$0 ``` **Before this PR** ```text st Bar (adds ->) Bar kw where ``` **After this PR** ```text st Bar (adds ->) Bar ``` --- .../crates/ide-completion/src/completions.rs | 4 +++- .../ide-completion/src/tests/type_pos.rs | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 2ed582598b729..ed972a1e2a2dc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -34,7 +34,8 @@ use crate::{ CompletionContext, CompletionItem, CompletionItemKind, context::{ DotAccess, ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind, - PathCompletionCtx, PathKind, PatternContext, TypeAscriptionTarget, TypeLocation, Visible, + PathCompletionCtx, PathKind, PatternContext, Qualified, TypeAscriptionTarget, TypeLocation, + Visible, }, item::Builder, render::{ @@ -752,6 +753,7 @@ pub(super) fn complete_name_ref( if let TypeAscriptionTarget::RetType { item: Some(item), .. } = ascription && path_ctx.required_thin_arrow().is_some() + && matches!(path_ctx.qualified, Qualified::No) { keyword::complete_for_and_where(acc, ctx, &item.clone().into()); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index 7d4a7fe6b8872..1a4c255fc03dd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -142,6 +142,26 @@ fn x() $0 kw where "#]], ); + + check_with_base_items( + r#" +mod foo { pub struct Bar; } +fn x() foo::$0 +"#, + expect![[r#" + st Bar (adds ->) Bar + "#]], + ); + + check_with_base_items( + r#" +mod foo { pub struct Bar; } +fn x() foo::b$0 +"#, + expect![[r#" + st Bar (adds ->) Bar + "#]], + ); } #[test] From 15fc0c584e6b92def4ca3ec9480fcb23d4600ad3 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 23 Apr 2026 16:53:14 +0800 Subject: [PATCH 073/289] Add tests for where kw in stmt_list --- .../crates/ide-completion/src/tests/item.rs | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs index 45024ad21638c..2d116c403b9b8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs @@ -2,7 +2,7 @@ //! //! Except for use items which are tested in [super::use_tree] and mod declarations with are tested //! in [crate::completions::mod_]. -use expect_test::expect; +use expect_test::{Expect, expect}; use crate::tests::{check, check_edit, check_with_base_items}; @@ -134,6 +134,12 @@ fn completes_where() { kw where "#]], ); + check_with_base_items( + r"fn func() -> foo::Bar $0", + expect![[r#" + kw where + "#]], + ); check_with_base_items( r"enum Enum $0", expect![[r#" @@ -154,6 +160,62 @@ fn completes_where() { ); } +#[test] +fn completes_where_in_stmt_list() { + fn check_in_stmt_list(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + check(&format!("const _: () = {{{ra_fixture}}};"), expect); + } + check_in_stmt_list( + r"struct Struct $0", + expect![[r#" + kw where + "#]], + ); + check_in_stmt_list( + r"struct Struct $0 {}", + expect![[r#" + kw where + "#]], + ); + check_in_stmt_list( + r"fn func() $0", + expect![[r#" + bt u32 (adds ->) u32 + kw crate:: (adds ->) + kw dyn (adds ->) + kw fn (adds ->) + kw for (adds ->) + kw impl (adds ->) + kw self:: (adds ->) + kw where + "#]], + ); + check_in_stmt_list( + r"fn func() -> foo::Bar $0", + expect![[r#" + kw where + "#]], + ); + check_in_stmt_list( + r"enum Enum $0", + expect![[r#" + kw where + "#]], + ); + check_in_stmt_list( + r"enum Enum $0 {}", + expect![[r#" + kw where + "#]], + ); + check_in_stmt_list( + r"trait Trait $0 {}", + expect![[r#" + kw where + "#]], + ); +} + #[test] fn before_record_field() { check_with_base_items( From 9f705f0c7088baaced7ad93ce73a02d40129da22 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 23 Apr 2026 12:00:22 +0200 Subject: [PATCH 074/289] Use windows-sys 0.61 This version no longer requires using big import libraries and instead uses raw-dylib. windows-sys 0.60 is still pulled in by the notify crate. The upcoming 9.0 version of notify will use windows-sys 0.61. --- src/tools/rust-analyzer/Cargo.lock | 6 +++--- src/tools/rust-analyzer/crates/profile/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/stdx/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index e6575c28c1dd0..768c3a9aa616a 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1924,7 +1924,7 @@ dependencies = [ "libc", "perf-event", "tikv-jemalloc-ctl", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -2355,7 +2355,7 @@ dependencies = [ "vfs", "vfs-notify", "walkdir", - "windows-sys 0.60.2", + "windows-sys 0.61.0", "xflags", "xshell", ] @@ -2694,7 +2694,7 @@ dependencies = [ "libc", "miow", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml index 8377e94c8d608..048d3776ca33f 100644 --- a/src/tools/rust-analyzer/crates/profile/Cargo.toml +++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml @@ -23,7 +23,7 @@ perf-event = "=0.4.8" libc.workspace = true [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.60", features = [ +windows-sys = { version = "0.61", features = [ "Win32_System_Threading", "Win32_System_ProcessStatus", ] } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index da2ec740197ea..27d9576e29d0a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -78,7 +78,7 @@ paths.workspace = true ra-ap-rustc_type_ir.workspace = true [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.60", features = [ +windows-sys = { version = "0.61", features = [ "Win32_System_Diagnostics_Debug", "Win32_System_Threading", ] } diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml index 2c19f00f0822f..a9d19a1347f3d 100644 --- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml +++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml @@ -26,7 +26,7 @@ libc.workspace = true [target.'cfg(windows)'.dependencies] miow = "0.6.0" -windows-sys = { version = "0.60", features = ["Win32_Foundation"] } +windows-sys = { version = "0.61", features = ["Win32_Foundation"] } [features] # Uncomment to enable for the whole crate graph From 05c841876a675e6a332605f4f2897c98b50a4a3f Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 23 Apr 2026 10:04:52 +0200 Subject: [PATCH 075/289] feat: add diagnostic for E0067 hehe six seven --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 3 + .../crates/hir-ty/src/infer/expr.rs | 3 +- .../crates/hir/src/diagnostics.rs | 10 +++ .../src/handlers/invalid_lhs_of_assignment.rs | 90 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 5 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 54f334b66d2b1..e50e1372e052d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -399,6 +399,9 @@ pub enum InferenceDiagnostic { /// Whether the `GenericArgs` contains a `Self` arg. has_self_arg: bool, }, + InvalidLhsOfAssignment { + lhs: ExprId, + }, } /// A mismatch between an expected and an inferred type. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index e84a03a2e72a9..8419c5404713f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -248,13 +248,12 @@ impl<'db> InferenceContext<'_, 'db> { } } - #[expect(clippy::needless_return)] pub(crate) fn check_lhs_assignable(&self, lhs: ExprId) { if self.is_syntactic_place_expr(lhs) { return; } - // FIXME: Emit diagnostic. + self.push_diagnostic(InferenceDiagnostic::InvalidLhsOfAssignment { lhs }); } fn infer_expr_coerce_never( diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 473a25379a76a..1009a8b31bd04 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -63,6 +63,7 @@ diagnostics![AnyDiagnostic<'db> -> IncorrectCase, InvalidCast<'db>, InvalidDeriveTarget, + InvalidLhsOfAssignment, MacroDefError, MacroError, MacroExpansionParseError, @@ -472,6 +473,11 @@ pub struct GenericDefaultRefersToSelf { pub segment: InFile>, } +#[derive(Debug)] +pub struct InvalidLhsOfAssignment { + pub lhs: InFile>>, +} + impl<'db> AnyDiagnostic<'db> { pub(crate) fn body_validation_diagnostic( db: &'db dyn HirDatabase, @@ -790,6 +796,10 @@ impl<'db> AnyDiagnostic<'db> { let expected_kind = GenericArgKind::from_id(param_id); IncorrectGenericsOrder { provided_arg, expected_kind }.into() } + &InferenceDiagnostic::InvalidLhsOfAssignment { lhs } => { + let lhs = expr_syntax(lhs)?; + InvalidLhsOfAssignment { lhs }.into() + } }) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs new file mode 100644 index 0000000000000..02716f2b86ac8 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs @@ -0,0 +1,90 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: invalid-lhs-of-assignment +// +// This diagnostic is triggered if the left-hand side of an assignment can't be assigned to. +pub(crate) fn invalid_lhs_of_assignment( + ctx: &DiagnosticsContext<'_>, + d: &hir::InvalidLhsOfAssignment, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0067"), + "invalid left-hand side of assignment", + d.lhs.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn unit_struct_literal() { + check_diagnostics( + r#" +//- minicore: add +struct Struct; +impl core::ops::AddAssign for Struct { + fn add_assign(&mut self, _other: Self) {} +} +fn test() { + Struct += Struct; + // ^^^^^^ error: invalid left-hand side of assignment +} + "#, + ); + } + + #[test] + fn struct_literal() { + check_diagnostics( + r#" +//- minicore: add +struct Struct { foo: i32, bar: i32 } +impl core::ops::AddAssign for Struct { + fn add_assign(&mut self, _other: Self) {} +} +fn test() { + Struct { foo: 0, bar: 0 } += Struct { foo: 1, bar: 2 }; + // ^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid left-hand side of assignment +} + "#, + ); + } + + #[test] + fn destructuring_assignment() { + // no diagnostic, as `=` is not a _compound_ assignment + check_diagnostics( + r#" +//- minicore: add +struct Struct { foo: i32, bar: i32 } +impl core::ops::AddAssign for Struct { + fn add_assign(&mut self, _other: Self) {} +} +fn test(mut foo: i32, mut bar: i32) { + Struct { foo, bar } = Struct { foo: 1, bar: 2 }; +} + "#, + ); + } + + #[test] + fn destructuring_compound_assignment() { + check_diagnostics( + r#" +//- minicore: add +struct Struct { foo: i32, bar: i32 } +impl core::ops::AddAssign for Struct { + fn add_assign(&mut self, _other: Self) {} +} +fn test(foo: i32, bar: i32) { + Struct { foo, bar } += Struct { foo: 1, bar: 2 }; + // ^^^^^^^^^^^^^^^^^^^ error: invalid left-hand side of assignment +} + "#, + ); + } +} 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 54af3a35f46cf..74f0653660e1f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -43,6 +43,7 @@ mod handlers { pub(crate) mod incorrect_generics_order; pub(crate) mod invalid_cast; pub(crate) mod invalid_derive_target; + pub(crate) mod invalid_lhs_of_assignment; pub(crate) mod macro_error; pub(crate) mod malformed_derive; pub(crate) mod mismatched_arg_count; @@ -479,6 +480,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::MissingLifetime(d) => handlers::missing_lifetime::missing_lifetime(&ctx, &d), AnyDiagnostic::ElidedLifetimesInPath(d) => handlers::elided_lifetimes_in_path::elided_lifetimes_in_path(&ctx, &d), AnyDiagnostic::GenericDefaultRefersToSelf(d) => handlers::generic_default_refers_to_self::generic_default_refers_to_self(&ctx, &d), + AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d), }; res.push(d) } From 69d8a1e0318cb70dac97677fcf74fc903a368a6e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 20:41:07 +0300 Subject: [PATCH 076/289] Feature a "type annotations needed" diagnostic There are two kinds of this diagnostic: 1 Some constructs, like calling or indexing, require the type to be known *immediately*. 2. Otherwise, if we cannot resolve the variable at the end of inference, it's an error. This PR implements only the first kind, which is also simpler to implement. We just need to push an error in `structurally_resolve_type()` (as opposed to `try_structurally_resolve_type()` that never errors) if the type cannot be resolved, and of course also make sure we only call this function where rustc does. The second case needs to track the origin of infer vars, and it's for another time. --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 54 +++++++++++--- .../crates/hir-ty/src/infer/callee.rs | 22 ++++-- .../hir-ty/src/infer/closure/analysis.rs | 8 +-- .../closure/analysis/expr_use_visitor.rs | 26 ++++--- .../crates/hir-ty/src/infer/expr.rs | 72 +++++++++---------- .../crates/hir-ty/src/infer/pat.rs | 26 ++++--- .../crates/hir-ty/src/infer/place_op.rs | 2 +- .../crates/hir-ty/src/infer/unify.rs | 10 --- .../crates/hir-ty/src/tests/never_type.rs | 4 +- .../crates/hir/src/diagnostics.rs | 11 ++- .../src/handlers/missing_unsafe.rs | 2 +- .../src/handlers/type_must_be_known.rs | 39 ++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 13 files changed, 186 insertions(+), 92 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 82c5834e64743..8b0702c4833c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -401,6 +401,9 @@ pub enum InferenceDiagnostic { InvalidLhsOfAssignment { lhs: ExprId, }, + TypeMustBeKnown { + at_point: ExprOrPatId, + }, } /// A mismatch between an expected and an inferred type. @@ -1178,6 +1181,7 @@ pub(crate) struct InferenceContext<'body, 'db> { deferred_call_resolutions: FxHashMap>>, diagnostics: Diagnostics, + vars_emitted_type_must_be_known_for: FxHashSet>, } #[derive(Clone, Debug)] @@ -1267,6 +1271,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { deferred_cast_checks: Vec::new(), inside_assignment: false, diagnostics: Diagnostics::default(), + vars_emitted_type_must_be_known_for: FxHashSet::default(), deferred_call_resolutions: FxHashMap::default(), } } @@ -1581,6 +1586,33 @@ impl<'body, 'db> InferenceContext<'body, 'db> { &self.table.infer_ctxt } + /// If `ty` is an error, returns an infer var instead. Otherwise, returns it. + /// + /// "Refreshing" types like this is useful for getting better types, but it is also + /// very dangerous: we might create duplicate diagnostics, for example if we try + /// to resolve it and fail. rustc doesn't do that for this reason (and is in general + /// more strict with how it uses error types; an error type in inputs will almost + /// always cause it to infer an error type in output, while we infer some type as much + /// as we can). + /// + /// Unfortunately, we cannot allow ourselves to do that. Not only we more often work + /// with incomplete code, we also have assists, for example "Generate constant", that + /// will assume the inferred type is the expected type even if the expression itself + /// cannot be inferred. Therefore, we choose a middle ground: refresh the type, + /// but if we return a new var, mark it so that no diagnostics will be issued on it. + fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { + if ty.is_ty_error() { + let var = self.table.next_ty_var(); + + // Suppress future errors on this var. Add more things here when we add more diagnostics. + self.vars_emitted_type_must_be_known_for.insert(var); + + var + } else { + ty + } + } + fn infer_body(&mut self, body_expr: ExprId) { match self.return_coercion { Some(_) => self.infer_return(body_expr), @@ -1781,11 +1813,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.insert_type_vars(lt) } - /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. - fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { - self.table.insert_type_vars_shallow(ty) - } - fn insert_type_vars(&mut self, ty: T) -> T where T: TypeFoldable>, @@ -1873,6 +1900,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.table.resolve_vars_if_possible(t) } + pub(crate) fn structurally_resolve_type(&mut self, node: ExprOrPatId, ty: Ty<'db>) -> Ty<'db> { + let result = self.table.try_structurally_resolve_type(ty); + if result.is_ty_var() { self.type_must_be_known_at_this_point(node, ty) } else { result } + } + fn demand_eqtype( &mut self, id: ExprOrPatId, @@ -1938,11 +1970,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } pub(crate) fn type_must_be_known_at_this_point( - &self, - _id: ExprOrPatId, - _ty: Ty<'db>, + &mut self, + node: ExprOrPatId, + ty: Ty<'db>, ) -> Ty<'db> { - // FIXME: Emit an diagnostic. + if self.vars_emitted_type_must_be_known_for.insert(ty) { + self.push_diagnostic(InferenceDiagnostic::TypeMustBeKnown { at_point: node }); + } self.types.types.error } @@ -2477,7 +2511,7 @@ impl<'db> Expectation<'db> { fn adjust_for_branches(&self, table: &mut unify::InferenceTable<'db>) -> Expectation<'db> { match *self { Expectation::HasType(ety) => { - let ety = table.structurally_resolve_type(ety); + let ety = table.try_structurally_resolve_type(ety); if ety.is_ty_var() { Expectation::None } else { Expectation::HasType(ety) } } Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs index 3d478912a3db2..a470c92124076 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs @@ -47,9 +47,15 @@ impl<'db> InferenceContext<'_, 'db> { let mut autoderef = GeneralAutoderef::new_from_inference_context(self, expr_ty); let mut result = None; + let mut error_reported = false; while result.is_none() && autoderef.next().is_some() { - result = - Self::try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &mut autoderef); + result = Self::try_overloaded_call_step( + call_expr, + callee_expr, + arg_exprs, + &mut autoderef, + &mut error_reported, + ); } // FIXME: rustc does some ABI checks here, but the ABI mapping is in rustc_target and we don't have access to that crate. @@ -65,10 +71,12 @@ impl<'db> InferenceContext<'_, 'db> { self.infer_expr_no_expect(arg, ExprIsRead::Yes); } - self.push_diagnostic(InferenceDiagnostic::ExpectedFunction { - call_expr, - found: original_callee_ty.store(), - }); + if !error_reported { + self.push_diagnostic(InferenceDiagnostic::ExpectedFunction { + call_expr, + found: original_callee_ty.store(), + }); + } self.types.types.error } @@ -97,6 +105,7 @@ impl<'db> InferenceContext<'_, 'db> { callee_expr: ExprId, arg_exprs: &[ExprId], autoderef: &mut InferenceContextAutoderef<'_, '_, 'db>, + error_reported: &mut bool, ) -> Option> { let final_ty = autoderef.final_ty(); let adjusted_ty = autoderef.ctx().table.try_structurally_resolve_type(final_ty); @@ -211,6 +220,7 @@ impl<'db> InferenceContext<'_, 'db> { autoderef .ctx() .type_must_be_known_at_this_point(callee_expr.into(), adjusted_ty); + *error_reported = true; return None; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs index b0f5533b3b902..31b6252475d30 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -741,7 +741,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { }; let Some(min_cap_list) = root_var_min_capture_list.get_mut(&var_hir_id) else { - let mutability = self.determine_capture_mutability(&place); + let mutability = self.determine_capture_mutability(closure_def_id, &place); let min_cap_list = vec![CapturedPlace { place, info: capture_info, mutability }]; root_var_min_capture_list.insert(var_hir_id, min_cap_list); continue; @@ -850,7 +850,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // Only need to insert when we don't have an ancestor in the existing min capture list if !ancestor_found { - let mutability = self.determine_capture_mutability(&place); + let mutability = self.determine_capture_mutability(closure_def_id, &place); let captured_place = CapturedPlace { place, info: updated_capture_info, mutability }; min_cap_list.push(captured_place); @@ -1016,7 +1016,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { /// A captured place is mutable if /// 1. Projections don't include a Deref of an immut-borrow, **and** /// 2. PlaceBase is mut or projections include a Deref of a mut-borrow. - fn determine_capture_mutability(&mut self, place: &Place) -> Mutability { + fn determine_capture_mutability(&mut self, closure_expr: ExprId, place: &Place) -> Mutability { let var_hir_id = match place.base { PlaceBase::Upvar { var_id, .. } => var_id, _ => unreachable!(), @@ -1029,7 +1029,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { }; for pointer_ty in place.deref_tys() { - match self.table.structurally_resolve_type(pointer_ty).kind() { + match self.structurally_resolve_type(closure_expr.into(), pointer_ty).kind() { // We don't capture derefs of raw ptrs TyKind::RawPtr(_, _) => unreachable!(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index bddb01ff99d25..f140b1249173a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -797,7 +797,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { // Select just those fields of the `with` // expression that will actually be used - match self.cx.table.structurally_resolve_type(with_place.place.ty()).kind() { + match self.cx.structurally_resolve_type(with_expr.into(), with_place.place.ty()).kind() { TyKind::Adt(adt, args) if adt.is_struct() => { let AdtId::StructId(adt) = adt.def_id().0 else { unreachable!() }; let adt_fields = VariantId::from(adt).fields(self.cx.db).fields(); @@ -982,7 +982,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { // FIXME: Does the MIR code skip this read when matching on a ZST? // If so, we can also skip it here. read_discriminant(this); - } else if this.is_multivariant_adt(place.place.ty()) { + } else if this.is_multivariant_adt(pat.into(), place.place.ty()) { // Otherwise, this is a struct/enum variant, and so it's // only a read if we need to read the discriminant. read_discriminant(this); @@ -1001,7 +1001,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { read_discriminant(this); } Pat::Record { .. } | Pat::TupleStruct { .. } => { - if this.is_multivariant_adt(place.place.ty()) { + if this.is_multivariant_adt(pat.into(), place.place.ty()) { read_discriminant(this); } } @@ -1225,7 +1225,11 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { // a bind-by-ref means that the base_ty will be the type of the ident itself, // but what we want here is the type of the underlying value being borrowed. // So peel off one-level, turning the &T into T. - match self.cx.table.structurally_resolve_type(base_ty).builtin_deref(false) { + match self + .cx + .structurally_resolve_type(pat.into(), base_ty) + .builtin_deref(false) + { Some(ty) => Ok(ty), None => { debug!("By-ref binding of non-derefable type: {base_ty:?}"); @@ -1430,7 +1434,8 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { let place_ty = self.expr_ty(expr)?; let base_ty = self.expr_ty_adjusted(base)?; - let TyKind::Ref(region, _, mutbl) = self.cx.table.structurally_resolve_type(base_ty).kind() + let TyKind::Ref(region, _, mutbl) = + self.cx.structurally_resolve_type(base.into(), base_ty).kind() else { return Err(ErrorGuaranteed); }; @@ -1447,7 +1452,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { ) -> Result { let base_curr_ty = base_place.place.ty(); let Some(deref_ty) = - self.cx.table.structurally_resolve_type(base_curr_ty).builtin_deref(true) + self.cx.structurally_resolve_type(node, base_curr_ty).builtin_deref(true) else { debug!("explicit deref of non-derefable type: {:?}", base_curr_ty); return Err(ErrorGuaranteed); @@ -1474,7 +1479,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { /// Here `pat_hir_id` is the ExprId of the pattern itself. fn total_fields_in_tuple(&mut self, pat_id: PatId) -> usize { let ty = self.cx.result.pat_ty(pat_id); - match self.cx.table.structurally_resolve_type(ty).kind() { + match self.cx.structurally_resolve_type(pat_id.into(), ty).kind() { TyKind::Tuple(args) => args.len(), _ => panic!("tuple pattern not applied to a tuple"), } @@ -1629,8 +1634,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { Pat::Slice { prefix: ref before, slice, suffix: ref after } => { let Some(element_ty) = self .cx - .table - .structurally_resolve_type(place_with_id.place.ty()) + .structurally_resolve_type(pat.into(), place_with_id.place.ty()) .builtin_index() else { debug!("explicit index of non-indexable type {:?}", place_with_id); @@ -1689,8 +1693,8 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { /// FIXME(never_patterns): update this comment once the aforementioned MIR builder /// code is changed to be insensitive to inhhabitedness. #[instrument(skip(self), level = "debug")] - fn is_multivariant_adt(&mut self, ty: Ty<'db>) -> bool { - if let TyKind::Adt(def, _) = self.cx.table.structurally_resolve_type(ty).kind() { + fn is_multivariant_adt(&mut self, node: ExprOrPatId, ty: Ty<'db>) -> bool { + if let TyKind::Adt(def, _) = self.cx.structurally_resolve_type(node, ty).kind() { // Note that if a non-exhaustive SingleVariant is defined in another crate, we need // to assume that more cases will be added to the variant in the future. This mean // that we should handle non-exhaustive SingleVariant the same way we would handle diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 79010fbe1bb16..e179ca4bd3c81 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -397,9 +397,7 @@ impl<'db> InferenceContext<'_, 'db> { .1 } &Expr::Loop { body, label } => { - // FIXME: should be: - // let ty = expected.coercion_target_type(&mut self.table); - let ty = self.table.next_ty_var(); + let ty = expected.coercion_target_type(&mut self.table); let (breaks, ()) = self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| { this.infer_expr( @@ -732,7 +730,7 @@ impl<'db> InferenceContext<'_, 'db> { let base_t = self.infer_expr_no_expect(*base, ExprIsRead::Yes); let idx_t = self.infer_expr_no_expect(*index, ExprIsRead::Yes); - let base_t = self.table.structurally_resolve_type(base_t); + let base_t = self.structurally_resolve_type((*base).into(), base_t); match self.lookup_indexing(tgt_expr, *base, base_t, idx_t) { Some((trait_index_ty, trait_element_ty)) => { // two-phase not needed because index_ty is never mutable @@ -890,7 +888,7 @@ impl<'db> InferenceContext<'_, 'db> { // allows them to be inferred based on how they are used later in the // function. if is_input { - let ty = this.table.structurally_resolve_type(ty); + let ty = this.structurally_resolve_type(expr.into(), ty); match ty.kind() { TyKind::FnDef(def, parameters) => { let fnptr_ty = Ty::new_fn_ptr( @@ -955,7 +953,6 @@ impl<'db> InferenceContext<'_, 'db> { } } }; - // use a new type variable if we got unknown here let ty = self.insert_type_vars_shallow(ty); self.write_expr_ty(tgt_expr, ty); if self.shallow_resolve(ty).is_never() @@ -1304,7 +1301,7 @@ impl<'db> InferenceContext<'_, 'db> { }; let mut oprnd_t = self.infer_expr_inner(oprnd, expected_inner, ExprIsRead::Yes); - oprnd_t = self.table.structurally_resolve_type(oprnd_t); + oprnd_t = self.structurally_resolve_type(oprnd.into(), oprnd_t); match unop { UnaryOp::Deref => { if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) { @@ -1681,7 +1678,7 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { // Field projections don't constitute reads. let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::No); - let receiver_ty = self.table.structurally_resolve_type(receiver_ty); + let receiver_ty = self.structurally_resolve_type(receiver.into(), receiver_ty); if name.is_missing() { // Bail out early, don't even try to look up field. Also, we don't issue an unresolved @@ -1979,38 +1976,39 @@ impl<'db> InferenceContext<'_, 'db> { .unwrap_or_default(); // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here - let (formal_input_tys, expected_input_tys) = - if tuple_arguments == TupleArgumentsFlag::TupleArguments { - let tuple_type = self.table.structurally_resolve_type(formal_input_tys[0]); - match tuple_type.kind() { - // We expected a tuple and got a tuple - TyKind::Tuple(arg_types) => { - // Argument length differs - if arg_types.len() != provided_args.len() { - // FIXME: Emit an error. - } - let expected_input_tys = match expected_input_tys { - Some(expected_input_tys) => match expected_input_tys.first() { - Some(ty) => match ty.kind() { - TyKind::Tuple(tys) => Some(tys.iter().collect()), - _ => None, - }, - None => None, - }, - None => None, - }; - (arg_types.iter().collect(), expected_input_tys) - } - _ => { - // Otherwise, there's a mismatch, so clear out what we're expecting, and set - // our input types to err_args so we don't blow up the error messages + let (formal_input_tys, expected_input_tys) = if tuple_arguments + == TupleArgumentsFlag::TupleArguments + { + let tuple_type = self.structurally_resolve_type(call_expr.into(), formal_input_tys[0]); + match tuple_type.kind() { + // We expected a tuple and got a tuple + TyKind::Tuple(arg_types) => { + // Argument length differs + if arg_types.len() != provided_args.len() { // FIXME: Emit an error. - (vec![self.types.types.error; provided_args.len()], None) } + let expected_input_tys = match expected_input_tys { + Some(expected_input_tys) => match expected_input_tys.first() { + Some(ty) => match ty.kind() { + TyKind::Tuple(tys) => Some(tys.iter().collect()), + _ => None, + }, + None => None, + }, + None => None, + }; + (arg_types.iter().collect(), expected_input_tys) } - } else { - (formal_input_tys.to_vec(), expected_input_tys) - }; + _ => { + // Otherwise, there's a mismatch, so clear out what we're expecting, and set + // our input types to err_args so we don't blow up the error messages + // FIXME: Emit an error. + (vec![self.types.types.error; provided_args.len()], None) + } + } + } else { + (formal_input_tys.to_vec(), expected_input_tys) + }; // If there are no external expectations at the call site, just use the types from the function defn let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index bba69c758a685..3cd7461cf317b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -311,6 +311,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { }; let adjust_mode = self.calc_adjust_mode(pat, opt_path_res); let ty = self.infer_pat_inner(pat_id, opt_path_res, adjust_mode, expected, pat_info); + let ty = self.insert_type_vars_shallow(ty); self.write_pat_ty(pat_id, ty); // If we implicitly inserted overloaded dereferences before matching check the pattern to @@ -457,7 +458,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> { ty } Pat::Lit(expr) => self.infer_lit_pat(expr, expected), - Pat::Range { start: lhs, end: rhs, .. } => self.infer_range_pat(lhs, rhs, expected), + Pat::Range { start: lhs, end: rhs, .. } => { + self.infer_range_pat(pat, lhs, rhs, expected) + } Pat::Bind { id: var_id, subpat } => { self.infer_bind_pat(pat, var_id, subpat, expected, pat_info) } @@ -755,7 +758,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // types `[u8]` or `[u8; N]`, in order to type, e.g., `deref!(b"..."): Vec`. let mut pat_ty = ty; if matches!(literal, Literal::ByteString(_)) { - let expected = self.table.structurally_resolve_type(expected); + let expected = self.structurally_resolve_type(expr.into(), expected); match expected.kind() { // Allow `b"...": &[u8]` TyKind::Ref(_, inner_ty, _) @@ -806,8 +809,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> { fn infer_range_pat( &mut self, - lhs: Option, - rhs: Option, + pat: PatId, + lhs_expr: Option, + rhs_expr: Option, expected: Ty<'db>, ) -> Ty<'db> { let mut calc_side = |opt_expr: Option| match opt_expr { @@ -826,8 +830,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { Some((fail, ty, expr)) } }; - let mut lhs = calc_side(lhs); - let mut rhs = calc_side(rhs); + let mut lhs = calc_side(lhs_expr); + let mut rhs = calc_side(rhs_expr); if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { // There exists a side that didn't meet our criteria that the end-point @@ -854,7 +858,10 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // This check is needed if both sides are inference variables. // We require types to be resolved here so that we emit inference failure // rather than "_ is not a char or numeric". - let ty = self.table.structurally_resolve_type(expected); + let ty = self.structurally_resolve_type( + lhs_expr.or(rhs_expr).map(ExprOrPatId::ExprId).unwrap_or(pat.into()), + expected, + ); if !(ty.is_numeric() || ty.is_char() || ty.references_error()) { // FIXME: Emit an error. return self.types.types.error; @@ -1096,7 +1103,8 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; let mut expected_len = elements.len(); if ddpos.is_some() { // Require known type only when `..` is present. - if let TyKind::Tuple(tys) = self.table.structurally_resolve_type(expected).kind() { + if let TyKind::Tuple(tys) = self.structurally_resolve_type(pat.into(), expected).kind() + { expected_len = tys.len(); } } @@ -1574,7 +1582,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; let _ = self.demand_eqtype(pat.into(), expected, resolved_arr_ty); } - let expected = self.table.structurally_resolve_type(expected); + let expected = self.structurally_resolve_type(pat.into(), expected); debug!(?expected); let (element_ty, opt_slice_ty, inferred) = match expected.kind() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs index 1298b38097034..1671b67d347e1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs @@ -108,7 +108,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { index_ty: Ty<'db>, ) -> Option<(/*index type*/ Ty<'db>, /*element type*/ Ty<'db>)> { let ty = autoderef.final_ty(); - let adjusted_ty = autoderef.ctx().table.structurally_resolve_type(ty); + let adjusted_ty = autoderef.ctx().structurally_resolve_type(base_expr.into(), ty); debug!( "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \ index_ty={:?})", diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index c28fea0ff17be..be4d370c24886 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -343,11 +343,6 @@ impl<'db> InferenceTable<'db> { } } - pub(crate) fn structurally_resolve_type(&mut self, ty: Ty<'db>) -> Ty<'db> { - self.try_structurally_resolve_type(ty) - // FIXME: Err if it still contain infer vars. - } - pub(crate) fn snapshot(&mut self) -> CombinedSnapshot { self.infer_ctxt.start_snapshot() } @@ -433,11 +428,6 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.insert_type_vars(ty) } - /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. - pub(super) fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { - if ty.is_ty_error() { self.next_ty_var() } else { ty } - } - /// Whenever you lower a user-written type, you should call this. pub(crate) fn process_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { self.process_remote_user_written_ty(ty) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 993293bb56857..91273cd177e8f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -354,10 +354,10 @@ fn diverging_expression_3_break() { 11..85 '{ ...} }; }': () 54..55 'x': u32 63..82 '{ loop...k; } }': u32 - 65..80 'loop { break; }': () + 65..80 'loop { break; }': u32 70..80 '{ break; }': () 72..77 'break': ! - 65..80: expected u32, got () + 72..77: expected u32, got () 97..343 '{ ...; }; }': () 140..141 'x': u32 149..175 '{ for ...; }; }': u32 diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 1009a8b31bd04..8f9465cf1b5d6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -8,7 +8,7 @@ use either::Either; use hir_def::{ DefWithBodyId, GenericParamId, SyntheticSyntax, expr_store::{ - ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast, + ExprOrPatPtr, ExprOrPatSource, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast, hir_generic_arg_to_ast, hir_segment_to_ast_segment, }, hir::ExprOrPatId, @@ -108,6 +108,7 @@ diagnostics![AnyDiagnostic<'db> -> IncorrectGenericsOrder, MissingLifetime, ElidedLifetimesInPath, + TypeMustBeKnown, ]; #[derive(Debug)] @@ -444,6 +445,11 @@ pub struct ElidedLifetimesInPath { pub hard_error: bool, } +#[derive(Debug)] +pub struct TypeMustBeKnown { + pub at_point: ExprOrPatSource, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum GenericArgKind { Lifetime, @@ -800,6 +806,9 @@ impl<'db> AnyDiagnostic<'db> { let lhs = expr_syntax(lhs)?; InvalidLhsOfAssignment { lhs }.into() } + &InferenceDiagnostic::TypeMustBeKnown { at_point } => { + TypeMustBeKnown { at_point: expr_or_pat_syntax(at_point)? }.into() + } }) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 6a37702fc50e2..3351f5dc1cfba 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -485,7 +485,7 @@ fn main() { let b = &raw const x.a; - let tmp = Vec::from([1, 2, 3]); + let tmp = [1, 2, 3]; let c = &raw const tmp[x.a]; // ^^^ 💡 error: access to union field is unsafe and requires an unsafe function or block diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs new file mode 100644 index 0000000000000..4b7249740857c --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs @@ -0,0 +1,39 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: type-must-be-known +// +// This diagnostic is triggered when rust-analyzer cannot infer some type. +pub(crate) fn type_must_be_known( + ctx: &DiagnosticsContext<'_>, + d: &hir::TypeMustBeKnown, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0282"), + "type annotations needed; type must be known at this point", + d.at_point.map(|it| it.into()), + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn smoke_test() { + check_diagnostics( + r#" +fn foo() { + let var = loop {}; + // ^^^ 💡 warn: unused variable + var(); + // ^^^ error: type annotations needed; type must be known at this point + let var = loop {}; + // ^^^ 💡 warn: unused variable + var[0]; + // ^^^ error: type annotations needed; type must be known at this point +} + "#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 74f0653660e1f..a083447335d05 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -66,6 +66,7 @@ mod handlers { pub(crate) mod trait_impl_orphan; pub(crate) mod trait_impl_redundant_assoc_item; pub(crate) mod type_mismatch; + pub(crate) mod type_must_be_known; pub(crate) mod typed_hole; pub(crate) mod undeclared_label; pub(crate) mod unimplemented_builtin_macro; @@ -481,6 +482,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::ElidedLifetimesInPath(d) => handlers::elided_lifetimes_in_path::elided_lifetimes_in_path(&ctx, &d), AnyDiagnostic::GenericDefaultRefersToSelf(d) => handlers::generic_default_refers_to_self::generic_default_refers_to_self(&ctx, &d), AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d), + AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d), }; res.push(d) } From a930ec5dc860fbf50da641181c85c93d71865e70 Mon Sep 17 00:00:00 2001 From: Xiaobo Liu Date: Fri, 24 Apr 2026 16:19:57 +0800 Subject: [PATCH 077/289] internal: Remove redundant database query in mutability diagnostics The `unused_mut` diagnostic previously called `d.local.primary_source(ctx.sema.db).syntax_ptr()` twice, resulting in a redundant database query. This removes the second invocation, reusing the `ast` variable that was already declared. Signed-off-by: Xiaobo Liu --- .../crates/ide-diagnostics/src/handlers/mutability_errors.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 18280a4addec9..9a5ad375dcb67 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -87,7 +87,6 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Op use_range, )]) })(); - let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); Some( Diagnostic::new_with_syntax_node_ptr( ctx, From 2f56ea5919f6c9c46f2b8263d6134342e14746b5 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 24 Apr 2026 12:10:08 +0200 Subject: [PATCH 078/289] fix: Fix closure capture hints being misplaced for async closures --- .../ide/src/inlay_hints/closure_captures.rs | 85 +++++++++++++++---- 1 file changed, 70 insertions(+), 15 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index f4ac9c42f459c..df2c42a68c9f3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -2,7 +2,6 @@ //! //! Tests live in [`bind_pat`][super::bind_pat] module. use ide_db::famous_defs::FamousDefs; -use ide_db::text_edit::{TextRange, TextSize}; use span::Edition; use stdx::{TupleExt, never}; use syntax::ast::{self, AstNode}; @@ -29,14 +28,11 @@ pub(super) fn hints( return None; } - let (range, label) = match closure.move_token() { - Some(t) => (t.text_range(), InlayHintLabel::default()), + let (range, label, position, pad_right) = match closure.move_token() { + Some(t) => (t.text_range(), InlayHintLabel::default(), InlayHintPosition::After, false), None => { - let prev_token = closure.syntax().first_token()?.prev_token()?.text_range(); - ( - TextRange::new(prev_token.end() - TextSize::from(1), prev_token.end()), - InlayHintLabel::from("move"), - ) + let l_pipe = closure.param_list()?.pipe_token()?.text_range(); + (l_pipe, InlayHintLabel::from("move"), InlayHintPosition::Before, true) } }; let mut hint = InlayHint { @@ -44,9 +40,9 @@ pub(super) fn hints( kind: InlayKind::ClosureCapture, label, text_edit: None, - position: InlayHintPosition::After, + position, pad_left: false, - pad_right: true, + pad_right, resolve_parent: Some(closure.syntax().text_range()), }; hint.label.append_str("("); @@ -107,7 +103,7 @@ mod tests { check_with_config( InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, r#" -//- minicore: copy, derive +//- minicore: copy, derive, fn #[derive(Copy, Clone)] @@ -121,29 +117,75 @@ fn main() { let mut baz = NonCopy; let qux = &mut NonCopy; || { -// ^ move(&foo, bar, baz, qux) + // ^ move(&foo, bar, baz, qux) foo; bar; baz; qux; }; || { -// ^ move(&foo, &bar, &baz, &qux) + // ^ move(&foo, &bar, &baz, &qux) &foo; &bar; &baz; &qux; }; || { -// ^ move(&mut baz) + // ^ move(&mut baz) &mut baz; }; || { -// ^ move(&mut baz, &mut *qux) + // ^ move(&mut baz, &mut *qux) + baz = NonCopy; + *qux = NonCopy; + }; +} +"#, + ); + } + + #[test] + fn all_capture_kinds_async_closure() { + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, derive, fn, future, async_fn + +#[derive(Copy, Clone)] +struct Copy; + +struct NonCopy; + +fn main() { + let foo = Copy; + let bar = NonCopy; + let mut baz = NonCopy; + let qux = &mut NonCopy; + async || { + // ^ move(&foo, bar, baz, qux) + foo; + bar; + baz; + qux; + }; + async || { + // ^ move(&foo, &bar, &baz, &qux) + &foo; + &bar; + &baz; + &qux; + }; + async || { + // ^ move(&mut baz) + &mut baz; + }; + async || { + // ^ move(&mut baz, &mut *qux) baz = NonCopy; *qux = NonCopy; }; } + "#, ); } @@ -161,6 +203,19 @@ fn main() { foo; }; } +"#, + ); + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, derive +fn main() { + let foo = u32; + async move || { + // ^^^^ (foo) + foo; + }; +} "#, ); } From da64c428af051db47fe3fb8d7466beccccd6b0a0 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 24 Apr 2026 18:21:36 +0800 Subject: [PATCH 079/289] Migrate prettify_macro_expansion to SyntaxEditor --- .../src/prettify_macro_expansion.rs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs index 43f270800d821..83c92f5eb1c94 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs @@ -3,8 +3,7 @@ use syntax::{ NodeOrToken, SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, T, WalkEvent, - ast::make, - ted::{self, Position}, + syntax_editor::{Position, SyntaxEditor}, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -29,7 +28,7 @@ pub fn prettify_macro_expansion( let mut last: Option = None; let mut mods = Vec::new(); let mut dollar_crate_replacements = Vec::new(); - let syn = syn.clone_subtree().clone_for_update(); + let (editor, syn) = SyntaxEditor::new(syn); let before = Position::before; let after = Position::after; @@ -110,7 +109,9 @@ pub fn prettify_macro_expansion( if indent > 0 { mods.push(do_indent(after, tok, indent)); } - mods.push(do_nl(after, tok)); + if tok.text_range().end() != syn.text_range().end() { + mods.push(do_nl(after, tok)); + } } T![=] if is_next(|it| it == T![>], false) => { // FIXME: this branch is for `=>` in macro_rules!, which is currently parsed as @@ -139,24 +140,24 @@ pub fn prettify_macro_expansion( inspect_mods(&mods); for (pos, insert) in mods { - ted::insert_raw( + editor.insert( pos, match insert { - PrettifyWsKind::Space => make::tokens::single_space(), - PrettifyWsKind::Indent(indent) => make::tokens::whitespace(&" ".repeat(4 * indent)), - PrettifyWsKind::Newline => make::tokens::single_newline(), + PrettifyWsKind::Space => editor.make().whitespace(" "), + PrettifyWsKind::Indent(indent) => editor.make().whitespace(&" ".repeat(4 * indent)), + PrettifyWsKind::Newline => editor.make().whitespace("\n"), }, ); } for (old, new) in dollar_crate_replacements { - ted::replace(old, new); + editor.replace(old, new); } if let Some(it) = syn.last_token().filter(|it| it.kind() == SyntaxKind::WHITESPACE) { - ted::remove(it); + editor.delete(it); } - syn + editor.finish().new_root().clone() } fn is_text(k: SyntaxKind) -> bool { @@ -173,9 +174,8 @@ mod tests { fn check_pretty(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { let ra_fixture = stdx::trim_indent(ra_fixture); let source_file = syntax::ast::SourceFile::parse(&ra_fixture, span::Edition::CURRENT); - let syn = source_file.syntax_node().clone_for_update(); + let syn = remove_whitespaces(&source_file.syntax_node()); - remove_whitespaces(&syn); let pretty = prettify_macro_expansion(syn, &mut |_| None, |_| ()); let mut pretty = pretty.to_string(); if pretty.contains('\n') { @@ -183,15 +183,15 @@ mod tests { } expect.assert_eq(&pretty); - fn remove_whitespaces(node: &SyntaxNode) { - let mut to_remove = vec![]; + fn remove_whitespaces(node: &SyntaxNode) -> SyntaxNode { + let (editor, node) = SyntaxEditor::new(node.clone()); node.preorder_with_tokens().for_each(|it| match it { WalkEvent::Enter(NodeOrToken::Token(tok)) if tok.kind().is_trivia() => { - to_remove.push(tok); + editor.delete(tok); } _ => (), }); - to_remove.iter().for_each(ted::remove) + editor.finish().new_root().clone() } } From c0032bad6adea11b2e2a4cd714513e0a139a8ce8 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 23:26:46 +0300 Subject: [PATCH 080/289] Store a span (`ExprId`, `PatId` or `TypeRefId`) in infer vars This is required for reporting unresolved vars, which will come in the next commit. --- .../crates/hir-ty/src/autoderef.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 47 +++-- .../crates/hir-ty/src/infer/callee.rs | 19 +- .../crates/hir-ty/src/infer/closure.rs | 66 ++++--- .../crates/hir-ty/src/infer/coerce.rs | 19 +- .../crates/hir-ty/src/infer/expr.rs | 84 +++++---- .../crates/hir-ty/src/infer/mutability.rs | 1 + .../crates/hir-ty/src/infer/op.rs | 4 +- .../crates/hir-ty/src/infer/pat.rs | 23 ++- .../crates/hir-ty/src/infer/path.rs | 19 +- .../crates/hir-ty/src/infer/place_op.rs | 27 ++- .../crates/hir-ty/src/infer/unify.rs | 44 ++--- .../rust-analyzer/crates/hir-ty/src/lib.rs | 32 +++- .../crates/hir-ty/src/method_resolution.rs | 26 ++- .../hir-ty/src/method_resolution/confirm.rs | 18 +- .../hir-ty/src/method_resolution/probe.rs | 24 ++- .../src/next_solver/format_proof_tree.rs | 4 +- .../crates/hir-ty/src/next_solver/fulfill.rs | 35 ++-- .../crates/hir-ty/src/next_solver/infer/at.rs | 9 +- .../infer/canonical/instantiate.rs | 12 +- .../src/next_solver/infer/canonical/mod.rs | 25 ++- .../hir-ty/src/next_solver/infer/context.rs | 25 ++- .../hir-ty/src/next_solver/infer/mod.rs | 176 +++++++----------- .../infer/region_constraints/mod.rs | 30 ++- .../next_solver/infer/relate/generalize.rs | 18 +- .../src/next_solver/infer/relate/lattice.rs | 29 +-- .../hir-ty/src/next_solver/infer/select.rs | 24 ++- .../src/next_solver/infer/snapshot/fudge.rs | 48 ++--- .../hir-ty/src/next_solver/infer/traits.rs | 25 ++- .../src/next_solver/infer/type_variable.rs | 41 +--- .../hir-ty/src/next_solver/infer/unify_key.rs | 16 +- .../crates/hir-ty/src/next_solver/inspect.rs | 62 +++--- .../crates/hir-ty/src/next_solver/interner.rs | 8 +- .../hir-ty/src/next_solver/normalize.rs | 2 +- .../crates/hir-ty/src/next_solver/solver.rs | 18 +- .../src/next_solver/structural_normalize.rs | 2 +- .../crates/hir-ty/src/opaques.rs | 4 +- .../crates/hir-ty/src/specialization.rs | 3 +- .../rust-analyzer/crates/hir-ty/src/traits.rs | 3 +- .../rust-analyzer/crates/hir/src/attrs.rs | 2 + src/tools/rust-analyzer/crates/hir/src/lib.rs | 14 +- .../rust-analyzer/crates/hir/src/semantics.rs | 6 +- 42 files changed, 612 insertions(+), 486 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index abab3bfb25093..2c2400a14a7f6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -10,7 +10,7 @@ use rustc_type_ir::inherent::{IntoKind, Ty as _}; use tracing::debug; use crate::{ - ParamEnvAndCrate, + ParamEnvAndCrate, Span, db::HirDatabase, infer::InferenceContext, next_solver::{ @@ -39,7 +39,7 @@ pub fn autoderef<'db>( ) -> impl Iterator> + use<'db> { let interner = DbInterner::new_with(db, env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let (ty, _) = infcx.instantiate_canonical(&ty); + let (ty, _) = infcx.instantiate_canonical(Span::Dummy, &ty); let autoderef = Autoderef::new(&infcx, env.param_env, ty); let mut v = Vec::new(); for (ty, _steps) in autoderef { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 8b0702c4833c1..30b420b6d5f86 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -54,7 +54,7 @@ use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ AliasTyKind, TypeFoldable, - inherent::{AdtDef, IntoKind, Ty as _}, + inherent::{AdtDef, Const as _, IntoKind, Ty as _}, }; use smallvec::SmallVec; use span::Edition; @@ -62,7 +62,7 @@ use stdx::never; use thin_vec::ThinVec; use crate::{ - ImplTraitId, IncorrectGenericsLenKind, PathLoweringDiagnostic, TargetFeatures, + ImplTraitId, IncorrectGenericsLenKind, PathLoweringDiagnostic, Span, TargetFeatures, closure_analysis::PlaceBase, collect_type_inference_vars, db::{HirDatabase, InternedOpaqueTyId}, @@ -1537,7 +1537,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { GenericArgs::for_item_with_defaults( self.interner(), va_list.into(), - |_, id, _| self.table.next_var_for_param(id), + |_, id, _| self.table.var_for_def(id, Span::Dummy), ), ), None => self.err_ty(), @@ -1545,7 +1545,8 @@ impl<'body, 'db> InferenceContext<'body, 'db> { param_tys.push(va_list_ty); } - let mut param_tys = param_tys.into_iter().chain(iter::repeat(self.table.next_ty_var())); + let mut param_tys = + param_tys.into_iter().chain(iter::repeat(self.table.next_ty_var(Span::Dummy))); if let Some(self_param) = self_param && let Some(ty) = param_tys.next() { @@ -1602,7 +1603,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { /// but if we return a new var, mark it so that no diagnostics will be issued on it. fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { if ty.is_ty_error() { - let var = self.table.next_ty_var(); + let var = self.table.next_ty_var(Span::Dummy); // Suppress future errors on this var. Add more things here when we add more diagnostics. self.vars_emitted_type_must_be_known_for.insert(var); @@ -1613,6 +1614,10 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } } + pub(super) fn insert_const_vars_shallow(&mut self, c: Const<'db>) -> Const<'db> { + if c.is_ct_error() { self.table.next_const_var(Span::Dummy) } else { c } + } + fn infer_body(&mut self, body_expr: ExprId) { match self.return_coercion { Some(_) => self.infer_return(body_expr), @@ -1786,7 +1791,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { LifetimeElisionKind::Infer, |ctx| ctx.lower_const(const_ref, ty), ); - self.insert_type_vars(const_) + self.insert_type_vars(const_, Span::Dummy) } pub(crate) fn make_path_as_body_const(&mut self, path: &Path, ty: Ty<'db>) -> Const<'db> { @@ -1796,7 +1801,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { LifetimeElisionKind::Infer, |ctx| ctx.lower_path_as_const(path, ty), ); - self.insert_type_vars(const_) + self.insert_type_vars(const_, Span::Dummy) } fn err_ty(&self) -> Ty<'db> { @@ -1810,14 +1815,14 @@ impl<'body, 'db> InferenceContext<'body, 'db> { LifetimeElisionKind::Infer, |ctx| ctx.lower_lifetime(lifetime_ref), ); - self.insert_type_vars(lt) + self.insert_type_vars(lt, Span::Dummy) } - fn insert_type_vars(&mut self, ty: T) -> T + fn insert_type_vars(&mut self, ty: T, span: Span) -> T where T: TypeFoldable>, { - self.table.insert_type_vars(ty) + self.table.insert_type_vars(ty, span) } /// Attempts to returns the deeply last field of nested structures, but @@ -2036,7 +2041,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { .associated_type_by_name(segments.first().unwrap().name) { // `::AssocType` - let args = self.infcx().fill_rest_fresh_args(assoc_type.into(), trait_ref.args); + let args = self.infcx().fill_rest_fresh_args( + node.into(), + assoc_type.into(), + trait_ref.args, + ); let alias = Ty::new_alias( self.interner(), AliasTy::new_from_args( @@ -2090,14 +2099,14 @@ impl<'body, 'db> InferenceContext<'body, 'db> { .db .ty(var.lookup(self.db).parent.into()) .instantiate(interner, args); - let ty = self.insert_type_vars(ty); + let ty = self.insert_type_vars(ty, Span::Dummy); return (ty, Some(var.into())); } ValueNs::StructId(strukt) => { let args = path_ctx.substs_from_path(strukt.into(), true, false); drop(ctx); let ty = self.db.ty(strukt.into()).instantiate(interner, args); - let ty = self.insert_type_vars(ty); + let ty = self.insert_type_vars(ty, Span::Dummy); return (ty, Some(strukt.into())); } ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None), @@ -2119,21 +2128,21 @@ impl<'body, 'db> InferenceContext<'body, 'db> { let args = path_ctx.substs_from_path(strukt.into(), true, false); drop(ctx); let ty = self.db.ty(strukt.into()).instantiate(interner, args); - let ty = self.insert_type_vars(ty); + let ty = self.insert_type_vars(ty, Span::Dummy); forbid_unresolved_segments(self, (ty, Some(strukt.into())), unresolved) } TypeNs::AdtId(AdtId::UnionId(u)) => { let args = path_ctx.substs_from_path(u.into(), true, false); drop(ctx); let ty = self.db.ty(u.into()).instantiate(interner, args); - let ty = self.insert_type_vars(ty); + let ty = self.insert_type_vars(ty, Span::Dummy); forbid_unresolved_segments(self, (ty, Some(u.into())), unresolved) } TypeNs::EnumVariantId(var) => { let args = path_ctx.substs_from_path(var.into(), true, false); drop(ctx); let ty = self.db.ty(var.lookup(self.db).parent.into()).instantiate(interner, args); - let ty = self.insert_type_vars(ty); + let ty = self.insert_type_vars(ty, Span::Dummy); forbid_unresolved_segments(self, (ty, Some(var.into())), unresolved) } TypeNs::SelfType(impl_id) => { @@ -2256,7 +2265,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { drop(ctx); let interner = DbInterner::conjure(); let ty = self.db.ty(it.into()).instantiate(interner, args); - let ty = self.insert_type_vars(ty); + let ty = self.insert_type_vars(ty, Span::Dummy); self.resolve_variant_on_alias(ty, unresolved, mod_path) } @@ -2487,8 +2496,8 @@ impl<'db> Expectation<'db> { } } - fn coercion_target_type(&self, table: &mut unify::InferenceTable<'db>) -> Ty<'db> { - self.only_has_type(table).unwrap_or_else(|| table.next_ty_var()) + fn coercion_target_type(&self, table: &mut unify::InferenceTable<'db>, span: Span) -> Ty<'db> { + self.only_has_type(table).unwrap_or_else(|| table.next_ty_var(span)) } /// Comment copied from rustc: diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs index a470c92124076..d8639a79bd0f4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs @@ -82,7 +82,7 @@ impl<'db> InferenceContext<'_, 'db> { } Some(CallStep::Builtin(callee_ty)) => { - self.confirm_builtin_call(call_expr, callee_ty, arg_exprs, expected) + self.confirm_builtin_call(callee_expr, call_expr, callee_ty, arg_exprs, expected) } Some(CallStep::DeferredClosure(_def_id, fn_sig)) => { @@ -128,6 +128,7 @@ impl<'db> InferenceContext<'_, 'db> { { let closure_sig = args.as_closure().sig(); let closure_sig = autoderef.ctx().infcx().instantiate_binder_with_fresh_vars( + callee_expr.into(), BoundRegionConversionTime::FnCall, closure_sig, ); @@ -158,15 +159,16 @@ impl<'db> InferenceContext<'_, 'db> { let closure_args = args.as_coroutine_closure(); let coroutine_closure_sig = autoderef.ctx().infcx().instantiate_binder_with_fresh_vars( + callee_expr.into(), BoundRegionConversionTime::FnCall, closure_args.coroutine_closure_sig(), ); - let tupled_upvars_ty = autoderef.ctx().table.next_ty_var(); + let tupled_upvars_ty = autoderef.ctx().table.next_ty_var(call_expr.into()); // We may actually receive a coroutine back whose kind is different // from the closure that this dispatched from. This is because when // we have no captures, we automatically implement `FnOnce`. This // impl forces the closure kind to `FnOnce` i.e. `u8`. - let kind_ty = autoderef.ctx().table.next_ty_var(); + let kind_ty = autoderef.ctx().table.next_ty_var(call_expr.into()); let interner = autoderef.ctx().interner(); let call_sig = interner.mk_fn_sig( [coroutine_closure_sig.tupled_inputs_ty], @@ -297,7 +299,7 @@ impl<'db> InferenceContext<'_, 'db> { let opt_input_type = opt_arg_exprs.map(|arg_exprs| { Ty::new_tup_from_iter( self.interner(), - arg_exprs.iter().map(|_| self.table.next_ty_var()), + arg_exprs.iter().map(|&arg| self.table.next_ty_var(arg.into())), ) }); @@ -388,6 +390,7 @@ impl<'db> InferenceContext<'_, 'db> { fn confirm_builtin_call( &mut self, + callee_expr: ExprId, call_expr: ExprId, callee_ty: Ty<'db>, arg_exprs: &[ExprId], @@ -411,9 +414,11 @@ impl<'db> InferenceContext<'_, 'db> { // renormalize the associated types at this point, since they // previously appeared within a `Binder<>` and hence would not // have been normalized before. - let fn_sig = self - .infcx() - .instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, fn_sig); + let fn_sig = self.infcx().instantiate_binder_with_fresh_vars( + callee_expr.into(), + BoundRegionConversionTime::FnCall, + fn_sig, + ); let indices_to_skip = self.check_legacy_const_generics(def_id, arg_exprs); self.check_call_arguments( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index a53ab7daaaa34..078c5c707d0a9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -65,19 +65,19 @@ impl<'db> InferenceContext<'_, 'db> { let (expected_sig, expected_kind) = match expected.to_option(&mut self.table) { Some(ty) => { let ty = self.table.try_structurally_resolve_type(ty); - self.deduce_closure_signature(ty, closure_kind) + self.deduce_closure_signature(closure_expr, ty, closure_kind) } None => (None, None), }; let ClosureSignatures { bound_sig, mut liberated_sig } = - self.sig_of_closure(arg_types, ret_type, expected_sig); + self.sig_of_closure(closure_expr, arg_types, ret_type, expected_sig); debug!(?bound_sig, ?liberated_sig); let parent_args = GenericArgs::identity_for_item(interner, self.generic_def.into()); - let tupled_upvars_ty = self.table.next_ty_var(); + let tupled_upvars_ty = self.table.next_ty_var(closure_expr.into()); // FIXME: We could probably actually just unify this further -- // instead of having a `FnSig` and a `Option`, @@ -103,7 +103,7 @@ impl<'db> InferenceContext<'_, 'db> { Some(kind) => Ty::from_closure_kind(interner, kind), // Create a type variable (for now) to represent the closure kind. // It will be unified during the upvar inference phase (`upvar.rs`) - None => self.table.next_ty_var(), + None => self.table.next_ty_var(closure_expr.into()), }; let closure_args = ClosureArgs::new( @@ -123,7 +123,7 @@ impl<'db> InferenceContext<'_, 'db> { } ClosureKind::Coroutine(_) | ClosureKind::AsyncBlock { .. } => { let yield_ty = match closure_kind { - ClosureKind::Coroutine(_) => self.table.next_ty_var(), + ClosureKind::Coroutine(_) => self.table.next_ty_var(closure_expr.into()), ClosureKind::AsyncBlock { .. } => self.types.types.unit, _ => unreachable!(), }; @@ -138,7 +138,7 @@ impl<'db> InferenceContext<'_, 'db> { // ty of `().` let kind_ty = match closure_kind { ClosureKind::AsyncBlock { source: CoroutineSource::Closure } => { - self.table.next_ty_var() + self.table.next_ty_var(closure_expr.into()) } _ => self.types.types.unit, }; @@ -169,17 +169,17 @@ impl<'db> InferenceContext<'_, 'db> { let (bound_return_ty, bound_yield_ty) = (bound_sig.skip_binder().output(), self.types.types.unit); // Compute all of the variables that will be used to populate the coroutine. - let resume_ty = self.table.next_ty_var(); + let resume_ty = self.table.next_ty_var(closure_expr.into()); let closure_kind_ty = match expected_kind { Some(kind) => Ty::from_closure_kind(interner, kind), // Create a type variable (for now) to represent the closure kind. // It will be unified during the upvar inference phase (`upvar.rs`) - None => self.table.next_ty_var(), + None => self.table.next_ty_var(closure_expr.into()), }; - let coroutine_captures_by_ref_ty = self.table.next_ty_var(); + let coroutine_captures_by_ref_ty = self.table.next_ty_var(closure_expr.into()); let closure_args = CoroutineClosureArgs::new( interner, @@ -214,10 +214,10 @@ impl<'db> InferenceContext<'_, 'db> { // Create a type variable (for now) to represent the closure kind. // It will be unified during the upvar inference phase (`upvar.rs`) - None => self.table.next_ty_var(), + None => self.table.next_ty_var(closure_expr.into()), }; - let coroutine_upvars_ty = self.table.next_ty_var(); + let coroutine_upvars_ty = self.table.next_ty_var(closure_expr.into()); let coroutine_closure_id = InternedCoroutineClosureId::new( self.db, @@ -316,12 +316,14 @@ impl<'db> InferenceContext<'_, 'db> { /// are about to type check: fn deduce_closure_signature( &mut self, + closure_expr: ExprId, expected_ty: Ty<'db>, closure_kind: ClosureKind, ) -> (Option>, Option) { match expected_ty.kind() { TyKind::Alias(AliasTy { kind: rustc_type_ir::Opaque { def_id }, args, .. }) => self .deduce_closure_signature_from_predicates( + closure_expr, expected_ty, closure_kind, def_id @@ -333,7 +335,7 @@ impl<'db> InferenceContext<'_, 'db> { TyKind::Dynamic(object_type, ..) => { let sig = object_type.projection_bounds().into_iter().find_map(|pb| { let pb = pb.with_self_ty(self.interner(), Ty::new_unit(self.interner())); - self.deduce_sig_from_projection(closure_kind, pb) + self.deduce_sig_from_projection(closure_expr, closure_kind, pb) }); let kind = object_type .principal_def_id() @@ -342,6 +344,7 @@ impl<'db> InferenceContext<'_, 'db> { } TyKind::Infer(rustc_type_ir::TyVar(vid)) => self .deduce_closure_signature_from_predicates( + closure_expr, Ty::new_var(self.interner(), self.table.infer_ctxt.root_var(vid)), closure_kind, self.table.obligations_for_self_ty(vid).into_iter().map(|obl| obl.predicate), @@ -361,6 +364,7 @@ impl<'db> InferenceContext<'_, 'db> { fn deduce_closure_signature_from_predicates( &mut self, + closure_expr: ExprId, expected_ty: Ty<'db>, closure_kind: ClosureKind, predicates: impl DoubleEndedIterator>, @@ -388,6 +392,7 @@ impl<'db> InferenceContext<'_, 'db> { bound_predicate.skip_binder() { let inferred_sig = self.deduce_sig_from_projection( + closure_expr, closure_kind, bound_predicate.rebind(proj_predicate), ); @@ -430,7 +435,7 @@ impl<'db> InferenceContext<'_, 'db> { // This is a bit weird and means we may wind up discarding the goal due to it naming `expected_ty` // even though the normalized form may not name `expected_ty`. However, this matches the existing // behaviour of the old solver and would be technically a breaking change to fix. - let generalized_fnptr_sig = self.table.next_ty_var(); + let generalized_fnptr_sig = self.table.next_ty_var(closure_expr.into()); let inferred_fnptr_sig = Ty::new_fn_ptr(self.interner(), inferred_sig); // FIXME: Report diagnostics. _ = self @@ -500,6 +505,7 @@ impl<'db> InferenceContext<'_, 'db> { /// know that. fn deduce_sig_from_projection( &mut self, + closure_expr: ExprId, closure_kind: ClosureKind, projection: PolyProjectionPredicate<'db>, ) -> Option> { @@ -518,7 +524,7 @@ impl<'db> InferenceContext<'_, 'db> { // `F: FnOnce() -> Fut, Fut: Future` style bound. Let's still // guide inference here, since it's beneficial for the user. ClosureKind::AsyncClosure if Some(def_id) == self.lang_items.FnOnceOutput => { - self.extract_sig_from_projection_and_future_bound(projection) + self.extract_sig_from_projection_and_future_bound(closure_expr, projection) } _ => None, } @@ -573,6 +579,7 @@ impl<'db> InferenceContext<'_, 'db> { /// projection, and the output will be an unconstrained type variable instead. fn extract_sig_from_projection_and_future_bound( &mut self, + closure_expr: ExprId, projection: PolyProjectionPredicate<'db>, ) -> Option> { let projection = self.resolve_vars_if_possible(projection); @@ -624,7 +631,7 @@ impl<'db> InferenceContext<'_, 'db> { // // FIXME: We probably should store this signature inference output in a way // that does not misuse a `FnSig` type, but that can be done separately. - let return_ty = return_ty.unwrap_or_else(|| self.table.next_ty_var()); + let return_ty = return_ty.unwrap_or_else(|| self.table.next_ty_var(closure_expr.into())); let sig = projection.rebind(self.interner().mk_fn_sig_safe_rust_abi(input_tys, return_ty)); @@ -633,14 +640,15 @@ impl<'db> InferenceContext<'_, 'db> { fn sig_of_closure( &mut self, + closure_expr: ExprId, decl_inputs: &[Option], decl_output: Option, expected_sig: Option>, ) -> ClosureSignatures<'db> { if let Some(e) = expected_sig { - self.sig_of_closure_with_expectation(decl_inputs, decl_output, e) + self.sig_of_closure_with_expectation(closure_expr, decl_inputs, decl_output, e) } else { - self.sig_of_closure_no_expectation(decl_inputs, decl_output) + self.sig_of_closure_no_expectation(closure_expr, decl_inputs, decl_output) } } @@ -648,10 +656,11 @@ impl<'db> InferenceContext<'_, 'db> { /// types that the user gave into a signature. fn sig_of_closure_no_expectation( &mut self, + closure_expr: ExprId, decl_inputs: &[Option], decl_output: Option, ) -> ClosureSignatures<'db> { - let bound_sig = self.supplied_sig_of_closure(decl_inputs, decl_output); + let bound_sig = self.supplied_sig_of_closure(closure_expr, decl_inputs, decl_output); self.closure_sigs(bound_sig) } @@ -705,6 +714,7 @@ impl<'db> InferenceContext<'_, 'db> { /// regions with depth 1, which are bound then by the closure. fn sig_of_closure_with_expectation( &mut self, + closure_expr: ExprId, decl_inputs: &[Option], decl_output: Option, expected_sig: PolyFnSig<'db>, @@ -713,7 +723,7 @@ impl<'db> InferenceContext<'_, 'db> { // expectation if things don't see to match up with what we // expect. if expected_sig.c_variadic() { - return self.sig_of_closure_no_expectation(decl_inputs, decl_output); + return self.sig_of_closure_no_expectation(closure_expr, decl_inputs, decl_output); } else if expected_sig.skip_binder().inputs_and_output.len() != decl_inputs.len() + 1 { return self .sig_of_closure_with_mismatched_number_of_arguments(decl_inputs, decl_output); @@ -745,9 +755,14 @@ impl<'db> InferenceContext<'_, 'db> { // Along the way, it also writes out entries for types that the user // wrote into our typeck results, which are then later used by the privacy // check. - match self.merge_supplied_sig_with_expectation(decl_inputs, decl_output, closure_sigs) { + match self.merge_supplied_sig_with_expectation( + closure_expr, + decl_inputs, + decl_output, + closure_sigs, + ) { Ok(infer_ok) => self.table.register_infer_ok(infer_ok), - Err(_) => self.sig_of_closure_no_expectation(decl_inputs, decl_output), + Err(_) => self.sig_of_closure_no_expectation(closure_expr, decl_inputs, decl_output), } } @@ -766,6 +781,7 @@ impl<'db> InferenceContext<'_, 'db> { /// strategy. fn merge_supplied_sig_with_expectation( &mut self, + closure_expr: ExprId, decl_inputs: &[Option], decl_output: Option, mut expected_sigs: ClosureSignatures<'db>, @@ -774,7 +790,7 @@ impl<'db> InferenceContext<'_, 'db> { // // (See comment on `sig_of_closure_with_expectation` for the // meaning of these letters.) - let supplied_sig = self.supplied_sig_of_closure(decl_inputs, decl_output); + let supplied_sig = self.supplied_sig_of_closure(closure_expr, decl_inputs, decl_output); debug!(?supplied_sig); @@ -795,6 +811,7 @@ impl<'db> InferenceContext<'_, 'db> { self.table.commit_if_ok(|table| { let mut all_obligations = PredicateObligations::new(); let supplied_sig = table.infer_ctxt.instantiate_binder_with_fresh_vars( + closure_expr.into(), BoundRegionConversionTime::FnCall, supplied_sig, ); @@ -842,6 +859,7 @@ impl<'db> InferenceContext<'_, 'db> { /// Also, record this closure signature for later. fn supplied_sig_of_closure( &mut self, + closure_expr: ExprId, decl_inputs: &[Option], decl_output: Option, ) -> PolyFnSig<'db> { @@ -852,7 +870,7 @@ impl<'db> InferenceContext<'_, 'db> { let output = self.make_body_ty(output); self.process_user_written_ty(output) } - None => self.table.next_ty_var(), + None => self.table.next_ty_var(closure_expr.into()), }; // First, convert the types that the user supplied (if any). let supplied_arguments = decl_inputs.iter().map(|&input| match input { @@ -860,7 +878,7 @@ impl<'db> InferenceContext<'_, 'db> { let input = self.make_body_ty(input); self.process_user_written_ty(input) } - None => self.table.next_ty_var(), + None => self.table.next_ty_var(closure_expr.into()), }); Binder::dummy(interner.mk_fn_sig( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 962fc752a5eb3..60f7b4073ac45 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -52,7 +52,7 @@ use smallvec::SmallVec; use tracing::{debug, instrument}; use crate::{ - Adjust, Adjustment, AutoBorrow, ParamEnvAndCrate, PointerCast, TargetFeatures, + Adjust, Adjustment, AutoBorrow, ParamEnvAndCrate, PointerCast, Span, TargetFeatures, autoderef::Autoderef, db::{HirDatabase, InternedClosure, InternedClosureId}, infer::{ @@ -316,7 +316,8 @@ where if b.is_infer() { // Two unresolved type variables: create a `Coerce` predicate. - let target_ty = if self.use_lub { self.infcx().next_ty_var() } else { b }; + let target_ty = + if self.use_lub { self.infcx().next_ty_var(self.cause.span()) } else { b }; let mut obligations = PredicateObligations::with_capacity(2); for &source_ty in &[a, b] { @@ -463,7 +464,7 @@ where } else { if r_borrow_var.is_none() { // create var lazily, at most once - let r = self.infcx().next_region_var(); + let r = self.infcx().next_region_var(self.cause.span()); r_borrow_var = Some(r); // [4] above } r_borrow_var.unwrap() @@ -624,7 +625,7 @@ where (TyKind::Ref(_, ty_a, mutbl_a), TyKind::Ref(_, _, mutbl_b)) => { coerce_mutbls(mutbl_a, mutbl_b)?; - let r_borrow = self.infcx().next_region_var(); + let r_borrow = self.infcx().next_region_var(self.cause.span()); // We don't allow two-phase borrows here, at least for initial // implementation. If it happens that this coercion is a function argument, @@ -658,7 +659,7 @@ where // the `CoerceUnsized` target type and the expected type. // We only have the latter, so we use an inference variable // for the former and let type inference do the rest. - let coerce_target = self.infcx().next_ty_var(); + let coerce_target = self.infcx().next_ty_var(self.cause.span()); let mut coercion = self.unify_and( coerce_target, @@ -688,6 +689,7 @@ where errored: false, unsize_did, coerce_unsized_did, + span: self.cause.span(), }, ) .is_break() @@ -1451,7 +1453,7 @@ fn coerce<'db>( ) -> Result<(Vec, Ty<'db>), TypeError>> { let interner = DbInterner::new_with(db, env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let ((ty1_with_vars, ty2_with_vars), vars) = infcx.instantiate_canonical(tys); + let ((ty1_with_vars, ty2_with_vars), vars) = infcx.instantiate_canonical(Span::Dummy, tys); let cause = ObligationCause::new(); // FIXME: Target features. @@ -1611,11 +1613,16 @@ struct CoerceVisitor<'a, D> { errored: bool, unsize_did: TraitId, coerce_unsized_did: TraitId, + span: Span, } impl<'a, 'db, D: CoerceDelegate<'db>> ProofTreeVisitor<'db> for CoerceVisitor<'a, D> { type Result = ControlFlow<()>; + fn span(&self) -> Span { + self.span + } + fn visit_goal(&mut self, goal: &InspectGoal<'_, 'db>) -> Self::Result { let Some(pred) = goal.goal().predicate.as_trait_clause() else { return ControlFlow::Continue(()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index e179ca4bd3c81..d2d3849b2e370 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -27,7 +27,7 @@ use syntax::ast::RangeOp; use tracing::debug; use crate::{ - Adjust, Adjustment, CallableDefId, Rawness, consteval, + Adjust, Adjustment, CallableDefId, Rawness, Span, consteval, infer::{AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable, pat::PatOrigin}, lower::lower_mutability, method_resolution::{self, CandidateId, MethodCallee, MethodError}, @@ -343,7 +343,7 @@ impl<'db> InferenceContext<'_, 'db> { coercion_sites[1] = else_branch; } let mut coerce = CoerceMany::with_coercion_sites( - expected.coercion_target_type(&mut self.table), + expected.coercion_target_type(&mut self.table, then_branch.into()), &coercion_sites, ); coerce.coerce(self, &ObligationCause::new(), then_branch, then_ty, ExprIsRead::Yes); @@ -397,7 +397,7 @@ impl<'db> InferenceContext<'_, 'db> { .1 } &Expr::Loop { body, label } => { - let ty = expected.coercion_target_type(&mut self.table); + let ty = expected.coercion_target_type(&mut self.table, tgt_expr.into()); let (breaks, ()) = self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| { this.infer_expr( @@ -466,7 +466,7 @@ impl<'db> InferenceContext<'_, 'db> { // We don't coerce to `()` so that if the match expression is a // statement it's branches can have any consistent type. Expectation::HasType(ty) if *ty != self.types.types.unit => *ty, - _ => self.table.next_ty_var(), + _ => self.table.next_ty_var((*expr).into()), }; let mut coerce = CoerceMany::new(result_ty); @@ -629,7 +629,7 @@ impl<'db> InferenceContext<'_, 'db> { match rawness { Rawness::RawPtr => Ty::new_ptr(self.interner(), inner_ty, mutability), Rawness::Ref => { - let lt = self.table.next_region_var(); + let lt = self.table.next_region_var(tgt_expr.into()); Ty::new_ref(self.interner(), lt, inner_ty, mutability) } } @@ -675,7 +675,7 @@ impl<'db> InferenceContext<'_, 'db> { // However, rustc lowers destructuring assignments into blocks, and blocks return `!` if they have no tail // expression and they diverge. Therefore, we have to do the same here, even though we don't lower destructuring // assignments into blocks. - self.table.new_maybe_never_var() + self.table.new_maybe_never_var(value.into()) } else { self.types.types.unit } @@ -731,7 +731,7 @@ impl<'db> InferenceContext<'_, 'db> { let idx_t = self.infer_expr_no_expect(*index, ExprIsRead::Yes); let base_t = self.structurally_resolve_type((*base).into(), base_t); - match self.lookup_indexing(tgt_expr, *base, base_t, idx_t) { + match self.lookup_indexing(tgt_expr, *base, *index, base_t, idx_t) { Some((trait_index_ty, trait_element_ty)) => { // two-phase not needed because index_ty is never mutable self.demand_coerce( @@ -755,10 +755,10 @@ impl<'db> InferenceContext<'_, 'db> { { Some(TyKind::Tuple(substs)) => substs .iter() - .chain(repeat_with(|| self.table.next_ty_var())) + .chain(repeat_with(|| self.table.next_ty_var(Span::Dummy))) .take(exprs.len()) .collect::>(), - _ => (0..exprs.len()).map(|_| self.table.next_ty_var()).collect(), + _ => exprs.iter().map(|&expr| self.table.next_ty_var(expr.into())).collect(), }; for (expr, ty) in exprs.iter().zip(tys.iter_mut()) { @@ -768,7 +768,7 @@ impl<'db> InferenceContext<'_, 'db> { Ty::new_tup(self.interner(), &tys) } - Expr::Array(array) => self.infer_expr_array(array, expected), + Expr::Array(array) => self.infer_expr_array(tgt_expr, array, expected), Expr::Literal(lit) => match lit { Literal::Bool(..) => self.types.types.bool, Literal::String(..) => self.types.types.static_str_ref, @@ -1119,7 +1119,7 @@ impl<'db> InferenceContext<'_, 'db> { if self.features.type_changing_struct_update { if matches!(adt_id, AdtId::StructId(_)) { // Make some fresh generic parameters for our ADT type. - let fresh_args = self.table.fresh_args_for_item(adt_id.into()); + let fresh_args = self.table.fresh_args_for_item(expr.into(), adt_id.into()); // We do subtyping on the FRU fields first, so we can // learn exactly what types we expect the base expr // needs constrained to be compatible with the struct @@ -1266,7 +1266,7 @@ impl<'db> InferenceContext<'_, 'db> { // ...but otherwise we want to use any supertype of the // scrutinee. This is sort of a workaround, see note (*) in // `check_pat` for some details. - let scrut_ty = self.table.next_ty_var(); + let scrut_ty = self.table.next_ty_var(scrut.into()); self.infer_expr_coerce_never(scrut, &Expectation::HasType(scrut_ty), scrutinee_is_read); scrut_ty } @@ -1328,13 +1328,19 @@ impl<'db> InferenceContext<'_, 'db> { } oprnd_t } - fn infer_expr_array(&mut self, array: &Array, expected: &Expectation<'db>) -> Ty<'db> { + + fn infer_expr_array( + &mut self, + expr: ExprId, + array: &Array, + expected: &Expectation<'db>, + ) -> Ty<'db> { let elem_ty = match expected .to_option(&mut self.table) .map(|t| self.table.try_structurally_resolve_type(t).kind()) { Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st, - _ => self.table.next_ty_var(), + _ => self.table.next_ty_var(expr.into()), }; let krate = self.resolver.krate(); @@ -1371,7 +1377,7 @@ impl<'db> InferenceContext<'_, 'db> { let len = match self.store[repeat] { Expr::Underscore => { self.write_expr_ty(repeat, usize); - self.table.next_const_var() + self.table.next_const_var(repeat.into()) } _ => { self.infer_expr(repeat, &Expectation::HasType(usize), ExprIsRead::Yes); @@ -1383,7 +1389,7 @@ impl<'db> InferenceContext<'_, 'db> { } }; // Try to evaluate unevaluated constant, and insert variable if is not possible. - let len = self.table.insert_const_vars_shallow(len); + let len = self.insert_const_vars_shallow(len); Ty::new_array_with_const_len(self.interner(), elem_ty, len) } @@ -1477,7 +1483,7 @@ impl<'db> InferenceContext<'_, 'db> { label: Option, expected: &Expectation<'db>, ) -> Ty<'db> { - let coerce_ty = expected.coercion_target_type(&mut self.table); + let coerce_ty = expected.coercion_target_type(&mut self.table, expr.into()); let g = self.resolver.update_to_inner_scope(self.db, self.owner, expr); let (break_ty, ty) = @@ -1488,7 +1494,7 @@ impl<'db> InferenceContext<'_, 'db> { let decl_ty = type_ref .as_ref() .map(|&tr| this.make_body_ty(tr)) - .unwrap_or_else(|| this.table.next_ty_var()); + .unwrap_or_else(|| this.table.next_ty_var((*pat).into())); let ty = if let Some(expr) = initializer { // If we have a subpattern that performs a read, we want to consider this @@ -1561,7 +1567,7 @@ impl<'db> InferenceContext<'_, 'db> { // `!`). if this.diverges.is_always() { // we don't even make an attempt at coercion - this.table.new_maybe_never_var() + this.table.new_maybe_never_var(expr.into()) } else if let Some(t) = expected.only_has_type(&mut this.table) { if this .coerce( @@ -1728,10 +1734,13 @@ impl<'db> InferenceContext<'_, 'db> { fn instantiate_erroneous_method(&mut self, def_id: FunctionId) -> MethodCallee<'db> { // FIXME: Using fresh infer vars for the method args isn't optimal, // we can do better by going thorough the full probe/confirm machinery. - let args = self.table.fresh_args_for_item(def_id.into()); + let args = self.table.fresh_args_for_item(Span::Dummy, def_id.into()); let sig = self.db.callable_item_signature(def_id.into()).instantiate(self.interner(), args); - let sig = - self.infcx().instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, sig); + let sig = self.infcx().instantiate_binder_with_fresh_vars( + Span::Dummy, + BoundRegionConversionTime::FnCall, + sig, + ); MethodCallee { def_id, args, sig } } @@ -1810,20 +1819,21 @@ impl<'db> InferenceContext<'_, 'db> { None => None, }; - let assoc_func_with_same_name = self.with_method_resolution(|ctx| { - if !matches!( - receiver_ty.kind(), - TyKind::Infer(InferTy::TyVar(_)) | TyKind::Error(_) - ) { - ctx.probe_for_name( - method_resolution::Mode::Path, - method_name.clone(), - receiver_ty, - ) - } else { - Err(MethodError::ErrorReported) - } - }); + let assoc_func_with_same_name = + self.with_method_resolution(tgt_expr.into(), receiver.into(), |ctx| { + if !matches!( + receiver_ty.kind(), + TyKind::Infer(InferTy::TyVar(_)) | TyKind::Error(_) + ) { + ctx.probe_for_name( + method_resolution::Mode::Path, + method_name.clone(), + receiver_ty, + ) + } else { + Err(MethodError::ErrorReported) + } + }); let assoc_func_with_same_name = match assoc_func_with_same_name { Ok(method_resolution::Pick { item: CandidateId::FunctionId(def_id), .. @@ -1928,7 +1938,7 @@ impl<'db> InferenceContext<'_, 'db> { let formal_input_tys: Vec<_> = formal_input_tys .iter() .map(|&ty| { - let generalized_ty = self.table.next_ty_var(); + let generalized_ty = self.table.next_ty_var(call_expr.into()); let _ = self.demand_eqtype(call_expr.into(), ty, generalized_ty); generalized_ty }) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index b2369f6a87e83..c3b532638f9d3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -32,6 +32,7 @@ impl<'db> InferenceContext<'_, 'db> { }; if let Some(infer_ok) = Self::try_mutable_overloaded_place_op( &self.table, + tgt_expr, source_ty, None, PlaceOp::Deref, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs index 2916a46ca3302..5900027a98769 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs @@ -180,7 +180,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`. let lhs_ty = self.infer_expr_no_expect(lhs_expr, ExprIsRead::Yes); - let fresh_var = self.table.next_ty_var(); + let fresh_var = self.table.next_ty_var(lhs_expr.into()); self.demand_coerce(lhs_expr, lhs_ty, fresh_var, AllowTwoPhase::No, ExprIsRead::Yes) } }; @@ -192,7 +192,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // using this variable as the expected type, which sometimes lets // us do better coercions than we would be able to do otherwise, // particularly for things like `String + &String`. - let rhs_ty_var = self.table.next_ty_var(); + let rhs_ty_var = self.table.next_ty_var(rhs_expr.into()); let result = self.lookup_op_method( lhs_ty, Some((rhs_expr, rhs_ty_var)), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 3cd7461cf317b..2c38fe74b14e8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -26,7 +26,7 @@ use span::Edition; use tracing::{debug, instrument, trace}; use crate::{ - BindingMode, InferenceDiagnostic, + BindingMode, InferenceDiagnostic, Span, infer::{ AllowTwoPhase, ByRef, Expectation, InferenceContext, PatAdjust, PatAdjustment, TypeMismatch, expr::ExprIsRead, @@ -928,7 +928,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` // is required. However, we use equality, which is stronger. // See (note_1) for an explanation. - self.new_ref_ty(mutbl, expected) + self.new_ref_ty(pat.into(), mutbl, expected) } // Otherwise, the type of x is the expected type `T`. ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). @@ -1110,7 +1110,9 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; } let max_len = cmp::max(expected_len, elements.len()); - let element_tys_iter = (0..max_len).map(|_| self.table.next_ty_var()); + let element_tys_iter = (0..max_len).map(|i| { + self.table.next_ty_var(elements.get(i).copied().map(Span::PatId).unwrap_or(Span::Dummy)) + }); let element_tys = Tys::new_from_iter(interner, element_tys_iter); let pat_ty = Ty::new(interner, TyKind::Tuple(element_tys)); if self.demand_eqtype(pat.into(), expected, pat_ty).is_err() { @@ -1249,7 +1251,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; .map(|()| { // Here, `demand::subtype` is good enough, but I don't // think any errors can be introduced by using `demand::eqtype`. - let inner_ty = self.table.next_ty_var(); + let inner_ty = self.table.next_ty_var(inner.into()); let box_ty = Ty::new_box(interner, inner_ty); _ = self.demand_eqtype(pat.into(), expected, box_ty); (box_ty, inner_ty) @@ -1472,8 +1474,8 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; (expected, r_ty) } _ => { - let inner_ty = self.table.next_ty_var(); - let ref_ty = self.new_ref_ty(pat_mutbl, inner_ty); + let inner_ty = self.table.next_ty_var(inner.into()); + let ref_ty = self.new_ref_ty(inner.into(), pat_mutbl, inner_ty); debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); _ = self.demand_eqtype(pat.into(), expected, ref_ty); @@ -1492,8 +1494,8 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; } /// Create a reference or pinned reference type with a fresh region variable. - fn new_ref_ty(&self, mutbl: Mutability, ty: Ty<'db>) -> Ty<'db> { - let region = self.table.next_region_var(); + fn new_ref_ty(&self, span: Span, mutbl: Mutability, ty: Ty<'db>) -> Ty<'db> { + let region = self.table.next_region_var(span); Ty::new_ref(self.interner(), region, ty, mutbl) } @@ -1501,6 +1503,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; &self, before: &[PatId], slice: Option, + pat: PatId, ) -> Option> { if slice.is_some() { return None; @@ -1508,7 +1511,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; let interner = self.interner(); let len = before.len(); - let inner_ty = self.table.next_ty_var(); + let inner_ty = self.table.next_ty_var(pat.into()); Some(Ty::new_array(interner, inner_ty, len.try_into().unwrap())) } @@ -1576,7 +1579,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; // to an array if the given pattern allows it. See issue #76342 if self.pat_is_irrefutable(pat_info.pat_origin) && expected.is_ty_var() - && let Some(resolved_arr_ty) = self.try_resolve_slice_ty_to_array_ty(before, slice) + && let Some(resolved_arr_ty) = self.try_resolve_slice_ty_to_array_ty(before, slice, pat) { debug!(?resolved_arr_ty); let _ = self.demand_eqtype(pat.into(), expected, resolved_arr_ty); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 0687d56024d38..2c094f572fa97 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -11,7 +11,7 @@ use rustc_type_ir::inherent::{SliceLike, Ty as _}; use stdx::never; use crate::{ - InferenceDiagnostic, ValueTyDefId, + InferenceDiagnostic, Span, ValueTyDefId, infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, lower::{GenericPredicates, LifetimeElisionKind}, method_resolution::{self, CandidateId, MethodError}, @@ -38,7 +38,7 @@ impl<'db> InferenceContext<'_, 'db> { } ValuePathResolution::NonGeneric(ty) => return Some((value, ty)), }; - let args = self.insert_type_vars(substs); + let args = self.insert_type_vars(substs, Span::Dummy); self.add_required_obligations_for_value_path(generic_def, args); @@ -187,7 +187,7 @@ impl<'db> InferenceContext<'_, 'db> { let (resolution, substs) = match (def, is_before_last) { (TypeNs::TraitId(trait_), true) => { - let self_ty = self.table.next_ty_var(); + let self_ty = self.table.next_ty_var(id.into()); let trait_ref = path_ctx.lower_trait_ref_from_resolved_path(trait_, self_ty, true); drop_ctx(ctx, no_diagnostics); @@ -290,7 +290,7 @@ impl<'db> InferenceContext<'_, 'db> { return Some(result); } - let res = self.with_method_resolution(|ctx| { + let res = self.with_method_resolution(Span::Dummy, Span::Dummy, |ctx| { ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty) }); let (item, visible) = match res { @@ -310,7 +310,7 @@ impl<'db> InferenceContext<'_, 'db> { }; let substs = match container { ItemContainerId::ImplId(impl_id) => { - let impl_substs = self.table.fresh_args_for_item(impl_id.into()); + let impl_substs = self.table.fresh_args_for_item(id.into(), impl_id.into()); let impl_self_ty = self.db.impl_self_ty(impl_id).instantiate(self.interner(), impl_substs); _ = self.demand_eqtype(id, impl_self_ty, ty); @@ -318,9 +318,12 @@ impl<'db> InferenceContext<'_, 'db> { } ItemContainerId::TraitId(trait_) => { // we're picking this method - GenericArgs::fill_rest(self.interner(), trait_.into(), [ty.into()], |_, id, _| { - self.table.next_var_for_param(id) - }) + GenericArgs::fill_rest( + self.interner(), + trait_.into(), + [ty.into()], + |_, param, _| self.table.var_for_def(param, id.into()), + ) } ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => { never!("assoc item contained in module/extern block"); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs index 1671b67d347e1..63841af682ed9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs @@ -29,9 +29,10 @@ pub(super) enum PlaceOp { impl<'a, 'db> InferenceContext<'a, 'db> { pub(super) fn try_overloaded_deref( &self, + expr: ExprId, base_ty: Ty<'db>, ) -> Option>> { - self.try_overloaded_place_op(base_ty, None, PlaceOp::Deref) + self.try_overloaded_place_op(expr, base_ty, None, PlaceOp::Deref) } /// For the overloaded place expressions (`*x`, `x[3]`), the trait @@ -57,7 +58,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { return Some(ty); } - let ok = self.try_overloaded_deref(oprnd_ty)?; + let ok = self.try_overloaded_deref(expr, oprnd_ty)?; let method = self.table.register_infer_ok(ok); if let TyKind::Ref(_, _, Mutability::Not) = method.sig.inputs_and_output.inputs()[0].kind() { @@ -81,6 +82,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { &mut self, expr: ExprId, base_expr: ExprId, + index_expr: ExprId, base_ty: Ty<'db>, idx_ty: Ty<'db>, ) -> Option<(/*index type*/ Ty<'db>, /*element type*/ Ty<'db>)> { @@ -91,7 +93,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { let mut autoderef = InferenceContextAutoderef::new_from_inference_context(self, base_ty); let mut result = None; while result.is_none() && autoderef.next().is_some() { - result = Self::try_index_step(expr, base_expr, &mut autoderef, idx_ty); + result = Self::try_index_step(expr, base_expr, index_expr, &mut autoderef, idx_ty); } result } @@ -104,6 +106,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { fn try_index_step( expr: ExprId, base_expr: ExprId, + index_expr: ExprId, autoderef: &mut InferenceContextAutoderef<'_, 'a, 'db>, index_ty: Ty<'db>, ) -> Option<(/*index type*/ Ty<'db>, /*element type*/ Ty<'db>)> { @@ -136,9 +139,13 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // If some lookup succeeds, write callee into table and extract index/element // type from the method signature. // If some lookup succeeded, install method in table - let input_ty = autoderef.ctx().table.next_ty_var(); - let method = - autoderef.ctx().try_overloaded_place_op(self_ty, Some(input_ty), PlaceOp::Index); + let input_ty = autoderef.ctx().table.next_ty_var(index_expr.into()); + let method = autoderef.ctx().try_overloaded_place_op( + expr, + self_ty, + Some(input_ty), + PlaceOp::Index, + ); if let Some(result) = method { debug!("try_index_step: success, using overloaded indexing"); @@ -180,6 +187,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { /// `convert_place_derefs_to_mutable`. pub(super) fn try_overloaded_place_op( &self, + expr: ExprId, base_ty: Ty<'db>, opt_rhs_ty: Option>, op: PlaceOp, @@ -198,7 +206,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // opaque types as rigid here to support `impl Deref>`. let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; self.table.lookup_method_for_operator( - ObligationCause::new(), + ObligationCause::with_span(expr.into()), imm_op, imm_tr, base_ty, @@ -209,6 +217,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { pub(super) fn try_mutable_overloaded_place_op( table: &InferenceTable<'db>, + expr: ExprId, base_ty: Ty<'db>, opt_rhs_ty: Option>, op: PlaceOp, @@ -230,7 +239,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // of the opaque. let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; table.lookup_method_for_operator( - ObligationCause::new(), + ObligationCause::with_span(expr.into()), mut_op, mut_tr, base_ty, @@ -276,7 +285,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { )) } }; - let method = Self::try_mutable_overloaded_place_op(&self.table, base_ty, arg_ty, op); + let method = Self::try_mutable_overloaded_place_op(&self.table, expr, base_ty, arg_ty, op); let method = match method { Some(ok) => self.table.register_infer_ok(ok), // Couldn't find the mutable variant of the place op, keep the diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index be4d370c24886..31b6d50886266 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -7,12 +7,13 @@ use hir_def::{ExpressionStoreOwnerId, GenericParamId, TraitId}; use rustc_hash::FxHashSet; use rustc_type_ir::{ TyVid, TypeFoldable, TypeVisitableExt, - inherent::{Const as _, GenericArg as _, IntoKind, Ty as _}, + inherent::{GenericArg as _, IntoKind, Ty as _}, solve::Certainty, }; use smallvec::SmallVec; use crate::{ + Span, db::HirDatabase, next_solver::{ Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, @@ -41,6 +42,10 @@ struct NestedObligationsForSelfTy<'a, 'db> { impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { type Result = (); + fn span(&self) -> Span { + self.root_cause.span() + } + fn config(&self) -> InspectConfig { // Using an intentionally low depth to minimize the chance of future // breaking changes in case we adapt the approach later on. This also @@ -112,7 +117,7 @@ fn could_unify_impl<'db>( let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); let cause = ObligationCause::dummy(); let at = infcx.at(&cause, env.param_env); - let ((ty1_with_vars, ty2_with_vars), _) = infcx.instantiate_canonical(tys); + let ((ty1_with_vars, ty2_with_vars), _) = infcx.instantiate_canonical(Span::Dummy, tys); let mut ctxt = ObligationCtxt::new(&infcx); let can_unify = at .eq(ty1_with_vars, ty2_with_vars) @@ -245,12 +250,12 @@ impl<'db> InferenceTable<'db> { self.diverging_type_vars.insert(ty); } - pub(crate) fn next_ty_var(&self) -> Ty<'db> { - self.infer_ctxt.next_ty_var() + pub(crate) fn next_ty_var(&self, span: Span) -> Ty<'db> { + self.infer_ctxt.next_ty_var(span) } - pub(crate) fn next_const_var(&self) -> Const<'db> { - self.infer_ctxt.next_const_var() + pub(crate) fn next_const_var(&self, span: Span) -> Const<'db> { + self.infer_ctxt.next_const_var(span) } pub(crate) fn next_int_var(&self) -> Ty<'db> { @@ -261,18 +266,18 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.next_float_var() } - pub(crate) fn new_maybe_never_var(&mut self) -> Ty<'db> { - let var = self.next_ty_var(); + pub(crate) fn new_maybe_never_var(&mut self, span: Span) -> Ty<'db> { + let var = self.next_ty_var(span); self.set_diverging(var); var } - pub(crate) fn next_region_var(&self) -> Region<'db> { - self.infer_ctxt.next_region_var() + pub(crate) fn next_region_var(&self, span: Span) -> Region<'db> { + self.infer_ctxt.next_region_var(span) } - pub(crate) fn next_var_for_param(&self, id: GenericParamId) -> GenericArg<'db> { - self.infer_ctxt.next_var_for_param(id) + pub(crate) fn var_for_def(&self, id: GenericParamId, span: Span) -> GenericArg<'db> { + self.infer_ctxt.var_for_def(id, span) } pub(crate) fn resolve_completely(&mut self, value: T) -> T @@ -319,8 +324,8 @@ impl<'db> InferenceTable<'db> { } /// Create a `GenericArgs` full of infer vars for `def`. - pub(crate) fn fresh_args_for_item(&self, def: SolverDefId) -> GenericArgs<'db> { - self.infer_ctxt.fresh_args_for_item(def) + pub(crate) fn fresh_args_for_item(&self, span: Span, def: SolverDefId) -> GenericArgs<'db> { + self.infer_ctxt.fresh_args_for_item(span, def) } /// Try to resolve `ty` to a structural type, normalizing aliases. @@ -421,11 +426,11 @@ impl<'db> InferenceTable<'db> { } } - pub(super) fn insert_type_vars(&mut self, ty: T) -> T + pub(super) fn insert_type_vars(&mut self, ty: T, span: Span) -> T where T: TypeFoldable>, { - self.infer_ctxt.insert_type_vars(ty) + self.infer_ctxt.insert_type_vars(ty, span) } /// Whenever you lower a user-written type, you should call this. @@ -437,18 +442,13 @@ impl<'db> InferenceTable<'db> { /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation, /// while `process_user_written_ty()` should (but doesn't currently). pub(crate) fn process_remote_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { - let ty = self.insert_type_vars(ty); + let ty = self.insert_type_vars(ty, Span::Dummy); // See https://github.com/rust-lang/rust/blob/cdb45c87e2cd43495379f7e867e3cc15dcee9f93/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs#L487-L495: // Even though the new solver only lazily normalizes usually, here we eagerly normalize so that not everything needs // to normalize before inspecting the `TyKind`. // FIXME(next-solver): We should not deeply normalize here, only shallowly. self.try_structurally_resolve_type(ty) } - - /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it. - pub(super) fn insert_const_vars_shallow(&mut self, c: Const<'db>) -> Const<'db> { - if c.is_ct_error() { self.next_const_var() } else { c } - } } impl fmt::Debug for InferenceTable<'_> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 2973b970f3015..3fb8632d6bea6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -62,7 +62,10 @@ use std::{hash::Hash, ops::ControlFlow}; use hir_def::{ CallableDefId, ExpressionStoreOwnerId, GenericDefId, TypeAliasId, TypeOrConstParamId, - TypeParamId, resolver::TypeNs, type_ref::Rawness, + TypeParamId, + hir::{ExprId, ExprOrPatId, PatId}, + resolver::TypeNs, + type_ref::{Rawness, TypeRefId}, }; use hir_expand::name::Name; use indexmap::{IndexMap, map::Entry}; @@ -74,6 +77,7 @@ use rustc_type_ir::{ BoundVarIndexKind, TypeSuperVisitable, TypeVisitableExt, inherent::{IntoKind, Ty as _}, }; +use stdx::impl_from; use syntax::ast::{ConstArg, make}; use traits::FnTrait; @@ -541,7 +545,7 @@ pub fn callable_sig_from_fn_trait<'db>( let impls_trait = |trait_: FnTrait| { let mut ocx = ObligationCtxt::new(&infcx); - let tupled_args = infcx.next_ty_var(); + let tupled_args = infcx.next_ty_var(Span::Dummy); let args = GenericArgs::new_from_slice(&[self_ty.into(), tupled_args.into()]); let trait_id = trait_.get_id(lang_items)?; let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); @@ -661,6 +665,30 @@ pub fn known_const_to_ast<'db>( Some(make::expr_const_value(konst.display(db, display_target).to_string().as_str())) } +/// A `Span` represents some location in lowered code - a type, expression or pattern. +/// +/// It has no meaning outside its body therefore it should not exit the pass it was created in +/// (e.g. inference). It is usually associated with a solver obligation or an infer var, which +/// should also not cross the pass they were created in. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Span { + ExprId(ExprId), + PatId(PatId), + TypeRefId(TypeRefId), + /// An unimportant location. Errors on this will be suppressed. + Dummy, +} +impl_from!(ExprId, PatId, TypeRefId for Span); + +impl From for Span { + fn from(value: ExprOrPatId) -> Self { + match value { + ExprOrPatId::ExprId(idx) => idx.into(), + ExprOrPatId::PatId(idx) => idx.into(), + } + } +} + pub fn setup_tracing() -> Option { use std::env; use std::sync::LazyLock; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 25c2df0d9f7d9..2c6c7ed9a5543 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -36,7 +36,7 @@ use stdx::impl_from; use triomphe::Arc; use crate::{ - all_super_traits, + Span, all_super_traits, db::HirDatabase, infer::{InferenceContext, unify::InferenceTable}, lower::GenericPredicates, @@ -65,6 +65,8 @@ pub struct MethodResolutionContext<'a, 'db> { pub traits_in_scope: &'a FxHashSet, pub edition: Edition, pub features: &'a UnstableFeatures, + pub call_span: Span, + pub receiver_span: Span, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)] @@ -134,7 +136,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { receiver: ExprId, call_expr: ExprId, ) -> Result<(MethodCallee<'db>, bool), MethodError<'db>> { - let (pick, is_visible) = match self.lookup_probe(name, self_ty) { + let (pick, is_visible) = match self.lookup_probe(call_expr, receiver, name, self_ty) { Ok(it) => (it, true), Err(MethodError::PrivateMatch(it)) => { // FIXME: Report error. @@ -159,10 +161,12 @@ impl<'a, 'db> InferenceContext<'a, 'db> { #[instrument(level = "debug", skip(self))] pub(crate) fn lookup_probe( &self, + call_expr: ExprId, + receiver: ExprId, method_name: Name, self_ty: Ty<'db>, ) -> probe::PickResult<'db> { - self.with_method_resolution(|ctx| { + self.with_method_resolution(call_expr.into(), receiver.into(), |ctx| { let pick = ctx.probe_for_name(probe::Mode::MethodCall, method_name, self_ty)?; Ok(pick) }) @@ -170,6 +174,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { pub(crate) fn with_method_resolution( &self, + call_span: Span, + receiver_span: Span, f: impl FnOnce(&MethodResolutionContext<'_, 'db>) -> R, ) -> R { let traits_in_scope = self.get_traits_in_scope(); @@ -184,6 +190,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { traits_in_scope, edition: self.edition, features: self.features, + call_span, + receiver_span, }; f(&ctx) } @@ -240,7 +248,7 @@ impl<'db> InferenceTable<'db> { // FIXME: We should stop passing `None` for the failure case // when probing for call exprs. I.e. `opt_rhs_ty` should always // be set when it needs to be. - self.next_var_for_param(param_id) + self.var_for_def(param_id, cause.span()) } } }, @@ -248,7 +256,7 @@ impl<'db> InferenceTable<'db> { let obligation = Obligation::new( self.interner(), - cause, + cause.clone(), self.param_env, TraitRef::new_from_args(self.interner(), trait_def_id.into(), args), ); @@ -291,9 +299,11 @@ impl<'db> InferenceTable<'db> { // with bound regions. let fn_sig = self.db.callable_item_signature(method_item.into()).instantiate(interner, args); - let fn_sig = self - .infer_ctxt - .instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, fn_sig); + let fn_sig = self.infer_ctxt.instantiate_binder_with_fresh_vars( + cause.span(), + BoundRegionConversionTime::FnCall, + fn_sig, + ); // Register obligations for the parameters. This will include the // `Self` parameter, which in turn has a bound of the main trait, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs index 3bdef44dd3664..6536b599c844e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs @@ -15,7 +15,7 @@ use tracing::debug; use crate::{ Adjust, Adjustment, AutoBorrow, IncorrectGenericsLenKind, InferenceDiagnostic, - LifetimeElisionKind, PointerCast, + LifetimeElisionKind, PointerCast, Span, db::HirDatabase, infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch}, lower::{ @@ -190,7 +190,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { self.ctx.table.register_infer_ok(autoderef.adjust_steps_as_infer_ok()); match pick.autoref_or_ptr_adjustment { Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => { - let region = self.infcx().next_region_var(); + let region = self.infcx().next_region_var(self.expr.into()); // Type we're wrapping in a reference, used later for unsizing let base_ty = target; @@ -254,7 +254,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { ) -> GenericArgs<'db> { match pick.kind { probe::InherentImplPick(impl_def_id) => { - self.infcx().fresh_args_for_item(impl_def_id.into()) + self.infcx().fresh_args_for_item(self.expr.into(), impl_def_id.into()) } probe::ObjectPick(trait_def_id) => { @@ -296,7 +296,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { // the process we will unify the transformed-self-type // of the method with the actual type in order to // unify some of these variables. - self.infcx().fresh_args_for_item(trait_def_id.into()) + self.infcx().fresh_args_for_item(self.expr.into(), trait_def_id.into()) } probe::WhereClausePick(poly_trait_ref) => { @@ -414,7 +414,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { ) -> Const<'db> { match arg { TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path, const_ty), - TypeLikeConst::Infer => self.ctx.table.next_const_var(), + TypeLikeConst::Infer => self.ctx.table.next_const_var(Span::Dummy), } } @@ -428,7 +428,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { ) -> GenericArg<'db> { // Always create an inference var, even when `infer_args == false`. This helps with diagnostics, // and I think it's also required in the presence of `impl Trait` (that must be inferred). - self.ctx.table.next_var_for_param(param_id) + self.ctx.table.var_for_def(param_id, Span::Dummy) } fn parent_arg(&mut self, param_idx: u32, _param_id: GenericParamId) -> GenericArg<'db> { @@ -609,6 +609,10 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { where T: TypeFoldable> + Copy, { - self.infcx().instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, value) + self.infcx().instantiate_binder_with_fresh_vars( + self.expr.into(), + BoundRegionConversionTime::FnCall, + value, + ) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index afdc62183e7ad..8a28b16724548 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs @@ -284,7 +284,8 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { // special handling for this "trivial case" is a good idea. let infcx = self.infcx; - let (self_ty, var_values) = infcx.instantiate_canonical(&query_input); + let (self_ty, var_values) = + infcx.instantiate_canonical(self.call_span, &query_input); debug!(?self_ty, ?query_input, "probe_op: Mode::Path"); let prev_opaque_entries = self.infcx.inner.borrow_mut().opaque_types().num_entries(); @@ -380,7 +381,8 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { // chain to support recursive calls. We do error if the final // infer var is not an opaque. let infcx = self.infcx; - let (self_ty, inference_vars) = infcx.instantiate_canonical(self_ty); + let (self_ty, inference_vars) = + infcx.instantiate_canonical(self.receiver_span, self_ty); let prev_opaque_entries = infcx.inner.borrow_mut().opaque_types().num_entries(); let self_ty_is_opaque = |ty: Ty<'_>| { @@ -921,7 +923,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { // will still match the original object type, but it won't pollute our // type variables in any form, so just do that! let (QueryResponse { value: generalized_self_ty, .. }, _ignored_var_values) = - self.infcx().instantiate_canonical(self_ty); + self.infcx().instantiate_canonical(self.ctx.call_span, self_ty); self.assemble_inherent_candidates_from_object(generalized_self_ty); self.assemble_inherent_impl_candidates_for_type( @@ -1117,7 +1119,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { #[instrument(level = "debug", skip(self))] fn assemble_extension_candidates_for_trait(&mut self, trait_def_id: TraitId) { - let trait_args = self.infcx().fresh_args_for_item(trait_def_id.into()); + let trait_args = self.infcx().fresh_args_for_item(self.ctx.call_span, trait_def_id.into()); let trait_ref = TraitRef::new_from_args(self.interner(), trait_def_id.into(), trait_args); self.with_trait_item(trait_def_id, |this, item| { @@ -1510,6 +1512,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { } TraitCandidate(trait_ref) => self.infcx().probe(|_| { let trait_ref = self.infcx().instantiate_binder_with_fresh_vars( + self.ctx.call_span, BoundRegionConversionTime::FnCall, trait_ref, ); @@ -1574,7 +1577,8 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { match probe.kind { InherentImplCandidate { impl_def_id, .. } => { - let impl_args = self.infcx().fresh_args_for_item(impl_def_id.into()); + let impl_args = + self.infcx().fresh_args_for_item(self.ctx.call_span, impl_def_id.into()); let impl_ty = self.db().impl_self_ty(impl_def_id).instantiate(self.interner(), impl_args); (xform_self_ty, xform_ret_ty) = @@ -1632,6 +1636,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { } let trait_ref = self.infcx().instantiate_binder_with_fresh_vars( + self.ctx.call_span, BoundRegionConversionTime::FnCall, poly_trait_ref, ); @@ -1667,6 +1672,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { } ObjectCandidate(poly_trait_ref) | WhereClauseCandidate(poly_trait_ref) => { let trait_ref = self.infcx().instantiate_binder_with_fresh_vars( + self.ctx.call_span, BoundRegionConversionTime::FnCall, poly_trait_ref, ); @@ -2029,8 +2035,12 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { // In general, during probe we erase regions. Region::new_erased(self.interner()).into() } - GenericParamId::TypeParamId(_) => self.infcx().next_ty_var().into(), - GenericParamId::ConstParamId(_) => self.infcx().next_const_var().into(), + GenericParamId::TypeParamId(_) => { + self.infcx().next_ty_var(self.ctx.call_span).into() + } + GenericParamId::ConstParamId(_) => { + self.infcx().next_const_var(self.ctx.call_span).into() + } } } }, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/format_proof_tree.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/format_proof_tree.rs index 66da6d540082d..4b2f66a17d519 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/format_proof_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/format_proof_tree.rs @@ -3,7 +3,7 @@ use serde_derive::{Deserialize, Serialize}; use crate::next_solver::inspect::{InspectCandidate, InspectGoal}; use crate::next_solver::{AnyImplId, infer::InferCtxt}; -use crate::next_solver::{DbInterner, Span}; +use crate::{Span, next_solver::DbInterner}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ProofTreeData { @@ -59,7 +59,7 @@ impl<'a, 'db> ProofTreeSerializer<'a, 'db> { let mut nested = Vec::new(); self.infcx.probe(|_| { - for nested_goal in candidate.instantiate_nested_goals() { + for nested_goal in candidate.instantiate_nested_goals(Span::Dummy) { nested.push(self.serialize_goal(&nested_goal)); } }); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index cd0cb597602c3..ba9cd39d448f4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -9,17 +9,20 @@ use rustc_next_trait_solver::{ }; use rustc_type_ir::{ Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, - inherent::{IntoKind, Span as _}, + inherent::IntoKind, solve::{Certainty, NoSolution}, }; -use crate::next_solver::{ - DbInterner, SolverContext, SolverDefId, Span, Ty, TyKind, TypingMode, - infer::{ - InferCtxt, - traits::{PredicateObligation, PredicateObligations}, +use crate::{ + Span, + next_solver::{ + DbInterner, SolverContext, SolverDefId, Ty, TyKind, TypingMode, + infer::{ + InferCtxt, + traits::{PredicateObligation, PredicateObligations}, + }, + inspect::ProofTreeVisitor, }, - inspect::ProofTreeVisitor, }; type PendingObligations<'db> = @@ -97,7 +100,7 @@ impl<'db> ObligationStorage<'db> { let goal = o.as_goal(); let result = <&SolverContext<'db>>::from(infcx).evaluate_root_goal( goal, - Span::dummy(), + o.cause.span(), stalled_on.take(), ); matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. })) @@ -169,7 +172,9 @@ impl<'db> FulfillmentCtxt<'db> { let goal = obligation.as_goal(); let delegate = <&SolverContext<'db>>::from(infcx); - if let Some(certainty) = delegate.compute_goal_fast_path(goal, Span::dummy()) { + if let Some(certainty) = + delegate.compute_goal_fast_path(goal, obligation.cause.span()) + { match certainty { Certainty::Yes => {} Certainty::Maybe { .. } => { @@ -179,9 +184,11 @@ impl<'db> FulfillmentCtxt<'db> { continue; } - let result = delegate.evaluate_root_goal(goal, Span::dummy(), stalled_on); + let result = delegate.evaluate_root_goal(goal, obligation.cause.span(), stalled_on); infcx.inspect_evaluated_obligation(&obligation, &result, || { - Some(delegate.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1) + Some( + delegate.evaluate_root_goal_for_proof_tree(goal, obligation.cause.span()).1, + ) }); let GoalEvaluation { goal: _, certainty, has_changed, stalled_on } = match result { Ok(result) => result, @@ -259,6 +266,7 @@ impl<'db> FulfillmentCtxt<'db> { obl.as_goal(), &mut StalledOnCoroutines { stalled_coroutines, + span: obl.cause.span(), cache: Default::default(), }, ) @@ -280,12 +288,17 @@ impl<'db> FulfillmentCtxt<'db> { /// so we want to keep this visitor *precise* too. pub struct StalledOnCoroutines<'a, 'db> { pub stalled_coroutines: &'a [SolverDefId], + pub span: Span, pub cache: FxHashSet>, } impl<'db> ProofTreeVisitor<'db> for StalledOnCoroutines<'_, 'db> { type Result = ControlFlow<()>; + fn span(&self) -> Span { + self.span + } + fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'db>) -> Self::Result { inspect_goal.goal().predicate.visit_with(self)?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs index dc0b584084e88..4784edf60f5a6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs @@ -28,13 +28,12 @@ use rustc_type_ir::{ FnSig, GenericArgKind, TypeFoldable, TypingMode, Variance, error::ExpectedFound, - inherent::Span as _, relate::{Relate, TypeRelation, solver_relating::RelateExt}, }; use crate::next_solver::{ AliasTerm, AliasTy, Binder, Const, DbInterner, GenericArg, Goal, ParamEnv, - PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, Predicate, Region, Span, Term, + PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, Predicate, Region, Term, TraitRef, Ty, fulfill::NextSolverError, infer::relate::lattice::{LatticeOp, LatticeOpKind}, @@ -109,7 +108,7 @@ impl<'a, 'db> At<'a, 'db> { expected, Variance::Contravariant, actual, - Span::dummy(), + self.cause.span(), ) .map(|goals| self.goals_to_obligations(goals)) } @@ -125,7 +124,7 @@ impl<'a, 'db> At<'a, 'db> { expected, Variance::Covariant, actual, - Span::dummy(), + self.cause.span(), ) .map(|goals| self.goals_to_obligations(goals)) } @@ -141,7 +140,7 @@ impl<'a, 'db> At<'a, 'db> { expected, Variance::Invariant, actual, - Span::dummy(), + self.cause.span(), ) .map(|goals| self.goals_to_obligations(goals)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs index 1738552a8e015..bda418cf203ab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs @@ -404,13 +404,17 @@ impl<'db> InferCtxt<'db> { if kind.universe() != UniverseIndex::ROOT { // A variable from inside a binder of the query. While ideally these shouldn't // exist at all, we have to deal with them for now. - self.instantiate_canonical_var(kind, var_values, |u| universe_map[u.as_usize()]) + self.instantiate_canonical_var(cause.span(), kind, var_values, |u| { + universe_map[u.as_usize()] + }) } else if kind.is_existential() { match opt_values[BoundVar::new(var_values.len())] { Some(k) => k, - None => self.instantiate_canonical_var(kind, var_values, |u| { - universe_map[u.as_usize()] - }), + None => { + self.instantiate_canonical_var(cause.span(), kind, var_values, |u| { + universe_map[u.as_usize()] + }) + } } } else { // For placeholders which were already part of the input, we simply map this diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs index 1fefc0f265c55..a5e29e7836c49 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs @@ -21,10 +21,13 @@ //! //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html -use crate::next_solver::{ - ArgOutlivesPredicate, Canonical, CanonicalVarValues, Const, DbInterner, GenericArg, - OpaqueTypeKey, PlaceholderConst, PlaceholderRegion, PlaceholderType, Region, Ty, TyKind, - infer::InferCtxt, +use crate::{ + Span, + next_solver::{ + ArgOutlivesPredicate, Canonical, CanonicalVarValues, Const, DbInterner, GenericArg, + OpaqueTypeKey, PlaceholderConst, PlaceholderRegion, PlaceholderType, Region, Ty, TyKind, + infer::InferCtxt, + }, }; use instantiate::CanonicalExt; use macros::{TypeFoldable, TypeVisitable}; @@ -50,6 +53,7 @@ impl<'db> InferCtxt<'db> { /// for each of the canonical inputs to your query. pub fn instantiate_canonical( &self, + span: Span, canonical: &Canonical<'db, T>, ) -> (T, CanonicalVarValues<'db>) where @@ -71,7 +75,9 @@ impl<'db> InferCtxt<'db> { let var_values = CanonicalVarValues::instantiate( self.interner, canonical.var_kinds, - |var_values, info| self.instantiate_canonical_var(info, var_values, |ui| universes[ui]), + |var_values, info| { + self.instantiate_canonical_var(span, info, var_values, |ui| universes[ui]) + }, ); let result = canonical.instantiate(self.interner, &var_values); (result, var_values) @@ -87,13 +93,14 @@ impl<'db> InferCtxt<'db> { /// We should somehow deduplicate all of this. pub fn instantiate_canonical_var( &self, + span: Span, cv_info: CanonicalVarKind>, previous_var_values: &[GenericArg<'db>], universe_map: impl Fn(UniverseIndex) -> UniverseIndex, ) -> GenericArg<'db> { match cv_info { CanonicalVarKind::Ty { ui, sub_root } => { - let vid = self.next_ty_var_id_in_universe(universe_map(ui)); + let vid = self.next_ty_var_id_in_universe(universe_map(ui), span); // If this inference variable is related to an earlier variable // via subtyping, we need to add that info to the inference context. if let Some(prev) = previous_var_values.get(sub_root.as_usize()) { @@ -117,7 +124,7 @@ impl<'db> InferCtxt<'db> { } CanonicalVarKind::Region(ui) => { - self.next_region_var_in_universe(universe_map(ui)).into() + self.next_region_var_in_universe(universe_map(ui), span).into() } CanonicalVarKind::PlaceholderRegion(PlaceholderRegion { universe, bound, .. }) => { @@ -126,7 +133,9 @@ impl<'db> InferCtxt<'db> { Region::new_placeholder(self.interner, placeholder_mapped).into() } - CanonicalVarKind::Const(ui) => self.next_const_var_in_universe(universe_map(ui)).into(), + CanonicalVarKind::Const(ui) => { + self.next_const_var_in_universe(universe_map(ui), span).into() + } CanonicalVarKind::PlaceholderConst(PlaceholderConst { universe, bound, .. }) => { let universe_mapped = universe_map(universe); let placeholder_mapped = PlaceholderConst::new(universe_mapped, bound); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs index 397986e2edd3b..9ea466500062f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs @@ -7,10 +7,13 @@ use rustc_type_ir::{ relate::combine::PredicateEmittingRelation, }; -use crate::next_solver::{ - Binder, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, OpaqueTypeKey, Region, - SolverDefId, Span, Ty, TyKind, - infer::opaque_types::{OpaqueHiddenType, table::OpaqueTypeStorageEntries}, +use crate::{ + Span, + next_solver::{ + Binder, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, OpaqueTypeKey, Region, + SolverDefId, Ty, TyKind, + infer::opaque_types::{OpaqueHiddenType, table::OpaqueTypeStorageEntries}, + }, }; use super::{BoundRegionConversionTime, InferCtxt, relate::RelateResult}; @@ -139,26 +142,30 @@ impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { } fn next_ty_infer(&self) -> Ty<'db> { - self.next_ty_var() + self.next_ty_var(Span::Dummy) } fn next_region_infer(&self) -> ::Region { - self.next_region_var() + self.next_region_var(Span::Dummy) } fn next_const_infer(&self) -> Const<'db> { - self.next_const_var() + self.next_const_var(Span::Dummy) } fn fresh_args_for_item(&self, def_id: SolverDefId) -> GenericArgs<'db> { - self.fresh_args_for_item(def_id) + self.fresh_args_for_item(Span::Dummy, def_id) } fn instantiate_binder_with_infer> + Clone>( &self, value: Binder<'db, T>, ) -> T { - self.instantiate_binder_with_fresh_vars(BoundRegionConversionTime::HigherRankedType, value) + self.instantiate_binder_with_fresh_vars( + Span::Dummy, + BoundRegionConversionTime::HigherRankedType, + value, + ) } fn enter_forall> + Clone, U>( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 1eacc295c91c1..33003f5375d46 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -28,16 +28,18 @@ use rustc_type_ir::{ use snapshot::undo_log::InferCtxtUndoLogs; use tracing::{debug, instrument}; use traits::{ObligationCause, PredicateObligations}; -use type_variable::TypeVariableOrigin; -use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; +use unify_key::{ConstVariableValue, ConstVidKey}; pub use crate::next_solver::infer::traits::ObligationInspector; -use crate::next_solver::{ - ArgOutlivesPredicate, BoundConst, BoundRegion, BoundTy, BoundVariableKind, Goal, Predicate, - SolverContext, - fold::BoundVarReplacerDelegate, - infer::{at::ToTrace, select::EvaluationResult, traits::PredicateObligation}, - obligation_ctxt::ObligationCtxt, +use crate::{ + Span, + next_solver::{ + ArgOutlivesPredicate, BoundConst, BoundRegion, BoundTy, BoundVariableKind, Goal, Predicate, + SolverContext, + fold::BoundVarReplacerDelegate, + infer::{at::ToTrace, select::EvaluationResult, traits::PredicateObligation}, + obligation_ctxt::ObligationCtxt, + }, }; use super::{ @@ -364,13 +366,14 @@ impl<'db> InferCtxtBuilder<'db> { /// (in other words, `S(C) = V`). pub fn build_with_canonical( mut self, + span: Span, input: &CanonicalQueryInput<'db, T>, ) -> (InferCtxt<'db>, T, CanonicalVarValues<'db>) where T: TypeFoldable>, { let infcx = self.build(input.typing_mode.0); - let (value, args) = infcx.instantiate_canonical(&input.canonical); + let (value, args) = infcx.instantiate_canonical(span, &input.canonical); (infcx, value, args) } @@ -427,12 +430,13 @@ impl<'db> InferCtxt<'db> { )) } - pub(crate) fn insert_type_vars(&self, ty: T) -> T + pub(crate) fn insert_type_vars(&self, ty: T, span: Span) -> T where T: TypeFoldable>, { struct Folder<'a, 'db> { infcx: &'a InferCtxt<'db>, + span: Span, } impl<'db> TypeFolder> for Folder<'_, 'db> { fn cx(&self) -> DbInterner<'db> { @@ -444,7 +448,11 @@ impl<'db> InferCtxt<'db> { return ty; } - if ty.is_ty_error() { self.infcx.next_ty_var() } else { ty.super_fold_with(self) } + if ty.is_ty_error() { + self.infcx.next_ty_var(self.span) + } else { + ty.super_fold_with(self) + } } fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { @@ -453,18 +461,18 @@ impl<'db> InferCtxt<'db> { } if ct.is_ct_error() { - self.infcx.next_const_var() + self.infcx.next_const_var(self.span) } else { ct.super_fold_with(self) } } fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { - if r.is_error() { self.infcx.next_region_var() } else { r } + if r.is_error() { self.infcx.next_region_var(self.span) } else { r } } } - ty.fold_with(&mut Folder { infcx: self }) + ty.fold_with(&mut Folder { infcx: self, span }) } /// Evaluates whether the predicate can be satisfied in the given @@ -737,80 +745,49 @@ impl<'db> InferCtxt<'db> { self.inner.borrow_mut().type_variables().num_vars() } - pub fn next_var_for_param(&self, id: GenericParamId) -> GenericArg<'db> { - match id { - GenericParamId::TypeParamId(_) => self.next_ty_var().into(), - GenericParamId::ConstParamId(_) => self.next_const_var().into(), - GenericParamId::LifetimeParamId(_) => self.next_region_var().into(), - } + pub fn next_ty_var(&self, span: Span) -> Ty<'db> { + let vid = self.next_ty_vid(span); + Ty::new_var(self.interner, vid) } - pub fn next_ty_var(&self) -> Ty<'db> { - self.next_ty_var_with_origin(TypeVariableOrigin { param_def_id: None }) + pub fn next_ty_vid(&self, span: Span) -> TyVid { + self.next_ty_var_id_in_universe(self.universe(), span) } - pub fn next_ty_vid(&self) -> TyVid { - self.inner - .borrow_mut() - .type_variables() - .new_var(self.universe(), TypeVariableOrigin { param_def_id: None }) + pub fn next_ty_var_id_in_universe(&self, universe: UniverseIndex, span: Span) -> TyVid { + self.inner.borrow_mut().type_variables().new_var(universe, span) } - pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'db> { - let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin); + pub fn next_ty_var_in_universe(&self, universe: UniverseIndex, span: Span) -> Ty<'db> { + let vid = self.next_ty_var_id_in_universe(universe, span); Ty::new_var(self.interner, vid) } - pub fn next_ty_var_id_in_universe(&self, universe: UniverseIndex) -> TyVid { - let origin = TypeVariableOrigin { param_def_id: None }; - self.inner.borrow_mut().type_variables().new_var(universe, origin) - } - - pub fn next_ty_var_in_universe(&self, universe: UniverseIndex) -> Ty<'db> { - let vid = self.next_ty_var_id_in_universe(universe); - Ty::new_var(self.interner, vid) + pub fn next_const_var(&self, span: Span) -> Const<'db> { + let vid = self.next_const_vid(span); + Const::new_var(self.interner, vid) } - pub fn next_const_var(&self) -> Const<'db> { - self.next_const_var_with_origin(ConstVariableOrigin {}) + pub fn next_const_vid(&self, span: Span) -> ConstVid { + self.next_const_vid_in_universe(self.universe(), span) } - pub fn next_const_vid(&self) -> ConstVid { + pub fn next_const_vid_in_universe(&self, universe: UniverseIndex, span: Span) -> ConstVid { self.inner .borrow_mut() .const_unification_table() - .new_key(ConstVariableValue::Unknown { - origin: ConstVariableOrigin {}, - universe: self.universe(), - }) + .new_key(ConstVariableValue::Unknown { span, universe }) .vid } - pub fn next_const_var_with_origin(&self, origin: ConstVariableOrigin) -> Const<'db> { - let vid = self - .inner - .borrow_mut() - .const_unification_table() - .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() }) - .vid; - Const::new_var(self.interner, vid) - } - - pub fn next_const_var_in_universe(&self, universe: UniverseIndex) -> Const<'db> { - let origin = ConstVariableOrigin {}; - let vid = self - .inner - .borrow_mut() - .const_unification_table() - .new_key(ConstVariableValue::Unknown { origin, universe }) - .vid; + pub fn next_const_var_in_universe(&self, universe: UniverseIndex, span: Span) -> Const<'db> { + let vid = self.next_const_vid_in_universe(universe, span); Const::new_var(self.interner, vid) } pub fn next_int_var(&self) -> Ty<'db> { - let next_int_var_id = - self.inner.borrow_mut().int_unification_table().new_key(IntVarValue::Unknown); - Ty::new_int_var(self.interner, next_int_var_id) + let vid = self.next_int_vid(); + Ty::new_int_var(self.interner, vid) } pub fn next_int_vid(&self) -> IntVid { @@ -828,27 +805,27 @@ impl<'db> InferCtxt<'db> { /// Creates a fresh region variable with the next available index. /// The variable will be created in the maximum universe created /// thus far, allowing it to name any region created thus far. - pub fn next_region_var(&self) -> Region<'db> { - self.next_region_var_in_universe(self.universe()) + pub fn next_region_var(&self, span: Span) -> Region<'db> { + self.next_region_var_in_universe(self.universe(), span) } - pub fn next_region_vid(&self) -> RegionVid { - self.inner.borrow_mut().unwrap_region_constraints().new_region_var(self.universe()) + pub fn next_region_vid(&self, span: Span) -> RegionVid { + self.inner.borrow_mut().unwrap_region_constraints().new_region_var(self.universe(), span) } /// Creates a fresh region variable with the next available index /// in the given universe; typically, you can use /// `next_region_var` and just use the maximal universe. - pub fn next_region_var_in_universe(&self, universe: UniverseIndex) -> Region<'db> { + pub fn next_region_var_in_universe(&self, universe: UniverseIndex, span: Span) -> Region<'db> { let region_var = - self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe); + self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe, span); Region::new_var(self.interner, region_var) } - pub fn next_term_var_of_kind(&self, term: Term<'db>) -> Term<'db> { + pub fn next_term_var_of_kind(&self, term: Term<'db>, span: Span) -> Term<'db> { match term.kind() { - TermKind::Ty(_) => self.next_ty_var().into(), - TermKind::Const(_) => self.next_const_var().into(), + TermKind::Ty(_) => self.next_ty_var(span).into(), + TermKind::Const(_) => self.next_const_var(span).into(), } } @@ -866,24 +843,12 @@ impl<'db> InferCtxt<'db> { self.inner.borrow_mut().unwrap_region_constraints().num_region_vars() } - /// Just a convenient wrapper of `next_region_var` for using during NLL. - #[instrument(skip(self), level = "debug")] - pub fn next_nll_region_var(&self) -> Region<'db> { - self.next_region_var() - } - - /// Just a convenient wrapper of `next_region_var` for using during NLL. - #[instrument(skip(self), level = "debug")] - pub fn next_nll_region_var_in_universe(&self, universe: UniverseIndex) -> Region<'db> { - self.next_region_var_in_universe(universe) - } - - fn var_for_def(&self, id: GenericParamId) -> GenericArg<'db> { + pub fn var_for_def(&self, id: GenericParamId, span: Span) -> GenericArg<'db> { match id { GenericParamId::LifetimeParamId(_) => { // Create a region inference variable for the given // region parameter definition. - self.next_region_var().into() + self.next_region_var(span).into() } GenericParamId::TypeParamId(_) => { // Create a type inference variable for the given @@ -894,41 +859,27 @@ impl<'db> InferCtxt<'db> { // used in a path such as `Foo::::new()` will // use an inference variable for `C` with `[T, U]` // as the generic parameters for the default, `(T, U)`. - let ty_var_id = self - .inner - .borrow_mut() - .type_variables() - .new_var(self.universe(), TypeVariableOrigin { param_def_id: None }); - - Ty::new_var(self.interner, ty_var_id).into() - } - GenericParamId::ConstParamId(_) => { - let origin = ConstVariableOrigin {}; - let const_var_id = self - .inner - .borrow_mut() - .const_unification_table() - .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() }) - .vid; - Const::new_var(self.interner, const_var_id).into() + self.next_ty_var(span).into() } + GenericParamId::ConstParamId(_) => self.next_const_var(span).into(), } } /// Given a set of generics defined on a type or impl, returns the generic parameters mapping /// each type/region parameter to a fresh inference variable. - pub fn fresh_args_for_item(&self, def_id: SolverDefId) -> GenericArgs<'db> { - GenericArgs::for_item(self.interner, def_id, |_index, kind, _| self.var_for_def(kind)) + pub fn fresh_args_for_item(&self, span: Span, def_id: SolverDefId) -> GenericArgs<'db> { + GenericArgs::for_item(self.interner, def_id, |_index, kind, _| self.var_for_def(kind, span)) } /// Like `fresh_args_for_item()`, but first uses the args from `first`. pub fn fill_rest_fresh_args( &self, + span: Span, def_id: SolverDefId, first: impl IntoIterator>, ) -> GenericArgs<'db> { GenericArgs::fill_rest(self.interner, def_id, first, |_index, kind, _| { - self.var_for_def(kind) + self.var_for_def(kind, span) }) } @@ -1141,7 +1092,7 @@ impl<'db> InferCtxt<'db> { pub fn probe_const_var(&self, vid: ConstVid) -> Result, UniverseIndex> { match self.inner.borrow_mut().const_unification_table().probe_value(vid) { ConstVariableValue::Known { value } => Ok(value), - ConstVariableValue::Unknown { origin: _, universe } => Err(universe), + ConstVariableValue::Unknown { span: _, universe } => Err(universe), } } @@ -1154,6 +1105,7 @@ impl<'db> InferCtxt<'db> { // use [`InferCtxt::enter_forall`] instead. pub fn instantiate_binder_with_fresh_vars( &self, + span: Span, _lbrct: BoundRegionConversionTime, value: Binder<'db, T>, ) -> T @@ -1169,9 +1121,9 @@ impl<'db> InferCtxt<'db> { for bound_var_kind in bound_vars { let arg: GenericArg<'db> = match bound_var_kind { - BoundVariableKind::Ty(_) => self.next_ty_var().into(), - BoundVariableKind::Region(_) => self.next_region_var().into(), - BoundVariableKind::Const => self.next_const_var().into(), + BoundVariableKind::Ty(_) => self.next_ty_var(span).into(), + BoundVariableKind::Region(_) => self.next_region_var(span).into(), + BoundVariableKind::Const => self.next_const_var(span).into(), }; args.push(arg); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs index 7bb39519f50ac..dda1bb56ef949 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs @@ -15,9 +15,12 @@ use self::CombineMapType::*; use self::UndoLog::*; use super::MemberConstraint; use super::unify_key::RegionVidKey; -use crate::next_solver::infer::snapshot::undo_log::{InferCtxtUndoLogs, Snapshot}; use crate::next_solver::infer::unify_key::RegionVariableValue; use crate::next_solver::{AliasTy, Binder, DbInterner, ParamTy, PlaceholderType, Region, Ty}; +use crate::{ + Span, + next_solver::infer::snapshot::undo_log::{InferCtxtUndoLogs, Snapshot}, +}; #[derive(Debug, Clone, Default)] pub struct RegionConstraintStorage<'db> { @@ -281,6 +284,7 @@ pub struct RegionVariableInfo { // This would be currently unsound as it would cause us to drop the universe // changes in `lexical_region_resolve`. pub universe: UniverseIndex, + pub span: Span, } pub(crate) struct RegionSnapshot { @@ -372,8 +376,8 @@ impl<'db> RegionConstraintCollector<'db, '_> { self.storage.any_unifications = snapshot.any_unifications; } - pub(super) fn new_region_var(&mut self, universe: UniverseIndex) -> RegionVid { - let vid = self.storage.var_infos.push(RegionVariableInfo { universe }); + pub(super) fn new_region_var(&mut self, universe: UniverseIndex, span: Span) -> RegionVid { + let vid = self.storage.var_infos.push(RegionVariableInfo { universe, span }); let u_vid = self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe }); assert_eq!(vid, u_vid.vid); @@ -459,6 +463,7 @@ impl<'db> RegionConstraintCollector<'db, '_> { pub(super) fn lub_regions( &mut self, db: DbInterner<'db>, + origin: Span, a: Region<'db>, b: Region<'db>, ) -> Region<'db> { @@ -470,13 +475,14 @@ impl<'db> RegionConstraintCollector<'db, '_> { } else if a == b { a // LUB(a,a) = a } else { - self.combine_vars(db, Lub, a, b) + self.combine_vars(db, Lub, a, b, origin) } } pub(super) fn glb_regions( &mut self, db: DbInterner<'db>, + origin: Span, a: Region<'db>, b: Region<'db>, ) -> Region<'db> { @@ -490,7 +496,7 @@ impl<'db> RegionConstraintCollector<'db, '_> { } else if a == b { a // GLB(a,a) = a } else { - self.combine_vars(db, Glb, a, b) + self.combine_vars(db, Glb, a, b, origin) } } @@ -529,6 +535,7 @@ impl<'db> RegionConstraintCollector<'db, '_> { t: CombineMapType, a: Region<'db>, b: Region<'db>, + origin: Span, ) -> Region<'db> { let vars = TwoRegions { a, b }; if let Some(c) = self.combine_map(t.clone()).get(&vars) { @@ -537,7 +544,7 @@ impl<'db> RegionConstraintCollector<'db, '_> { let a_universe = self.universe(a); let b_universe = self.universe(b); let c_universe = cmp::max(a_universe, b_universe); - let c = self.new_region_var(c_universe); + let c = self.new_region_var(c_universe, origin); self.combine_map(t.clone()).insert(vars.clone(), c); self.undo_log.push(AddCombination(t.clone(), vars)); let new_r = Region::new_var(cx, c); @@ -567,8 +574,15 @@ impl<'db> RegionConstraintCollector<'db, '_> { } } - pub fn vars_since_snapshot(&self, value_count: usize) -> Range { - RegionVid::from(value_count)..RegionVid::from(self.storage.unification_table.len()) + pub fn vars_since_snapshot(&self, value_count: usize) -> (Range, Vec) { + let range = + RegionVid::from(value_count)..RegionVid::from(self.storage.unification_table.len()); + ( + range.clone(), + (range.start.as_usize()..range.end.as_usize()) + .map(|index| self.storage.var_infos[RegionVid::from_usize(index)].span) + .collect(), + ) } /// See `InferCtxt::region_constraints_added_in_snapshot`. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs index d621dd4906e81..152592683ac26 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs @@ -16,7 +16,6 @@ use tracing::{debug, instrument, warn}; use super::{ PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation, }; -use crate::next_solver::infer::unify_key::ConstVariableValue; use crate::next_solver::infer::{InferCtxt, relate}; use crate::next_solver::util::MaxUniverse; use crate::next_solver::{ @@ -24,6 +23,7 @@ use crate::next_solver::{ Term, TermVid, Ty, TyKind, TypingMode, UnevaluatedConst, }; use crate::next_solver::{GenericArgs, infer::type_variable::TypeVariableValue}; +use crate::{Span, next_solver::infer::unify_key::ConstVariableValue}; impl<'db> InferCtxt<'db> { /// The idea is that we should ensure that the type variable `target_vid` @@ -60,6 +60,7 @@ impl<'db> InferCtxt<'db> { // `?1 <: ?3`. let Generalization { value_may_be_infer: generalized_ty, has_unconstrained_ty_var } = self .generalize( + relation.span(), relation.structurally_relate_aliases(), target_vid, instantiation_variance, @@ -179,6 +180,7 @@ impl<'db> InferCtxt<'db> { // constants and generic expressions are not yet handled correctly. let Generalization { value_may_be_infer: generalized_ct, has_unconstrained_ty_var } = self .generalize( + relation.span(), relation.structurally_relate_aliases(), target_vid, Variance::Invariant, @@ -220,6 +222,7 @@ impl<'db> InferCtxt<'db> { /// This checks for cycles -- that is, whether `source_term` references `target_vid`. fn generalize> + Relate>>( &self, + span: Span, structurally_relate_aliases: StructurallyRelateAliases, target_vid: impl Into, ambient_variance: Variance, @@ -238,6 +241,7 @@ impl<'db> InferCtxt<'db> { let mut generalizer = Generalizer { infcx: self, + span, structurally_relate_aliases, root_vid, for_universe, @@ -270,6 +274,8 @@ impl<'db> InferCtxt<'db> { struct Generalizer<'me, 'db> { infcx: &'me InferCtxt<'db>, + span: Span, + /// Whether aliases should be related structurally. If not, we have to /// be careful when generalizing aliases. structurally_relate_aliases: StructurallyRelateAliases, @@ -318,7 +324,7 @@ impl<'db> Generalizer<'_, 'db> { /// if we're currently in a bivariant context. fn next_ty_var_for_alias(&mut self) -> Ty<'db> { self.has_unconstrained_ty_var |= self.ambient_variance == Variance::Bivariant; - self.infcx.next_ty_var_in_universe(self.for_universe) + self.infcx.next_ty_var_in_universe(self.for_universe, self.span) } /// An occurs check failure inside of an alias does not mean @@ -477,7 +483,7 @@ impl<'db> TypeRelation> for Generalizer<'_, 'db> { Variance::Covariant | Variance::Contravariant => (), } - let origin = inner.type_variables().var_origin(vid); + let origin = inner.type_variables().var_span(vid); let new_var_id = inner.type_variables().new_var(self.for_universe, origin); // If we're in the new solver and create a new inference @@ -579,7 +585,7 @@ impl<'db> TypeRelation> for Generalizer<'_, 'db> { } } - Ok(self.infcx.next_region_var_in_universe(self.for_universe)) + Ok(self.infcx.next_region_var_in_universe(self.for_universe, self.span)) } #[instrument(level = "debug", skip(self, c2), ret)] @@ -605,13 +611,13 @@ impl<'db> TypeRelation> for Generalizer<'_, 'db> { drop(inner); self.relate(u, u) } - ConstVariableValue::Unknown { origin, universe } => { + ConstVariableValue::Unknown { span, universe } => { if self.for_universe.can_name(universe) { Ok(c) } else { let new_var_id = variable_table .new_key(ConstVariableValue::Unknown { - origin, + span, universe: self.for_universe, }) .vid; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs index 3522827a9e959..0443dbd8142bd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs @@ -19,7 +19,7 @@ use rustc_type_ir::{ AliasRelationDirection, Interner, TypeVisitableExt, Upcast, Variance, - inherent::{IntoKind, Span as _}, + inherent::IntoKind, relate::{ Relate, StructurallyRelateAliases, TypeRelation, VarianceDiagInfo, combine::{ @@ -28,13 +28,16 @@ use rustc_type_ir::{ }, }; -use crate::next_solver::{ - AliasTy, Binder, Const, DbInterner, GenericArgs, Goal, ParamEnv, Predicate, PredicateKind, - Region, SolverDefId, Span, Ty, TyKind, - infer::{ - InferCtxt, TypeTrace, - relate::RelateResult, - traits::{Obligation, PredicateObligations}, +use crate::{ + Span, + next_solver::{ + AliasTy, Binder, Const, DbInterner, GenericArgs, Goal, ParamEnv, Predicate, PredicateKind, + Region, SolverDefId, Ty, TyKind, + infer::{ + InferCtxt, TypeTrace, + relate::RelateResult, + traits::{Obligation, PredicateObligations}, + }, }, }; @@ -154,12 +157,12 @@ impl<'db> TypeRelation> for LatticeOp<'_, 'db> { // iterate on the subtype obligations that are returned, but I // think this suffices. -nmatsakis (TyKind::Infer(rustc_type_ir::TyVar(..)), _) => { - let v = infcx.next_ty_var(); + let v = infcx.next_ty_var(self.span()); self.relate_bound(v, b, a)?; Ok(v) } (_, TyKind::Infer(rustc_type_ir::TyVar(..))) => { - let v = infcx.next_ty_var(); + let v = infcx.next_ty_var(self.span()); self.relate_bound(v, a, b)?; Ok(v) } @@ -178,10 +181,10 @@ impl<'db> TypeRelation> for LatticeOp<'_, 'db> { let mut constraints = inner.unwrap_region_constraints(); Ok(match self.kind { // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8 - LatticeOpKind::Glb => constraints.lub_regions(self.cx(), a, b), + LatticeOpKind::Glb => constraints.lub_regions(self.cx(), self.span(), a, b), // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8 - LatticeOpKind::Lub => constraints.glb_regions(self.cx(), a, b), + LatticeOpKind::Lub => constraints.glb_regions(self.cx(), self.span(), a, b), }) } @@ -239,7 +242,7 @@ impl<'infcx, 'db> LatticeOp<'infcx, 'db> { impl<'db> PredicateEmittingRelation> for LatticeOp<'_, 'db> { fn span(&self) -> Span { - Span::dummy() + self.trace.cause.span() } fn structurally_relate_aliases(&self) -> StructurallyRelateAliases { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs index bbfc8a4757818..d6f0379c111a2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs @@ -10,6 +10,7 @@ use rustc_type_ir::{ }; use crate::{ + Span, db::InternedOpaqueTyId, next_solver::{ AnyImplId, Const, ErrorGuaranteed, GenericArgs, Goal, TraitRef, Ty, TypeError, @@ -263,18 +264,24 @@ impl<'db> InferCtxt<'db> { ) -> SelectionResult<'db, Selection<'db>> { self.visit_proof_tree( Goal::new(self.interner, obligation.param_env, obligation.predicate), - &mut Select {}, + &mut Select { span: obligation.cause.span() }, ) .break_value() .unwrap() } } -struct Select {} +struct Select { + span: Span, +} impl<'db> ProofTreeVisitor<'db> for Select { type Result = ControlFlow>>; + fn span(&self) -> Span { + self.span + } + fn visit_goal(&mut self, goal: &InspectGoal<'_, 'db>) -> Self::Result { let mut candidates = goal.candidates(); candidates.retain(|cand| cand.result().is_ok()); @@ -286,7 +293,10 @@ impl<'db> ProofTreeVisitor<'db> for Select { // One candidate, no need to winnow. if candidates.len() == 1 { - return ControlFlow::Break(Ok(to_selection(candidates.into_iter().next().unwrap()))); + return ControlFlow::Break(Ok(to_selection( + self.span, + candidates.into_iter().next().unwrap(), + ))); } // Don't winnow until `Certainty::Yes` -- we don't need to winnow until @@ -311,7 +321,7 @@ impl<'db> ProofTreeVisitor<'db> for Select { } } - ControlFlow::Break(Ok(to_selection(candidates.into_iter().next().unwrap()))) + ControlFlow::Break(Ok(to_selection(self.span, candidates.into_iter().next().unwrap()))) } } @@ -368,7 +378,7 @@ fn candidate_should_be_dropped_in_favor_of<'db>( } } -fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option> { +fn to_selection<'db>(span: Span, cand: InspectCandidate<'_, 'db>) -> Option> { if let Certainty::Maybe { .. } = cand.shallow_certainty() { return None; } @@ -376,7 +386,7 @@ fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option> let nested = match cand.result().expect("expected positive result") { Certainty::Yes => Vec::new(), Certainty::Maybe { .. } => cand - .instantiate_nested_goals() + .instantiate_nested_goals(span) .into_iter() .map(|nested| { Obligation::new( @@ -396,7 +406,7 @@ fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option> // For impl candidates, we do the rematch manually to compute the args. ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, - args: cand.instantiate_impl_args(), + args: cand.instantiate_impl_args(span), nested, }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs index 5902f8043b5ea..7cb3ab09d4b28 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs @@ -5,17 +5,19 @@ use ena::{ unify::{self as ut, UnifyKey}, }; use rustc_type_ir::{ - ConstVid, FloatVid, IntVid, RegionKind, RegionVid, TyVid, TypeFoldable, TypeFolder, - TypeSuperFoldable, TypeVisitableExt, inherent::IntoKind, + ConstVid, FloatVid, IntVid, RegionVid, TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, inherent::IntoKind, }; -use crate::next_solver::{ - Const, ConstKind, DbInterner, Region, Ty, TyKind, - infer::{ - InferCtxt, UnificationTable, iter_idx_range, - snapshot::VariableLengths, - type_variable::TypeVariableOrigin, - unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}, +use crate::{ + Span, + next_solver::{ + Const, ConstKind, DbInterner, Region, RegionKind, Ty, TyKind, + infer::{ + InferCtxt, UnificationTable, iter_idx_range, + snapshot::VariableLengths, + unify_key::{ConstVariableValue, ConstVidKey}, + }, }, }; @@ -33,7 +35,7 @@ where fn const_vars_since_snapshot<'db>( table: &mut UnificationTable<'_, 'db, ConstVidKey<'db>>, snapshot_var_len: usize, -) -> (Range, Vec) { +) -> (Range, Vec) { let range = vars_since_snapshot(table, snapshot_var_len); let range = range.start.vid..range.end.vid; @@ -41,8 +43,8 @@ fn const_vars_since_snapshot<'db>( range.clone(), iter_idx_range(range) .map(|index| match table.probe_value(index) { - ConstVariableValue::Known { value: _ } => ConstVariableOrigin {}, - ConstVariableValue::Unknown { origin, universe: _ } => origin, + ConstVariableValue::Known { value: _ } => Span::Dummy, + ConstVariableValue::Unknown { span, universe: _ } => span, }) .collect(), ) @@ -128,11 +130,11 @@ impl<'db> InferCtxt<'db> { } struct SnapshotVarData { - region_vars: Range, - type_vars: (Range, Vec), + region_vars: (Range, Vec), + type_vars: (Range, Vec), int_vars: Range, float_vars: Range, - const_vars: (Range, Vec), + const_vars: (Range, Vec), } impl SnapshotVarData { @@ -156,7 +158,7 @@ impl SnapshotVarData { fn is_empty(&self) -> bool { let SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars } = self; - region_vars.is_empty() + region_vars.0.is_empty() && type_vars.0.is_empty() && int_vars.is_empty() && float_vars.is_empty() @@ -182,8 +184,8 @@ impl<'a, 'db> TypeFolder> for InferenceFudger<'a, 'db> { // This variable was created during the fudging. // Recreate it with a fresh variable here. let idx = vid.as_usize() - self.snapshot_vars.type_vars.0.start.as_usize(); - let origin = self.snapshot_vars.type_vars.1[idx]; - self.infcx.next_ty_var_with_origin(origin) + let span = self.snapshot_vars.type_vars.1[idx]; + self.infcx.next_ty_var(span) } else { // This variable was created before the // "fudging". Since we refresh all type @@ -225,8 +227,10 @@ impl<'a, 'db> TypeFolder> for InferenceFudger<'a, 'db> { fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { if let RegionKind::ReVar(vid) = r.kind() { - if self.snapshot_vars.region_vars.contains(&vid) { - self.infcx.next_region_var() + if self.snapshot_vars.region_vars.0.contains(&vid) { + let idx = vid.index() - self.snapshot_vars.region_vars.0.start.index(); + let span = self.snapshot_vars.region_vars.1[idx]; + self.infcx.next_region_var(span) } else { r } @@ -241,8 +245,8 @@ impl<'a, 'db> TypeFolder> for InferenceFudger<'a, 'db> { rustc_type_ir::InferConst::Var(vid) => { if self.snapshot_vars.const_vars.0.contains(&vid) { let idx = vid.index() - self.snapshot_vars.const_vars.0.start.index(); - let origin = self.snapshot_vars.const_vars.1[idx]; - self.infcx.next_const_var_with_origin(origin) + let span = self.snapshot_vars.const_vars.1[idx]; + self.infcx.next_const_var(span) } else { ct } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index 5b875d29608a3..1edf256d012f0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -16,9 +16,12 @@ use rustc_type_ir::{ }; use tracing::debug; -use crate::next_solver::{ - Clause, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, Span, TraitPredicate, - TraitRef, Ty, +use crate::{ + Span, + next_solver::{ + Clause, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, TraitPredicate, + TraitRef, Ty, + }, }; use super::InferCtxt; @@ -33,15 +36,18 @@ use super::InferCtxt; /// only live for a short period of time. #[derive(Clone, Debug, PartialEq, Eq)] pub struct ObligationCause { - // FIXME: This should contain an `ExprId`/`PatId` etc., and a cause code. But for now we - // don't report trait solving diagnostics, so this is irrelevant. - _private: (), + span: Span, } impl ObligationCause { #[inline] pub fn new() -> ObligationCause { - ObligationCause { _private: () } + ObligationCause { span: Span::Dummy } + } + + #[inline] + pub fn with_span(span: Span) -> ObligationCause { + ObligationCause { span } } #[inline] @@ -53,6 +59,11 @@ impl ObligationCause { pub fn misc() -> ObligationCause { ObligationCause::new() } + + #[inline] + pub(crate) fn span(&self) -> Span { + self.span + } } impl Default for ObligationCause { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs index 29e7b883c93bf..070d2582d88f4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs @@ -13,7 +13,7 @@ use rustc_type_ir::UniverseIndex; use rustc_type_ir::inherent::Ty as _; use tracing::debug; -use crate::next_solver::SolverDefId; +use crate::Span; use crate::next_solver::Ty; use crate::next_solver::infer::{InferCtxtUndoLogs, iter_idx_range}; @@ -94,17 +94,9 @@ pub(crate) struct TypeVariableTable<'a, 'db> { undo_log: &'a mut InferCtxtUndoLogs<'db>, } -#[derive(Copy, Clone, Debug)] -pub struct TypeVariableOrigin { - /// `DefId` of the type parameter this was instantiated for, if any. - /// - /// This should only be used for diagnostics. - pub param_def_id: Option, -} - #[derive(Debug, Clone)] pub(crate) struct TypeVariableData { - origin: TypeVariableOrigin, + span: Span, } #[derive(Clone, Debug)] @@ -152,12 +144,12 @@ impl<'db> TypeVariableStorage<'db> { } impl<'db> TypeVariableTable<'_, 'db> { - /// Returns the origin that was given when `vid` was created. + /// Returns the span that was given when `vid` was created. /// /// Note that this function does not return care whether /// `vid` has been unified with something else or not. - pub(crate) fn var_origin(&self, vid: TyVid) -> TypeVariableOrigin { - self.storage.values[vid].origin + pub(crate) fn var_span(&self, vid: TyVid) -> Span { + self.storage.values[vid].span } /// Records that `a == b`, depending on `dir`. @@ -195,26 +187,16 @@ impl<'db> TypeVariableTable<'_, 'db> { self.eq_relations().union_value(vid, TypeVariableValue::Known { value: ty }); } - /// Creates a new type variable. - /// - /// - `diverging`: indicates if this is a "diverging" type - /// variable, e.g., one created as the type of a `return` - /// expression. The code in this module doesn't care if a - /// variable is diverging, but the main Rust type-checker will - /// sometimes "unify" such variables with the `!` or `()` types. - /// - `origin`: indicates *why* the type variable was created. - /// The code in this module doesn't care, but it can be useful - /// for improving error messages. - pub(crate) fn new_var(&mut self, universe: UniverseIndex, origin: TypeVariableOrigin) -> TyVid { + pub(crate) fn new_var(&mut self, universe: UniverseIndex, span: Span) -> TyVid { let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); let sub_key = self.sub_unification_table().new_key(()); debug_assert_eq!(eq_key.vid, sub_key.vid); - let index = self.storage.values.push(TypeVariableData { origin }); + let index = self.storage.values.push(TypeVariableData { span }); debug_assert_eq!(eq_key.vid, index); - debug!("new_var(index={:?}, universe={:?}, origin={:?})", eq_key.vid, universe, origin); + debug!("new_var(index={:?}, universe={:?}, span={:?})", eq_key.vid, universe, span); index } @@ -268,12 +250,9 @@ impl<'db> TypeVariableTable<'_, 'db> { } /// Returns a range of the type variables created during the snapshot. - pub(crate) fn vars_since_snapshot( - &mut self, - value_count: usize, - ) -> (Range, Vec) { + pub(crate) fn vars_since_snapshot(&mut self, value_count: usize) -> (Range, Vec) { let range = TyVid::from_usize(value_count)..TyVid::from_usize(self.num_vars()); - (range.clone(), iter_idx_range(range).map(|index| self.var_origin(index)).collect()) + (range.clone(), iter_idx_range(range).map(|index| self.var_span(index)).collect()) } /// Returns indices of all variables that are not yet diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs index a09f65f082d97..07eeda255b434 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs @@ -6,7 +6,10 @@ use std::marker::PhantomData; use ena::unify::{NoError, UnifyKey, UnifyValue}; use rustc_type_ir::{ConstVid, RegionKind, RegionVid, UniverseIndex, inherent::IntoKind}; -use crate::next_solver::{Const, Region}; +use crate::{ + Span, + next_solver::{Const, Region}, +}; #[derive(Clone, Debug)] pub(crate) enum RegionVariableValue<'db> { @@ -89,13 +92,10 @@ impl<'db> UnifyValue for RegionVariableValue<'db> { // Generic consts. -#[derive(Copy, Clone, Debug)] -pub struct ConstVariableOrigin {} - #[derive(Clone, Debug)] pub(crate) enum ConstVariableValue<'db> { Known { value: Const<'db> }, - Unknown { origin: ConstVariableOrigin, universe: UniverseIndex }, + Unknown { span: Span, universe: UniverseIndex }, } impl<'db> ConstVariableValue<'db> { @@ -158,8 +158,8 @@ impl<'db> UnifyValue for ConstVariableValue<'db> { // If both sides are *unknown*, it hardly matters, does it? ( - ConstVariableValue::Unknown { origin, universe: universe1 }, - ConstVariableValue::Unknown { origin: _, universe: universe2 }, + ConstVariableValue::Unknown { span: origin, universe: universe1 }, + ConstVariableValue::Unknown { span: _, universe: universe2 }, ) => { // If we unify two unbound variables, ?T and ?U, then whatever // value they wind up taking (which must be the same value) must @@ -167,7 +167,7 @@ impl<'db> UnifyValue for ConstVariableValue<'db> { // universe is the minimum of the two universes, because that is // the one which contains the fewest names in scope. let universe = cmp::min(*universe1, *universe2); - Ok(ConstVariableValue::Unknown { origin: *origin, universe }) + Ok(ConstVariableValue::Unknown { span: *origin, universe }) } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs index 63a225b98f9fd..566f72fbd8e62 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs @@ -6,19 +6,22 @@ use rustc_next_trait_solver::{ }; use rustc_type_ir::{ VisitorResult, - inherent::{IntoKind, Span as _}, + inherent::IntoKind, solve::{Certainty, GoalSource, MaybeCause, NoSolution}, }; -use crate::next_solver::{ - DbInterner, GenericArg, GenericArgs, Goal, NormalizesTo, ParamEnv, Predicate, PredicateKind, - QueryResult, SolverContext, Span, Term, - fulfill::NextSolverError, - infer::{ - InferCtxt, - traits::{Obligation, ObligationCause}, +use crate::{ + Span, + next_solver::{ + DbInterner, GenericArg, GenericArgs, Goal, NormalizesTo, ParamEnv, Predicate, + PredicateKind, QueryResult, SolverContext, Term, + fulfill::NextSolverError, + infer::{ + InferCtxt, + traits::{Obligation, ObligationCause}, + }, + obligation_ctxt::ObligationCtxt, }, - obligation_ctxt::ObligationCtxt, }; pub(crate) struct InspectConfig { @@ -142,7 +145,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { &self, visitor: &mut V, ) -> V::Result { - for goal in self.instantiate_nested_goals() { + for goal in self.instantiate_nested_goals(visitor.span()) { try_visit!(goal.visit_with(visitor)); } @@ -153,7 +156,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { /// inference constraints. This function modifies the state of the `infcx`. /// /// See [`Self::instantiate_impl_args`] if you need the impl args too. - pub(crate) fn instantiate_nested_goals(&self) -> Vec> { + pub(crate) fn instantiate_nested_goals(&self, span: Span) -> Vec> { let infcx = self.goal.infcx; let param_env = self.goal.goal.param_env; let mut orig_values = self.goal.orig_values.to_vec(); @@ -163,13 +166,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { match **step { inspect::ProbeStep::AddGoal(source, goal) => instantiated_goals.push(( source, - instantiate_canonical_state( - infcx, - Span::dummy(), - param_env, - &mut orig_values, - goal, - ), + instantiate_canonical_state(infcx, span, param_env, &mut orig_values, goal), )), inspect::ProbeStep::RecordImplArgs { .. } => {} inspect::ProbeStep::MakeCanonicalResponse { .. } @@ -177,13 +174,8 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { } } - let () = instantiate_canonical_state( - infcx, - Span::dummy(), - param_env, - &mut orig_values, - self.final_state, - ); + let () = + instantiate_canonical_state(infcx, span, param_env, &mut orig_values, self.final_state); if let Some(term_hack) = &self.goal.normalizes_to_term_hack { // FIXME: We ignore the expected term of `NormalizesTo` goals @@ -194,14 +186,14 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { instantiated_goals .into_iter() - .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal)) + .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span)) .collect() } /// Instantiate the args of an impl if this candidate came from a /// `CandidateSource::Impl`. This function modifies the state of the /// `infcx`. - pub(crate) fn instantiate_impl_args(&self) -> GenericArgs<'db> { + pub(crate) fn instantiate_impl_args(&self, span: Span) -> GenericArgs<'db> { let infcx = self.goal.infcx; let param_env = self.goal.goal.param_env; let mut orig_values = self.goal.orig_values.to_vec(); @@ -211,7 +203,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { inspect::ProbeStep::RecordImplArgs { impl_args } => { let impl_args = instantiate_canonical_state( infcx, - Span::dummy(), + span, param_env, &mut orig_values, impl_args, @@ -219,7 +211,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { let () = instantiate_canonical_state( infcx, - Span::dummy(), + span, param_env, &mut orig_values, self.final_state, @@ -246,11 +238,12 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { &self, source: GoalSource, goal: Goal<'db, Predicate<'db>>, + span: Span, ) -> InspectGoal<'a, 'db> { let infcx = self.goal.infcx; match goal.predicate.kind().no_bound_vars() { Some(PredicateKind::NormalizesTo(NormalizesTo { alias, term })) => { - let unconstrained_term = infcx.next_term_var_of_kind(term); + let unconstrained_term = infcx.next_term_var_of_kind(term, span); let goal = goal.with(infcx.interner, NormalizesTo { alias, term: unconstrained_term }); // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the @@ -265,8 +258,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { // considering the constrained RHS, and pass the resulting certainty to // `InspectGoal::new` so that the goal has the right result (and maintains // the impression that we don't do this normalizes-to infer hack at all). - let (nested, proof_tree) = - infcx.evaluate_root_goal_for_proof_tree(goal, Span::dummy()); + let (nested, proof_tree) = infcx.evaluate_root_goal_for_proof_tree(goal, span); let nested_goals_result = nested.and_then(|nested| { normalizes_to_term_hack.constrain_and( infcx, @@ -300,7 +292,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { // constraints, we get an ICE if we already applied the constraints // from the chosen candidate. let proof_tree = - infcx.probe(|_| infcx.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1); + infcx.probe(|_| infcx.evaluate_root_goal_for_proof_tree(goal, span).1); InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source) } } @@ -469,6 +461,8 @@ impl<'a, 'db> InspectGoal<'a, 'db> { pub(crate) trait ProofTreeVisitor<'db> { type Result: VisitorResult; + fn span(&self) -> Span; + fn config(&self) -> InspectConfig { InspectConfig { max_depth: 10 } } @@ -496,7 +490,7 @@ impl<'db> InferCtxt<'db> { visitor: &mut V, ) -> V::Result { let (_, proof_tree) = <&SolverContext<'db>>::from(self) - .evaluate_root_goal_for_proof_tree(goal, Span::dummy()); + .evaluate_root_goal_for_proof_tree(goal, visitor.span()); visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc)) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 4095dbe47d852..af3798f1dadc2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -37,7 +37,7 @@ use rustc_type_ir::{ }; use crate::{ - FnAbi, + FnAbi, Span, db::{HirDatabase, InternedClosure, InternedCoroutineId}, lower::GenericPredicates, method_resolution::TraitImpls, @@ -382,13 +382,9 @@ impl<'db> DbInterner<'db> { } } -// This is intentionally left as `()` -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub struct Span(()); - impl<'db> inherent::Span> for Span { fn dummy() -> Self { - Span(()) + Span::Dummy } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs index 5d8f3fe5194aa..aa6f27c4c2d2a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs @@ -99,7 +99,7 @@ impl<'db> NormalizationFolder<'_, 'db> { self.depth += 1; - let infer_term = infcx.next_term_var_of_kind(alias_term); + let infer_term = infcx.next_term_var_of_kind(alias_term, self.at.cause.span()); let obligation = Obligation::new( interner, self.at.cause.clone(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index d45ac6c959695..018ecd66e0995 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -15,7 +15,7 @@ use rustc_type_ir::{ use tracing::debug; use crate::{ - ParamEnvAndCrate, + ParamEnvAndCrate, Span, next_solver::{ AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs, ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, UnevaluatedConst, @@ -24,7 +24,7 @@ use crate::{ }; use super::{ - Const, DbInterner, ErrorGuaranteed, GenericArg, SolverDefId, Span, + Const, DbInterner, ErrorGuaranteed, GenericArg, SolverDefId, infer::{DbInternerInferExt, InferCtxt, canonical::instantiate::CanonicalExt}, }; @@ -63,15 +63,15 @@ impl<'db> SolverDelegate for SolverContext<'db> { where V: rustc_type_ir::TypeFoldable, { - let (infcx, value, vars) = cx.infer_ctxt().build_with_canonical(canonical); + let (infcx, value, vars) = cx.infer_ctxt().build_with_canonical(Span::Dummy, canonical); (SolverContext(infcx), value, vars) } - fn fresh_var_for_kind_with_span(&self, arg: GenericArg<'db>, _span: Span) -> GenericArg<'db> { + fn fresh_var_for_kind_with_span(&self, arg: GenericArg<'db>, span: Span) -> GenericArg<'db> { match arg.kind() { - GenericArgKind::Lifetime(_) => self.next_region_var().into(), - GenericArgKind::Type(_) => self.next_ty_var().into(), - GenericArgKind::Const(_) => self.next_const_var().into(), + GenericArgKind::Lifetime(_) => self.next_region_var(span).into(), + GenericArgKind::Type(_) => self.next_ty_var(span).into(), + GenericArgKind::Const(_) => self.next_const_var(span).into(), } } @@ -124,11 +124,11 @@ impl<'db> SolverDelegate for SolverContext<'db> { fn instantiate_canonical_var( &self, kind: CanonicalVarKind<'db>, - _span: ::Span, + span: Span, var_values: &[GenericArg<'db>], universe_map: impl Fn(rustc_type_ir::UniverseIndex) -> rustc_type_ir::UniverseIndex, ) -> GenericArg<'db> { - self.0.instantiate_canonical_var(kind, var_values, universe_map) + self.0.instantiate_canonical_var(span, kind, var_values, universe_map) } fn add_item_bounds_for_hidden_type( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs index 00c3708358b92..7a70bae97cfea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs @@ -34,7 +34,7 @@ impl<'db> At<'_, 'db> { return Ok(term); } - let new_infer = self.infcx.next_term_var_of_kind(term); + let new_infer = self.infcx.next_term_var_of_kind(term, self.cause.span()); // We simply emit an `alias-eq` goal here, since that will take care of // normalizing the LHS of the projection until it is a rigid projection diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs index 2e85beea9163d..c78e9d7c5dfa1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs @@ -10,7 +10,7 @@ use rustc_type_ir::inherent::Ty as _; use syntax::ast; use crate::{ - ImplTraitId, InferenceResult, + ImplTraitId, InferenceResult, Span, db::{HirDatabase, InternedOpaqueTyId}, lower::{ImplTraitIdx, ImplTraits}, next_solver::{ @@ -140,7 +140,7 @@ pub(crate) fn tait_hidden_types<'db>( } // In the presence of errors, we attempt to create a unified type from all // types. rustc doesn't do that, but this should improve the experience. - let hidden_type = infcx.insert_type_vars(hidden_type.as_ref()); + let hidden_type = infcx.insert_type_vars(hidden_type.as_ref(), Span::Dummy); match result.entry(opaque_idx) { la_arena::Entry::Vacant(entry) => { entry.insert(StoredEarlyBinder::bind(hidden_type.store())); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs index a0bac482500d5..7e4d3a83549ea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs @@ -7,6 +7,7 @@ use hir_def::{ use tracing::debug; use crate::{ + Span, db::HirDatabase, lower::GenericPredicates, next_solver::{ @@ -91,7 +92,7 @@ fn specializes_query( let mut ocx = ObligationCtxt::new(&infcx); - let parent_args = infcx.fresh_args_for_item(parent_impl_def_id.into()); + let parent_args = infcx.fresh_args_for_item(Span::Dummy, parent_impl_def_id.into()); let parent_impl_trait_ref = db .impl_trait(parent_impl_def_id) .expect("expected source impl to be a trait impl") diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 0dc834ddcc71e..ad668f2eee0b1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -21,6 +21,7 @@ use rustc_type_ir::{ }; use crate::{ + Span, db::HirDatabase, next_solver::{ DbInterner, GenericArgs, ParamEnv, StoredClauses, Ty, TyKind, @@ -121,7 +122,7 @@ pub fn implements_trait_unique<'db>( trait_: TraitId, ) -> bool { implements_trait_unique_impl(db, env, trait_, &mut |infcx| { - infcx.fill_rest_fresh_args(trait_.into(), [ty.into()]) + infcx.fill_rest_fresh_args(Span::Dummy, trait_.into(), [ty.into()]) }) } diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 9943b718fd38b..f9cf05e73a103 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -504,6 +504,8 @@ fn resolve_impl_trait_item<'db>( traits_in_scope: &traits_in_scope, edition: krate.edition(db), features, + call_span: hir_ty::Span::Dummy, + receiver_span: hir_ty::Span::Dummy, }; let resolution = ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty.ty); let resolution = match resolution { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 11598f2a103af..f5ca22e10c8a9 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1525,7 +1525,7 @@ impl Struct { } pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedStruct<'db> { - let args = infer_ctxt.fresh_args_for_item(self.id.into()); + let args = infer_ctxt.fresh_args_for_item(hir_ty::Span::Dummy, self.id.into()); InstantiatedStruct { inner: self, args } } } @@ -1804,8 +1804,10 @@ impl EnumVariant { } pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedVariant<'db> { - let args = - infer_ctxt.fresh_args_for_item(self.parent_enum(infer_ctxt.interner.db()).id.into()); + let args = infer_ctxt.fresh_args_for_item( + hir_ty::Span::Dummy, + self.parent_enum(infer_ctxt.interner.db()).id.into(), + ); InstantiatedVariant { inner: self, args } } } @@ -6191,6 +6193,8 @@ impl<'db> Type<'db> { traits_in_scope, edition: resolver.krate().data(db).edition, features, + call_span: hir_ty::Span::Dummy, + receiver_span: hir_ty::Span::Dummy, }; f(&ctx) } @@ -6217,7 +6221,7 @@ impl<'db> Type<'db> { self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| { // There should be no inference vars in types passed here let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty); - let (self_ty, _) = ctx.infcx.instantiate_canonical(&canonical); + let (self_ty, _) = ctx.infcx.instantiate_canonical(hir_ty::Span::Dummy, &canonical); match name { Some(name) => { @@ -6325,7 +6329,7 @@ impl<'db> Type<'db> { self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| { // There should be no inference vars in types passed here let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty); - let (self_ty, _) = ctx.infcx.instantiate_canonical(&canonical); + let (self_ty, _) = ctx.infcx.instantiate_canonical(hir_ty::Span::Dummy, &canonical); match name { Some(name) => { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 9d97dc7421882..17f5b51cc3e4b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -36,14 +36,13 @@ use hir_ty::{ diagnostics::unsafe_operations, infer_query_with_inspect, next_solver::{ - AnyImplId, DbInterner, Span, + AnyImplId, DbInterner, format_proof_tree::{ProofTreeData, dump_proof_tree_structured}, }, }; use intern::{Interned, Symbol, sym}; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_type_ir::inherent::Span as _; use smallvec::{SmallVec, smallvec}; use span::{FileId, SyntaxContext}; use stdx::{TupleExt, always}; @@ -2459,7 +2458,8 @@ impl<'db> SemanticsImpl<'db> { if result.is_err() && let Some(tree) = proof_tree { - let data = dump_proof_tree_structured(tree, Span::dummy(), infer_ctxt); + let data = + dump_proof_tree_structured(tree, hir_ty::Span::Dummy, infer_ctxt); RESULT.with(|ctx| ctx.borrow_mut().push(data)); } }), From e9ece045e652afe105ca144aeb4e6a824f846a1e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 24 Apr 2026 15:33:02 +0300 Subject: [PATCH 081/289] Parse impl restrictions after the visibility To match rustc. --- .../crates/parser/src/grammar/items.rs | 38 +++++++++---------- .../parser/inline/ok/impl_restrictions.rast | 4 +- .../parser/inline/ok/impl_restrictions.rs | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs index c5c6e04dd49aa..88a43972329ba 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs @@ -119,6 +119,25 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker, is_in_extern: bool) -> Res let mut has_mods = false; let mut has_extern = false; + if p.at(T![impl]) + && p.nth(1) == T!['('] + && ((matches!(p.nth(2), T![crate] | T![super] | T![self]) && p.nth(3) == T![')']) + || p.nth(2) == T![in]) + { + // test impl_restrictions + // pub impl(crate) unsafe trait Foo {} + // impl(in super::bar) trait Bar {} + // impl () {} + // impl (i32) {} + let m = p.start(); + p.bump(T![impl]); + if !opt_visibility_inner(p, false) { + p.error("expected an impl restriction"); + } + m.complete(p, IMPL_RESTRICTION); + has_mods = true; + } + // modifiers if p.at(T![const]) && p.nth(1) != T!['{'] { p.eat(T![const]); @@ -167,25 +186,6 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker, is_in_extern: bool) -> Res has_mods = true; } - if p.at(T![impl]) - && p.nth(1) == T!['('] - && ((matches!(p.nth(2), T![crate] | T![super] | T![self]) && p.nth(3) == T![')']) - || p.nth(2) == T![in]) - { - // test impl_restrictions - // pub unsafe impl(crate) trait Foo {} - // impl(in super::bar) trait Bar {} - // impl () {} - // impl (i32) {} - let m = p.start(); - p.bump(T![impl]); - if !opt_visibility_inner(p, false) { - p.error("expected an impl restriction"); - } - m.complete(p, IMPL_RESTRICTION); - has_mods = true; - } - // test default_item // default impl T for Foo {} if p.at_contextual_kw(T![default]) { diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast index 5f2680cbaa921..bf7b5c5a34f08 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast @@ -3,8 +3,6 @@ SOURCE_FILE VISIBILITY PUB_KW "pub" WHITESPACE " " - UNSAFE_KW "unsafe" - WHITESPACE " " IMPL_RESTRICTION IMPL_KW "impl" VISIBILITY_INNER @@ -15,6 +13,8 @@ SOURCE_FILE CRATE_KW "crate" R_PAREN ")" WHITESPACE " " + UNSAFE_KW "unsafe" + WHITESPACE " " TRAIT_KW "trait" WHITESPACE " " NAME diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs index 0a46b158affc9..429ac93ad50fd 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs @@ -1,4 +1,4 @@ -pub unsafe impl(crate) trait Foo {} +pub impl(crate) unsafe trait Foo {} impl(in super::bar) trait Bar {} impl () {} impl (i32) {} From 449c27b30fcdc5e6029d9a1efef4404d8f96708b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 24 Apr 2026 15:24:55 +0300 Subject: [PATCH 082/289] Feature a "type annotations needed diagnostic" when an infer var cannot be resolved at the end of inference --- .../crates/hir-def/src/expr_store.rs | 1 + .../crates/hir-def/src/expr_store/lower.rs | 10 +- .../crates/hir-def/src/expr_store/pretty.rs | 1 + .../rust-analyzer/crates/hir-def/src/hir.rs | 3 + .../rust-analyzer/crates/hir-ty/src/db.rs | 2 +- .../hir-ty/src/diagnostics/unsafe_check.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 199 +++++++------ .../crates/hir-ty/src/infer/callee.rs | 48 ++- .../crates/hir-ty/src/infer/closure.rs | 10 +- .../closure/analysis/expr_use_visitor.rs | 4 +- .../crates/hir-ty/src/infer/coerce.rs | 16 +- .../crates/hir-ty/src/infer/expr.rs | 2 +- .../crates/hir-ty/src/infer/pat.rs | 7 +- .../crates/hir-ty/src/infer/path.rs | 10 +- .../crates/hir-ty/src/infer/unify.rs | 275 +++++++++++++++--- .../rust-analyzer/crates/hir-ty/src/lib.rs | 12 + .../rust-analyzer/crates/hir-ty/src/lower.rs | 2 +- .../crates/hir-ty/src/lower/path.rs | 12 +- .../hir-ty/src/method_resolution/confirm.rs | 8 +- .../crates/hir-ty/src/mir/lower.rs | 2 +- .../hir-ty/src/mir/lower/pattern_matching.rs | 2 +- .../hir-ty/src/next_solver/generic_arg.rs | 38 +++ .../hir-ty/src/next_solver/infer/mod.rs | 26 +- .../infer/region_constraints/mod.rs | 21 +- .../next_solver/infer/relate/generalize.rs | 4 +- .../src/next_solver/infer/type_variable.rs | 62 ++-- .../hir-ty/src/next_solver/infer/unify_key.rs | 53 ++-- .../crates/hir-ty/src/next_solver/region.rs | 2 +- .../crates/hir-ty/src/next_solver/solver.rs | 39 ++- .../crates/hir-ty/src/next_solver/ty.rs | 69 +++-- .../crates/hir/src/diagnostics.rs | 43 ++- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- .../rust-analyzer/crates/hir/src/semantics.rs | 8 +- .../src/handlers/await_outside_of_async.rs | 3 + .../src/handlers/break_outside_of_loop.rs | 2 +- .../src/handlers/incorrect_case.rs | 10 +- .../src/handlers/incorrect_generics_len.rs | 12 + .../src/handlers/mismatched_arg_count.rs | 2 + .../src/handlers/missing_fields.rs | 7 +- .../src/handlers/type_mismatch.rs | 1 + .../src/handlers/type_must_be_known.rs | 79 ++++- .../src/handlers/typed_hole.rs | 2 + .../src/tests/overly_long_real_world_cases.rs | 9 +- .../rust-analyzer/crates/macros/src/lib.rs | 14 +- .../crates/test-utils/src/minicore.rs | 25 +- 45 files changed, 852 insertions(+), 309 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index 497ed7d37f417..51951896f2c7d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -561,6 +561,7 @@ impl ExpressionStore { | Pat::ConstBlock(..) | Pat::Wild | Pat::Missing + | Pat::Rest | Pat::Expr(_) => {} &Pat::Bind { subpat, .. } => { if let Some(subpat) = subpat { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 3440fbee6d78c..fd8b50d714dd2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -2622,15 +2622,7 @@ impl<'db> ExprCollector<'db> { let expr_id = self.alloc_expr(expr, expr_ptr); Pat::Lit(expr_id) } - ast::Pat::RestPat(_) => { - // `RestPat` requires special handling and should not be mapped - // to a Pat. Here we are using `Pat::Missing` as a fallback for - // when `RestPat` is mapped to `Pat`, which can easily happen - // when the source code being analyzed has a malformed pattern - // which includes `..` in a place where it isn't valid. - - Pat::Missing - } + ast::Pat::RestPat(_) => Pat::Rest, ast::Pat::BoxPat(boxpat) => { let inner = self.collect_pat_opt(boxpat.pat(), binding_list); Pat::Box { inner } 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 fdd0654508474..bb35009f36246 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 @@ -895,6 +895,7 @@ impl Printer<'_> { match pat { Pat::Missing => w!(self, "�"), + Pat::Rest => w!(self, ".."), Pat::Wild => w!(self, "_"), Pat::Tuple { args, ellipsis } => { w!(self, "("); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index a1a346cabc307..93fa7ff961791 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -666,6 +666,8 @@ pub struct RecordFieldPat { #[derive(Debug, Clone, Eq, PartialEq)] pub enum Pat { Missing, + /// A rest pattern. Not valid outside special context. + Rest, Wild, Tuple { args: Box<[PatId]>, @@ -721,6 +723,7 @@ impl Pat { | Pat::ConstBlock(..) | Pat::Wild | Pat::Missing + | Pat::Rest | Pat::Expr(_) => {} Pat::Bind { subpat, .. } => { subpat.iter().copied().for_each(f); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 3bf2d9a6a60b4..99bad2682bacf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -147,7 +147,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::lower::const_param_ty_query)] #[salsa::transparent] - fn const_param_ty_ns<'db>(&'db self, def: ConstParamId) -> Ty<'db>; + fn const_param_ty<'db>(&'db self, def: ConstParamId) -> Ty<'db>; #[salsa::invoke(crate::lower::impl_trait_with_diagnostics)] #[salsa::transparent] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index ee33f7d1585e3..4893d72a5c731 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -258,7 +258,7 @@ impl<'db> UnsafeVisitor<'db> { self.on_unsafe_op(current.into(), UnsafetyReason::UnionField) } // `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read. - Pat::Missing | Pat::Wild | Pat::Or(_) => {} + Pat::Missing | Pat::Rest | Pat::Wild | Pat::Or(_) => {} } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 30b420b6d5f86..4aeb5ec71cab2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -29,7 +29,7 @@ mod path; mod place_op; pub(crate) mod unify; -use std::{cell::OnceCell, convert::identity, fmt, iter, ops::Deref}; +use std::{cell::OnceCell, convert::identity, fmt, ops::Deref}; use base_db::{Crate, FxIndexMap}; use either::Either; @@ -50,6 +50,7 @@ use hir_def::{ use hir_expand::{mod_path::ModPath, name::Name}; use indexmap::IndexSet; use la_arena::ArenaMap; +use macros::{TypeFoldable, TypeVisitable}; use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ @@ -76,14 +77,15 @@ use crate::{ diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext}, expr::ExprIsRead, pat::PatOrigin, + unify::resolve_completely::WriteBackCtxt, }, lower::{ ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic, }, method_resolution::CandidateId, next_solver::{ - AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArgs, Region, StoredGenericArgs, - StoredTy, StoredTys, Ty, TyKind, Tys, + AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArgs, Region, StoredGenericArg, + StoredGenericArgs, StoredTy, StoredTys, Term, Ty, TyKind, Tys, abi::Safety, infer::{InferCtxt, ObligationInspector, traits::ObligationCause}, }, @@ -188,7 +190,7 @@ fn infer_signature_query(db: &dyn HirDatabase, def: GenericDefId) -> InferenceRe // Array lengths are always `usize`. RootExprOrigin::ArrayLength => Expectation::has_type(ctx.types.types.usize), // Const parameter default: look up the param's declared type. - RootExprOrigin::ConstParam(local_id) => Expectation::has_type(db.const_param_ty_ns( + RootExprOrigin::ConstParam(local_id) => Expectation::has_type(db.const_param_ty( ConstParamId::from_unchecked(TypeOrConstParamId { parent: def, local_id }), )), // Path const generic args: determining the expected type requires @@ -307,107 +309,152 @@ pub enum InferenceTyDiagnosticSource { Signature, } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, TypeVisitable, TypeFoldable)] pub enum InferenceDiagnostic { NoSuchField { + #[type_visitable(ignore)] field: ExprOrPatId, + #[type_visitable(ignore)] private: Option, + #[type_visitable(ignore)] variant: VariantId, }, PrivateField { + #[type_visitable(ignore)] expr: ExprId, + #[type_visitable(ignore)] field: FieldId, }, PrivateAssocItem { + #[type_visitable(ignore)] id: ExprOrPatId, + #[type_visitable(ignore)] item: AssocItemId, }, UnresolvedField { + #[type_visitable(ignore)] expr: ExprId, receiver: StoredTy, + #[type_visitable(ignore)] name: Name, + #[type_visitable(ignore)] method_with_same_name_exists: bool, }, UnresolvedMethodCall { + #[type_visitable(ignore)] expr: ExprId, receiver: StoredTy, + #[type_visitable(ignore)] name: Name, /// Contains the type the field resolves to field_with_same_name: Option, + #[type_visitable(ignore)] assoc_func_with_same_name: Option, }, UnresolvedAssocItem { + #[type_visitable(ignore)] id: ExprOrPatId, }, UnresolvedIdent { + #[type_visitable(ignore)] id: ExprOrPatId, }, // FIXME: This should be emitted in body lowering BreakOutsideOfLoop { + #[type_visitable(ignore)] expr: ExprId, + #[type_visitable(ignore)] is_break: bool, + #[type_visitable(ignore)] bad_value_break: bool, }, MismatchedArgCount { + #[type_visitable(ignore)] call_expr: ExprId, + #[type_visitable(ignore)] expected: usize, + #[type_visitable(ignore)] found: usize, }, MismatchedTupleStructPatArgCount { + #[type_visitable(ignore)] pat: PatId, + #[type_visitable(ignore)] expected: usize, + #[type_visitable(ignore)] found: usize, }, ExpectedFunction { + #[type_visitable(ignore)] call_expr: ExprId, found: StoredTy, }, TypedHole { + #[type_visitable(ignore)] expr: ExprId, expected: StoredTy, }, CastToUnsized { + #[type_visitable(ignore)] expr: ExprId, cast_ty: StoredTy, }, InvalidCast { + #[type_visitable(ignore)] expr: ExprId, + #[type_visitable(ignore)] error: CastError, expr_ty: StoredTy, cast_ty: StoredTy, }, TyDiagnostic { + #[type_visitable(ignore)] source: InferenceTyDiagnosticSource, + #[type_visitable(ignore)] diag: TyLoweringDiagnostic, }, PathDiagnostic { + #[type_visitable(ignore)] node: ExprOrPatId, + #[type_visitable(ignore)] diag: PathLoweringDiagnostic, }, MethodCallIncorrectGenericsLen { + #[type_visitable(ignore)] expr: ExprId, + #[type_visitable(ignore)] provided_count: u32, + #[type_visitable(ignore)] expected_count: u32, + #[type_visitable(ignore)] kind: IncorrectGenericsLenKind, + #[type_visitable(ignore)] def: GenericDefId, }, MethodCallIncorrectGenericsOrder { + #[type_visitable(ignore)] expr: ExprId, + #[type_visitable(ignore)] param_id: GenericParamId, + #[type_visitable(ignore)] arg_idx: u32, /// Whether the `GenericArgs` contains a `Self` arg. + #[type_visitable(ignore)] has_self_arg: bool, }, InvalidLhsOfAssignment { + #[type_visitable(ignore)] lhs: ExprId, }, TypeMustBeKnown { - at_point: ExprOrPatId, + #[type_visitable(ignore)] + at_point: Span, + top_term: Option, }, } /// A mismatch between an expected and an inferred type. -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, PartialEq, Eq, Debug, Hash, TypeVisitable, TypeFoldable)] pub struct TypeMismatch { pub expected: StoredTy, pub actual: StoredTy, @@ -1181,7 +1228,7 @@ pub(crate) struct InferenceContext<'body, 'db> { deferred_call_resolutions: FxHashMap>>, diagnostics: Diagnostics, - vars_emitted_type_must_be_known_for: FxHashSet>, + vars_emitted_type_must_be_known_for: FxHashSet>, } #[derive(Clone, Debug)] @@ -1331,14 +1378,15 @@ impl<'body, 'db> InferenceContext<'body, 'db> { // there is no problem in it being `pub(crate)`, remove this comment. fn resolve_all(self) -> InferenceResult { let InferenceContext { - mut table, + table, mut result, tuple_field_accesses_rev, diagnostics, types, + vars_emitted_type_must_be_known_for, .. } = self; - let mut diagnostics = diagnostics.finish(); + let diagnostics = diagnostics.finish(); // Destructure every single field so whenever new fields are added to `InferenceResult` we // don't forget to handle them here. let InferenceResult { @@ -1359,30 +1407,28 @@ impl<'body, 'db> InferenceContext<'body, 'db> { pat_adjustments, binding_modes: _, expr_adjustments, - tuple_field_access_types: _, + tuple_field_access_types, coercion_casts: _, - diagnostics: _, + diagnostics: result_diagnostics, } = &mut result; + let mut resolver = + WriteBackCtxt::new(table, diagnostics, vars_emitted_type_must_be_known_for); skipped_ref_pats.shrink_to_fit(); for ty in type_of_expr.values_mut() { - *ty = table.resolve_completely(ty.as_ref()).store(); - *has_errors = *has_errors || ty.as_ref().references_non_lt_error(); + resolver.resolve_completely(ty); } type_of_expr.shrink_to_fit(); for ty in type_of_pat.values_mut() { - *ty = table.resolve_completely(ty.as_ref()).store(); - *has_errors = *has_errors || ty.as_ref().references_non_lt_error(); + resolver.resolve_completely(ty); } type_of_pat.shrink_to_fit(); for ty in type_of_binding.values_mut() { - *ty = table.resolve_completely(ty.as_ref()).store(); - *has_errors = *has_errors || ty.as_ref().references_non_lt_error(); + resolver.resolve_completely(ty); } type_of_binding.shrink_to_fit(); for ty in type_of_type_placeholder.values_mut() { - *ty = table.resolve_completely(ty.as_ref()).store(); - *has_errors = *has_errors || ty.as_ref().references_non_lt_error(); + resolver.resolve_completely(ty); } type_of_type_placeholder.shrink_to_fit(); type_of_opaque.shrink_to_fit(); @@ -1390,61 +1436,25 @@ impl<'body, 'db> InferenceContext<'body, 'db> { if let Some(type_mismatches) = type_mismatches { *has_errors = true; for mismatch in type_mismatches.values_mut() { - mismatch.expected = table.resolve_completely(mismatch.expected.as_ref()).store(); - mismatch.actual = table.resolve_completely(mismatch.actual.as_ref()).store(); + resolver.resolve_type_mismatch(mismatch); } type_mismatches.shrink_to_fit(); } - diagnostics.retain_mut(|diagnostic| { - use InferenceDiagnostic::*; - match diagnostic { - ExpectedFunction { found: ty, .. } - | UnresolvedField { receiver: ty, .. } - | UnresolvedMethodCall { receiver: ty, .. } => { - *ty = table.resolve_completely(ty.as_ref()).store(); - // FIXME: Remove this when we are on par with rustc in terms of inference - if ty.as_ref().references_non_lt_error() { - return false; - } - - if let UnresolvedMethodCall { field_with_same_name, .. } = diagnostic - && let Some(ty) = field_with_same_name - { - *ty = table.resolve_completely(ty.as_ref()).store(); - if ty.as_ref().references_non_lt_error() { - *field_with_same_name = None; - } - } - } - TypedHole { expected: ty, .. } => { - *ty = table.resolve_completely(ty.as_ref()).store(); - } - _ => (), - } - true - }); - diagnostics.shrink_to_fit(); for (_, subst) in method_resolutions.values_mut() { - *subst = table.resolve_completely(subst.as_ref()).store(); - *has_errors = - *has_errors || subst.as_ref().types().any(|ty| ty.references_non_lt_error()); + resolver.resolve_completely(subst); } method_resolutions.shrink_to_fit(); for (_, subst) in assoc_resolutions.values_mut() { - *subst = table.resolve_completely(subst.as_ref()).store(); - *has_errors = - *has_errors || subst.as_ref().types().any(|ty| ty.references_non_lt_error()); + resolver.resolve_completely(subst); } assoc_resolutions.shrink_to_fit(); for adjustment in expr_adjustments.values_mut().flatten() { - adjustment.target = table.resolve_completely(adjustment.target.as_ref()).store(); - *has_errors = *has_errors || adjustment.target.as_ref().references_non_lt_error(); + resolver.resolve_completely(&mut adjustment.target); } expr_adjustments.shrink_to_fit(); for adjustments in pat_adjustments.values_mut() { for adjustment in &mut *adjustments { - adjustment.source = table.resolve_completely(adjustment.source.as_ref()).store(); - *has_errors = *has_errors || adjustment.source.as_ref().references_non_lt_error(); + resolver.resolve_completely(&mut adjustment.source); } adjustments.shrink_to_fit(); } @@ -1458,7 +1468,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { }; for (place, _, sources) in fake_reads { - *place = table.resolve_completely(std::mem::replace(place, dummy_place())); + resolver.resolve_completely_with_default(place, dummy_place()); place.projections.shrink_to_fit(); for source in &mut *sources { source.shrink_to_fit(); @@ -1469,7 +1479,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { for min_capture in min_captures.values_mut() { for captured in &mut *min_capture { let CapturedPlace { place, info, mutability: _ } = captured; - *place = table.resolve_completely(std::mem::replace(place, dummy_place())); + resolver.resolve_completely_with_default(place, dummy_place()); let CaptureInfo { sources, capture_kind: _ } = info; for source in &mut *sources { source.shrink_to_fit(); @@ -1481,17 +1491,18 @@ impl<'body, 'db> InferenceContext<'body, 'db> { min_captures.shrink_to_fit(); } closures_data.shrink_to_fit(); - result.tuple_field_access_types = tuple_field_accesses_rev + *tuple_field_access_types = tuple_field_accesses_rev .into_iter() - .map(|subst| table.resolve_completely(subst).store()) - .inspect(|subst| { - *has_errors = - *has_errors || subst.as_ref().iter().any(|ty| ty.references_non_lt_error()); + .map(|mut subst| { + resolver.resolve_completely(&mut subst); + subst.store() }) .collect(); - result.tuple_field_access_types.shrink_to_fit(); + tuple_field_access_types.shrink_to_fit(); - result.diagnostics = diagnostics; + let (diagnostics, resolver_has_errors) = resolver.resolve_diagnostics(); + *result_diagnostics = diagnostics; + *has_errors |= resolver_has_errors; result } @@ -1502,6 +1513,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { &data.store, InferenceTyDiagnosticSource::Signature, LifetimeElisionKind::for_const(self.interner(), id.loc(self.db).container), + Span::Dummy, ); self.return_ty = return_ty; @@ -1513,6 +1525,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { &data.store, InferenceTyDiagnosticSource::Signature, LifetimeElisionKind::Elided(self.types.regions.statik), + Span::Dummy, ); self.return_ty = return_ty; @@ -1545,16 +1558,16 @@ impl<'body, 'db> InferenceContext<'body, 'db> { param_tys.push(va_list_ty); } - let mut param_tys = - param_tys.into_iter().chain(iter::repeat(self.table.next_ty_var(Span::Dummy))); + let mut param_tys = param_tys.into_iter(); if let Some(self_param) = self_param && let Some(ty) = param_tys.next() { - let ty = self.process_user_written_ty(ty); + let ty = self.process_user_written_ty(Span::Dummy, ty); self.write_binding_ty(self_param, ty); } - for (ty, pat) in param_tys.zip(params) { - let ty = self.process_user_written_ty(ty); + for pat in params { + let ty = param_tys.next().unwrap_or_else(|| self.table.next_ty_var(Span::Dummy)); + let ty = self.process_user_written_ty(Span::Dummy, ty); self.infer_top_pat(*pat, ty, PatOrigin::Param); } @@ -1569,7 +1582,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { ctx.lower_ty(return_ty) }, ); - self.process_user_written_ty(return_ty) + self.process_user_written_ty(Span::Dummy, return_ty) } None => self.types.types.unit, }; @@ -1606,7 +1619,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { let var = self.table.next_ty_var(Span::Dummy); // Suppress future errors on this var. Add more things here when we add more diagnostics. - self.vars_emitted_type_must_be_known_for.insert(var); + self.vars_emitted_type_must_be_known_for.insert(var.into()); var } else { @@ -1751,10 +1764,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { store: &ExpressionStore, type_source: InferenceTyDiagnosticSource, lifetime_elision: LifetimeElisionKind<'db>, + span: Span, ) -> Ty<'db> { let ty = self .with_ty_lowering(store, type_source, lifetime_elision, |ctx| ctx.lower_ty(type_ref)); - let ty = self.process_user_written_ty(ty); + let ty = self.process_user_written_ty(span, ty); // Record the association from placeholders' TypeRefId to type variables. // We only record them if their number matches. This assumes TypeRef::walk and TypeVisitable process the items in the same order. @@ -1781,6 +1795,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.store, InferenceTyDiagnosticSource::Body, LifetimeElisionKind::Infer, + type_ref.into(), ) } @@ -1791,17 +1806,22 @@ impl<'body, 'db> InferenceContext<'body, 'db> { LifetimeElisionKind::Infer, |ctx| ctx.lower_const(const_ref, ty), ); - self.insert_type_vars(const_, Span::Dummy) + self.insert_type_vars(const_, const_ref.expr.into()) } - pub(crate) fn make_path_as_body_const(&mut self, path: &Path, ty: Ty<'db>) -> Const<'db> { + pub(crate) fn make_path_as_body_const( + &mut self, + type_ref: TypeRefId, + path: &Path, + ty: Ty<'db>, + ) -> Const<'db> { let const_ = self.with_ty_lowering( self.store, InferenceTyDiagnosticSource::Body, LifetimeElisionKind::Infer, |ctx| ctx.lower_path_as_const(path, ty), ); - self.insert_type_vars(const_, Span::Dummy) + self.insert_type_vars(const_, type_ref.into()) } fn err_ty(&self) -> Ty<'db> { @@ -1887,8 +1907,8 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } /// Whenever you lower a user-written type, you should call this. - fn process_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { - self.table.process_user_written_ty(ty) + fn process_user_written_ty(&mut self, span: Span, ty: Ty<'db>) -> Ty<'db> { + self.table.process_user_written_ty(span, ty) } /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation, @@ -1979,8 +1999,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { node: ExprOrPatId, ty: Ty<'db>, ) -> Ty<'db> { - if self.vars_emitted_type_must_be_known_for.insert(ty) { - self.push_diagnostic(InferenceDiagnostic::TypeMustBeKnown { at_point: node }); + if self.vars_emitted_type_must_be_known_for.insert(ty.into()) { + self.push_diagnostic(InferenceDiagnostic::TypeMustBeKnown { + at_point: node.into(), + top_term: None, + }); } self.types.types.error } @@ -2029,7 +2052,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { return (self.err_ty(), None); } let (mut ty, type_ns) = ctx.lower_ty_ext(type_anchor); - ty = self.table.process_user_written_ty(ty); + ty = self.table.process_user_written_ty(type_anchor.into(), ty); if let Some(TypeNs::SelfType(impl_)) = type_ns && let Some(trait_ref) = self.db.impl_trait(impl_) @@ -2197,7 +2220,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { (ty, _) = path_ctx.lower_partly_resolved_path(resolution, true); tried_resolving_once = true; - ty = self.table.process_user_written_ty(ty); + ty = self.table.process_user_written_ty(node.into(), ty); if ty.is_ty_error() { return (self.err_ty(), None); } @@ -2228,7 +2251,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } let (mut ty, _) = path_ctx.lower_partly_resolved_path(resolution, true); - ty = self.table.process_user_written_ty(ty); + ty = self.table.process_user_written_ty(node.into(), ty); if let Some(segment) = remaining_segments.get(1) && let Some((AdtId::EnumId(id), _)) = ty.as_adt() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs index d8639a79bd0f4..ffdde58c48b60 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs @@ -5,7 +5,9 @@ use std::iter; use intern::sym; use tracing::debug; -use hir_def::{CallableDefId, hir::ExprId, signatures::FunctionSignature}; +use hir_def::{ + CallableDefId, ConstParamId, TypeOrConstParamId, hir::ExprId, signatures::FunctionSignature, +}; use rustc_type_ir::{ InferTy, Interner, inherent::{GenericArgs as _, IntoKind, Ty as _}, @@ -20,7 +22,7 @@ use crate::{ }, method_resolution::{MethodCallee, TreatNotYetDefinedOpaques}, next_solver::{ - FnSig, Ty, TyKind, + ConstKind, FnSig, Ty, TyKind, infer::{BoundRegionConversionTime, traits::ObligationCause}, }, }; @@ -349,12 +351,26 @@ impl<'db> InferenceContext<'_, 'db> { fn check_legacy_const_generics( &mut self, callee: Option, + callee_ty: Ty<'db>, args: &[ExprId], ) -> Box<[u32]> { - let func = match callee { - Some(CallableDefId::FunctionId(func)) => func, + let (func, fn_generic_args) = match (callee, callee_ty.kind()) { + (Some(CallableDefId::FunctionId(func)), TyKind::FnDef(_, fn_generic_args)) => { + (func, fn_generic_args) + } _ => return Default::default(), }; + let generics = crate::generics::generics(self.db, func.into()); + let const_params = generics + .iter_self_type_or_consts() + .filter(|(_, param_data)| param_data.const_param().is_some()) + .map(|(idx, _)| { + ConstParamId::from_unchecked(TypeOrConstParamId { + parent: func.into(), + local_id: idx, + }) + }) + .collect::>(); let data = FunctionSignature::of(self.db, func); let Some(legacy_const_generics_indices) = data.legacy_const_generics_indices(self.db, func) @@ -376,11 +392,29 @@ impl<'db> InferenceContext<'_, 'db> { } // check legacy const parameters - for arg_idx in legacy_const_generics_indices.iter().copied() { + for (const_idx, arg_idx) in legacy_const_generics_indices.iter().copied().enumerate() { if arg_idx >= args.len() as u32 { continue; } - let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly + + if let Some(const_arg) = fn_generic_args.get(const_idx).and_then(|it| it.konst()) + && let ConstKind::Infer(_) = const_arg.kind() + { + // Instantiate the generic arg with an error type, to prevent errors from it. + // FIXME: Actually lower the expression as const. + _ = self + .table + .at(&ObligationCause::dummy()) + .eq(self.types.consts.error, const_arg) + .map(|infer_ok| self.table.register_infer_ok(infer_ok)); + } + + let expected = if let Some(&const_param) = const_params.get(const_idx) { + Expectation::has_type(self.db.const_param_ty(const_param)) + } else { + Expectation::None + }; + self.infer_expr(args[arg_idx as usize], &expected, ExprIsRead::Yes); // FIXME: evaluate and unify with the const } @@ -420,7 +454,7 @@ impl<'db> InferenceContext<'_, 'db> { fn_sig, ); - let indices_to_skip = self.check_legacy_const_generics(def_id, arg_exprs); + let indices_to_skip = self.check_legacy_const_generics(def_id, callee_ty, arg_exprs); self.check_call_arguments( call_expr, fn_sig.inputs(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 078c5c707d0a9..93c98f2542b3e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -866,18 +866,12 @@ impl<'db> InferenceContext<'_, 'db> { let interner = self.interner(); let supplied_return = match decl_output { - Some(output) => { - let output = self.make_body_ty(output); - self.process_user_written_ty(output) - } + Some(output) => self.make_body_ty(output), None => self.table.next_ty_var(closure_expr.into()), }; // First, convert the types that the user supplied (if any). let supplied_arguments = decl_inputs.iter().map(|&input| match input { - Some(input) => { - let input = self.make_body_ty(input); - self.process_user_written_ty(input) - } + Some(input) => self.make_body_ty(input), None => self.table.next_ty_var(closure_expr.into()), }); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index f140b1249173a..0fd3cda31dc13 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -1026,7 +1026,8 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { | Pat::Ref { .. } | Pat::Tuple { .. } | Pat::Wild - | Pat::Missing => { + | Pat::Missing + | Pat::Rest => { // If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses // are made later as these patterns contains subpatterns. // If the PatKind is Missing, Wild or Err, any relevant accesses are made when processing @@ -1671,6 +1672,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { | Pat::ConstBlock(..) | Pat::Range { .. } | Pat::Missing + | Pat::Rest | Pat::Wild => { // always ok } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 60f7b4073ac45..55e02a6933b65 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -901,7 +901,7 @@ impl<'db> InferenceContext<'_, 'db> { target = self.table.try_structurally_resolve_type(target); debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target); - let cause = ObligationCause::new(); + let cause = ObligationCause::with_span(expr.into()); let coerce_never = self.expr_guaranteed_to_constitute_read_for_never(expr, expr_is_read); let mut coerce = Coerce { delegate: InferenceCoercionDelegate(self), @@ -976,8 +976,12 @@ impl<'db> InferenceContext<'_, 'db> { match self.table.commit_if_ok(|table| { // We need to eagerly handle nested obligations due to lazy norm. let mut ocx = ObligationCtxt::new(&table.infer_ctxt); - let value = - ocx.lub(&ObligationCause::new(), table.param_env, prev_ty, new_ty)?; + let value = ocx.lub( + &ObligationCause::with_span(new.into()), + table.param_env, + prev_ty, + new_ty, + )?; if ocx.try_evaluate_obligations().is_empty() { Ok(InferOk { value, obligations: ocx.into_pending_obligations() }) } else { @@ -1025,7 +1029,7 @@ impl<'db> InferenceContext<'_, 'db> { let sig = self .table .infer_ctxt - .at(&ObligationCause::new(), self.table.param_env) + .at(&ObligationCause::with_span(new.into()), self.table.param_env) .lub(a_sig, b_sig) .map(|ok| self.table.register_infer_ok(ok))?; @@ -1071,7 +1075,7 @@ impl<'db> InferenceContext<'_, 'db> { // operate on values and not places, so a never coercion is valid. let mut coerce = Coerce { delegate: InferenceCoercionDelegate(self), - cause: ObligationCause::new(), + cause: ObligationCause::with_span(new.into()), allow_two_phase: AllowTwoPhase::No, coerce_never: true, use_lub: true, @@ -1107,7 +1111,7 @@ impl<'db> InferenceContext<'_, 'db> { .commit_if_ok(|table| { table .infer_ctxt - .at(&ObligationCause::new(), table.param_env) + .at(&ObligationCause::with_span(new.into()), table.param_env) .lub(prev_ty, new_ty) }) .unwrap_err()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index d2d3849b2e370..a6c8cda404a07 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -176,7 +176,7 @@ impl<'db> InferenceContext<'_, 'db> { fn pat_guaranteed_to_constitute_read_for_never(&self, pat: PatId) -> bool { match &self.store[pat] { // Does not constitute a read. - Pat::Wild => false, + Pat::Wild | Pat::Rest => false, // This is unnecessarily restrictive when the pattern that doesn't // constitute a read is unreachable. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 2c38fe74b14e8..ac209adef8708 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -446,7 +446,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { pat_info, ) } - Pat::Missing | Pat::Wild => expected, + Pat::Missing => self.types.types.error, + Pat::Wild | Pat::Rest => expected, // We allow any type here; we ensure that the type is uninhabited during match checking. // Pat::Never => expected, Pat::Path(_) => { @@ -658,8 +659,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { Pat::Ref { .. } // No need to do anything on a missing pattern. | Pat::Missing - // A `_` pattern works with any expected type, so there's no need to do anything. - | Pat::Wild + // A `_`/`..` pattern works with any expected type, so there's no need to do anything. + | Pat::Wild | Pat::Rest // Bindings also work with whatever the expected type is, // and moreover if we peel references off, that will give us the wrong binding type. // Also, we can have a subpattern `binding @ pat`. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 2c094f572fa97..c020c9812b82e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -38,7 +38,7 @@ impl<'db> InferenceContext<'_, 'db> { } ValuePathResolution::NonGeneric(ty) => return Some((value, ty)), }; - let args = self.insert_type_vars(substs, Span::Dummy); + let args = self.insert_type_vars(substs, id.into()); self.add_required_obligations_for_value_path(generic_def, args); @@ -91,7 +91,7 @@ impl<'db> InferenceContext<'_, 'db> { }; } ValueNs::GenericParam(it) => { - return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty_ns(it))); + return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty(it))); } }; @@ -157,12 +157,12 @@ impl<'db> InferenceContext<'_, 'db> { let last = path.segments().last()?; let (ty, orig_ns) = path_ctx.ty_ctx().lower_ty_ext(type_ref); - let ty = self.table.process_user_written_ty(ty); + let ty = self.table.process_user_written_ty(type_ref.into(), ty); path_ctx.ignore_last_segment(); let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns, true); drop_ctx(ctx, no_diagnostics); - let ty = self.table.process_user_written_ty(ty); + let ty = self.table.process_user_written_ty(id.into(), ty); self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? } else { let hygiene = self.store.expr_or_pat_path_hygiene(id); @@ -205,7 +205,7 @@ impl<'db> InferenceContext<'_, 'db> { return None; } - let ty = self.process_user_written_ty(ty); + let ty = self.process_user_written_ty(id.into(), ty); self.resolve_ty_assoc_item(ty, last_segment.name, id) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 31b6d50886266..4342375621d1d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -280,19 +280,6 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.var_for_def(id, span) } - pub(crate) fn resolve_completely(&mut self, value: T) -> T - where - T: TypeFoldable>, - { - let value = self.infer_ctxt.resolve_vars_if_possible(value); - - let mut goals = vec![]; - - // FIXME(next-solver): Handle `goals`. - - value.fold_with(&mut resolve_completely::Resolver::new(self, true, &mut goals)) - } - pub(crate) fn at<'a>(&'a self, cause: &'a ObligationCause) -> At<'a, 'db> { self.infer_ctxt.at(cause, self.param_env) } @@ -404,19 +391,21 @@ impl<'db> InferenceTable<'db> { where I: IntoIterator>, { - obligations.into_iter().for_each(|obligation| { - self.register_predicate(obligation); - }); + self.fulfillment_cx.register_predicate_obligations(&self.infer_ctxt, obligations); } /// checking later, during regionck, that `arg` is well-formed. pub(crate) fn register_wf_obligation(&mut self, term: Term<'db>, cause: ObligationCause) { - self.register_predicate(Obligation::new( - self.interner(), - cause, - self.param_env, - ClauseKind::WellFormed(term), - )); + let _ = (term, cause); + // FIXME: We don't currently register an obligation here because we don't implement + // wf checking anyway and this function is currently often passed dummy spans, which could + // prevent reporting "type annotation needed" errors. + // self.register_predicate(Obligation::new( + // self.interner(), + // cause, + // self.param_env, + // ClauseKind::WellFormed(term), + // )); } /// Registers obligations that all `args` are well-formed. @@ -434,9 +423,9 @@ impl<'db> InferenceTable<'db> { } /// Whenever you lower a user-written type, you should call this. - pub(crate) fn process_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { - self.process_remote_user_written_ty(ty) - // FIXME: Register a well-formed obligation. + pub(crate) fn process_user_written_ty(&mut self, span: Span, ty: Ty<'db>) -> Ty<'db> { + let ty = self.insert_type_vars(ty, span); + self.try_structurally_resolve_type(ty) } /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation, @@ -460,20 +449,234 @@ impl fmt::Debug for InferenceTable<'_> { } } -mod resolve_completely { - use rustc_type_ir::{DebruijnIndex, Flags, TypeFolder, TypeSuperFoldable}; +pub(super) mod resolve_completely { + use rustc_hash::FxHashSet; + use rustc_type_ir::{ + DebruijnIndex, Flags, InferConst, InferTy, TypeFlags, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitableExt, inherent::IntoKind, + }; + use stdx::never; + use thin_vec::ThinVec; use crate::{ - infer::unify::InferenceTable, + InferenceDiagnostic, Span, + infer::{TypeMismatch, unify::InferenceTable}, next_solver::{ - Const, DbInterner, Goal, Predicate, Region, Term, Ty, + Const, ConstKind, DbInterner, DefaultAny, GenericArg, Goal, Predicate, Region, Term, + TermKind, Ty, TyKind, infer::{resolve::ReplaceInferWithError, traits::ObligationCause}, normalize::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals, }, }; + pub(crate) struct WriteBackCtxt<'db> { + table: InferenceTable<'db>, + diagnostics: ThinVec, + has_errors: bool, + spans_emitted_type_must_be_known_for: FxHashSet, + types: &'db DefaultAny<'db>, + } + + impl<'db> WriteBackCtxt<'db> { + pub(crate) fn new( + table: InferenceTable<'db>, + diagnostics: ThinVec, + vars_emitted_type_must_be_known_for: FxHashSet>, + ) -> Self { + let spans_emitted_type_must_be_known_for = vars_emitted_type_must_be_known_for + .into_iter() + .filter_map(|term| match term.kind() { + TermKind::Ty(ty) => match ty.kind() { + TyKind::Infer(InferTy::TyVar(vid)) => { + Some(table.infer_ctxt.type_var_span(vid)) + } + _ => None, + }, + TermKind::Const(ct) => match ct.kind() { + ConstKind::Infer(InferConst::Var(vid)) => { + table.infer_ctxt.const_var_span(vid) + } + _ => None, + }, + }) + .collect(); + + Self { + types: table.interner().default_types(), + table, + diagnostics, + has_errors: false, + spans_emitted_type_must_be_known_for, + } + } + + pub(crate) fn resolve_type_mismatch(&mut self, value_ref: &mut TypeMismatch) { + // Ignore diagnostics from type mismatches, which are diagnostics themselves. + // FIXME: We should make type mismatches just regular diagnostics. + let prev_diagnostics_len = self.diagnostics.len(); + self.resolve_completely(value_ref); + self.diagnostics.truncate(prev_diagnostics_len); + } + + pub(crate) fn resolve_completely(&mut self, value_ref: &mut T) + where + T: TypeFoldable>, + { + self.resolve_completely_with_default(value_ref, value_ref.clone()); + } + + pub(crate) fn resolve_completely_with_default(&mut self, value_ref: &mut T, default: T) + where + T: TypeFoldable>, + { + let value = std::mem::replace(value_ref, default); + + let value = self.table.resolve_vars_if_possible(value); + + let mut goals = vec![]; + + // FIXME(next-solver): Handle `goals`. + + *value_ref = value.fold_with(&mut Resolver::new(self, true, &mut goals)); + } + + pub(crate) fn resolve_diagnostics(mut self) -> (ThinVec, bool) { + let has_errors = self.has_errors; + + // Ignore diagnostics made from resolving diagnostics. + let mut diagnostics = std::mem::take(&mut self.diagnostics); + diagnostics.retain_mut(|diagnostic| { + self.resolve_completely(diagnostic); + + if let InferenceDiagnostic::ExpectedFunction { found: ty, .. } + | InferenceDiagnostic::UnresolvedField { receiver: ty, .. } + | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic + && ty.as_ref().references_non_lt_error() + { + false + } else { + true + } + }); + diagnostics.shrink_to_fit(); + + (diagnostics, has_errors) + } + } + + struct DiagnoseInferVars<'a, 'db> { + ctx: &'a mut WriteBackCtxt<'db>, + top_term: Term<'db>, + } + + impl<'db> DiagnoseInferVars<'_, 'db> { + const TYPE_FLAGS: TypeFlags = TypeFlags::HAS_INFER.union(TypeFlags::HAS_NON_REGION_ERROR); + + fn err_on_span(&mut self, span: Span) { + if !self.ctx.spans_emitted_type_must_be_known_for.insert(span) { + // Suppress duplicate diagnostics. + return; + } + + if span.is_dummy() { + return; + } + + // We have to be careful not to insert infer vars here, as we won't resolve this new diagnostic. + let top_term = self.top_term.fold_with(&mut ReplaceInferWithError::new(self.cx())); + self.ctx.diagnostics.push(InferenceDiagnostic::TypeMustBeKnown { + at_point: span, + top_term: Some(GenericArg::from(top_term).store()), + }); + } + } + + impl<'db> TypeFolder> for DiagnoseInferVars<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.ctx.table.interner() + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + if !t.has_type_flags(Self::TYPE_FLAGS) { + return t; + } + + match t.kind() { + TyKind::Error(_) => { + self.ctx.has_errors = true; + t + } + TyKind::Infer(infer_ty) => match infer_ty { + InferTy::TyVar(vid) => { + self.err_on_span(self.ctx.table.infer_ctxt.type_var_span(vid)); + self.ctx.has_errors = true; + self.ctx.types.types.error + } + InferTy::IntVar(_) => { + never!("fallback should have resolved all int vars"); + self.ctx.types.types.i32 + } + InferTy::FloatVar(_) => { + never!("fallback should have resolved all float vars"); + self.ctx.types.types.f64 + } + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => { + never!("should not have fresh infer vars outside of caching"); + self.ctx.has_errors = true; + self.ctx.types.types.error + } + }, + _ => t.super_fold_with(self), + } + } + + fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { + if !c.has_type_flags(Self::TYPE_FLAGS) { + return c; + } + + match c.kind() { + ConstKind::Error(_) => { + self.ctx.has_errors = true; + c + } + ConstKind::Infer(infer_ct) => match infer_ct { + InferConst::Var(vid) => { + if let Some(span) = self.ctx.table.infer_ctxt.const_var_span(vid) { + self.err_on_span(span); + } + self.ctx.has_errors = true; + self.ctx.types.consts.error + } + InferConst::Fresh(_) => { + never!("should not have fresh infer vars outside of caching"); + self.ctx.has_errors = true; + self.ctx.types.consts.error + } + }, + _ => c.super_fold_with(self), + } + } + + fn fold_predicate(&mut self, p: Predicate<'db>) -> Predicate<'db> { + if !p.has_type_flags(Self::TYPE_FLAGS) { + return p; + } + p.super_fold_with(self) + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + if r.is_var() { + // For now, we don't error on regions. + self.ctx.types.regions.error + } else { + r + } + } + } + pub(super) struct Resolver<'a, 'db> { - ctx: &'a mut InferenceTable<'db>, + ctx: &'a mut WriteBackCtxt<'db>, /// Whether we should normalize, disabled when resolving predicates. should_normalize: bool, nested_goals: &'a mut Vec>>, @@ -481,7 +684,7 @@ mod resolve_completely { impl<'a, 'db> Resolver<'a, 'db> { pub(super) fn new( - ctx: &'a mut InferenceTable<'db>, + ctx: &'a mut WriteBackCtxt<'db>, should_normalize: bool, nested_goals: &'a mut Vec>>, ) -> Resolver<'a, 'db> { @@ -498,7 +701,7 @@ mod resolve_completely { { let value = if self.should_normalize { let cause = ObligationCause::new(); - let at = self.ctx.at(&cause); + let at = self.ctx.table.at(&cause); let universes = vec![None; outer_exclusive_binder(value).as_usize()]; match deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( at, value, universes, @@ -516,17 +719,17 @@ mod resolve_completely { value }; - value.fold_with(&mut ReplaceInferWithError::new(self.ctx.interner())) + value.fold_with(&mut DiagnoseInferVars { ctx: self.ctx, top_term: value.into() }) } } - impl<'cx, 'db> TypeFolder> for Resolver<'cx, 'db> { + impl<'db> TypeFolder> for Resolver<'_, 'db> { fn cx(&self) -> DbInterner<'db> { - self.ctx.interner() + self.ctx.table.interner() } fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { - if r.is_var() { Region::error(self.ctx.interner()) } else { r } + if r.is_var() { self.ctx.types.regions.error } else { r } } fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 3fb8632d6bea6..4433dd6425ed6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -689,6 +689,18 @@ impl From for Span { } } +impl Span { + pub(crate) fn pick_best(a: Span, b: Span) -> Span { + // We prefer dummy spans to minimize the risk of false errors. + if b.is_dummy() { b } else { a } + } + + #[inline] + pub fn is_dummy(&self) -> bool { + matches!(self, Self::Dummy) + } +} + pub fn setup_tracing() -> Option { use std::env; use std::sync::LazyLock; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 335aff2c1df16..7325cd0ef8a5d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -2379,7 +2379,7 @@ fn push_const_arg_has_type_predicates<'db>( interner, ParamConst { id: param_id, index: (param_index + const_params_offset) as u32 }, ), - db.const_param_ty_ns(param_id), + db.const_param_ty(param_id), ) .upcast(interner), )); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index a3648945399eb..d6b9c375fc427 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -736,6 +736,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { fn provided_type_like_const( &mut self, + _type_ref: TypeRefId, const_ty: Ty<'db>, arg: TypeLikeConst<'_>, ) -> Const<'db> { @@ -1002,8 +1003,12 @@ pub(crate) trait GenericArgsLowerer<'db> { arg: &HirGenericArg, ) -> GenericArg<'db>; - fn provided_type_like_const(&mut self, const_ty: Ty<'db>, arg: TypeLikeConst<'_>) - -> Const<'db>; + fn provided_type_like_const( + &mut self, + type_ref: TypeRefId, + const_ty: Ty<'db>, + arg: TypeLikeConst<'_>, + ) -> Const<'db>; fn inferred_kind( &mut self, @@ -1225,7 +1230,8 @@ pub(crate) fn substs_from_args_and_bindings<'db>( panic!("unmatching param kinds"); }; let const_ty = const_param_ty_query(db, param_id); - substs.push(ctx.provided_type_like_const(const_ty, konst).into()); + substs + .push(ctx.provided_type_like_const(*type_ref, const_ty, konst).into()); args.next(); params.next(); } else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs index 6536b599c844e..a29e3db18d252 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs @@ -5,6 +5,7 @@ use hir_def::{ FunctionId, GenericDefId, GenericParamId, ItemContainerId, TraitId, expr_store::path::{GenericArg as HirGenericArg, GenericArgs as HirGenericArgs}, hir::{ExprId, generics::GenericParamDataRef}, + type_ref::TypeRefId, }; use rustc_type_ir::{ TypeFoldable, @@ -400,7 +401,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { let GenericParamId::ConstParamId(const_id) = param_id else { unreachable!("non-const param ID for const param"); }; - let const_ty = self.ctx.db.const_param_ty_ns(const_id); + let const_ty = self.ctx.db.const_param_ty(const_id); self.ctx.make_body_const(*konst, const_ty).into() } _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), @@ -409,11 +410,14 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { fn provided_type_like_const( &mut self, + type_ref: TypeRefId, const_ty: Ty<'db>, arg: TypeLikeConst<'_>, ) -> Const<'db> { match arg { - TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path, const_ty), + TypeLikeConst::Path(path) => { + self.ctx.make_path_as_body_const(type_ref, path, const_ty) + } TypeLikeConst::Infer => self.ctx.table.next_const_var(Span::Dummy), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index b935a6ed32619..49fb6f530586d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -581,7 +581,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { ParamConst { id: p, index }, ) .store(), - ty: self.db.const_param_ty_ns(p).store(), + ty: self.db.const_param_ty(p).store(), }, span: None, }), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 68b1c8b3b6271..03c608f4b2976 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -132,7 +132,7 @@ impl<'db> MirLowerCtx<'_, 'db> { .into(), ); Ok(match &self.store[pattern] { - Pat::Missing => return Err(MirLowerError::IncompletePattern), + Pat::Missing | Pat::Rest => return Err(MirLowerError::IncompletePattern), Pat::Wild => (current, current_else), Pat::Tuple { args, ellipsis } => { let subst = match self.infer.pat_ty(pattern).kind() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 72cf2f9f07f20..05955d060bd0e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -194,6 +194,25 @@ impl std::fmt::Debug for StoredGenericArg { } } +impl<'db> TypeVisitable> for StoredGenericArg { + fn visit_with>>(&self, visitor: &mut V) -> V::Result { + self.as_ref().visit_with(visitor) + } +} + +impl<'db> TypeFoldable> for StoredGenericArg { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(self.as_ref().try_fold_with(folder)?.store()) + } + + fn fold_with>>(self, folder: &mut F) -> Self { + self.as_ref().fold_with(folder).store() + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct GenericArg<'db> { ptr: GenericArgImpl<'db>, @@ -457,6 +476,25 @@ impl_foldable_for_interned_slice!(GenericArgs); impl<'db> rustc_type_ir::inherent::GenericArg> for GenericArg<'db> {} +impl<'db> TypeVisitable> for StoredGenericArgs { + fn visit_with>>(&self, visitor: &mut V) -> V::Result { + self.as_ref().visit_with(visitor) + } +} + +impl<'db> TypeFoldable> for StoredGenericArgs { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(self.as_ref().try_fold_with(folder)?.store()) + } + + fn fold_with>>(self, folder: &mut F) -> Self { + self.as_ref().fold_with(folder).store() + } +} + impl<'db> GenericArgs<'db> { /// Creates an `GenericArgs` for generic parameter definitions, /// by calling closures to obtain each kind. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 33003f5375d46..f038c47a8bcfd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -946,8 +946,8 @@ impl<'db> InferCtxt<'db> { use self::type_variable::TypeVariableValue; match self.inner.borrow_mut().type_variables().probe(vid) { - TypeVariableValue::Known { value } => Ok(value), - TypeVariableValue::Unknown { universe } => Err(universe), + TypeVariableValue::Known { value, .. } => Ok(value), + TypeVariableValue::Unknown { universe, .. } => Err(universe), } } @@ -1015,6 +1015,13 @@ impl<'db> InferCtxt<'db> { } } + pub fn shallow_resolve_term(&self, term: Term<'db>) -> Term<'db> { + match term.kind() { + TermKind::Ty(ty) => self.shallow_resolve(ty).into(), + TermKind::Const(ct) => self.shallow_resolve_const(ct).into(), + } + } + pub fn root_var(&self, var: TyVid) -> TyVid { self.inner.borrow_mut().type_variables().root_var(var) } @@ -1096,6 +1103,21 @@ impl<'db> InferCtxt<'db> { } } + /// Returns the span of the type variable identified by `vid`. + /// + /// No attempt is made to resolve `vid` to its root variable. + pub fn type_var_span(&self, vid: TyVid) -> Span { + self.inner.borrow_mut().type_variables().var_span(vid) + } + + /// Returns the span of the const variable identified by `vid` + pub fn const_var_span(&self, vid: ConstVid) -> Option { + match self.inner.borrow_mut().const_unification_table().probe_value(vid) { + ConstVariableValue::Known { .. } => None, + ConstVariableValue::Unknown { span, .. } => Some(span), + } + } + // Instantiates the bound variables in a given binder with fresh inference // variables in the current universe. // diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs index dda1bb56ef949..544d79daf0a3d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs @@ -354,8 +354,12 @@ impl<'db> RegionConstraintCollector<'db, '_> { *any_unifications = false; // Manually inlined `self.unification_table_mut()` as `self` is used in the closure. ut::UnificationTable::with_log(&mut self.storage.unification_table, &mut self.undo_log) - .reset_unifications(|key| RegionVariableValue::Unknown { - universe: self.storage.var_infos[key.vid].universe, + .reset_unifications(|key| { + let var_info = &self.storage.var_infos[key.vid]; + RegionVariableValue::Unknown { + universe: var_info.universe, + span: var_info.span, + } }); } @@ -379,7 +383,8 @@ impl<'db> RegionConstraintCollector<'db, '_> { pub(super) fn new_region_var(&mut self, universe: UniverseIndex, span: Span) -> RegionVid { let vid = self.storage.var_infos.push(RegionVariableInfo { universe, span }); - let u_vid = self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe }); + let u_vid = + self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe, span }); assert_eq!(vid, u_vid.vid); self.undo_log.push(AddVar(vid)); debug!("created new region variable {:?} in {:?}", vid, universe); @@ -413,7 +418,7 @@ impl<'db> RegionConstraintCollector<'db, '_> { debug!("make_eqregion: unifying {:?} with {:?}", vid, b); if self .unification_table_mut() - .unify_var_value(vid, RegionVariableValue::Known { value: b }) + .unify_var_value(vid, RegionVariableValue::Known { value: b, span: None }) .is_ok() { self.storage.any_unifications = true; @@ -423,7 +428,7 @@ impl<'db> RegionConstraintCollector<'db, '_> { debug!("make_eqregion: unifying {:?} with {:?}", a, vid); if self .unification_table_mut() - .unify_var_value(vid, RegionVariableValue::Known { value: a }) + .unify_var_value(vid, RegionVariableValue::Known { value: a, span: None }) .is_ok() { self.storage.any_unifications = true; @@ -510,15 +515,15 @@ impl<'db> RegionConstraintCollector<'db, '_> { let mut ut = self.unification_table_mut(); let root_vid = ut.find(vid).vid; match ut.probe_value(root_vid) { - RegionVariableValue::Known { value } => value, + RegionVariableValue::Known { value, .. } => value, RegionVariableValue::Unknown { .. } => Region::new_var(cx, root_vid), } } pub fn probe_value(&mut self, vid: RegionVid) -> Result, UniverseIndex> { match self.unification_table_mut().probe_value(vid) { - RegionVariableValue::Known { value } => Ok(value), - RegionVariableValue::Unknown { universe } => Err(universe), + RegionVariableValue::Known { value, .. } => Ok(value), + RegionVariableValue::Unknown { universe, .. } => Err(universe), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs index 152592683ac26..e55e43a4cdb32 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs @@ -459,11 +459,11 @@ impl<'db> TypeRelation> for Generalizer<'_, 'db> { } else { let probe = inner.type_variables().probe(vid); match probe { - TypeVariableValue::Known { value: u } => { + TypeVariableValue::Known { value: u, .. } => { drop(inner); self.relate(u, u) } - TypeVariableValue::Unknown { universe } => { + TypeVariableValue::Unknown { universe, .. } => { match self.ambient_variance { // Invariant: no need to make a fresh type variable // if we can name the universe. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs index 070d2582d88f4..6b3936ba80428 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs @@ -7,7 +7,6 @@ use std::ops::Range; use ena::snapshot_vec as sv; use ena::undo_log::Rollback; use ena::unify as ut; -use rustc_index::IndexVec; use rustc_type_ir::TyVid; use rustc_type_ir::UniverseIndex; use rustc_type_ir::inherent::Ty as _; @@ -61,8 +60,6 @@ impl<'tcx> Rollback> for TypeVariableStorage<'tcx> { #[derive(Debug, Clone, Default)] pub(crate) struct TypeVariableStorage<'db> { - /// The origins of each type variable. - values: IndexVec, /// Two variables are unified in `eq_relations` when we have a /// constraint `?X == ?Y`. This table also stores, for each key, /// the known value. @@ -94,12 +91,7 @@ pub(crate) struct TypeVariableTable<'a, 'db> { undo_log: &'a mut InferCtxtUndoLogs<'db>, } -#[derive(Debug, Clone)] -pub(crate) struct TypeVariableData { - span: Span, -} - -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub(crate) enum TypeVariableValue<'db> { Known { value: Ty<'db> }, Unknown { universe: UniverseIndex }, @@ -111,7 +103,7 @@ impl<'db> TypeVariableValue<'db> { pub(crate) fn known(&self) -> Option> { match self { TypeVariableValue::Unknown { .. } => None, - TypeVariableValue::Known { value } => Some(*value), + TypeVariableValue::Known { value, .. } => Some(*value), } } @@ -137,19 +129,14 @@ impl<'db> TypeVariableStorage<'db> { &self.eq_relations } - pub(super) fn finalize_rollback(&mut self) { - debug_assert!(self.values.len() >= self.eq_relations.len()); - self.values.truncate(self.eq_relations.len()); - } + pub(super) fn finalize_rollback(&mut self) {} } impl<'db> TypeVariableTable<'_, 'db> { - /// Returns the span that was given when `vid` was created. - /// - /// Note that this function does not return care whether - /// `vid` has been unified with something else or not. - pub(crate) fn var_span(&self, vid: TyVid) -> Span { - self.storage.values[vid].span + pub(crate) fn var_span(&mut self, vid: TyVid) -> Span { + // We return the span from unification and not equation, since when equating we also unify, + // and we want to prevent duplicate diagnostics from vars that were unified. + self.sub_unification_table().probe_value(vid).span } /// Records that `a == b`, depending on `dir`. @@ -190,20 +177,17 @@ impl<'db> TypeVariableTable<'_, 'db> { pub(crate) fn new_var(&mut self, universe: UniverseIndex, span: Span) -> TyVid { let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); - let sub_key = self.sub_unification_table().new_key(()); + let sub_key = self.sub_unification_table().new_key(TypeVariableSubValue { span }); debug_assert_eq!(eq_key.vid, sub_key.vid); - let index = self.storage.values.push(TypeVariableData { span }); - debug_assert_eq!(eq_key.vid, index); - debug!("new_var(index={:?}, universe={:?}, span={:?})", eq_key.vid, universe, span); - index + eq_key.vid } /// Returns the number of type variables created thus far. pub(crate) fn num_vars(&self) -> usize { - self.storage.values.len() + self.storage.eq_relations.len() } /// Returns the "root" variable of `vid` in the `eq_relations` @@ -303,9 +287,6 @@ impl<'db> ut::UnifyKey for TyVidEqKey<'db> { fn tag() -> &'static str { "TyVidEqKey" } - fn order_roots(a: Self, _: &Self::Value, b: Self, _: &Self::Value) -> Option<(Self, Self)> { - if a.vid.as_u32() < b.vid.as_u32() { Some((a, b)) } else { Some((b, a)) } - } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -320,8 +301,13 @@ impl From for TyVidSubKey { } } +#[derive(Debug, Clone, Copy)] +pub(crate) struct TypeVariableSubValue { + span: Span, +} + impl ut::UnifyKey for TyVidSubKey { - type Value = (); + type Value = TypeVariableSubValue; #[inline] fn index(&self) -> u32 { self.vid.as_u32() @@ -335,6 +321,14 @@ impl ut::UnifyKey for TyVidSubKey { } } +impl ut::UnifyValue for TypeVariableSubValue { + type Error = ut::NoError; + + fn unify_values(value1: &Self, value2: &Self) -> Result { + Ok(TypeVariableSubValue { span: Span::pick_best(value1.span, value2.span) }) + } +} + impl<'db> ut::UnifyValue for TypeVariableValue<'db> { type Error = ut::NoError; @@ -348,11 +342,9 @@ impl<'db> ut::UnifyValue for TypeVariableValue<'db> { } // If one side is known, prefer that one. - (&TypeVariableValue::Known { .. }, &TypeVariableValue::Unknown { .. }) => { - Ok(value1.clone()) - } - (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { .. }) => { - Ok(value2.clone()) + (&TypeVariableValue::Known { value }, &TypeVariableValue::Unknown { .. }) + | (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { value }) => { + Ok(TypeVariableValue::Known { value }) } // If both sides are *unknown*, it hardly matters, does it? diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs index 07eeda255b434..061b8531d3b33 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs @@ -11,10 +11,10 @@ use crate::{ next_solver::{Const, Region}, }; -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub(crate) enum RegionVariableValue<'db> { - Known { value: Region<'db> }, - Unknown { universe: UniverseIndex }, + Known { value: Region<'db>, span: Option }, + Unknown { universe: UniverseIndex, span: Span }, } #[derive(PartialEq, Copy, Clone, Debug)] @@ -54,9 +54,15 @@ impl<'db> UnifyValue for RegionVariableValue<'db> { Err(RegionUnificationError) } - (RegionVariableValue::Known { value }, RegionVariableValue::Unknown { universe }) - | (RegionVariableValue::Unknown { universe }, RegionVariableValue::Known { value }) => { - let universe_of_value = match (*value).kind() { + ( + &RegionVariableValue::Known { value, span: span_known }, + &RegionVariableValue::Unknown { universe, span: span_unknown }, + ) + | ( + &RegionVariableValue::Unknown { universe, span: span_unknown }, + &RegionVariableValue::Known { value, span: span_known }, + ) => { + let universe_of_value = match value.kind() { RegionKind::ReStatic | RegionKind::ReErased | RegionKind::ReLateParam(..) @@ -68,23 +74,28 @@ impl<'db> UnifyValue for RegionVariableValue<'db> { } }; + let span = match span_known { + Some(span_known) => Span::pick_best(span_known, span_unknown), + None => span_unknown, + }; if universe.can_name(universe_of_value) { - Ok(RegionVariableValue::Known { value: *value }) + Ok(RegionVariableValue::Known { value, span: Some(span) }) } else { Err(RegionUnificationError) } } ( - RegionVariableValue::Unknown { universe: a }, - RegionVariableValue::Unknown { universe: b }, + &RegionVariableValue::Unknown { universe: a, span: span1 }, + &RegionVariableValue::Unknown { universe: b, span: span2 }, ) => { // If we unify two unconstrained regions then whatever // value they wind up taking (which must be the same value) must // be nameable by both universes. Therefore, the resulting // universe is the minimum of the two universes, because that is // the one which contains the fewest names in scope. - Ok(RegionVariableValue::Unknown { universe: (*a).min(*b) }) + let span = Span::pick_best(span1, span2); + Ok(RegionVariableValue::Unknown { universe: a.min(b), span }) } } } @@ -92,7 +103,7 @@ impl<'db> UnifyValue for RegionVariableValue<'db> { // Generic consts. -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub(crate) enum ConstVariableValue<'db> { Known { value: Const<'db> }, Unknown { span: Span, universe: UniverseIndex }, @@ -134,9 +145,6 @@ impl<'db> UnifyKey for ConstVidKey<'db> { fn tag() -> &'static str { "ConstVidKey" } - fn order_roots(a: Self, _: &Self::Value, b: Self, _: &Self::Value) -> Option<(Self, Self)> { - if a.vid.as_u32() < b.vid.as_u32() { Some((a, b)) } else { Some((b, a)) } - } } impl<'db> UnifyValue for ConstVariableValue<'db> { @@ -149,25 +157,22 @@ impl<'db> UnifyValue for ConstVariableValue<'db> { } // If one side is known, prefer that one. - (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => { - Ok(value1.clone()) - } - (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => { - Ok(value2.clone()) - } + (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => Ok(*value1), + (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => Ok(*value2), // If both sides are *unknown*, it hardly matters, does it? ( - ConstVariableValue::Unknown { span: origin, universe: universe1 }, - ConstVariableValue::Unknown { span: _, universe: universe2 }, + &ConstVariableValue::Unknown { span: span1, universe: universe1 }, + &ConstVariableValue::Unknown { span: span2, universe: universe2 }, ) => { // If we unify two unbound variables, ?T and ?U, then whatever // value they wind up taking (which must be the same value) must // be nameable by both universes. Therefore, the resulting // universe is the minimum of the two universes, because that is // the one which contains the fewest names in scope. - let universe = cmp::min(*universe1, *universe2); - Ok(ConstVariableValue::Unknown { span: *origin, universe }) + let universe = cmp::min(universe1, universe2); + let span = Span::pick_best(span1, span2); + Ok(ConstVariableValue::Unknown { span, universe }) } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs index 3f0aebac2dea7..7acbcbf4aafa8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs @@ -136,7 +136,7 @@ impl<'db> Region<'db> { } RegionKind::ReError(..) => { flags |= TypeFlags::HAS_FREE_REGIONS; - flags |= TypeFlags::HAS_ERROR; + flags |= TypeFlags::HAS_RE_ERROR; } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index 018ecd66e0995..6abc87f088517 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -6,7 +6,7 @@ use hir_def::{ }; use rustc_next_trait_solver::delegate::SolverDelegate; use rustc_type_ir::{ - AliasTyKind, GenericArgKind, InferCtxtLike, Interner, PredicatePolarity, TypeFlags, + AliasTyKind, GenericArgKind, InferCtxtLike, InferTy, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt, inherent::{IntoKind, Term as _, Ty as _}, lang_items::SolverTraitLangItem, @@ -293,10 +293,10 @@ impl<'db> SolverDelegate for SolverContext<'db> { } if trait_pred.polarity() == PredicatePolarity::Positive { - match self.0.cx().as_trait_lang_item(trait_pred.def_id()) { + match self.0.interner.as_trait_lang_item(trait_pred.def_id()) { Some(SolverTraitLangItem::Sized) | Some(SolverTraitLangItem::MetaSized) => { let predicate = self.resolve_vars_if_possible(goal.predicate); - if sizedness_fast_path(self.cx(), predicate, goal.param_env) { + if sizedness_fast_path(self.interner, predicate, goal.param_env) { return Some(Certainty::Yes); } } @@ -322,17 +322,31 @@ impl<'db> SolverDelegate for SolverContext<'db> { let pred = goal.predicate.kind(); match pred.no_bound_vars()? { - PredicateKind::Clause(ClauseKind::RegionOutlives(_outlives)) => Some(Certainty::Yes), - PredicateKind::Clause(ClauseKind::TypeOutlives(_outlives)) => Some(Certainty::Yes), + PredicateKind::DynCompatible(def_id) + if self.0.interner.trait_is_dyn_compatible(def_id) => + { + Some(Certainty::Yes) + } + PredicateKind::Clause(ClauseKind::RegionOutlives(outlives)) => { + self.0.sub_regions(outlives.1, outlives.0); + Some(Certainty::Yes) + } + PredicateKind::Clause(ClauseKind::TypeOutlives(outlives)) => { + self.0.register_type_outlives_constraint(outlives.0, outlives.1); + + Some(Certainty::Yes) + } PredicateKind::Subtype(SubtypePredicate { a, b, .. }) | PredicateKind::Coerce(CoercePredicate { a, b }) => { - if self.shallow_resolve(a).is_ty_var() && self.shallow_resolve(b).is_ty_var() { - // FIXME: We also need to register a subtype relation between these vars - // when those are added, and if they aren't in the same sub root then - // we should mark this goal as `has_changed`. - Some(Certainty::AMBIGUOUS) - } else { - None + match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) { + ( + TyKind::Infer(InferTy::TyVar(a_vid)), + TyKind::Infer(InferTy::TyVar(b_vid)), + ) => { + self.sub_unify_ty_vids_raw(a_vid, b_vid); + Some(Certainty::AMBIGUOUS) + } + _ => None, } } PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, _)) => { @@ -343,6 +357,7 @@ impl<'db> SolverDelegate for SolverContext<'db> { } } PredicateKind::Clause(ClauseKind::WellFormed(arg)) => { + let arg = self.shallow_resolve_term(arg); if arg.is_trivially_wf(self.interner) { Some(Certainty::Yes) } else if arg.is_infer() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 0fd02eb8ca960..511259ecd8e51 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -17,7 +17,7 @@ use rustc_type_ir::{ IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, WithCachedTypeInfo, inherent::{ - AdtDef as _, BoundExistentialPredicates, Const as _, GenericArgs as _, IntoKind, ParamLike, + AdtDef as _, BoundExistentialPredicates, GenericArgs as _, IntoKind, ParamLike, Safety as _, SliceLike, Ty as _, }, relate::Relate, @@ -31,8 +31,8 @@ use crate::{ lower::GenericPredicates, next_solver::{ AdtDef, AliasTy, Binder, CallableIdWrapper, Clause, ClauseKind, ClosureIdWrapper, Const, - CoroutineClosureIdWrapper, CoroutineIdWrapper, FnSig, GenericArgKind, PolyFnSig, Region, - TraitRef, TypeAliasIdWrapper, + CoroutineClosureIdWrapper, CoroutineIdWrapper, FnSig, GenericArgKind, PolyFnSig, Predicate, + Region, TraitRef, TypeAliasIdWrapper, abi::Safety, impl_foldable_for_interned_slice, impl_stored_interned, interned_slice, util::{CoroutineArgsExt, IntegerTypeExt}, @@ -817,25 +817,11 @@ impl<'db> Ty<'db> { } pub fn references_non_lt_error<'db, T: TypeVisitableExt>>(t: &T) -> bool { - t.references_error() && t.visit_with(&mut ReferencesNonLifetimeError).is_break() -} - -struct ReferencesNonLifetimeError; - -impl<'db> TypeVisitor> for ReferencesNonLifetimeError { - type Result = ControlFlow<()>; - - fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result { - if ty.is_ty_error() { ControlFlow::Break(()) } else { ty.super_visit_with(self) } - } - - fn visit_const(&mut self, c: Const<'db>) -> Self::Result { - if c.is_ct_error() { ControlFlow::Break(()) } else { c.super_visit_with(self) } - } + t.has_non_region_error() } pub fn references_only_ty_error<'db, T: TypeVisitableExt>>(t: &T) -> bool { - t.references_error() && t.visit_with(&mut ReferencesOnlyTyError).is_break() + references_non_lt_error(t) && t.visit_with(&mut ReferencesOnlyTyError).is_break() } struct ReferencesOnlyTyError; @@ -844,7 +830,29 @@ impl<'db> TypeVisitor> for ReferencesOnlyTyError { type Result = ControlFlow<()>; fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result { - if ty.is_ty_error() { ControlFlow::Break(()) } else { ty.super_visit_with(self) } + if !ty.references_non_lt_error() { + ControlFlow::Continue(()) + } else if ty.is_ty_error() { + ControlFlow::Break(()) + } else { + ty.super_visit_with(self) + } + } + + fn visit_const(&mut self, c: Const<'db>) -> Self::Result { + if !references_non_lt_error(&c) { + ControlFlow::Continue(()) + } else { + c.super_visit_with(self) + } + } + + fn visit_predicate(&mut self, p: Predicate<'db>) -> Self::Result { + if !references_non_lt_error(&p) { + ControlFlow::Continue(()) + } else { + p.super_visit_with(self) + } } } @@ -880,6 +888,15 @@ impl<'db> TypeVisitable> for Ty<'db> { } } +impl<'db> TypeVisitable> for StoredTy { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.as_ref().visit_with(visitor) + } +} + impl<'db> TypeSuperVisitable> for Ty<'db> { fn super_visit_with>>( &self, @@ -946,6 +963,18 @@ impl<'db> TypeFoldable> for Ty<'db> { } } +impl<'db> TypeFoldable> for StoredTy { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(self.as_ref().try_fold_with(folder)?.store()) + } + fn fold_with>>(self, folder: &mut F) -> Self { + self.as_ref().fold_with(folder).store() + } +} + impl<'db> TypeSuperFoldable> for Ty<'db> { fn try_super_fold_with>>( self, diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 8f9465cf1b5d6..18f28541af66f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -6,9 +6,9 @@ use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_def::{ - DefWithBodyId, GenericParamId, SyntheticSyntax, + DefWithBodyId, GenericParamId, HasModule, SyntheticSyntax, expr_store::{ - ExprOrPatPtr, ExprOrPatSource, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast, + ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast, hir_generic_arg_to_ast, hir_segment_to_ast_segment, }, hir::ExprOrPatId, @@ -19,6 +19,7 @@ use hir_ty::{ PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind, db::HirDatabase, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, + display::{DisplayTarget, HirDisplay}, }; use syntax::{ AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange, @@ -108,7 +109,7 @@ diagnostics![AnyDiagnostic<'db> -> IncorrectGenericsOrder, MissingLifetime, ElidedLifetimesInPath, - TypeMustBeKnown, + TypeMustBeKnown<'db>, ]; #[derive(Debug)] @@ -446,8 +447,9 @@ pub struct ElidedLifetimesInPath { } #[derive(Debug)] -pub struct TypeMustBeKnown { - pub at_point: ExprOrPatSource, +pub struct TypeMustBeKnown<'db> { + pub at_point: InFile>>>, + pub top_term: Option, String>>, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -631,6 +633,12 @@ impl<'db> AnyDiagnostic<'db> { .inspect_err(|_| stdx::never!("inference diagnostic in desugared pattern")) .ok() }; + let type_syntax = |pat| { + source_map + .type_syntax(pat) + .inspect_err(|_| stdx::never!("inference diagnostic in desugared type")) + .ok() + }; let expr_or_pat_syntax = |id| match id { ExprOrPatId::ExprId(expr) => expr_syntax(expr), ExprOrPatId::PatId(pat) => pat_syntax(pat), @@ -806,8 +814,29 @@ impl<'db> AnyDiagnostic<'db> { let lhs = expr_syntax(lhs)?; InvalidLhsOfAssignment { lhs }.into() } - &InferenceDiagnostic::TypeMustBeKnown { at_point } => { - TypeMustBeKnown { at_point: expr_or_pat_syntax(at_point)? }.into() + &InferenceDiagnostic::TypeMustBeKnown { at_point, ref top_term } => { + let at_point = match at_point { + hir_ty::Span::ExprId(idx) => expr_syntax(idx)?.map(|it| it.wrap_right()), + hir_ty::Span::PatId(idx) => pat_syntax(idx)?.map(|it| it.wrap_right()), + hir_ty::Span::TypeRefId(idx) => type_syntax(idx)?.map(|it| it.wrap_left()), + hir_ty::Span::Dummy => unreachable!( + "should never create TypeMustBeKnown diagnostic for dummy spans" + ), + }; + let top_term = top_term.as_ref().map(|top_term| match top_term.as_ref().kind() { + rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(Type { + ty, + env: crate::body_param_env_from_has_crate(db, def), + }), + // FIXME: Printing the const to string is definitely not the correct thing to do here. + rustc_type_ir::GenericArgKind::Const(konst) => Either::Right( + konst.display(db, DisplayTarget::from_crate(db, def.krate(db))).to_string(), + ), + rustc_type_ir::GenericArgKind::Lifetime(_) => { + unreachable!("we currently don't emit TypeMustBeKnown for lifetimes") + } + }); + TypeMustBeKnown { at_point, top_term }.into() } }) } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index f5ca22e10c8a9..8cc82113b3934 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4795,7 +4795,7 @@ impl ConstParam { } pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { - Type::new(db, self.id.parent(), db.const_param_ty_ns(self.id)) + Type::new(db, self.id.parent(), db.const_param_ty(self.id)) } pub fn default( diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 17f5b51cc3e4b..252f4ac7f27b3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -47,8 +47,8 @@ use smallvec::{SmallVec, smallvec}; use span::{FileId, SyntaxContext}; use stdx::{TupleExt, always}; use syntax::{ - AstNode, AstToken, Direction, SmolStr, SmolStrBuilder, SyntaxElement, SyntaxKind, SyntaxNode, - SyntaxNodePtr, SyntaxToken, T, TextRange, TextSize, + AstNode, AstPtr, AstToken, Direction, SmolStr, SmolStrBuilder, SyntaxElement, SyntaxKind, + SyntaxNode, SyntaxNodePtr, SyntaxToken, T, TextRange, TextSize, algo::skip_trivia_token, ast::{self, HasAttrs as _, HasGenericParams}, }; @@ -543,6 +543,10 @@ impl<'db> SemanticsImpl<'db> { node } + pub fn to_node(&self, ptr: InFile>) -> N { + ptr.value.to_node(&self.parse_or_expand(ptr.file_id)) + } + pub fn expand(&self, file_id: MacroCallId) -> ExpandResult { let res = self.db.parse_macro_expansion(file_id).map(|it| it.0.syntax_node()); self.cache(res.value.clone(), file_id.into()); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs index 2a7b0098edfd9..7cadff84fb07f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs @@ -52,6 +52,7 @@ async fn bar() { fn await_inside_closure() { check_diagnostics( r#" +//- minicore: future async fn foo() {} async fn bar() { @@ -66,6 +67,7 @@ async fn bar() { fn await_inside_async_block() { check_diagnostics( r#" +//- minicore: future async fn foo() {} fn bar() { @@ -79,6 +81,7 @@ fn bar() { fn await_in_complex_context() { check_diagnostics( r#" +//- minicore: future async fn foo() {} fn bar() { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs index cbcaab6c74777..02f3cab565080 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs @@ -147,7 +147,7 @@ fn test() { r#" //- minicore: option, try fn test() { - try { + let _: Option<_> = try { || { let x = Some(2); Some(x?) 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 5410f8b58a9ac..5a456504e121f 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 @@ -1006,18 +1006,18 @@ fn func() { #![allow(unused_variables)] #[warn(nonstandard_style)] fn foo() { - let BAR; + let BAR: i32; // ^^^ 💡 warn: Variable `BAR` should have snake_case name, e.g. `bar` #[allow(non_snake_case)] - let FOO; + let FOO: i32; } #[warn(nonstandard_style)] fn foo() { - let BAR; + let BAR: i32; // ^^^ 💡 warn: Variable `BAR` should have snake_case name, e.g. `bar` #[expect(non_snake_case)] - let FOO; + let FOO: i32; #[allow(non_snake_case)] struct qux; // ^^^ 💡 warn: Structure `qux` should have UpperCamelCase name, e.g. `Qux` @@ -1060,7 +1060,7 @@ mod FINE_WITH_BAD_CASE; struct QUX; const foo: i32 = 0; fn BAR() { - let BAZ; + let BAZ: i32; _ = BAZ; } "#, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs index 25220704e04dd..2a31a41fbc212 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs @@ -43,6 +43,8 @@ struct Bar(T, U); fn foo() { let _ = Bar::<()>; // ^^^^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied + // ^^^^^^^^^ error: type annotations needed + // | full type: `fn Bar<(), {unknown}>((), {unknown}) -> Bar<(), {unknown}>` } "#, @@ -51,6 +53,10 @@ fn foo() { #[test] fn enum_variant() { + // FIXME: We should not have a "type annotations needed" error here, but to do that + // we'll need to have access to the `InferenceContext` in `TyLoweringContext`, to + // generate the infer var with a dummy span (instead of inserting it after the fact + // with a non-dummy span). check_diagnostics( r#" enum Enum { @@ -60,8 +66,12 @@ enum Enum { fn foo() { let _ = Enum::<()>::Variant; // ^^^^^^ error: this enum takes 2 generic arguments but 1 generic argument was supplied + // ^^^^^^^^^^^^^^^^^^^ error: type annotations needed + // | full type: `fn Variant<(), {unknown}>((), {unknown}) -> Enum<(), {unknown}>` let _ = Enum::Variant::<()>; // ^^^^^^ error: this enum takes 2 generic arguments but 1 generic argument was supplied + // ^^^^^^^^^^^^^^^^^^^ error: type annotations needed + // | full type: `fn Variant<(), {unknown}>((), {unknown}) -> Enum<(), {unknown}>` } "#, @@ -127,6 +137,8 @@ struct Bar(T); fn bar() { let _ = Bar::<()>; // ^^^^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied + // ^^^^^^^^^ error: type annotations needed + // | full type: `fn Bar<(), _>(()) -> Bar<(), _>` } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index 4c0985c7ae965..b900a8f5cc10e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -453,6 +453,8 @@ fn g() { b::<1, 3>(0, 2); b(0, 1, 2); + // ^ error: type annotations needed + // | full type: `fn b<_, _>(u8, u8)` //^ error: expected 4 arguments, found 3 } "#, 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 85368cc09f915..117702923b19e 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 @@ -293,12 +293,15 @@ fn baz(s: S) -> i32 { #[test] fn missing_record_pat_field_box() { check_diagnostics( - r" + r#" +#![feature(lang_items)] +#[lang = "owned_box"] +struct Box(T); struct S { s: Box } fn x(a: S) { let S { box s } = a; } -", +"#, ) } 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 355617a2b11d6..a845e0b59aca2 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 @@ -1340,6 +1340,7 @@ pub fn foo(_: T) -> (T::Out,) { loop { } } fn main() { let _x = foo(2); + // ^^ error: type annotations needed } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs index 4b7249740857c..5363f4a5cec58 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs @@ -1,17 +1,56 @@ +use either::Either; +use hir::HirDisplay; +use stdx::format_to; +use syntax::{AstNode, SyntaxNodePtr, ast}; + use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: type-must-be-known // // This diagnostic is triggered when rust-analyzer cannot infer some type. -pub(crate) fn type_must_be_known( - ctx: &DiagnosticsContext<'_>, - d: &hir::TypeMustBeKnown, +pub(crate) fn type_must_be_known<'db>( + ctx: &DiagnosticsContext<'db>, + d: &hir::TypeMustBeKnown<'db>, ) -> Diagnostic { + let mut at_point = d.at_point.map(|it| it.syntax_node_ptr()); + let mut top_term = d.top_term.clone(); + + // Do some adjustments to the node: FIXME: We should probably do that at the emitting site. + let node = ctx.sema.to_node(d.at_point); + if let Either::Right(Either::Left(expr)) = &node + && let Some(Either::Left(top_ty)) = &d.top_term + && let Some(expr_ty) = ctx.sema.type_of_expr(expr) + && expr_ty.original == *top_ty + && !top_ty.is_unknown() + && let Some(parent) = expr.syntax().parent().and_then(ast::CallExpr::cast) + && let Some(callable) = top_ty.as_callable(ctx.db()) + && let ret_ty = callable.return_type() + && ret_ty.contains_unknown() + { + top_term = Some(Either::Left(ret_ty)); + at_point.value = SyntaxNodePtr::new(parent.syntax()); + } + + let message = match &top_term { + Some(top_term) if !matches!(top_term, Either::Left(ty) if ty.is_unknown()) => { + let mut message = "type annotations needed\nfull type: `".to_owned(); + match top_term { + Either::Left(ty) => { + format_to!(message, "{}", ty.display(ctx.db(), ctx.display_target)) + } + Either::Right(konst) => message.push_str(konst), + } + message.push_str("`\n"); + message + } + Some(_) => "type annotations needed".to_owned(), + None => "type annotations needed; type must be known at this point".to_owned(), + }; Diagnostic::new_with_syntax_node_ptr( ctx, DiagnosticCode::RustcHardError("E0282"), - "type annotations needed; type must be known at this point", - d.at_point.map(|it| it.into()), + message, + at_point, ) } @@ -20,7 +59,7 @@ mod tests { use crate::tests::check_diagnostics; #[test] - fn smoke_test() { + fn some_expressions_require_knowing_type() { check_diagnostics( r#" fn foo() { @@ -32,6 +71,34 @@ fn foo() { // ^^^ 💡 warn: unused variable var[0]; // ^^^ error: type annotations needed; type must be known at this point +} + "#, + ); + } + + #[test] + fn binding_without_type() { + check_diagnostics( + r#" +fn any() -> T { loop {} } +fn foo() { + let _x = any(); + // ^^^^^ error: type annotations needed +} + "#, + ); + } + + #[test] + fn struct_with_generic() { + check_diagnostics( + r#" +struct X(T); +fn any() -> X { loop {} } +fn foo() { + let _x = any(); + // ^^^^^ error: type annotations needed + // | full type: `X<{unknown}>` } "#, ); 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 fd1674e2a47bf..ddd1dd402ea4e 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 @@ -166,6 +166,8 @@ fn t() -> T { loop {} } r#" fn main() { let _x = [(); _]; + // ^ error: type annotations needed + // | full type: `[(); _]` // FIXME: This should trigger error // let _y: [(); 10] = [(); _]; _ = 0; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs index 9883bcc84ff8c..301613e920191 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs @@ -2722,6 +2722,13 @@ fn foo() { tracing::error!(); } "#, - &["E0432", "inactive-code", "unresolved-macro-call", "syntax-error", "macro-error"], + &[ + "E0432", + "E0282", + "inactive-code", + "unresolved-macro-call", + "syntax-error", + "macro-error", + ], ); } diff --git a/src/tools/rust-analyzer/crates/macros/src/lib.rs b/src/tools/rust-analyzer/crates/macros/src/lib.rs index de8c3f2e55f14..9088efeca4fb3 100644 --- a/src/tools/rust-analyzer/crates/macros/src/lib.rs +++ b/src/tools/rust-analyzer/crates/macros/src/lib.rs @@ -5,7 +5,7 @@ use syn::parse_quote; use synstructure::decl_derive; decl_derive!( - [TypeFoldable, attributes(type_foldable)] => + [TypeFoldable, attributes(type_foldable, type_visitable)] => /// Derives `TypeFoldable` for the annotated `struct` or `enum` (`union` is not supported). /// /// The fold will produce a value of the same struct or enum variant as the input, with @@ -102,8 +102,10 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke vi.construct(|_, index| { let bind = &bindings[index]; - // retain value of fields with #[type_foldable(identity)] - if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") { + // retain value of fields with #[type_foldable(identity)] or #[type_visitable(ignore)] + if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") + || has_ignore_attr(&bind.ast().attrs, "type_visitable", "ignore") + { bind.to_token_stream() } else { quote! { @@ -118,8 +120,10 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke vi.construct(|_, index| { let bind = &bindings[index]; - // retain value of fields with #[type_foldable(identity)] - if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") { + // retain value of fields with #[type_foldable(identity)] or #[type_visitable(ignore)] + if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") + || has_ignore_attr(&bind.ast().attrs, "type_visitable", "ignore") + { bind.to_token_stream() } else { quote! { diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index c447870e96c0e..33a20951daced 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -67,7 +67,7 @@ //! size_of: sized //! sized: //! slice: -//! str: +//! str: sized, result //! sync: sized //! transmute: //! try: infallible @@ -1726,6 +1726,22 @@ pub mod iter { } } + pub struct Map { + iter: I, + f: F, + } + impl Iterator for Map + where + F: FnMut(I::Item) -> B, + { + type Item = B; + + #[inline] + fn next(&mut self) -> B { + loop {} + } + } + pub struct FilterMap { iter: I, f: F, @@ -1800,6 +1816,13 @@ pub mod iter { { loop {} } + fn map(self, _f: F) -> crate::iter::Map + where + Self: Sized, + F: FnMut(Self::Item) -> B, + { + loop {} + } fn filter_map(self, _f: F) -> crate::iter::FilterMap where Self: Sized, From 6538b90b25c942a23329b4646861c7e487a5f960 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 24 Apr 2026 15:57:18 +0300 Subject: [PATCH 083/289] Parse `return #[attr] expr` --- .../crates/parser/src/grammar/expressions.rs | 6 ++-- .../parser/src/grammar/expressions/atom.rs | 4 +++ .../parser/test_data/generated/runner.rs | 2 ++ .../parser/inline/ok/return_attr.rast | 34 +++++++++++++++++++ .../test_data/parser/inline/ok/return_attr.rs | 3 ++ 5 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_attr.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_attr.rs diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index 76d26c1ecdfc2..3f341c2ab846e 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -1,7 +1,5 @@ mod atom; -use crate::grammar::attributes::ATTRIBUTE_FIRST; - use super::*; pub(super) use atom::{EXPR_RECOVERY_SET, LITERAL_FIRST, literal, parse_asm_expr}; @@ -324,7 +322,7 @@ fn expr_bp( } const LHS_FIRST: TokenSet = - atom::ATOM_EXPR_FIRST.union(TokenSet::new(&[T![&], T![*], T![!], T![.], T![-], T![_]])); + atom::ATOM_EXPR_FIRST.union(TokenSet::new(&[T![&], T![*], T![!], T![.], T![-], T![_], T![#]])); fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> { let m; @@ -653,7 +651,7 @@ fn arg_list(p: &mut Parser<'_>) { T![')'], T![,], || "expected expression".into(), - EXPR_FIRST.union(ATTRIBUTE_FIRST), + EXPR_FIRST, |p| expr(p).is_some(), ); m.complete(p, ARG_LIST); diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index 3214fd90f29a9..3620a27c230dd 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -888,6 +888,10 @@ fn return_expr(p: &mut Parser<'_>) -> CompletedMarker { let m = p.start(); p.bump(T![return]); if p.at_ts(EXPR_FIRST) { + // test return_attr + // fn foo() { + // return #[attr] 1; + // } expr(p); } m.complete(p, RETURN_EXPR) diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index 6dfb78b12878b..ecb9ece67a0e0 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -584,6 +584,8 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/reference_type.rs"); } #[test] + fn return_attr() { run_and_expect_no_errors("test_data/parser/inline/ok/return_attr.rs"); } + #[test] fn return_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/return_expr.rs"); } #[test] fn return_type_syntax_in_path() { diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_attr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_attr.rast new file mode 100644 index 0000000000000..d5dcdf04479e7 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_attr.rast @@ -0,0 +1,34 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RETURN_EXPR + RETURN_KW "return" + WHITESPACE " " + LITERAL + ATTR + POUND "#" + L_BRACK "[" + PATH_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "attr" + R_BRACK "]" + WHITESPACE " " + INT_NUMBER "1" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_attr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_attr.rs new file mode 100644 index 0000000000000..dc8bd1a79747a --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_attr.rs @@ -0,0 +1,3 @@ +fn foo() { + return #[attr] 1; +} From 5ee2b370bc3f6e1b7dc02d88dd9cabc0401aa6ae Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 25 Apr 2026 16:31:18 +0800 Subject: [PATCH 084/289] feat: offer on compound assign for replace_arith_op Example --- ```rust fn main() { let mut x = 1; x $0+= 2; } ``` **Before this PR** Assist not applicable **After this PR** ```rust fn main() { let mut x = 1; x = x.wrapping_add(2); } ``` --- .../src/handlers/replace_arith_op.rs | 53 ++++++++++++++----- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs index 5ad5efac05c24..a04f6f38336a9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs @@ -4,7 +4,10 @@ use syntax::{ ast::{self, ArithOp, BinaryOp}, }; -use crate::assist_context::{AssistContext, Assists}; +use crate::{ + assist_context::{AssistContext, Assists}, + utils::wrap_paren, +}; // Assist: replace_arith_with_checked // @@ -70,7 +73,7 @@ pub(crate) fn replace_arith_with_wrapping( } fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) -> Option<()> { - let (lhs, op, rhs) = parse_binary_op(ctx)?; + let (lhs, op, is_assign, rhs) = parse_binary_op(ctx)?; let op_expr = lhs.syntax().parent()?; if !is_primitive_int(ctx, &lhs) || !is_primitive_int(ctx, &rhs) { @@ -87,11 +90,13 @@ fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) -> let make = editor.make(); let method_name = kind.method_name(op); - let needs_parentheses = - lhs.precedence().needs_parentheses_in(ast::prec::ExprPrecedence::Postfix); - let receiver = if needs_parentheses { make.expr_paren(lhs).into() } else { lhs }; - let arith_expr = - make.expr_method_call(receiver, make.name_ref(&method_name), make.arg_list([rhs])); + let receiver = wrap_paren(lhs.clone(), make, ast::prec::ExprPrecedence::Postfix); + let mut arith_expr = make + .expr_method_call(receiver, make.name_ref(&method_name), make.arg_list([rhs])) + .into(); + if is_assign { + arith_expr = make.expr_assignment(lhs, arith_expr).into(); + } editor.replace(op_expr, arith_expr.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, @@ -106,24 +111,25 @@ fn is_primitive_int(ctx: &AssistContext<'_>, expr: &ast::Expr) -> bool { } /// Extract the operands of an arithmetic expression (e.g. `1 + 2` or `1.checked_add(2)`) -fn parse_binary_op(ctx: &AssistContext<'_>) -> Option<(ast::Expr, ArithOp, ast::Expr)> { +fn parse_binary_op(ctx: &AssistContext<'_>) -> Option<(ast::Expr, ArithOp, bool, ast::Expr)> { if !ctx.has_empty_selection() { return None; } let expr = ctx.find_node_at_offset::()?; - let op = match expr.op_kind() { - Some(BinaryOp::ArithOp(ArithOp::Add)) => ArithOp::Add, - Some(BinaryOp::ArithOp(ArithOp::Sub)) => ArithOp::Sub, - Some(BinaryOp::ArithOp(ArithOp::Mul)) => ArithOp::Mul, - Some(BinaryOp::ArithOp(ArithOp::Div)) => ArithOp::Div, + let (op, is_assign) = match expr.op_kind()? { + BinaryOp::ArithOp(arith_op) => (arith_op, false), + BinaryOp::Assignment { op: Some(op) } => (op, true), _ => return None, }; + if !matches!(op, ArithOp::Add | ArithOp::Sub | ArithOp::Mul | ArithOp::Div) { + return None; + } let lhs = expr.lhs()?; let rhs = expr.rhs()?; - Some((lhs, op, rhs)) + Some((lhs, op, is_assign, rhs)) } pub(crate) enum ArithKind { @@ -249,6 +255,25 @@ fn main() { ) } + #[test] + fn replace_arith_with_wrapping_add_assign() { + check_assist( + replace_arith_with_wrapping, + r#" +fn main() { + let mut x = 1; + x $0+= 2; +} +"#, + r#" +fn main() { + let mut x = 1; + x = x.wrapping_add(2); +} +"#, + ) + } + #[test] fn replace_arith_not_applicable_with_non_empty_selection() { check_assist_not_applicable( From 2d9eff03a9307c8658024d60929b38802cfc0201 Mon Sep 17 00:00:00 2001 From: "workflows-rust-analyzer[bot]" <223433972+workflows-rust-analyzer[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 00:02:30 +0000 Subject: [PATCH 085/289] internal: update generated lints --- .../crates/ide-db/src/generated/lints.rs | 240 ++++++++++++++++-- 1 file changed, 218 insertions(+), 22 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index 52a5a95450974..c60e4b2e1dc28 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -1237,6 +1237,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ warn_since: None, deny_since: None, }, + Lint { + label: "tail_call_track_caller", + description: r##"detects tail calls of functions marked with `#[track_caller]`"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, Lint { label: "tail_expr_drop_order", description: r##"Detect and warn on significant change in drop order in tail expression location"##, @@ -4313,7 +4320,7 @@ defined in Rust. They may be called both from within Rust and via FFI. pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize { let mut sum = 0; for _ in 0..n { - sum += args.arg::(); + sum += args.next_arg::(); } sum } @@ -4799,22 +4806,6 @@ extern { This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "char_max_len", - description: r##"# `char_max_len` - - - -The tracking issue for this feature is: [#121714] - -[#121714]: https://github.com/rust-lang/rust/issues/121714 - ------------------------ "##, default_severity: Severity::Allow, @@ -5997,6 +5988,22 @@ This feature is internal to the Rust compiler and is not intended for general us This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "core_io", + description: r##"# `core_io` + + + +The tracking issue for this feature is: [#154046] + +[#154046]: https://github.com/rust-lang/rust/issues/154046 + ------------------------ "##, default_severity: Severity::Allow, @@ -6013,6 +6020,20 @@ The tracking issue for this feature is: [#117693] [#117693]: https://github.com/rust-lang/rust/issues/117693 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "core_io_internals", + description: r##"# `core_io_internals` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + ------------------------ "##, default_severity: Severity::Allow, @@ -6986,13 +7007,96 @@ The tracking issue for this feature is: [#143874] label: "diagnostic_on_move", description: r##"# `diagnostic_on_move` -Allows giving on-move borrowck custom diagnostic messages for a type - The tracking issue for this feature is: [#154181] -[#154181]: https://github.com/rust-lang/rust/issues/154181 - ------------------------ + +The `diagnostic_on_move` feature allows use of the `#[diagnostic::on_move]` attribute. It should be +placed on struct, enum and union declarations, though it is not an error to be located in other +positions. This attribute is a hint to the compiler to supplement the error message when the +annotated type is involved in a borrowcheck error. + +For example, [`File`] is annotated as such: +```rust +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move(note = "you can use `File::try_clone` \ + to duplicate a `File` instance")] +pub struct File { + // ... +} +``` + +When you try to use a `File` after it's already been moved, it will helpfully tell you about `try_clone`. + +The message and label can also be customized: + +```rust +#![feature(diagnostic_on_move)] + +use std::marker::PhantomData; + +#[diagnostic::on_move( + message = "`{Self}` cannot be used multiple times", + label = "this token may only be used once", + note = "you can create a new `Token` with `Token::conjure()`" +)] +pub struct Token<'brand> { + spooky: PhantomData<&'brand ()>, +} + +impl Token<'_> { + pub fn conjure<'u>() -> Token<'u> { + Token { + spooky: PhantomData, + } + } +} +``` +The user may try to use it like this: +```rust,compile_fail,E0382 +# #![feature(diagnostic_on_move)] +# +# use std::marker::PhantomData; +# +# #[diagnostic::on_move( +# message = "`{Self}` cannot be used multiple times", +# label = "this token may only be used once", +# note = "you can create a new `Token` with `Token::conjure()`" +# )] +# pub struct Token<'brand> { +# spooky: PhantomData<&'brand ()>, +# } +# +# impl Token<'_> { +# pub fn conjure<'u>() -> Token<'u> { +# Token { +# spooky: PhantomData, +# } +# } +# } +# fn main() { +let token = Token::conjure(); +let _ = (token, token); +# } +``` +This will result in the following error: +```text +error[E0382]: `Token` cannot be used multiple times + --> src/main.rs:24:21 + | + 1 | let token = Token::conjure(); + | ----- this token may only be used once + 2 | let _ = (token, token); + | ----- ^^^^^ value used here after move + | | + | value moved here + | + = note: you can create a new `Token` with `Token::conjure()` +``` + +[`File`]: https://doc.rust-lang.org/nightly/std/fs/struct.File.html "File in std::fs" +[#154181]: https://github.com/rust-lang/rust/issues/154181 "Tracking Issue for #[diagnostic::on_move]" "##, default_severity: Severity::Allow, warn_since: None, @@ -7009,6 +7113,66 @@ The tracking issue for this feature is: [#152900] [#152900]: https://github.com/rust-lang/rust/issues/152900 ------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "diagnostic_on_unmatch_args", + description: r##"# `diagnostic_on_unmatch_args` + +The tracking issue for this feature is: [#155642] + +[#155642]: https://github.com/rust-lang/rust/issues/155642 + +------------------------ + +The `diagnostic_on_unmatch_args` feature adds the +`#[diagnostic::on_unmatch_args(...)]` attribute for declarative macros. +It lets a macro definition customize diagnostics for matcher failures after all arms have been +tried, such as incomplete invocations or trailing extra arguments. + +This attribute currently applies to declarative macros such as `macro_rules!` and `pub macro`. +It is currently used for errors emitted by declarative macro matching itself; fragment parser +errors still use their existing diagnostics. + +```rust,compile_fail +#![feature(diagnostic_on_unmatch_args)] + +#[diagnostic::on_unmatch_args( + message = "invalid arguments to {This} macro invocation", + label = "expected a type and value here", + note = "this macro expects a type and a value, like `pair!(u8, 0)`", + note = "see ", +)] +macro_rules! pair { + ($ty:ty, $value:expr) => {}; +} + +pair!(u8); +``` + +This emits output like: + +```text +error: invalid arguments to pair macro invocation + --> example.rs:13:9 + | +9 | macro_rules! pair { + | ----------------- when calling this macro +... +13 | pair!(u8); + | ^ expected a type and value here + | +note: while trying to match `,` + --> example.rs:10:12 + | +10 | ($ty:ty, $value:expr) => {}; + | ^ + = note: this macro expects a type and a value, like `pair!(u8, 0)` + = note: see +``` "##, default_severity: Severity::Allow, warn_since: None, @@ -8070,6 +8234,22 @@ The tracking issue for this feature is: [#99842] [#99842]: https://github.com/rust-lang/rust/issues/99842 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "float_masks", + description: r##"# `float_masks` + + + +The tracking issue for this feature is: [#154064] + +[#154064]: https://github.com/rust-lang/rust/issues/154064 + ------------------------ "##, default_severity: Severity::Allow, @@ -9027,6 +9207,22 @@ The tracking issue for this feature is: [#99069] [#99069]: https://github.com/rust-lang/rust/issues/99069 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "integer_cast_extras", + description: r##"# `integer_cast_extras` + + + +The tracking issue for this feature is: [#154650] + +[#154650]: https://github.com/rust-lang/rust/issues/154650 + ------------------------ "##, default_severity: Severity::Allow, @@ -14912,7 +15108,7 @@ pub fn main() { foo(&1); // Use trait alias for trait objects. - let a: &Bar = &123; + let a: &dyn Bar = &123; println!("{:?}", a); let b = Box::new(456) as Box; println!("{:?}", b); From b57be3b1beb4c8f02185b541f70f05f56eb3ca06 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 25 Apr 2026 14:35:30 +0800 Subject: [PATCH 086/289] feat: Add unwrap_block, offer unwrap_block and unwrap_branch Example --- ```rust fn main() { match rel_path { Ok(rel_path) => {$0 if true { foo() } } Err(_) => None, } } ``` **Before this PR** ```text Unwrap branch ``` **After this PR** ```text Unwrap branch Unwrap block ``` ```rust fn main() { match rel_path { Ok(rel_path) => if true { foo() } Err(_) => None, } } ``` --- .../ide-assists/src/handlers/unwrap_branch.rs | 165 +++++++++++++++++- .../crates/ide-assists/src/lib.rs | 1 + .../crates/ide-assists/src/tests/generated.rs | 23 +++ 3 files changed, 188 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs index a71ba555d013f..f9f9545dd26ef 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs @@ -1,3 +1,4 @@ +use either::Either; use syntax::{ AstNode, SyntaxElement, SyntaxKind, SyntaxNode, T, ast::{ @@ -85,6 +86,55 @@ pub(crate) fn unwrap_branch(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio }) } +// Assist: unwrap_block +// +// This assist removes braces and unwrap single expressions block. +// +// ``` +// fn foo() { +// match () { +// _ => {$0 +// bar() +// } +// } +// } +// ``` +// -> +// ``` +// fn foo() { +// match () { +// _ => bar(), +// } +// } +// ``` +pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; + let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?; + let target = block.syntax().text_range(); + let tail_expr = block.tail_expr()?; + let stmt_list = block.stmt_list()?; + let container = Either::::cast(block.syntax().parent()?)?; + + if stmt_list.statements().next().is_some() { + return None; + } + + acc.add(AssistId::refactor_rewrite("unwrap_block"), "Unwrap block", target, |builder| { + let editor = builder.make_editor(block.syntax()); + let replacement = stmt_list.dedent(tail_expr.indent_level()).indent(block.indent_level()); + let mut replacement = extract_statements(replacement); + + if container.left().is_some_and(|it| it.comma_token().is_none()) + && !tail_expr.is_block_like() + { + replacement.push(editor.make().token(T![,]).into()); + } + + editor.replace_with_many(block.syntax(), replacement); + builder.add_file_edits(ctx.vfs_file_id(), editor); + }) +} + fn delete_else_before(container: SyntaxNode, editor: &SyntaxEditor) { let make = editor.make(); let Some(else_token) = container @@ -137,7 +187,10 @@ fn extract_statements(stmt_list: ast::StmtList) -> Vec { #[cfg(test)] mod tests { - use crate::tests::{check_assist, check_assist_not_applicable, check_assist_with_label}; + use crate::tests::{ + check_assist, check_assist_by_label, check_assist_not_applicable, + check_assist_not_applicable_by_label, check_assist_with_label, + }; use super::*; @@ -992,4 +1045,114 @@ fn main() { "Unwrap branch", ); } + + #[test] + fn unwrap_block_in_branch() { + check_assist_by_label( + unwrap_block, + r#" +fn main() { + match rel_path { + Ok(rel_path) => {$0 + if true { + foo() + } + } + Err(_) => None, + } +} +"#, + r#" +fn main() { + match rel_path { + Ok(rel_path) => if true { + foo() + } + Err(_) => None, + } +} +"#, + "Unwrap block", + ); + + check_assist_by_label( + unwrap_block, + r#" +fn main() { + match rel_path { + Ok(rel_path) => {$0 + 1 + 2 + } + Err(_) => None, + } +} +"#, + r#" +fn main() { + match rel_path { + Ok(rel_path) => 1 + 2, + Err(_) => None, + } +} +"#, + "Unwrap block", + ); + } + + #[test] + fn unwrap_block_in_branch_non_standalone() { + check_assist_not_applicable_by_label( + unwrap_block, + r#" +fn main() { + match rel_path { + Ok(rel_path) => { + if true {$0 + foo() + } + } + Err(_) => None, + } +} +"#, + "Unwrap block", + ); + } + + #[test] + fn unwrap_block_in_branch_non_tail_expr_only() { + check_assist_not_applicable_by_label( + unwrap_block, + r#" +fn main() { + match rel_path { + Ok(rel_path) => {$0 + x; + y + } + Err(_) => None, + } +} +"#, + "Unwrap block", + ); + } + + #[test] + fn unwrap_block_in_closure() { + check_assist_by_label( + unwrap_block, + r#" +fn main() { + let f = || {$0 foo() }; +} +"#, + r#" +fn main() { + let f = || foo(); +} +"#, + "Unwrap block", + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 1fe385e95bd48..18ff63470d352 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -380,6 +380,7 @@ mod handlers { unmerge_imports::unmerge_imports, unnecessary_async::unnecessary_async, unqualify_method_call::unqualify_method_call, + unwrap_branch::unwrap_block, unwrap_branch::unwrap_branch, unwrap_return_type::unwrap_return_type, unwrap_tuple::unwrap_tuple, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 7e66f02475fa0..d3ee35aa86940 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -3730,6 +3730,29 @@ mod std { pub mod ops { pub trait Add { fn add(self, _: Self) {} } impl Add for ) } +#[test] +fn doctest_unwrap_block() { + check_doc_test( + "unwrap_block", + r#####" +fn foo() { + match () { + _ => {$0 + bar() + } + } +} +"#####, + r#####" +fn foo() { + match () { + _ => bar(), + } +} +"#####, + ) +} + #[test] fn doctest_unwrap_branch() { check_doc_test( From 29c6dc0a93af891e3d13dd1b8e687117f3ed1e3d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 26 Apr 2026 11:42:02 +0300 Subject: [PATCH 087/289] Define the ABI of functions inside extern blocks as the ABI of the extern block --- .../src/expr_store/tests/signatures.rs | 14 ++++++++++ .../crates/hir-def/src/signatures.rs | 26 ++++++++++--------- .../hir-ty/src/tests/method_resolution.rs | 2 +- .../crates/ide/src/hover/tests.rs | 4 +-- 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs index 5e0184dfad82d..24bdc6ece31e7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs @@ -210,3 +210,17 @@ fn foo(v: for<'a> Trait1 + Trait2) {} "#]], ); } + +#[test] +fn extern_block_abi() { + lower_and_print( + r#" +extern "C" { + fn extern_fn(); +} + "#, + expect![[r#" + extern "C" fn extern_fn() {...} + "#]], + ); +} 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 6d704274f45d8..f03ad5dae8e8c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -652,9 +652,19 @@ impl FunctionSignature { } let name = as_name_opt(source.value.name()); - let abi = source.value.abi().map(|abi| { - abi.abi_string().map_or_else(|| sym::C, |it| Symbol::intern(it.text_without_quotes())) - }); + let abi = source + .value + .abi() + .map(|abi| { + abi.abi_string() + .map_or_else(|| sym::C, |it| Symbol::intern(it.text_without_quotes())) + }) + .or_else(|| match loc.container { + ItemContainerId::ExternBlockId(extern_block) => extern_block_abi(db, extern_block), + ItemContainerId::ModuleId(_) + | ItemContainerId::ImplId(_) + | ItemContainerId::TraitId(_) => None, + }); let (store, source_map, generic_params, params, ret_type, self_param, variadic) = lower_function(db, module, source, id); if self_param { @@ -738,15 +748,7 @@ impl FunctionSignature { let data = FunctionSignature::of(db, id); data.flags.contains(FnFlags::RUSTC_INTRINSIC) // Keep this around for a bit until extern "rustc-intrinsic" abis are no longer used - || match &data.abi { - Some(abi) => *abi == sym::rust_dash_intrinsic, - None => match id.lookup(db).container { - ItemContainerId::ExternBlockId(block) => { - block.abi(db) == Some(sym::rust_dash_intrinsic) - } - _ => false, - }, - } + || data.abi.as_ref().is_some_and(|abi| *abi == sym::rust_dash_intrinsic) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index fc8c1f8164801..4291c9ba18dc7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1193,7 +1193,7 @@ fn test() { 123..167 '{ ...o(); }': () 133..134 's': &'? S 137..151 'unsafe { f() }': &'? S - 146..147 'f': fn f() -> &'static S + 146..147 'f': extern "C" fn f() -> &'static S 146..149 'f()': &'static S 157..158 's': &'? S 157..164 's.foo()': bool diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 491471428fc61..89d467cef8713 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -3392,7 +3392,7 @@ fn main() { let foo_test = unsafe { fo$0o(1, 2, 3); } } ``` ```rust - pub unsafe fn foo(bar: i32, ...) -> i32 + pub unsafe extern "C" fn foo(bar: i32, ...) -> i32 ``` "#]], ); @@ -9197,7 +9197,7 @@ extern "C" { ``` ```rust - unsafe fn fun() + unsafe extern "C" fn fun() ``` "#]], ); From dfd77c4d7ebf4d091d35397868535fa415d9af0d Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 26 Apr 2026 17:04:12 +0800 Subject: [PATCH 088/289] feat: offer on non-block matcharm for unwrap_branch Example --- ```rust fn main() { match rel_path { Ok(rel_path) $0=> Foo { rel_path, }, Err(_) => None, } } ``` **Before this PR** Assist not applicable **After this PR** ```rust fn main() { Foo { rel_path, } } ``` --- .../ide-assists/src/handlers/unwrap_branch.rs | 59 ++++++++++++++++--- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs index a71ba555d013f..0f7eb262a8d5f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs @@ -3,6 +3,7 @@ use syntax::{ ast::{ self, edit::{AstNodeEdit, IndentLevel}, + syntax_factory::SyntaxFactory, }, match_ast, syntax_editor::{Element, Position, SyntaxEditor}, @@ -28,14 +29,15 @@ use crate::{AssistContext, AssistId, Assists}; // } // ``` pub(crate) fn unwrap_branch(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; - let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?; - let target = block.syntax().text_range(); - let mut container = block.syntax().clone(); + let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); + let place = unwrap_branch_place(ctx)?; + let target = place.syntax().text_range(); + let block = wrap_block_raw(&place, editor.make()); + let mut container = place.syntax().clone(); let mut replacement = block.clone(); let mut prefer_container = None; - let from_indent = block.indent_level(); + let from_indent = place.indent_level(); let into_indent = loop { let parent = container.parent()?; container = match_ast! { @@ -69,12 +71,11 @@ pub(crate) fn unwrap_branch(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio }; }; let is_branch = - !block.is_standalone() || block.syntax().parent().and_then(ast::MatchArm::cast).is_some(); + !block.is_standalone() || place.syntax().parent().and_then(ast::MatchArm::cast).is_some(); let label = if is_branch { "Unwrap branch" } else { "Unwrap block" }; let replacement = replacement.stmt_list()?; acc.add(AssistId::refactor_rewrite("unwrap_branch"), label, target, |builder| { - let editor = builder.make_editor(block.syntax()); let replacement = replacement.dedent(from_indent).indent(into_indent); let container = prefer_container.unwrap_or(container); @@ -124,6 +125,18 @@ fn wrap_let(assign: &ast::LetStmt, replacement: ast::BlockExpr) -> ast::BlockExp try_wrap_assign().unwrap_or(replacement) } +fn unwrap_branch_place(ctx: &AssistContext<'_>) -> Option { + if let Some(l_curly_token) = ctx.find_token_syntax_at_offset(T!['{']) { + let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?; + Some(block.into()) + } else if let Some(fat_arrow_token) = ctx.find_token_syntax_at_offset(T![=>]) { + let match_arm = fat_arrow_token.parent().and_then(ast::MatchArm::cast)?; + match_arm.expr() + } else { + None + } +} + fn extract_statements(stmt_list: ast::StmtList) -> Vec { let mut elements = stmt_list .syntax() @@ -135,6 +148,14 @@ fn extract_statements(stmt_list: ast::StmtList) -> Vec { elements } +fn wrap_block_raw(expr: &ast::Expr, make: &SyntaxFactory) -> ast::BlockExpr { + if let ast::Expr::BlockExpr(block) = expr { + block.clone() + } else { + make.tail_only_block_expr(expr.indent(1.into())) + } +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable, check_assist_with_label}; @@ -640,6 +661,30 @@ fn main() { ); } + #[test] + fn unwrap_match_arm_without_block() { + check_assist( + unwrap_branch, + r#" +fn main() { + match rel_path { + Ok(rel_path) $0=> Foo { + rel_path, + }, + Err(_) => None, + } +} +"#, + r#" +fn main() { + Foo { + rel_path, + } +} +"#, + ); + } + #[test] fn simple_if_in_while_bad_cursor_position() { check_assist_not_applicable( From b18aaa042b57a323d09dc0809911bb8a3b1ab68e Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Mon, 6 Apr 2026 19:02:38 +0900 Subject: [PATCH 089/289] fix update based on comment move async update test use lower_coroutine_with_moved_arguments in is_gen_fn branch split desugared_coroutine_expr to kind and source local fn use old impl like rustc split match branch for now ::Ref less diff fill in todo fmt comments add self.require_type_is_sized(yield_ty); update test --- .../crates/hir-def/src/expr_store/body.rs | 5 +- .../crates/hir-def/src/expr_store/lower.rs | 125 ++++++++++++---- .../crates/hir-def/src/expr_store/pretty.rs | 28 ++-- .../rust-analyzer/crates/hir-def/src/hir.rs | 13 +- .../crates/hir-def/src/lang_item.rs | 2 +- .../crates/hir-def/src/signatures.rs | 26 ++-- .../crates/hir-expand/src/mod_path.rs | 1 + .../rust-analyzer/crates/hir-ty/src/db.rs | 6 +- .../crates/hir-ty/src/display.rs | 139 ++++++++++++++++-- .../crates/hir-ty/src/infer/callee.rs | 1 + .../crates/hir-ty/src/infer/closure.rs | 83 ++++++++--- .../hir-ty/src/infer/closure/analysis.rs | 5 +- .../crates/hir-ty/src/infer/expr.rs | 2 +- .../crates/hir-ty/src/next_solver/interner.rs | 51 +++++-- .../crates/hir-ty/src/next_solver/ty.rs | 28 ++-- .../crates/hir-ty/src/tests/coercion.rs | 28 ++++ .../crates/hir-ty/src/tests/simple.rs | 132 +++++++++++++++++ .../crates/hir-ty/src/tests/traits.rs | 18 +++ .../rust-analyzer/crates/hir/src/display.rs | 5 +- .../crates/ide/src/references.rs | 2 +- .../crates/intern/src/symbol/symbols.rs | 4 +- .../crates/test-utils/src/minicore.rs | 19 +++ 22 files changed, 595 insertions(+), 128 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs index 6be3e49a70def..27646772269d8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs @@ -74,6 +74,7 @@ impl Body { let mut params = None; let mut is_async_fn = false; + let mut is_gen_fn = false; let InFile { file_id, value: body } = { match def { DefWithBodyId::FunctionId(f) => { @@ -81,6 +82,7 @@ impl Body { let src = f.source(db); params = src.value.param_list(); is_async_fn = src.value.async_token().is_some(); + is_gen_fn = src.value.gen_token().is_some(); src.map(|it| it.body().map(ast::Expr::from)) } DefWithBodyId::ConstId(c) => { @@ -101,7 +103,8 @@ impl Body { } }; let module = def.module(db); - let (body, source_map) = lower_body(db, def, file_id, module, params, body, is_async_fn); + let (body, source_map) = + lower_body(db, def, file_id, module, params, body, is_async_fn, is_gen_fn); (Arc::new(body), source_map) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index fd8b50d714dd2..5c8e87c0e3007 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -47,8 +47,8 @@ use crate::{ }, hir::{ Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, - CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, - OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement, + CoroutineKind, CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, + Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement, generics::GenericParams, }, item_scope::BuiltinShadowMode, @@ -72,6 +72,7 @@ pub(super) fn lower_body( parameters: Option, body: Option, is_async_fn: bool, + is_gen_fn: bool, ) -> (Body, BodySourceMap) { // We cannot leave the root span map empty and let any identifier from it be treated as root, // because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved @@ -176,6 +177,8 @@ pub(super) fn lower_body( DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"), } }, + is_async_fn, + is_gen_fn, ); collector.store.inference_roots = Some(smallvec![(body_expr, RootExprOrigin::BodyRoot)]); @@ -376,12 +379,20 @@ pub(crate) fn lower_function( expr_collector.lower_type_ref_opt(ret_type.ty(), &mut ExprCollector::impl_trait_allocator) }); - let return_type = if fn_.value.async_token().is_some() { - let path = hir_expand::mod_path::path![core::future::Future]; + let return_type = if fn_.value.async_token().is_some() || fn_.value.gen_token().is_some() { + let (path, assoc_name) = + match (fn_.value.async_token().is_some(), fn_.value.gen_token().is_some()) { + (true, true) => { + (hir_expand::mod_path::path![core::async_iter::AsyncIterator], sym::Item) + } + (true, false) => (hir_expand::mod_path::path![core::future::Future], sym::Output), + (false, true) => (hir_expand::mod_path::path![core::iter::Iterator], sym::Item), + (false, false) => unreachable!(), + }; let mut generic_args: Vec<_> = std::iter::repeat_n(None, path.segments().len() - 1).collect(); let binding = AssociatedTypeBinding { - name: Name::new_symbol_root(sym::Output), + name: Name::new_symbol_root(assoc_name), args: None, type_ref: Some( return_type @@ -950,10 +961,11 @@ impl<'db> ExprCollector<'db> { /// into the body. This is to make sure that the future actually owns the /// arguments that are passed to the function, and to ensure things like /// drop order are stable. - fn lower_async_block_with_moved_arguments( + fn lower_coroutine_with_moved_arguments( &mut self, params: &mut [PatId], body: ExprId, + kind: CoroutineKind, coroutine_source: CoroutineSource, ) -> ExprId { let mut statements = Vec::new(); @@ -989,7 +1001,8 @@ impl<'db> ExprCollector<'db> { *param = pat_id; } - let async_ = self.async_block( + let coroutine = self.desugared_coroutine_expr( + kind, coroutine_source, // The default capture mode here is by-ref. Later on during upvar analysis, // we will force the captured arguments to by-move, but for async closures, @@ -1001,11 +1014,12 @@ impl<'db> ExprCollector<'db> { Some(body), ); // It's important that this comes last, see the lowering of async closures for why. - self.alloc_expr_desugared(async_) + self.alloc_expr_desugared(coroutine) } - fn async_block( + fn desugared_coroutine_expr( &mut self, + kind: CoroutineKind, source: CoroutineSource, capture_by: CaptureBy, id: Option, @@ -1018,7 +1032,7 @@ impl<'db> ExprCollector<'db> { arg_types: Box::default(), ret_type: None, body: block, - closure_kind: ClosureKind::AsyncBlock { source }, + closure_kind: ClosureKind::Coroutine { kind, source }, capture_by, } } @@ -1028,12 +1042,20 @@ impl<'db> ExprCollector<'db> { params: &mut [PatId], expr: Option, awaitable: Awaitable, + is_async_fn: bool, + is_gen_fn: bool, ) -> ExprId { self.awaitable_context.replace(awaitable); self.with_label_rib(RibKind::Closure, |this| { let body = this.collect_expr_opt(expr); - if awaitable == Awaitable::Yes { - this.lower_async_block_with_moved_arguments(params, body, CoroutineSource::Fn) + if is_async_fn || is_gen_fn { + let kind = match (is_async_fn, is_gen_fn) { + (true, true) => CoroutineKind::AsyncGen, + (true, false) => CoroutineKind::Async, + (false, true) => CoroutineKind::Gen, + (false, false) => unreachable!(), + }; + this.lower_coroutine_with_moved_arguments(params, body, kind, CoroutineSource::Fn) } else { body } @@ -1192,7 +1214,44 @@ impl<'db> ExprCollector<'db> { self.with_label_rib(RibKind::Closure, |this| { this.with_awaitable_block(Awaitable::Yes, |this| { this.collect_block_(e, |this, id, statements, tail| { - this.async_block( + this.desugared_coroutine_expr( + CoroutineKind::Async, + CoroutineSource::Block, + capture_by, + id, + statements, + tail, + ) + }) + }) + }) + } + Some(ast::BlockModifier::Gen(_)) => { + let capture_by = + if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; + self.with_label_rib(RibKind::Closure, |this| { + this.with_awaitable_block(Awaitable::No("non-async gen block"), |this| { + this.collect_block_(e, |this, id, statements, tail| { + this.desugared_coroutine_expr( + CoroutineKind::Gen, + CoroutineSource::Block, + capture_by, + id, + statements, + tail, + ) + }) + }) + }) + } + Some(ast::BlockModifier::AsyncGen(_)) => { + let capture_by = + if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; + self.with_label_rib(RibKind::Closure, |this| { + this.with_awaitable_block(Awaitable::Yes, |this| { + this.collect_block_(e, |this, id, statements, tail| { + this.desugared_coroutine_expr( + CoroutineKind::AsyncGen, CoroutineSource::Block, capture_by, id, @@ -1213,14 +1272,6 @@ impl<'db> ExprCollector<'db> { }) }) } - // FIXME - Some(ast::BlockModifier::AsyncGen(_)) => { - self.with_awaitable_block(Awaitable::Yes, |this| this.collect_block(e)) - } - Some(ast::BlockModifier::Gen(_)) => self - .with_awaitable_block(Awaitable::No("non-async gen block"), |this| { - this.collect_block(e) - }), None => self.collect_block(e), }, ast::Expr::LoopExpr(e) => { @@ -1460,25 +1511,37 @@ impl<'db> ExprCollector<'db> { }; let mut body = this .with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body())); - - let closure_kind = if this.is_lowering_coroutine { - let movability = if e.static_token().is_some() { - Movability::Static + let kind = { + if e.async_token().is_some() && e.gen_token().is_some() { + Some(CoroutineKind::AsyncGen) + } else if e.async_token().is_some() { + Some(CoroutineKind::Async) + } else if e.gen_token().is_some() { + Some(CoroutineKind::Gen) } else { - Movability::Movable - }; - ClosureKind::Coroutine(movability) - } else if e.async_token().is_some() { + None + } + }; + + let closure_kind = if let Some(kind) = kind { // It's important that this expr is allocated immediately before the closure. // We rely on it for `coroutine_for_closure()`. - body = this.lower_async_block_with_moved_arguments( + body = this.lower_coroutine_with_moved_arguments( &mut args, body, + kind, CoroutineSource::Closure, ); body_is_bindings_owner = true; - ClosureKind::AsyncClosure + ClosureKind::CoroutineClosure(kind) + } else if this.is_lowering_coroutine { + let movability = if e.static_token().is_some() { + Movability::Static + } else { + Movability::Movable + }; + ClosureKind::OldCoroutine(movability) } else { ClosureKind::Closure }; 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 bb35009f36246..5d901915035ab 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 @@ -17,8 +17,8 @@ use crate::{ attrs::AttrFlags, expr_store::path::{GenericArg, GenericArgs}, hir::{ - Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, RecordSpread, - Statement, + Array, BindingAnnotation, CaptureBy, ClosureKind, CoroutineKind, Literal, Movability, + RecordSpread, Statement, generics::{GenericParams, WherePredicate}, }, lang_item::LangItemTarget, @@ -761,28 +761,36 @@ impl Printer<'_> { let mut body = *body; let mut print_pipes = true; match closure_kind { - ClosureKind::Coroutine(Movability::Static) => { + ClosureKind::OldCoroutine(Movability::Static) => { w!(self, "static "); } - ClosureKind::AsyncClosure => { + ClosureKind::CoroutineClosure(kind) => { if let Expr::Closure { body: inner_body, - closure_kind: ClosureKind::AsyncBlock { .. }, + closure_kind: ClosureKind::Coroutine { .. }, .. } = self.store[body] { body = inner_body; } else { - never!("async closure should always have an async block body"); + never!("coroutine closure should always have a coroutine body"); } - w!(self, "async "); + match kind { + CoroutineKind::Async => w!(self, "async "), + CoroutineKind::Gen => w!(self, "gen "), + CoroutineKind::AsyncGen => w!(self, "async gen "), + } } - ClosureKind::AsyncBlock { .. } => { - w!(self, "async "); + ClosureKind::Coroutine { kind, .. } => { + match kind { + CoroutineKind::Async => w!(self, "async "), + CoroutineKind::Gen => w!(self, "gen "), + CoroutineKind::AsyncGen => w!(self, "async gen "), + } print_pipes = false; } - ClosureKind::Closure | ClosureKind::Coroutine(Movability::Movable) => (), + ClosureKind::Closure | ClosureKind::OldCoroutine(Movability::Movable) => (), } match capture_by { CaptureBy::Value => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 93fa7ff961791..6bea505757fa8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -524,12 +524,19 @@ pub enum InlineAsmRegOrRegClass { RegClass(Symbol), } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CoroutineKind { + Async, + Gen, + AsyncGen, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ClosureKind { Closure, - Coroutine(Movability), - AsyncBlock { source: CoroutineSource }, - AsyncClosure, + OldCoroutine(Movability), + Coroutine { kind: CoroutineKind, source: CoroutineSource }, + CoroutineClosure(CoroutineKind), } /// In the case of a coroutine created as part of an async/gen construct, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index adc445c2a8083..e5a9b5d46cc63 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -413,6 +413,7 @@ language_item_table! { LangItems => FnOnceOutput, sym::fn_once_output, TypeAliasId; Future, sym::future_trait, TraitId; + AsyncIterator, sym::async_iterator, TraitId; CoroutineState, sym::coroutine_state, EnumId; Coroutine, sym::coroutine, TraitId; CoroutineReturn, sym::coroutine_return, TypeAliasId; @@ -522,7 +523,6 @@ language_item_table! { LangItems => IteratorNext, sym::next, FunctionId; Iterator, sym::iterator, TraitId; FusedIterator, sym::fused_iterator, TraitId; - AsyncIterator, sym::async_iterator, TraitId; PinNewUnchecked, sym::new_unchecked, FunctionId; 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 6d704274f45d8..4657c439148ef 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -567,19 +567,20 @@ bitflags! { const DEFAULT = 1 << 2; const CONST = 1 << 3; const ASYNC = 1 << 4; - const UNSAFE = 1 << 5; - const HAS_VARARGS = 1 << 6; - const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7; - const HAS_SELF_PARAM = 1 << 8; + const GEN = 1 << 5; + const UNSAFE = 1 << 6; + const HAS_VARARGS = 1 << 7; + const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 8; + const HAS_SELF_PARAM = 1 << 9; /// The `#[target_feature]` attribute is necessary to check safety (with RFC 2396), /// but keeping it for all functions will consume a lot of memory when there are /// only very few functions with it. So we only encode its existence here, and lookup /// it if needed. - const HAS_TARGET_FEATURE = 1 << 9; - const DEPRECATED_SAFE_2024 = 1 << 10; - const EXPLICIT_SAFE = 1 << 11; - const HAS_LEGACY_CONST_GENERICS = 1 << 12; - const RUSTC_INTRINSIC = 1 << 13; + const HAS_TARGET_FEATURE = 1 << 10; + const DEPRECATED_SAFE_2024 = 1 << 11; + const EXPLICIT_SAFE = 1 << 12; + const HAS_LEGACY_CONST_GENERICS = 1 << 13; + const RUSTC_INTRINSIC = 1 << 14; } } @@ -638,6 +639,9 @@ impl FunctionSignature { if source.value.async_token().is_some() { flags.insert(FnFlags::ASYNC); } + if source.value.gen_token().is_some() { + flags.insert(FnFlags::GEN); + } if source.value.const_token().is_some() { flags.insert(FnFlags::CONST); } @@ -701,6 +705,10 @@ impl FunctionSignature { self.flags.contains(FnFlags::ASYNC) } + pub fn is_gen(&self) -> bool { + self.flags.contains(FnFlags::GEN) + } + pub fn is_unsafe(&self) -> bool { self.flags.contains(FnFlags::UNSAFE) } 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 78228cf82e678..3e108f72bee0e 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 @@ -427,6 +427,7 @@ macro_rules! __known_path { (core::range::RangeFrom) => {}; (core::range::RangeInclusive) => {}; (core::range::RangeToInclusive) => {}; + (core::async_iter::AsyncIterator) => {}; (core::future::Future) => {}; (core::future::IntoFuture) => {}; (core::fmt::Debug) => {}; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 99bad2682bacf..821ab5fc042ef 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -276,8 +276,8 @@ impl InternedCoroutineId { matches!( expr, hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Coroutine(_) - | hir_def::hir::ClosureKind::AsyncBlock { .. }, + closure_kind: hir_def::hir::ClosureKind::OldCoroutine(_) + | hir_def::hir::ClosureKind::Coroutine { .. }, .. } ), @@ -305,7 +305,7 @@ impl InternedCoroutineClosureId { matches!( expr, hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::AsyncClosure, + closure_kind: hir_def::hir::ClosureKind::CoroutineClosure(_), .. } ), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index e4a8def4425a5..8b00bdf3918e0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -14,7 +14,10 @@ use hir_def::{ Lookup, ModuleDefId, ModuleId, TraitId, expr_store::{ExpressionStore, path::Path}, find_path::{self, PrefixKind}, - hir::generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate}, + hir::{ + ClosureKind as HirClosureKind, CoroutineKind, + generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate}, + }, item_scope::ItemInNs, item_tree::FieldsShape, lang_item::LangItems, @@ -65,6 +68,36 @@ use crate::{ utils::{detect_variant_from_bytes, fn_traits}, }; +fn async_gen_item_ty_from_yield_ty<'db>( + lang_items: &LangItems, + yield_ty: Ty<'db>, +) -> Option> { + let poll_id = lang_items.Poll.map(hir_def::AdtId::EnumId)?; + let option_id = lang_items.Option.map(hir_def::AdtId::EnumId)?; + + let TyKind::Adt(poll_def, poll_args) = yield_ty.kind() else { + return None; + }; + if poll_def.inner().id != poll_id { + return None; + } + let [poll_inner] = poll_args.as_slice() else { + return None; + }; + let poll_inner = poll_inner.ty()?; + + let TyKind::Adt(option_def, option_args) = poll_inner.kind() else { + return None; + }; + if option_def.inner().id != option_id { + return None; + } + let [item] = option_args.as_slice() else { + return None; + }; + item.ty() +} + pub type Result = std::result::Result; pub trait HirWrite: fmt::Write { @@ -1519,6 +1552,22 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } TyKind::CoroutineClosure(id, args) => { let id = id.0; + let closure_kind = match id.loc(db) { + InternedClosure(owner, expr_id) => { + match &ExpressionStore::of(db, owner)[expr_id] { + hir_def::hir::Expr::Closure { + closure_kind: HirClosureKind::CoroutineClosure(kind), + .. + } => *kind, + expr => panic!("invalid expr for coroutine closure: {expr:?}"), + } + } + }; + let closure_label = match closure_kind { + CoroutineKind::Async => "async closure", + CoroutineKind::Gen => "gen closure", + CoroutineKind::AsyncGen => "async gen closure", + }; if f.display_kind.is_source_code() { if !f.display_kind.allows_opaque() { return Err(HirDisplayError::DisplaySourceCodeError( @@ -1533,25 +1582,28 @@ impl<'db> HirDisplay<'db> for Ty<'db> { ClosureStyle::ClosureWithId => { return write!( f, - "{{async closure#{:?}}}", + "{{{closure_label}#{:?}}}", salsa::plumbing::AsId::as_id(&id).index() ); } ClosureStyle::ClosureWithSubst => { write!( f, - "{{async closure#{:?}}}", + "{{{closure_label}#{:?}}}", salsa::plumbing::AsId::as_id(&id).index() )?; return hir_fmt_generics(f, args.as_slice(), None, None); } _ => (), } - let kind = args.as_coroutine_closure().kind(); - let kind = match kind { - rustc_type_ir::ClosureKind::Fn => "AsyncFn", - rustc_type_ir::ClosureKind::FnMut => "AsyncFnMut", - rustc_type_ir::ClosureKind::FnOnce => "AsyncFnOnce", + let callable_kind = args.as_coroutine_closure().kind(); + let kind = match (closure_kind, callable_kind) { + (CoroutineKind::Async, rustc_type_ir::ClosureKind::Fn) => "AsyncFn", + (CoroutineKind::Async, rustc_type_ir::ClosureKind::FnMut) => "AsyncFnMut", + (CoroutineKind::Async, rustc_type_ir::ClosureKind::FnOnce) => "AsyncFnOnce", + (_, rustc_type_ir::ClosureKind::Fn) => "Fn", + (_, rustc_type_ir::ClosureKind::FnMut) => "FnMut", + (_, rustc_type_ir::ClosureKind::FnOnce) => "FnOnce", }; let coroutine_sig = args.as_coroutine_closure().coroutine_closure_sig(); let coroutine_sig = coroutine_sig.skip_binder(); @@ -1559,7 +1611,11 @@ impl<'db> HirDisplay<'db> for Ty<'db> { let coroutine_output = coroutine_sig.return_ty; match f.closure_style { ClosureStyle::ImplFn => write!(f, "impl {kind}(")?, - ClosureStyle::RANotation => write!(f, "async |")?, + ClosureStyle::RANotation => match closure_kind { + CoroutineKind::Async => write!(f, "async |")?, + CoroutineKind::Gen => write!(f, "gen |")?, + CoroutineKind::AsyncGen => write!(f, "async gen |")?, + }, _ => unreachable!(), } if coroutine_inputs.is_empty() { @@ -1677,7 +1733,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { let expr = &body[expr_id]; match expr { hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. }, + closure_kind: HirClosureKind::Coroutine { kind: CoroutineKind::Async, .. }, .. } => { let future_trait = f.lang_items().Future; @@ -1706,7 +1762,68 @@ impl<'db> HirDisplay<'db> for Ty<'db> { write!(f, ">")?; } hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Coroutine(..), + closure_kind: HirClosureKind::Coroutine { kind: CoroutineKind::Gen, .. }, + .. + } => { + let iterator_trait = f.lang_items().Iterator; + let item = iterator_trait.and_then(|t| { + t.trait_items(db) + .associated_type_by_name(&Name::new_symbol_root(sym::Item)) + }); + write!(f, "impl ")?; + if let Some(t) = iterator_trait { + f.start_location_link(t.into()); + } + write!(f, "Iterator")?; + if iterator_trait.is_some() { + f.end_location_link(); + } + write!(f, "<")?; + if let Some(t) = item { + f.start_location_link(t.into()); + } + write!(f, "Item")?; + if item.is_some() { + f.end_location_link(); + } + write!(f, " = ")?; + yield_ty.hir_fmt(f)?; + write!(f, ">")?; + } + hir_def::hir::Expr::Closure { + closure_kind: + HirClosureKind::Coroutine { kind: CoroutineKind::AsyncGen, .. }, + .. + } => { + let async_iterator_trait = f.lang_items().AsyncIterator; + let item = async_iterator_trait.and_then(|t| { + t.trait_items(db) + .associated_type_by_name(&Name::new_symbol_root(sym::Item)) + }); + write!(f, "impl ")?; + if let Some(t) = async_iterator_trait { + f.start_location_link(t.into()); + } + write!(f, "AsyncIterator")?; + if async_iterator_trait.is_some() { + f.end_location_link(); + } + write!(f, "<")?; + if let Some(t) = item { + f.start_location_link(t.into()); + } + write!(f, "Item")?; + if item.is_some() { + f.end_location_link(); + } + write!(f, " = ")?; + let item_ty = async_gen_item_ty_from_yield_ty(f.lang_items(), yield_ty) + .unwrap_or(yield_ty); + item_ty.hir_fmt(f)?; + write!(f, ">")?; + } + hir_def::hir::Expr::Closure { + closure_kind: HirClosureKind::OldCoroutine(..), .. } => { if f.display_kind.is_source_code() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs index ffdde58c48b60..6c86b6720f7c5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs @@ -272,6 +272,7 @@ impl<'db> InferenceContext<'_, 'db> { // ...or *ideally*, we just have `LendingFn`/`LendingFnMut`, which // would naturally unify these two trait hierarchies in the most // general way. + let call_trait_choices = if self.shallow_resolve(adjusted_ty).is_coroutine_closure() { [ (self.lang_items.AsyncFn, sym::async_call, true), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 93c98f2542b3e..f5b974b1dbf5e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -5,8 +5,8 @@ pub(crate) mod analysis; use std::{iter, mem, ops::ControlFlow}; use hir_def::{ - TraitId, - hir::{ClosureKind, CoroutineSource, ExprId, PatId}, + AdtId, TraitId, + hir::{ClosureKind, CoroutineKind, CoroutineSource, ExprId, PatId}, type_ref::TypeRefId, }; use rustc_type_ir::{ @@ -22,8 +22,8 @@ use crate::{ db::{InternedClosure, InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId}, infer::{BreakableKind, Diverges, coerce::CoerceMany, pat::PatOrigin}, next_solver::{ - AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig, - PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, Ty, TyKind, + AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArg, GenericArgs, + PolyFnSig, PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, Ty, TyKind, abi::Safety, infer::{ BoundRegionConversionTime, InferOk, InferResult, @@ -46,6 +46,22 @@ struct ClosureSignatures<'db> { } impl<'db> InferenceContext<'_, 'db> { + fn poll_option_ty(&mut self, item_ty: Ty<'db>) -> Ty<'db> { + let interner = self.interner(); + + let (Some(option), Some(poll)) = (self.lang_items.Option, self.lang_items.Poll) else { + return self.types.types.error; + }; + + let option_ty = Ty::new_adt( + interner, + AdtId::EnumId(option), + interner.mk_args(&[GenericArg::from(item_ty)]), + ); + + Ty::new_adt(interner, AdtId::EnumId(poll), interner.mk_args(&[GenericArg::from(option_ty)])) + } + pub(super) fn infer_closure( &mut self, body: ExprId, @@ -121,10 +137,22 @@ impl<'db> InferenceContext<'_, 'db> { (Ty::new_closure(interner, closure_id.into(), closure_args.args), None) } - ClosureKind::Coroutine(_) | ClosureKind::AsyncBlock { .. } => { + ClosureKind::OldCoroutine(_) | ClosureKind::Coroutine { .. } => { let yield_ty = match closure_kind { - ClosureKind::Coroutine(_) => self.table.next_ty_var(closure_expr.into()), - ClosureKind::AsyncBlock { .. } => self.types.types.unit, + ClosureKind::OldCoroutine(_) + | ClosureKind::Coroutine { kind: CoroutineKind::Gen, .. } => { + let yield_ty = self.table.next_ty_var(closure_expr.into()); + self.require_type_is_sized(yield_ty); + yield_ty + } + ClosureKind::Coroutine { kind: CoroutineKind::Async, .. } => { + self.types.types.unit + } + ClosureKind::Coroutine { kind: CoroutineKind::AsyncGen, .. } => { + let yield_ty = self.table.next_ty_var(closure_expr.into()); + self.require_type_is_sized(yield_ty); + self.poll_option_ty(yield_ty) + } _ => unreachable!(), }; @@ -137,7 +165,7 @@ impl<'db> InferenceContext<'_, 'db> { // later during upvar analysis. Regular coroutines always have the kind // ty of `().` let kind_ty = match closure_kind { - ClosureKind::AsyncBlock { source: CoroutineSource::Closure } => { + ClosureKind::Coroutine { source: CoroutineSource::Closure, .. } => { self.table.next_ty_var(closure_expr.into()) } _ => self.types.types.unit, @@ -163,11 +191,20 @@ impl<'db> InferenceContext<'_, 'db> { Some((resume_ty, yield_ty)), ) } - ClosureKind::AsyncClosure => { - // async closures always return the type ascribed after the `->` (if present), - // and yield `()`. - let (bound_return_ty, bound_yield_ty) = - (bound_sig.skip_binder().output(), self.types.types.unit); + ClosureKind::CoroutineClosure(coroutine_kind) => { + let (bound_return_ty, bound_yield_ty) = match coroutine_kind { + CoroutineKind::Gen => { + (self.types.types.unit, self.table.next_ty_var(closure_expr.into())) + } + CoroutineKind::Async => { + (bound_sig.skip_binder().output(), self.types.types.unit) + } + CoroutineKind::AsyncGen => { + let yield_ty = self.table.next_ty_var(closure_expr.into()); + (self.types.types.unit, self.poll_option_ty(yield_ty)) + } + }; + // Compute all of the variables that will be used to populate the coroutine. let resume_ty = self.table.next_ty_var(closure_expr.into()); @@ -354,9 +391,9 @@ impl<'db> InferenceContext<'_, 'db> { let expected_sig = sig_tys.with(hdr); (Some(expected_sig), Some(rustc_type_ir::ClosureKind::Fn)) } - ClosureKind::Coroutine(_) - | ClosureKind::AsyncClosure - | ClosureKind::AsyncBlock { .. } => (None, None), + ClosureKind::OldCoroutine(_) + | ClosureKind::Coroutine { .. } + | ClosureKind::CoroutineClosure(_) => (None, None), }, _ => (None, None), } @@ -469,8 +506,10 @@ impl<'db> InferenceContext<'_, 'db> { if let Some(trait_def_id) = trait_def_id { let found_kind = match closure_kind { - ClosureKind::Closure => self.fn_trait_kind_from_def_id(trait_def_id), - ClosureKind::AsyncClosure => self + ClosureKind::Closure | ClosureKind::CoroutineClosure(CoroutineKind::Gen) => { + self.fn_trait_kind_from_def_id(trait_def_id) + } + ClosureKind::CoroutineClosure(CoroutineKind::Async) => self .async_fn_trait_kind_from_def_id(trait_def_id) .or_else(|| self.fn_trait_kind_from_def_id(trait_def_id)), _ => None, @@ -517,13 +556,17 @@ impl<'db> InferenceContext<'_, 'db> { ClosureKind::Closure if Some(def_id) == self.lang_items.FnOnceOutput => { self.extract_sig_from_projection(projection) } - ClosureKind::AsyncClosure if Some(def_id) == self.lang_items.AsyncFnOnceOutput => { + ClosureKind::CoroutineClosure(CoroutineKind::Async) + if Some(def_id) == self.lang_items.AsyncFnOnceOutput => + { self.extract_sig_from_projection(projection) } // It's possible we've passed the closure to a (somewhat out-of-fashion) // `F: FnOnce() -> Fut, Fut: Future` style bound. Let's still // guide inference here, since it's beneficial for the user. - ClosureKind::AsyncClosure if Some(def_id) == self.lang_items.FnOnceOutput => { + ClosureKind::CoroutineClosure(CoroutineKind::Async) + if Some(def_id) == self.lang_items.FnOnceOutput => + { self.extract_sig_from_projection_and_future_bound(closure_expr, projection) } _ => None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs index 31b6252475d30..79bdc6cea1042 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -284,7 +284,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // coroutine-closures that are `move` since otherwise they themselves will // be borrowing from the outer environment, so there's no self-borrows occurring. if let UpvarArgs::Coroutine(..) = args - && let hir_def::hir::ClosureKind::AsyncBlock { source: CoroutineSource::Closure } = + && let hir_def::hir::ClosureKind::Coroutine { source: CoroutineSource::Closure, .. } = closure_kind && let parent_hir_id = ExpressionStore::closure_for_coroutine(closure_expr_id) && let parent_ty = self.result.expr_ty(parent_hir_id) @@ -310,8 +310,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // // FIXME(async_closures): This could be cleaned up. It's a bit janky that we're just // moving all of the `LocalSource::AsyncFn` locals here. - if let hir_def::hir::ClosureKind::AsyncBlock { + if let hir_def::hir::ClosureKind::Coroutine { source: CoroutineSource::Fn | CoroutineSource::Closure, + .. } = closure_kind { let Expr::Block { statements, .. } = &self.store[body] else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index a6c8cda404a07..2b19c445ed7a5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -2129,7 +2129,7 @@ impl<'db> InferenceContext<'_, 'db> { // closure wrapped in a block. // See . let is_closure = if let Expr::Closure { closure_kind, .. } = self.store[*arg] { - !matches!(closure_kind, ClosureKind::Coroutine(_)) + !matches!(closure_kind, ClosureKind::OldCoroutine(_)) } else { false }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index af3798f1dadc2..14e20dfe80530 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1318,11 +1318,11 @@ impl<'db> Interner for DbInterner<'db> { let expr = &store[expr_id]; match *expr { hir_def::hir::Expr::Closure { closure_kind, .. } => match closure_kind { - hir_def::hir::ClosureKind::Coroutine(movability) => match movability { + hir_def::hir::ClosureKind::OldCoroutine(movability) => match movability { hir_def::hir::Movability::Static => rustc_ast_ir::Movability::Static, hir_def::hir::Movability::Movable => rustc_ast_ir::Movability::Movable, }, - hir_def::hir::ClosureKind::AsyncBlock { .. } => rustc_ast_ir::Movability::Static, + hir_def::hir::ClosureKind::Coroutine { .. } => rustc_ast_ir::Movability::Static, _ => panic!("unexpected expression for a coroutine: {expr:?}"), }, _ => panic!("unexpected expression for a coroutine: {expr:?}"), @@ -1560,7 +1560,6 @@ impl<'db> Interner for DbInterner<'db> { ignore = { AsyncFnKindHelper, - AsyncIterator, BikeshedGuaranteedNoDrop, FusedIterator, Field, @@ -1587,6 +1586,7 @@ impl<'db> Interner for DbInterner<'db> { Unpin, Tuple, Iterator, + AsyncIterator, AsyncFn, AsyncFnMut, AsyncFnOnce, @@ -1652,7 +1652,6 @@ impl<'db> Interner for DbInterner<'db> { ignore = { AsyncFnKindHelper, - AsyncIterator, BikeshedGuaranteedNoDrop, FusedIterator, Field, @@ -1679,6 +1678,7 @@ impl<'db> Interner for DbInterner<'db> { Unpin, Tuple, Iterator, + AsyncIterator, AsyncFn, AsyncFnMut, AsyncFnOnce, @@ -1943,7 +1943,7 @@ impl<'db> Interner for DbInterner<'db> { matches!( store[expr_id], hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Coroutine(_), + closure_kind: hir_def::hir::ClosureKind::OldCoroutine(_), .. } ) @@ -1957,20 +1957,43 @@ impl<'db> Interner for DbInterner<'db> { matches!( store[expr_id], hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. }, + closure_kind: hir_def::hir::ClosureKind::Coroutine { + kind: hir_def::hir::CoroutineKind::Async, + .. + }, .. } ) } - fn coroutine_is_gen(self, _coroutine_def_id: Self::CoroutineId) -> bool { - // We don't handle gen coroutines yet. - false + fn coroutine_is_gen(self, def_id: Self::CoroutineId) -> bool { + let InternedClosure(owner, expr_id) = def_id.0.loc(self.db); + let store = ExpressionStore::of(self.db, owner); + matches!( + store[expr_id], + hir_def::hir::Expr::Closure { + closure_kind: hir_def::hir::ClosureKind::Coroutine { + kind: hir_def::hir::CoroutineKind::Gen, + .. + }, + .. + } + ) } - fn coroutine_is_async_gen(self, _coroutine_def_id: Self::CoroutineId) -> bool { - // We don't handle gen coroutines yet. - false + fn coroutine_is_async_gen(self, def_id: Self::CoroutineId) -> bool { + let InternedClosure(owner, expr_id) = def_id.0.loc(self.db); + let store = ExpressionStore::of(self.db, owner); + matches!( + store[expr_id], + hir_def::hir::Expr::Closure { + closure_kind: hir_def::hir::ClosureKind::Coroutine { + kind: hir_def::hir::CoroutineKind::AsyncGen, + .. + }, + .. + } + ) } fn unsizing_params_for_adt(self, id: Self::AdtId) -> Self::UnsizingParams { @@ -2084,8 +2107,8 @@ impl<'db> Interner for DbInterner<'db> { if matches!( expr, hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. } - | hir_def::hir::ClosureKind::Coroutine(_), + closure_kind: hir_def::hir::ClosureKind::Coroutine { .. } + | hir_def::hir::ClosureKind::OldCoroutine(_), .. } ) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 511259ecd8e51..3bd20e9064523 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -12,10 +12,10 @@ use macros::GenericTypeVisitable; use rustc_abi::{Float, Integer, Size}; use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; use rustc_type_ir::{ - AliasTyKind, BoundVar, BoundVarIndexKind, ClosureKind, CoroutineArgs, CoroutineArgsParts, - DebruijnIndex, FlagComputation, Flags, FloatTy, FloatVid, GenericTypeVisitable, InferTy, IntTy, - IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, UintTy, Upcast, WithCachedTypeInfo, + AliasTyKind, BoundVar, BoundVarIndexKind, ClosureKind, DebruijnIndex, FlagComputation, Flags, + FloatTy, FloatVid, GenericTypeVisitable, InferTy, IntTy, IntVid, Interner, TyVid, TypeFoldable, + TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, + Upcast, WithCachedTypeInfo, inherent::{ AdtDef as _, BoundExistentialPredicates, GenericArgs as _, IntoKind, ParamLike, Safety as _, SliceLike, Ty as _, @@ -575,23 +575,13 @@ impl<'db> Ty<'db> { } TyKind::CoroutineClosure(coroutine_id, args) => { Some(args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| { - let unit_ty = Ty::new_unit(interner); - let return_ty = Ty::new_coroutine( + let closure_args = args.as_coroutine_closure(); + let return_ty = sig.to_coroutine( interner, + closure_args.parent_args(), + closure_args.kind_ty(), interner.coroutine_for_closure(coroutine_id), - CoroutineArgs::new( - interner, - CoroutineArgsParts { - parent_args: args.as_coroutine_closure().parent_args(), - kind_ty: unit_ty, - resume_ty: unit_ty, - yield_ty: unit_ty, - return_ty: sig.return_ty, - // FIXME: Deduce this from the coroutine closure's upvars. - tupled_upvars_ty: unit_ty, - }, - ) - .args, + closure_args.tupled_upvars_ty(), ); FnSig { inputs_and_output: Tys::new_from_iter( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index cc3464cd7fa9f..05dbb1a8ac23f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -391,6 +391,34 @@ fn test() { ); } +#[test] +fn gen_yield_coerce() { + check_no_mismatches( + r#" +fn test() { + let g = gen { + yield &1u32; + yield &&1u32; + }; +} + "#, + ); +} + +#[test] +fn async_gen_yield_coerce() { + check_no_mismatches( + r#" +fn test() { + let g = async gen { + yield &1u32; + yield &&1u32; + }; +} + "#, + ); +} + #[test] fn assign_coerce() { check_no_mismatches( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index fb46e4b58b8fc..1e75c31fa190f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -2065,6 +2065,138 @@ fn test() { ); } +#[test] +fn gen_block_types_inferred() { + check_infer( + r#" +//- minicore: iterator, deref +use core::iter::Iterator; + +fn test() { + let mut generator = gen { + yield 1i8; + }; + let result = generator.next(); +} + "#, + expect![[r#" + 37..131 '{ ...t(); }': () + 47..60 'mut generator': impl Iterator + 63..93 'gen { ... }': impl Iterator + 77..86 'yield 1i8': () + 83..86 '1i8': i8 + 103..109 'result': Option + 112..121 'generator': impl Iterator + 112..128 'genera...next()': Option + "#]], + ); +} + +#[test] +fn async_gen_block_types_inferred() { + check_infer( + r#" +//- minicore: async_iterator, option, future, deref, pin +use core::async_iter::AsyncIterator; +use core::pin::Pin; +use core::task::Context; + +fn test(mut cx: Context<'_>) { + let mut generator = async gen { + yield 1i8; + }; + let result = Pin::new(&mut generator).poll_next(&mut cx); +} + "#, + expect![[r#" + 91..97 'mut cx': Context<'?> + 112..239 '{ ...cx); }': () + 122..135 'mut generator': impl AsyncIterator + 138..174 'async ... }': impl AsyncIterator + 158..167 'yield 1i8': () + 164..167 '1i8': i8 + 184..190 'result': Poll> + 193..201 'Pin::new': fn new<&'? mut impl AsyncIterator>(&'? mut impl AsyncIterator) -> Pin<&'? mut impl AsyncIterator> + 193..217 'Pin::n...rator)': Pin<&'? mut impl AsyncIterator> + 193..236 'Pin::n...ut cx)': Poll> + 202..216 '&mut generator': &'? mut impl AsyncIterator + 207..216 'generator': impl AsyncIterator + 228..235 '&mut cx': &'? mut Context<'?> + 233..235 'cx': Context<'?> + "#]], + ); +} + +#[test] +fn gen_fn_types_inferred() { + check_infer( + r#" +//- minicore: iterator, deref +use core::iter::Iterator; + +gen fn html() { + yield (); +} + +fn test() { + let mut generator = html(); + let result = generator.next(); +} + "#, + expect![[r#" + 41..58 '{ ... (); }': () + 47..55 'yield ()': () + 53..55 '()': () + 70..140 '{ ...t(); }': () + 80..93 'mut generator': impl Iterator + 96..100 'html': fn html() -> impl Iterator + 96..102 'html()': impl Iterator + 112..118 'result': Option<()> + 121..130 'generator': impl Iterator + 121..137 'genera...next()': Option<()> + "#]], + ); +} + +#[test] +fn async_gen_fn_types_inferred() { + check_infer( + r#" +//- minicore: async_iterator, option, future, deref, pin +use core::async_iter::AsyncIterator; +use core::pin::Pin; +use core::task::Context; + +async gen fn html() { + yield (); +} + +fn test(mut cx: Context<'_>) { + let mut generator = html(); + let result = Pin::new(&mut generator).poll_next(&mut cx); +} + "#, + expect![[r#" + 103..120 '{ ... (); }': () + 109..117 'yield ()': () + 115..117 '()': () + 130..136 'mut cx': Context<'?> + 151..248 '{ ...cx); }': () + 161..174 'mut generator': impl AsyncIterator + 177..181 'html': fn html() -> impl AsyncIterator + 177..183 'html()': impl AsyncIterator + 193..199 'result': Poll> + 202..210 'Pin::new': fn new<&'? mut impl AsyncIterator>(&'? mut impl AsyncIterator) -> Pin<&'? mut impl AsyncIterator> + 202..226 'Pin::n...rator)': Pin<&'? mut impl AsyncIterator> + 202..245 'Pin::n...ut cx)': Poll> + 211..225 '&mut generator': &'? mut impl AsyncIterator + 216..225 'generator': impl AsyncIterator + 237..244 '&mut cx': &'? mut Context<'?> + 242..244 'cx': Context<'?> + "#]], + ); +} + #[test] fn tuple_pattern_nested_match_ergonomics() { check_no_mismatches( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 278666ef35923..48a8371bc82f4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -123,6 +123,24 @@ async fn test() { ); } +#[test] +fn infer_async_gen_closure() { + check_types( + r#" +//- minicore: async_iterator, fn +//- /main.rs edition:2024 +fn test() { + let f = async gen move |x: i32| { + yield x + 42; + }; + let a = f(4); + a; +// ^ impl AsyncIterator +} +"#, + ); +} + #[test] fn auto_sized_async_block() { check_no_mismatches( diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 53f24713cdccc..139f078eef6b0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -163,6 +163,9 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re if data.is_async() { f.write_str("async ")?; } + if data.is_gen() { + f.write_str("gen ")?; + } // FIXME: This will show `unsafe` for functions that are `#[target_feature]` but not unsafe // (they are conditionally unsafe to call). We probably should show something else. if func.is_unsafe_to_call(db, None, f.edition()) { @@ -223,7 +226,7 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re // `FunctionData::ret_type` will be `::core::future::Future` for async fns. // Use ugly pattern match to strip the Future trait. // Better way? - let ret_type = if !data.is_async() { + let ret_type = if !data.is_async() && !data.is_gen() { data.ret_type } else if let Some(ret_type) = data.ret_type { match &data.store[ret_type] { diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 4ed3d1c7d7e4a..a2b317be5884a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -599,7 +599,7 @@ fn main() { false, false, expect![[r#" - Some Variant FileId(1) 5999..6031 6024..6028 + Some Variant FileId(1) 6022..6054 6047..6051 FileId(0) 46..50 "#]], diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 25c2e3f733f75..c0053a3f210ea 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -128,6 +128,9 @@ define_symbols! { as_str, asm, assert, + async_iter, + async_iterator, + AsyncIterator, attr, attributes, begin_panic, @@ -304,7 +307,6 @@ define_symbols! { Iterator, iterator, fused_iterator, - async_iterator, keyword, lang, lang_items, diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 33a20951daced..c23ae3152aee9 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -11,6 +11,7 @@ //! add: //! asm: //! assert: +//! async_iterator: option, future, pin //! as_mut: sized //! as_ref: sized //! async_fn: fn, tuple, future, copy @@ -1530,6 +1531,7 @@ pub mod slice { // region:option pub mod option { + #[lang = "Option"] pub enum Option { #[lang = "None"] None, @@ -1679,6 +1681,7 @@ pub mod future { } } pub mod task { + #[lang = "Poll"] pub enum Poll { #[lang = "Ready"] Ready(T), @@ -1692,6 +1695,22 @@ pub mod task { } // endregion:future +// region:async_iterator +pub mod async_iter { + use crate::{ + pin::Pin, + task::{Context, Poll}, + }; + + #[lang = "async_iterator"] + pub trait AsyncIterator { + type Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + } +} +// endregion:async_iterator + // region:iterator pub mod iter { // region:iterators From 20f9804c7b417642f429c023355eaff0cedd89ef Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Sun, 26 Apr 2026 20:45:26 +0900 Subject: [PATCH 090/289] patch fix --- src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 48a8371bc82f4..dd14ec9cdd5f7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -125,17 +125,18 @@ async fn test() { #[test] fn infer_async_gen_closure() { - check_types( + check( r#" //- minicore: async_iterator, fn //- /main.rs edition:2024 fn test() { let f = async gen move |x: i32| { yield x + 42; + //^^^^^^ expected Poll>, got i32 }; let a = f(4); a; -// ^ impl AsyncIterator +// ^ type: impl AsyncIterator } "#, ); From 0dfc3a34d309aea2d2fe3bfee0833c47b53992b6 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Sun, 26 Apr 2026 21:17:10 +0900 Subject: [PATCH 091/289] add add, builtin_impls to minicore --- src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index dd14ec9cdd5f7..bcb5e5de16ba9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -127,7 +127,7 @@ async fn test() { fn infer_async_gen_closure() { check( r#" -//- minicore: async_iterator, fn +//- minicore: async_iterator, fn, add, builtin_impls //- /main.rs edition:2024 fn test() { let f = async gen move |x: i32| { From 361cf75fc0acd77a0fa0003662c4f4d237992449 Mon Sep 17 00:00:00 2001 From: Souradip Pal Date: Sun, 26 Apr 2026 21:36:55 +0530 Subject: [PATCH 092/289] internal: drop container_max_len helper, pass max_len - 1 Per review feedback: the previous code passing the current best path's length as the container search ceiling was a flawed local optimization. The correct ceiling is the recursive budget the caller passed down, minus the one segment we'll push (the item name) on top. Replacing both call sites with `max_len - 1` removes the broken pruning (which also originally caused the prelude-avoidance bug) and makes the container_max_len helper unnecessary. The bidirectional prelude comparison in Choice::try_select still picks the better-ranked path among everything the budget allows. Generated with AI assistance (Claude). --- .../crates/hir-def/src/find_path.rs | 35 ++++--------------- 1 file changed, 6 insertions(+), 29 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 44e4d002def33..78d0a2f0af5fb 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 @@ -414,19 +414,6 @@ fn find_in_sysroot( }); } -/// Computes the maximum path length allowed for a container module when searching for an item. -/// -/// When `prefer_prelude` is false and the current best path goes through a `prelude` module, -/// we relax the limit by 1 so that non-prelude paths one segment longer can still be found -/// and then preferred by `Choice::try_select`. -fn container_max_len(prefer_prelude: bool, max_len: usize, best_choice: &Option) -> usize { - match best_choice { - Some(best) if !prefer_prelude && best.has_prelude_segment => best.path.len(), - Some(best) => best.path.len() - 1, - None => max_len, - } -} - fn find_in_dep( ctx: &FindPathCtx<'_>, visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, @@ -447,13 +434,7 @@ fn find_in_dep( // Determine best path for containing module and append last segment from `info`. // FIXME: we should guide this to look up the path locally, or from the same crate again? - let choice = find_path_for_module( - ctx, - visited_modules, - info.container, - true, - container_max_len(ctx.cfg.prefer_prelude, max_len, best_choice), - ); + let choice = find_path_for_module(ctx, visited_modules, info.container, true, max_len - 1); let Some(mut choice) = choice else { continue; }; @@ -479,15 +460,11 @@ fn calculate_best_path_local( ) { // FIXME: cache the `find_local_import_locations` output? find_local_import_locations(ctx, item, visited_modules, |visited_modules, name, module_id| { - // we are looking for paths of length up to best_path_len, any longer will make it be - // less optimal. The -1 is due to us pushing name onto it afterwards. - if let Some(choice) = find_path_for_module( - ctx, - visited_modules, - module_id, - false, - container_max_len(ctx.cfg.prefer_prelude, max_len, best_choice), - ) { + // The container path may be at most `max_len - 1` segments since we push + // `name` on top of it. + if let Some(choice) = + find_path_for_module(ctx, visited_modules, module_id, false, max_len - 1) + { Choice::try_select(best_choice, choice, ctx.cfg.prefer_prelude, name.clone()); } }); From 0bcf444b1051d89a1368d16f63f428da07eaee9b Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Sun, 12 Apr 2026 09:44:01 +0600 Subject: [PATCH 093/289] internal: migrate inline_call assist to SyntaxEditor Replace clone_for_update + ted mutations with SyntaxEditor throughout. inline() uses SyntaxEditor::with_ast_node on the body clone for all parameter replacements, self-token renaming, and Self type substitution. inline_into_callers uses make_editor + add_file_edits; import removal uses the Removable trait instead of split_refs_and_uses + make_syntax_mut. --- .../ide-assists/src/handlers/inline_call.rs | 264 ++++++++++++------ 1 file changed, 172 insertions(+), 92 deletions(-) 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 21f2249a19c9d..0f5b42bf22c31 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,6 +1,5 @@ use std::collections::BTreeSet; -use ast::make; use either::Either; use hir::{ FileRange, PathResolution, Semantics, TypeInfo, @@ -11,7 +10,6 @@ use ide_db::{ EditionedFileId, RootDatabase, base_db::Crate, defs::Definition, - imports::insert_use::remove_path_if_in_use_stmt, path_transform::PathTransform, search::{FileReference, FileReferenceNode, SearchScope}, source_change::SourceChangeBuilder, @@ -21,9 +19,11 @@ use itertools::{Itertools, izip}; use syntax::{ AstNode, NodeOrToken, SyntaxKind, ast::{ - self, HasArgList, HasGenericArgs, Pat, PathExpr, edit::IndentLevel, edit_in_place::Indent, + self, HasArgList, HasGenericArgs, Pat, PathExpr, + edit::{AstNodeEdit, IndentLevel}, + make, }, - ted, + syntax_editor::SyntaxEditor, }; use crate::{ @@ -108,35 +108,62 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> let mut remove_def = true; let mut inline_refs_for_file = |file_id: EditionedFileId, refs: Vec| { + use syntax::syntax_editor::Removable; + let file_id = file_id.file_id(ctx.db()); builder.edit_file(file_id); let call_krate = ctx.sema.file_to_module_def(file_id).map(|it| it.krate(ctx.db())); let count = refs.len(); - // The collects are required as we are otherwise iterating while mutating 🙅‍♀️🙅‍♂️ - let (name_refs, name_refs_use) = split_refs_and_uses(builder, refs, Some); + // Split refs into call-site name refs and use-tree paths + let (name_refs, use_trees): (Vec, Vec) = refs + .into_iter() + .filter_map(|file_ref| match file_ref.name { + FileReferenceNode::NameRef(name_ref) => Some(name_ref), + _ => None, + }) + .partition_map(|name_ref| { + match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) { + Some(use_tree) => Either::Right(use_tree), + None => Either::Left(name_ref), + } + }); let call_infos: Vec<_> = name_refs .into_iter() .filter_map(|it| CallInfo::from_name_ref(it, call_krate?.into())) // FIXME: do not handle callsites in macros' parameters, because // directly inlining into macros may cause errors. .filter(|call_info| !ctx.sema.hir_file_for(call_info.node.syntax()).is_macro()) - .map(|call_info| { - let mut_node = builder.make_syntax_mut(call_info.node.syntax().clone()); - (call_info, mut_node) - }) .collect(); - let replaced = call_infos - .into_iter() - .map(|(call_info, mut_node)| { - let replacement = - inline(&ctx.sema, def_file, function, &func_body, ¶ms, &call_info); - ted::replace(mut_node, replacement.syntax()); - }) - .count(); - if replaced + name_refs_use.len() == count { - // we replaced all usages in this file, so we can remove the imports - name_refs_use.iter().for_each(remove_path_if_in_use_stmt); - } else { + if let Some(first) = call_infos.first() { + let mut editor = builder.make_editor(first.node.syntax()); + let replaced = call_infos + .into_iter() + .map(|call_info| { + let replacement = inline( + &ctx.sema, def_file, function, &func_body, ¶ms, &call_info, + ); + editor.replace(call_info.node.syntax(), replacement.syntax()); + }) + .count(); + if replaced + use_trees.len() == count { + // we replaced all usages in this file, so we can remove the imports + for use_tree in &use_trees { + if use_tree.use_tree_list().is_some() || use_tree.star_token().is_some() + { + continue; + } + if let Some(use_) = use_tree.syntax().parent().and_then(ast::Use::cast) + { + use_.remove(&mut editor); + } else { + use_tree.remove(&mut editor); + } + } + } else { + remove_def = false; + } + builder.add_file_edits(file_id, editor); + } else if use_trees.len() != count { remove_def = false; } }; @@ -148,7 +175,9 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> None => builder.edit_file(vfs_def_file), } if remove_def { - builder.delete(ast_func.syntax().text_range()); + let mut editor = builder.make_editor(ast_func.syntax()); + editor.delete(ast_func.syntax()); + builder.add_file_edits(vfs_def_file, editor); } }, ) @@ -320,19 +349,28 @@ fn inline( CallInfo { node, arguments, generic_arg_list, krate }: &CallInfo, ) -> ast::Expr { let file_id = sema.hir_file_for(fn_body.syntax()); - let mut body = if let Some(macro_file) = file_id.macro_file() { + 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 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_for_update() - } + if let Some(body) = ast::BlockExpr::cast(body_prettified) { body } else { fn_body.clone() } } else { - fn_body.clone_for_update() + fn_body.clone() }; + + // Capture indent level before SyntaxEditor re-roots via clone_subtree. + // For non-macro bodies, body_to_clone still has its parent, so from_node + // finds the whitespace before `{` and returns the correct source indent level. + // For macro/prettified bodies (already re-rooted at 0), from_node returns 0, + // which is also correct since prettify_macro_expansion rebuilds indent from 0. + let original_body_indent = IndentLevel::from_node(body_to_clone.syntax()); + + // The original body's start offset — needed to adjust FileReference ranges + // since SyntaxEditor::with_ast_node re-roots the tree to offset 0 + let body_offset = body_to_clone.syntax().text_range().start(); + let (mut editor, body) = SyntaxEditor::with_ast_node(&body_to_clone); + let usages_for_locals = |local| { Definition::Local(local) .usages(sema) @@ -355,7 +393,7 @@ fn inline( .map(|FileReference { name, range, .. }| match name { FileReferenceNode::NameRef(_) => body .syntax() - .covering_element(range) + .covering_element(range - body_offset) .ancestors() .nth(3) .and_then(ast::PathExpr::cast), @@ -368,48 +406,59 @@ fn inline( }) .collect(); - if function.self_param(sema.db).is_some() { - let this = || { - make::name_ref("this") - .syntax() - .clone_for_update() - .first_token() - .expect("NameRef should have had a token.") - }; - if let Some(self_local) = params[0].2.as_local(sema.db) { - usages_for_locals(self_local) - .filter_map(|FileReference { name, range, .. }| match name { - FileReferenceNode::NameRef(_) => Some(body.syntax().covering_element(range)), - _ => None, - }) - .for_each(|usage| { - ted::replace(usage, this()); - }); - } - } + let has_self_param = function.self_param(sema.db).is_some(); + + // Collect all self token usages upfront (needed when a let binding is emitted). + // When self can be directly inlined, the parameter loop handles those PathExprs; + // when a let stmt is needed, we rename all self tokens to `this` here. + let self_token_usages: Vec<_> = if has_self_param { + params[0] + .2 + .as_local(sema.db) + .map(|self_local| { + usages_for_locals(self_local) + .filter_map(|FileReference { name, range, .. }| match name { + FileReferenceNode::NameRef(_) => { + Some(body.syntax().covering_element(range - body_offset)) + } + _ => None, + }) + .collect() + }) + .unwrap_or_default() + } else { + Vec::new() + }; - // We should place the following code after last usage of `usages_for_locals` - // because `ted::replace` will change the offset in syntax tree, which makes - // `FileReference` incorrect + // Replace `Self` type keywords with the actual impl type if let Some(imp) = sema.ancestors_with_macros(fn_body.syntax().clone()).find_map(ast::Impl::cast) && !node.syntax().ancestors().any(|anc| &anc == imp.syntax()) && let Some(t) = imp.self_ty() { - while let Some(self_tok) = body + let self_tokens: Vec<_> = body .syntax() .descendants_with_tokens() .filter_map(NodeOrToken::into_token) - .find(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW) - { - let replace_with = t.clone_subtree().syntax().clone_for_update(); - if !is_in_type_path(&self_tok) - && let Some(ty) = ast::Type::cast(replace_with.clone()) - && let Some(generic_arg_list) = ty.generic_arg_list() - { - ted::remove(generic_arg_list.syntax()); + .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW) + .collect(); + for self_tok in self_tokens { + let mut replace_with = t.clone_subtree().syntax().clone_subtree(); + if !is_in_type_path(&self_tok) { + if let Some(ty) = ast::Type::cast(replace_with.clone()) { + if let Some(generic_arg_list) = ty.generic_arg_list() { + // Build replacement without generics using a sub-editor + let (mut sub_editor, sub_root) = SyntaxEditor::new(replace_with.clone()); + let generic_node = sub_root + .descendants() + .find(|n| n.text_range() == generic_arg_list.syntax().text_range()) + .unwrap(); + sub_editor.delete(generic_node); + replace_with = sub_editor.finish().new_root().clone(); + } + } } - ted::replace(self_tok, replace_with); + editor.replace(self_tok, replace_with); } } @@ -428,7 +477,7 @@ fn inline( } } - let mut let_stmts = Vec::new(); + let mut let_stmts: Vec = Vec::new(); // Inline parameter expressions or generate `let` statements depending on whether inlining works or not. for ((pat, param_ty, param), usages, expr) in izip!(params, param_use_nodes, arguments) { @@ -486,30 +535,46 @@ fn inline( } } }; - let_stmts - .push(make::let_stmt(this_pat.into(), ty, Some(expr)).clone_for_update().into()) + let_stmts.push(make::let_stmt(this_pat.into(), ty, Some(expr)).into()) } else { - let_stmts.push( - make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(), - ); + let_stmts.push(make::let_stmt(pat.clone(), ty, Some(expr.clone())).into()); } }; + let is_self_param = + has_self_param && param.name(sema.db).is_some_and(|name| name == sym::self_); + // check if there is a local var in the function that conflicts with parameter // if it does then emit a let statement and continue if func_let_vars.contains(&expr.syntax().text().to_string()) { + if is_self_param { + for usage in &self_token_usages { + let this_token = make::name_ref("this") + .syntax() + .clone_subtree() + .first_token() + .expect("NameRef should have had a token."); + editor.replace(usage.clone(), this_token); + } + } insert_let_stmt(); continue; } - let inline_direct = |usage, replacement: &ast::Expr| { - if let Some(field) = path_expr_as_record_field(usage) { - cov_mark::hit!(inline_call_inline_direct_field); - field.replace_expr(replacement.clone_for_update()); - } else { - ted::replace(usage.syntax(), replacement.syntax().clone_for_update()); - } - }; + let inline_direct = + |editor: &mut SyntaxEditor, usage: &PathExpr, replacement: &ast::Expr| { + if let Some(field) = path_expr_as_record_field(usage) { + cov_mark::hit!(inline_call_inline_direct_field); + let field_name = field.field_name().unwrap(); + let new_field = make::record_expr_field( + make::name_ref(&field_name.text()), + Some(replacement.clone()), + ); + editor.replace(field.syntax(), new_field.syntax()); + } else { + editor.replace(usage.syntax(), replacement.syntax().clone_subtree()); + } + }; match usages { // inline single use closure arguments @@ -519,29 +584,45 @@ fn inline( { cov_mark::hit!(inline_call_inline_closure); let expr = make::expr_paren(expr.clone()).into(); - inline_direct(usage, &expr); + inline_direct(&mut editor, usage, &expr); } // inline single use literals [usage] if matches!(expr, ast::Expr::Literal(_)) => { cov_mark::hit!(inline_call_inline_literal); - inline_direct(usage, expr); + inline_direct(&mut editor, usage, expr); } // inline direct local arguments [_, ..] if expr_as_name_ref(expr).is_some() => { cov_mark::hit!(inline_call_inline_locals); - usages.iter().for_each(|usage| inline_direct(usage, expr)); + usages.iter().for_each(|usage| inline_direct(&mut editor, usage, expr)); } // can't inline, emit a let statement _ => { + if is_self_param { + // Rename all `self` tokens to `this` so the let binding matches. + for usage in &self_token_usages { + let this_token = make::name_ref("this") + .syntax() + .clone_subtree() + .first_token() + .expect("NameRef should have had a token."); + editor.replace(usage.clone(), this_token); + } + } insert_let_stmt(); } } } + // Apply all edits to get the transformed body + let edit = editor.finish(); + let mut body = ast::BlockExpr::cast(edit.new_root().clone()) + .expect("editor root should still be a BlockExpr"); + + // Apply generic substitution (needs immutable tree) if let Some(generic_arg_list) = generic_arg_list.clone() && let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax())) { - body.reindent_to(IndentLevel(0)); if let Some(new_body) = ast::BlockExpr::cast( PathTransform::function_call(target, source, function, generic_arg_list) .apply(body.syntax()), @@ -553,31 +634,30 @@ fn inline( let is_async_fn = function.is_async(sema.db); if is_async_fn { cov_mark::hit!(inline_call_async_fn); - body = make::async_move_block_expr(body.statements(), body.tail_expr()).clone_for_update(); + body = make::async_move_block_expr(body.statements(), body.tail_expr()); // Arguments should be evaluated outside the async block, and then moved into it. if !let_stmts.is_empty() { cov_mark::hit!(inline_call_async_fn_with_let_stmts); - body.indent(IndentLevel(1)); - body = make::block_expr(let_stmts, Some(body.into())).clone_for_update(); + body = body.indent(IndentLevel(1)); + body = make::block_expr(let_stmts, Some(body.into())); } - } else if let Some(stmt_list) = body.stmt_list() { - let position = stmt_list.l_curly_token().expect("L_CURLY for StatementList is missing."); - let_stmts.into_iter().rev().for_each(|let_stmt| { - ted::insert(ted::Position::after(position.clone()), let_stmt.syntax().clone()); - }); + } else if !let_stmts.is_empty() { + // Prepend let statements to the body's existing statements + let stmts: Vec = let_stmts.into_iter().chain(body.statements()).collect(); + body = make::block_expr(stmts, body.tail_expr()); } let original_indentation = match node { ast::CallableExpr::Call(it) => it.indent_level(), ast::CallableExpr::MethodCall(it) => it.indent_level(), }; - body.reindent_to(original_indentation); + body = body.dedent(original_body_indent).indent(original_indentation); let no_stmts = body.statements().next().is_none(); match body.tail_expr() { Some(expr) if matches!(expr, ast::Expr::ClosureExpr(_)) && no_stmts => { - make::expr_paren(expr).clone_for_update().into() + make::expr_paren(expr).into() } Some(expr) if !is_async_fn && no_stmts => expr, _ => match node @@ -587,7 +667,7 @@ fn inline( .and_then(|bin_expr| bin_expr.lhs()) { Some(lhs) if lhs.syntax() == node.syntax() => { - make::expr_paren(ast::Expr::BlockExpr(body)).clone_for_update().into() + make::expr_paren(ast::Expr::BlockExpr(body)).into() } _ => ast::Expr::BlockExpr(body), }, From 91d124ff65347bd4300caecfccb9b209f5195ccc Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Sun, 12 Apr 2026 09:58:25 +0600 Subject: [PATCH 094/289] internal: fix collapsible_if clippy lints in inline_call --- .../ide-assists/src/handlers/inline_call.rs | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) 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 0f5b42bf22c31..f4ba213e339c0 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 @@ -444,19 +444,18 @@ fn inline( .collect(); for self_tok in self_tokens { let mut replace_with = t.clone_subtree().syntax().clone_subtree(); - if !is_in_type_path(&self_tok) { - if let Some(ty) = ast::Type::cast(replace_with.clone()) { - if let Some(generic_arg_list) = ty.generic_arg_list() { - // Build replacement without generics using a sub-editor - let (mut sub_editor, sub_root) = SyntaxEditor::new(replace_with.clone()); - let generic_node = sub_root - .descendants() - .find(|n| n.text_range() == generic_arg_list.syntax().text_range()) - .unwrap(); - sub_editor.delete(generic_node); - replace_with = sub_editor.finish().new_root().clone(); - } - } + if !is_in_type_path(&self_tok) + && let Some(ty) = ast::Type::cast(replace_with.clone()) + && let Some(generic_arg_list) = ty.generic_arg_list() + { + // Build replacement without generics using a sub-editor + let (mut sub_editor, sub_root) = SyntaxEditor::new(replace_with.clone()); + let generic_node = sub_root + .descendants() + .find(|n| n.text_range() == generic_arg_list.syntax().text_range()) + .unwrap(); + sub_editor.delete(generic_node); + replace_with = sub_editor.finish().new_root().clone(); } editor.replace(self_tok, replace_with); } @@ -622,13 +621,12 @@ fn inline( // Apply generic substitution (needs immutable tree) if let Some(generic_arg_list) = generic_arg_list.clone() && let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax())) - { - if let Some(new_body) = ast::BlockExpr::cast( + && let Some(new_body) = ast::BlockExpr::cast( PathTransform::function_call(target, source, function, generic_arg_list) .apply(body.syntax()), - ) { - body = new_body; - } + ) + { + body = new_body; } let is_async_fn = function.is_async(sema.db); From 00f9a8ad5b4e9088b5b6d729b274620ea69753de Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Tue, 14 Apr 2026 19:24:28 +0600 Subject: [PATCH 095/289] internal: address review feedback for inline_call SyntaxEditor migration --- .../ide-assists/src/handlers/inline_call.rs | 62 +++++++------------ .../src/handlers/inline_type_alias.rs | 10 ++- .../crates/ide-db/src/imports/insert_use.rs | 11 ++++ 3 files changed, 37 insertions(+), 46 deletions(-) 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 f4ba213e339c0..60ab364795c30 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 @@ -10,9 +10,9 @@ use ide_db::{ EditionedFileId, RootDatabase, base_db::Crate, defs::Definition, + imports::insert_use::remove_use_tree_if_simple, path_transform::PathTransform, search::{FileReference, FileReferenceNode, SearchScope}, - source_change::SourceChangeBuilder, syntax_helpers::{node_ext::expr_as_name_ref, prettify_macro_expansion}, }; use itertools::{Itertools, izip}; @@ -22,6 +22,7 @@ use syntax::{ self, HasArgList, HasGenericArgs, Pat, PathExpr, edit::{AstNodeEdit, IndentLevel}, make, + syntax_factory::SyntaxFactory, }, syntax_editor::SyntaxEditor, }; @@ -108,25 +109,11 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> let mut remove_def = true; let mut inline_refs_for_file = |file_id: EditionedFileId, refs: Vec| { - use syntax::syntax_editor::Removable; - let file_id = file_id.file_id(ctx.db()); builder.edit_file(file_id); let call_krate = ctx.sema.file_to_module_def(file_id).map(|it| it.krate(ctx.db())); let count = refs.len(); - // Split refs into call-site name refs and use-tree paths - let (name_refs, use_trees): (Vec, Vec) = refs - .into_iter() - .filter_map(|file_ref| match file_ref.name { - FileReferenceNode::NameRef(name_ref) => Some(name_ref), - _ => None, - }) - .partition_map(|name_ref| { - match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) { - Some(use_tree) => Either::Right(use_tree), - None => Either::Left(name_ref), - } - }); + let (name_refs, use_trees) = split_refs_and_uses(refs, Some); let call_infos: Vec<_> = name_refs .into_iter() .filter_map(|it| CallInfo::from_name_ref(it, call_krate?.into())) @@ -134,8 +121,12 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> // directly inlining into macros may cause errors. .filter(|call_info| !ctx.sema.hir_file_for(call_info.node.syntax()).is_macro()) .collect(); - if let Some(first) = call_infos.first() { - let mut editor = builder.make_editor(first.node.syntax()); + let anchor = call_infos + .first() + .map(|ci| ci.node.syntax().clone()) + .or_else(|| use_trees.first().map(|ut| ut.syntax().clone())); + if let Some(anchor) = anchor { + let mut editor = builder.make_editor(&anchor); let replaced = call_infos .into_iter() .map(|call_info| { @@ -148,16 +139,7 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> if replaced + use_trees.len() == count { // we replaced all usages in this file, so we can remove the imports for use_tree in &use_trees { - if use_tree.use_tree_list().is_some() || use_tree.star_token().is_some() - { - continue; - } - if let Some(use_) = use_tree.syntax().parent().and_then(ast::Use::cast) - { - use_.remove(&mut editor); - } else { - use_tree.remove(&mut editor); - } + remove_use_tree_if_simple(use_tree, &mut editor); } } else { remove_def = false; @@ -184,17 +166,16 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> } pub(super) fn split_refs_and_uses( - builder: &mut SourceChangeBuilder, iter: impl IntoIterator, mut map_ref: impl FnMut(ast::NameRef) -> Option, -) -> (Vec, Vec) { +) -> (Vec, Vec) { iter.into_iter() .filter_map(|file_ref| match file_ref.name { FileReferenceNode::NameRef(name_ref) => Some(name_ref), _ => None, }) .filter_map(|name_ref| match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) { - Some(use_tree) => builder.make_mut(use_tree).path().map(Either::Right), + Some(use_tree) => Some(Either::Right(use_tree)), None => map_ref(name_ref).map(Either::Left), }) .partition_map(|either| either) @@ -443,7 +424,7 @@ fn inline( .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW) .collect(); for self_tok in self_tokens { - let mut replace_with = t.clone_subtree().syntax().clone_subtree(); + let mut replace_with = t.syntax().clone_subtree(); if !is_in_type_path(&self_tok) && let Some(ty) = ast::Type::cast(replace_with.clone()) && let Some(generic_arg_list) = ty.generic_arg_list() @@ -550,7 +531,6 @@ fn inline( for usage in &self_token_usages { let this_token = make::name_ref("this") .syntax() - .clone_subtree() .first_token() .expect("NameRef should have had a token."); editor.replace(usage.clone(), this_token); @@ -565,13 +545,14 @@ fn inline( if let Some(field) = path_expr_as_record_field(usage) { cov_mark::hit!(inline_call_inline_direct_field); let field_name = field.field_name().unwrap(); - let new_field = make::record_expr_field( - make::name_ref(&field_name.text()), + let factory = SyntaxFactory::without_mappings(); + let new_field = factory.record_expr_field( + factory.name_ref(&field_name.text()), Some(replacement.clone()), ); editor.replace(field.syntax(), new_field.syntax()); } else { - editor.replace(usage.syntax(), replacement.syntax().clone_subtree()); + editor.replace(usage.syntax(), replacement.syntax()); } }; @@ -629,6 +610,7 @@ fn inline( body = new_body; } + let factory = SyntaxFactory::without_mappings(); let is_async_fn = function.is_async(sema.db); if is_async_fn { cov_mark::hit!(inline_call_async_fn); @@ -638,12 +620,12 @@ fn inline( if !let_stmts.is_empty() { cov_mark::hit!(inline_call_async_fn_with_let_stmts); body = body.indent(IndentLevel(1)); - body = make::block_expr(let_stmts, Some(body.into())); + body = factory.block_expr(let_stmts, Some(body.into())); } } else if !let_stmts.is_empty() { // Prepend let statements to the body's existing statements let stmts: Vec = let_stmts.into_iter().chain(body.statements()).collect(); - body = make::block_expr(stmts, body.tail_expr()); + body = factory.block_expr(stmts, body.tail_expr()); } let original_indentation = match node { @@ -655,7 +637,7 @@ fn inline( let no_stmts = body.statements().next().is_none(); match body.tail_expr() { Some(expr) if matches!(expr, ast::Expr::ClosureExpr(_)) && no_stmts => { - make::expr_paren(expr).into() + factory.expr_paren(expr).into() } Some(expr) if !is_async_fn && no_stmts => expr, _ => match node @@ -665,7 +647,7 @@ fn inline( .and_then(|bin_expr| bin_expr.lhs()) { Some(lhs) if lhs.syntax() == node.syntax() => { - make::expr_paren(ast::Expr::BlockExpr(body)).into() + factory.expr_paren(ast::Expr::BlockExpr(body)).into() } _ => ast::Expr::BlockExpr(body), }, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index 6d8750afdcff1..9d3c334a51835 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -5,7 +5,8 @@ use hir::{HasSource, PathResolution}; use ide_db::FxHashMap; use ide_db::{ - defs::Definition, imports::insert_use::ast_to_remove_for_path_in_use_stmt, + defs::Definition, + imports::insert_use::remove_use_tree_if_simple, search::FileReference, }; use itertools::Itertools; @@ -73,13 +74,12 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) let editor = builder.make_editor(source.syntax()); let (path_types, path_type_uses) = - split_refs_and_uses(builder, refs, |path_type| { + split_refs_and_uses(refs, |path_type| { path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast) }); path_type_uses .iter() - .flat_map(ast_to_remove_for_path_in_use_stmt) - .for_each(|x| editor.delete(x.syntax())); + .for_each(|use_tree| remove_use_tree_if_simple(use_tree, &mut editor)); for (target, replacement) in path_types.into_iter().filter_map(|path_type| { let replacement = @@ -1094,7 +1094,6 @@ fn f() -> Vec<&str> { } //- /foo.rs - fn foo() { let _: Vec = Vec::new(); } @@ -1123,7 +1122,6 @@ mod foo; //- /foo.rs - fn foo() { let _: i32 = 0; } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index fe30a4dc5cc92..4c4a01a1571d0 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -347,6 +347,17 @@ pub fn remove_path_if_in_use_stmt(path: &ast::Path) { } } +pub fn remove_use_tree_if_simple(use_tree: &ast::UseTree, editor: &mut SyntaxEditor) { + if use_tree.use_tree_list().is_some() || use_tree.star_token().is_some() { + return; + } + if let Some(use_) = use_tree.syntax().parent().and_then(ast::Use::cast) { + syntax::syntax_editor::Removable::remove(&use_, editor); + } else { + syntax::syntax_editor::Removable::remove(use_tree, editor); + } +} + #[derive(Eq, PartialEq, PartialOrd, Ord)] enum ImportGroup { // the order here defines the order of new group inserts From c440b95186f184b2d1cf75add72705e35e498eca Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Tue, 14 Apr 2026 19:28:24 +0600 Subject: [PATCH 096/289] internal: fix rustfmt in inline_type_alias --- .../ide-assists/src/handlers/inline_type_alias.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index 9d3c334a51835..cf9d80539795c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -5,9 +5,7 @@ use hir::{HasSource, PathResolution}; use ide_db::FxHashMap; use ide_db::{ - defs::Definition, - imports::insert_use::remove_use_tree_if_simple, - search::FileReference, + defs::Definition, imports::insert_use::remove_use_tree_if_simple, search::FileReference, }; use itertools::Itertools; use syntax::ast::syntax_factory::SyntaxFactory; @@ -73,10 +71,9 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) let source = ctx.sema.parse(file_id); let editor = builder.make_editor(source.syntax()); - let (path_types, path_type_uses) = - split_refs_and_uses(refs, |path_type| { - path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast) - }); + let (path_types, path_type_uses) = split_refs_and_uses(refs, |path_type| { + path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast) + }); path_type_uses .iter() .for_each(|use_tree| remove_use_tree_if_simple(use_tree, &mut editor)); From 8c11579fa8f30372ce859a60d371bc8ef93e7edf Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Sun, 26 Apr 2026 23:55:58 +0600 Subject: [PATCH 097/289] internal: address round 2 review for inline_call SyntaxEditor migration - file-keyed editor map in inline_into_callers (one editor per file) - inline() takes the file editor as a parameter - SyntaxFactory gains async_move_block_expr and expr_reborrow - Self type generic stripping uses replacen, no sub-editor - factory used for record_expr_field, expr_paren, expr_ref, ident_pat, let_stmt, ty - self-token rewriting hoisted into a shared closure --- .../ide-assists/src/handlers/inline_call.rs | 121 +++++++++--------- .../src/ast/syntax_factory/constructors.rs | 52 ++++++++ 2 files changed, 110 insertions(+), 63 deletions(-) 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 60ab364795c30..37a992f6bd16d 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 @@ -7,7 +7,7 @@ use hir::{ sym, }; use ide_db::{ - EditionedFileId, RootDatabase, + EditionedFileId, FileId, FxHashMap, RootDatabase, base_db::Crate, defs::Definition, imports::insert_use::remove_use_tree_if_simple, @@ -107,6 +107,7 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> let mut usages = usages.all(); let current_file_usage = usages.references.remove(&def_file); + let mut file_editors: FxHashMap = FxHashMap::default(); let mut remove_def = true; let mut inline_refs_for_file = |file_id: EditionedFileId, refs: Vec| { let file_id = file_id.file_id(ctx.db()); @@ -126,12 +127,14 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> .map(|ci| ci.node.syntax().clone()) .or_else(|| use_trees.first().map(|ut| ut.syntax().clone())); if let Some(anchor) = anchor { - let mut editor = builder.make_editor(&anchor); + let editor = + file_editors.entry(file_id).or_insert_with(|| builder.make_editor(&anchor)); let replaced = call_infos .into_iter() .map(|call_info| { let replacement = inline( &ctx.sema, def_file, function, &func_body, ¶ms, &call_info, + editor, ); editor.replace(call_info.node.syntax(), replacement.syntax()); }) @@ -139,12 +142,11 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> if replaced + use_trees.len() == count { // we replaced all usages in this file, so we can remove the imports for use_tree in &use_trees { - remove_use_tree_if_simple(use_tree, &mut editor); + remove_use_tree_if_simple(use_tree, editor); } } else { remove_def = false; } - builder.add_file_edits(file_id, editor); } else if use_trees.len() != count { remove_def = false; } @@ -157,9 +159,13 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> None => builder.edit_file(vfs_def_file), } if remove_def { - let mut editor = builder.make_editor(ast_func.syntax()); + let editor = file_editors + .entry(vfs_def_file) + .or_insert_with(|| builder.make_editor(ast_func.syntax())); editor.delete(ast_func.syntax()); - builder.add_file_edits(vfs_def_file, editor); + } + for (file_id, editor) in file_editors { + builder.add_file_edits(file_id, editor); } }, ) @@ -245,14 +251,11 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< let syntax = call_info.node.syntax().clone(); acc.add(AssistId::refactor_inline("inline_call"), label, syntax.text_range(), |builder| { - let replacement = inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info); - builder.replace_ast( - match call_info.node { - ast::CallableExpr::Call(it) => ast::Expr::CallExpr(it), - ast::CallableExpr::MethodCall(it) => ast::Expr::MethodCallExpr(it), - }, - replacement, - ); + let mut editor = builder.make_editor(call_info.node.syntax()); + let replacement = + inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info, &mut editor); + editor.replace(call_info.node.syntax(), replacement.syntax()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }) } @@ -328,7 +331,9 @@ fn inline( fn_body: &ast::BlockExpr, params: &[(ast::Pat, Option, hir::Param<'_>)], CallInfo { node, arguments, generic_arg_list, krate }: &CallInfo, + file_editor: &mut SyntaxEditor, ) -> ast::Expr { + let factory = SyntaxFactory::with_mappings(); 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); @@ -340,15 +345,8 @@ fn inline( fn_body.clone() }; - // Capture indent level before SyntaxEditor re-roots via clone_subtree. - // For non-macro bodies, body_to_clone still has its parent, so from_node - // finds the whitespace before `{` and returns the correct source indent level. - // For macro/prettified bodies (already re-rooted at 0), from_node returns 0, - // which is also correct since prettify_macro_expansion rebuilds indent from 0. + // Capture before `with_ast_node` re-roots and loses the source-relative position. let original_body_indent = IndentLevel::from_node(body_to_clone.syntax()); - - // The original body's start offset — needed to adjust FileReference ranges - // since SyntaxEditor::with_ast_node re-roots the tree to offset 0 let body_offset = body_to_clone.syntax().text_range().start(); let (mut editor, body) = SyntaxEditor::with_ast_node(&body_to_clone); @@ -424,20 +422,22 @@ fn inline( .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW) .collect(); for self_tok in self_tokens { - let mut replace_with = t.syntax().clone_subtree(); - if !is_in_type_path(&self_tok) - && let Some(ty) = ast::Type::cast(replace_with.clone()) - && let Some(generic_arg_list) = ty.generic_arg_list() + let replace_with = if !is_in_type_path(&self_tok) + && let Some(generic_arg_list) = t.generic_arg_list() { - // Build replacement without generics using a sub-editor - let (mut sub_editor, sub_root) = SyntaxEditor::new(replace_with.clone()); - let generic_node = sub_root - .descendants() - .find(|n| n.text_range() == generic_arg_list.syntax().text_range()) - .unwrap(); - sub_editor.delete(generic_node); - replace_with = sub_editor.finish().new_root().clone(); - } + // Strip the outer generic arg list and reparse, since turbofish-less + // generics aren't valid in expression position. The outermost + // `GenericArgList` text is unique within `t`'s text (any inner generics + // are nested inside it), so `replacen(.., 1)` is safe. + let stripped = t.syntax().text().to_string().replacen( + &generic_arg_list.syntax().text().to_string(), + "", + 1, + ); + factory.ty(&stripped).syntax().clone() + } else { + t.syntax().clone() + }; editor.replace(self_tok, replace_with); } } @@ -459,6 +459,14 @@ fn inline( let mut let_stmts: Vec = Vec::new(); + let this_token = + factory.name_ref("this").syntax().first_token().expect("NameRef should have had a token."); + let rewrite_self_to_this = |editor: &mut SyntaxEditor| { + for usage in &self_token_usages { + editor.replace(usage.clone(), this_token.clone()); + } + }; + // Inline parameter expressions or generate `let` statements depending on whether inlining works or not. for ((pat, param_ty, param), usages, expr) in izip!(params, param_use_nodes, arguments) { // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors @@ -487,7 +495,7 @@ fn inline( let is_self = param.name(sema.db).is_some_and(|name| name == sym::self_); if is_self { - let mut this_pat = make::ident_pat(false, false, make::name("this")); + let mut this_pat = factory.ident_pat(false, false, factory.name("this")); let mut expr = expr.clone(); if let Pat::IdentPat(pat) = pat { match (pat.ref_token(), pat.mut_token()) { @@ -495,11 +503,11 @@ fn inline( (None, None) => {} // mut self => let mut this = obj (None, Some(_)) => { - this_pat = make::ident_pat(false, true, make::name("this")); + this_pat = factory.ident_pat(false, true, factory.name("this")); } // &self => let this = &obj (Some(_), None) => { - expr = make::expr_ref(expr, false); + expr = factory.expr_ref(expr, false); } // let foo = &mut X; &mut self => let this = &mut obj // let mut foo = X; &mut self => let this = &mut *obj (reborrow) @@ -508,16 +516,16 @@ fn inline( .type_of_expr(&expr) .map(|ty| ty.original.is_mutable_reference()); expr = if let Some(true) = should_reborrow { - make::expr_reborrow(expr) + factory.expr_reborrow(expr) } else { - make::expr_ref(expr, true) + factory.expr_ref(expr, true) }; } } }; - let_stmts.push(make::let_stmt(this_pat.into(), ty, Some(expr)).into()) + let_stmts.push(factory.let_stmt(this_pat.into(), ty, Some(expr)).into()) } else { - let_stmts.push(make::let_stmt(pat.clone(), ty, Some(expr.clone())).into()); + let_stmts.push(factory.let_stmt(pat.clone(), ty, Some(expr.clone())).into()); } }; @@ -528,13 +536,7 @@ fn inline( // if it does then emit a let statement and continue if func_let_vars.contains(&expr.syntax().text().to_string()) { if is_self_param { - for usage in &self_token_usages { - let this_token = make::name_ref("this") - .syntax() - .first_token() - .expect("NameRef should have had a token."); - editor.replace(usage.clone(), this_token); - } + rewrite_self_to_this(&mut editor); } insert_let_stmt(); continue; @@ -545,7 +547,6 @@ fn inline( if let Some(field) = path_expr_as_record_field(usage) { cov_mark::hit!(inline_call_inline_direct_field); let field_name = field.field_name().unwrap(); - let factory = SyntaxFactory::without_mappings(); let new_field = factory.record_expr_field( factory.name_ref(&field_name.text()), Some(replacement.clone()), @@ -563,7 +564,7 @@ fn inline( && usage.syntax().parent().and_then(ast::Expr::cast).is_some() => { cov_mark::hit!(inline_call_inline_closure); - let expr = make::expr_paren(expr.clone()).into(); + let expr = factory.expr_paren(expr.clone()).into(); inline_direct(&mut editor, usage, &expr); } // inline single use literals @@ -580,14 +581,7 @@ fn inline( _ => { if is_self_param { // Rename all `self` tokens to `this` so the let binding matches. - for usage in &self_token_usages { - let this_token = make::name_ref("this") - .syntax() - .clone_subtree() - .first_token() - .expect("NameRef should have had a token."); - editor.replace(usage.clone(), this_token); - } + rewrite_self_to_this(&mut editor); } insert_let_stmt(); } @@ -610,11 +604,10 @@ fn inline( body = new_body; } - let factory = SyntaxFactory::without_mappings(); let is_async_fn = function.is_async(sema.db); if is_async_fn { cov_mark::hit!(inline_call_async_fn); - body = make::async_move_block_expr(body.statements(), body.tail_expr()); + body = factory.async_move_block_expr(body.statements(), body.tail_expr()); // Arguments should be evaluated outside the async block, and then moved into it. if !let_stmts.is_empty() { @@ -635,7 +628,7 @@ fn inline( body = body.dedent(original_body_indent).indent(original_indentation); let no_stmts = body.statements().next().is_none(); - match body.tail_expr() { + let result = match body.tail_expr() { Some(expr) if matches!(expr, ast::Expr::ClosureExpr(_)) && no_stmts => { factory.expr_paren(expr).into() } @@ -651,7 +644,9 @@ fn inline( } _ => ast::Expr::BlockExpr(body), }, - } + }; + file_editor.add_mappings(factory.finish_with_mappings()); + result } fn is_in_type_path(self_tok: &syntax::SyntaxToken) -> bool { 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 b9106408b3fbf..5a01580c56fc2 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 @@ -985,6 +985,37 @@ impl SyntaxFactory { ast } + pub fn async_move_block_expr( + &self, + statements: impl IntoIterator, + tail_expr: Option, + ) -> ast::BlockExpr { + let (statements, mut input) = iterator_input(statements); + + let ast = make::async_move_block_expr(statements, tail_expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let stmt_list = ast.stmt_list().unwrap(); + let mut builder = SyntaxMappingBuilder::new(stmt_list.syntax().clone()); + + if let Some(input) = tail_expr { + builder.map_node( + input.syntax().clone(), + stmt_list.tail_expr().unwrap().syntax().clone(), + ); + } else if let Some(ast_tail) = stmt_list.tail_expr() { + let last_stmt = input.pop().unwrap(); + builder.map_node(last_stmt, ast_tail.syntax().clone()); + } + + builder.map_children(input, stmt_list.statements().map(|it| it.syntax().clone())); + + builder.finish(&mut mapping); + } + + ast + } + pub fn expr_empty_block(&self) -> ast::BlockExpr { make::expr_empty_block().clone_for_update() } @@ -1135,6 +1166,27 @@ impl SyntaxFactory { ast.into() } + pub fn expr_reborrow(&self, expr: ast::Expr) -> ast::Expr { + let ast::Expr::RefExpr(ast) = make::expr_reborrow(expr.clone()).clone_for_update() else { + unreachable!() + }; + + if let Some(mut mapping) = self.mappings() { + // Layout: RefExpr(&mut, PrefixExpr(*, expr)). Map `expr` to the + // inner expr inside the synthesized PrefixExpr. + let prefix = match ast.expr() { + Some(ast::Expr::PrefixExpr(p)) => p, + _ => unreachable!("expr_reborrow always produces `&mut *expr`"), + }; + let inner = prefix.expr().unwrap(); + let mut builder = SyntaxMappingBuilder::new(prefix.syntax().clone()); + builder.map_node(expr.syntax().clone(), inner.syntax().clone()); + builder.finish(&mut mapping); + } + + ast.into() + } + pub fn expr_raw_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr { let ast::Expr::RefExpr(ast) = make::expr_raw_ref(expr.clone(), exclusive).clone_for_update() From e4e5819c25a2f34945b8dd13a7cbadb1b73b7139 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 27 Apr 2026 01:45:45 +0300 Subject: [PATCH 098/289] Minor simplification --- src/tools/rust-analyzer/crates/parser/src/event.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/parser/src/event.rs b/src/tools/rust-analyzer/crates/parser/src/event.rs index 9177f4210b5dc..001adfb780936 100644 --- a/src/tools/rust-analyzer/crates/parser/src/event.rs +++ b/src/tools/rust-analyzer/crates/parser/src/event.rs @@ -18,7 +18,7 @@ use crate::{ /// `forward_parent` uses `NonZeroU32` so `Option` is niche-optimised away /// (the offset is always ≥ 1 because the forward parent sits later in the /// event stream). -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone, Copy)] pub(crate) enum Event { /// This event signifies the start of the node. /// It should be either abandoned (in which case the @@ -93,7 +93,7 @@ pub(super) fn process(mut events: Vec, mut errors: Vec) -> Output let mut forward_parents = Vec::new(); for i in 0..events.len() { - match mem::replace(&mut events[i], Event::tombstone()) { + match events[i] { Event::Start { kind, forward_parent } => { // For events[A, B, C], B is A's forward_parent, C is B's forward_parent, // in the normal control flow, the parent-child relation: `A -> B -> C`, From 537ab11b9a48b63d0cf961e5bf7bddc4e109070a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 27 Apr 2026 01:57:05 +0300 Subject: [PATCH 099/289] Remove `Arc` from `file_item_tree` --- .../rust-analyzer/crates/hir-def/src/db.rs | 4 +- .../crates/hir-def/src/item_tree.rs | 42 ++++++++++--------- 2 files changed, 25 insertions(+), 21 deletions(-) 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 9dd7768ead868..d644c1e9fe6c3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -14,7 +14,7 @@ use crate::{ StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, attrs::AttrFlags, - item_tree::{ItemTree, file_item_tree_query}, + item_tree::{ItemTree, file_item_tree}, nameres::crate_def_map, visibility::{self, Visibility}, }; @@ -87,7 +87,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase { fn expand_proc_attr_macros(&self) -> bool; /// Computes an [`ItemTree`] for the given file or macro expansion. - #[salsa::invoke(file_item_tree_query)] + #[salsa::invoke(file_item_tree)] #[salsa::transparent] fn file_item_tree(&self, file_id: HirFileId, krate: Crate) -> &ItemTree; 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 e7ab2b390f264..4b50161c04cd0 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 @@ -61,7 +61,6 @@ use span::{ use stdx::never; use syntax::{SourceFile, SyntaxKind, ast, match_ast}; use thin_vec::ThinVec; -use triomphe::Arc; use tt::TextRange; use crate::{BlockId, Lookup, attrs::parse_extra_crate_attrs, db::DefDatabase}; @@ -121,14 +120,30 @@ fn lower_extra_crate_attrs<'a>( AttrsOrCfg::lower(db, &crate_attrs_as_src, cfg_options, span_map) } -#[salsa_macros::tracked(returns(deref))] -pub(crate) fn file_item_tree_query( +pub(crate) fn file_item_tree(db: &dyn DefDatabase, file_id: HirFileId, krate: Crate) -> &ItemTree { + match file_item_tree_query(db, file_id, krate) { + Some(item_tree) => item_tree, + None => { + static EMPTY: OnceLock = OnceLock::new(); + EMPTY.get_or_init(|| ItemTree { + top_level: Box::new([]), + attrs: FxHashMap::default(), + small_data: FxHashMap::default(), + big_data: FxHashMap::default(), + top_attrs: AttrsOrCfg::empty(), + vis: ItemVisibilities { arena: ThinVec::new() }, + }) + } + } +} + +#[salsa_macros::tracked(returns(ref))] +fn file_item_tree_query( db: &dyn DefDatabase, file_id: HirFileId, krate: Crate, -) -> Arc { +) -> Option> { let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered(); - static EMPTY: OnceLock> = OnceLock::new(); let ctx = lower::Ctx::new(db, file_id, krate); let syntax = db.parse_or_expand(file_id); @@ -174,21 +189,10 @@ pub(crate) fn file_item_tree_query( && top_attrs.is_empty() && vis.arena.is_empty() { - EMPTY - .get_or_init(|| { - Arc::new(ItemTree { - top_level: Box::new([]), - attrs: FxHashMap::default(), - small_data: FxHashMap::default(), - big_data: FxHashMap::default(), - top_attrs: AttrsOrCfg::empty(), - vis: ItemVisibilities { arena: ThinVec::new() }, - }) - }) - .clone() + None } else { item_tree.shrink_to_fit(); - Arc::new(item_tree) + Some(Box::new(item_tree)) } } @@ -343,7 +347,7 @@ impl TreeId { pub(crate) fn item_tree<'db>(&self, db: &'db dyn DefDatabase, krate: Crate) -> &'db ItemTree { match self.block { Some(block) => block_item_tree_query(db, block, krate), - None => file_item_tree_query(db, self.file, krate), + None => file_item_tree(db, self.file, krate), } } From 426fc6cae11d20cad4f0dc95eaa8437c71841a3d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 27 Apr 2026 02:02:16 +0300 Subject: [PATCH 100/289] Replace some `flat_map()` calls with `filter_map()` `filter_map()` is faster for `Option`. --- .../rust-analyzer/crates/hir-def/src/item_tree/lower.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 31e409d86e490..68b6cd79aea3b 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 @@ -65,7 +65,7 @@ impl<'db> Ctx<'db> { } pub(super) fn lower_module_items(mut self, item_owner: &dyn HasModuleItem) -> ItemTree { - self.top_level = item_owner.items().flat_map(|item| self.lower_mod_item(&item)).collect(); + self.top_level = item_owner.items().filter_map(|item| self.lower_mod_item(&item)).collect(); self.tree.vis.arena = self.visibilities.into_iter().collect(); self.tree.top_level = self.top_level.into_boxed_slice(); self.tree @@ -89,7 +89,7 @@ impl<'db> Ctx<'db> { _ => None, } }) - .flat_map(|item| self.lower_mod_item(&item)) + .filter_map(|item| self.lower_mod_item(&item)) .collect(); if let Some(ast::Expr::MacroExpr(tail_macro)) = stmts.expr() @@ -245,7 +245,9 @@ impl<'db> Ctx<'db> { ModKind::Inline { items: module .item_list() - .map(|list| list.items().flat_map(|item| self.lower_mod_item(&item)).collect()) + .map(|list| { + list.items().filter_map(|item| self.lower_mod_item(&item)).collect() + }) .unwrap_or_else(|| { cov_mark::hit!(name_res_works_for_broken_modules); Box::new([]) as Box<[_]> From 80f16552efb746ed503ed449f28666661948ecaf Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 27 Apr 2026 03:27:32 +0300 Subject: [PATCH 101/289] Ignore another hangy test CI commonly gets stuck lately. I'm not entirely sure this test is the cause since nextest does not show the hanging test but I think I saw it hanging in the past and also its friend, `test_flycheck_diagnostics_with_override_command_cleared_after_fix()`, was used to hang. --- .../crates/rust-analyzer/tests/slow-tests/flycheck.rs | 1 + 1 file changed, 1 insertion(+) 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 e5d4d7c88e803..386edb82f4e93 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 @@ -76,6 +76,7 @@ fn main() { } #[test] +#[ignore = "this test tends to stuck, FIXME: investigate that"] fn test_flycheck_diagnostic_with_override_command() { if skip_slow_tests() { return; From fd7e2447a9b7c4ca901e5416b2ae72b64bebbb87 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 27 Apr 2026 04:04:54 +0300 Subject: [PATCH 102/289] Keep the same nonce when cloning a `RootDatabase` It has the same data. --- src/tools/rust-analyzer/crates/ide-db/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 6b72a3033990b..190a754ab53a8 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -112,7 +112,7 @@ impl Clone for RootDatabase { storage: self.storage.clone(), files: self.files.clone(), crates_map: self.crates_map.clone(), - nonce: Nonce::new(), + nonce: self.nonce, } } } From 46b296cfdbc46e5ec0620c2bb1a42724067f6330 Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Mon, 27 Apr 2026 09:14:45 +0600 Subject: [PATCH 103/289] internal: adapt inline_call to new SyntaxFactory-inside-SyntaxEditor API --- .../ide-assists/src/handlers/inline_call.rs | 56 +++++++++---------- .../src/handlers/inline_type_alias.rs | 2 +- 2 files changed, 27 insertions(+), 31 deletions(-) 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 37a992f6bd16d..08302e6d4ceda 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 @@ -22,7 +22,6 @@ use syntax::{ self, HasArgList, HasGenericArgs, Pat, PathExpr, edit::{AstNodeEdit, IndentLevel}, make, - syntax_factory::SyntaxFactory, }, syntax_editor::SyntaxEditor, }; @@ -251,9 +250,9 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< let syntax = call_info.node.syntax().clone(); acc.add(AssistId::refactor_inline("inline_call"), label, syntax.text_range(), |builder| { - let mut editor = builder.make_editor(call_info.node.syntax()); + let editor = builder.make_editor(call_info.node.syntax()); let replacement = - inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info, &mut editor); + inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info, &editor); editor.replace(call_info.node.syntax(), replacement.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); }) @@ -331,9 +330,9 @@ fn inline( fn_body: &ast::BlockExpr, params: &[(ast::Pat, Option, hir::Param<'_>)], CallInfo { node, arguments, generic_arg_list, krate }: &CallInfo, - file_editor: &mut SyntaxEditor, + file_editor: &SyntaxEditor, ) -> ast::Expr { - let factory = SyntaxFactory::with_mappings(); + let factory = file_editor.make(); 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); @@ -348,7 +347,7 @@ fn inline( // Capture before `with_ast_node` re-roots and loses the source-relative position. let original_body_indent = IndentLevel::from_node(body_to_clone.syntax()); let body_offset = body_to_clone.syntax().text_range().start(); - let (mut editor, body) = SyntaxEditor::with_ast_node(&body_to_clone); + let (editor, body) = SyntaxEditor::with_ast_node(&body_to_clone); let usages_for_locals = |local| { Definition::Local(local) @@ -461,7 +460,7 @@ fn inline( let this_token = factory.name_ref("this").syntax().first_token().expect("NameRef should have had a token."); - let rewrite_self_to_this = |editor: &mut SyntaxEditor| { + let rewrite_self_to_this = |editor: &SyntaxEditor| { for usage in &self_token_usages { editor.replace(usage.clone(), this_token.clone()); } @@ -536,26 +535,25 @@ fn inline( // if it does then emit a let statement and continue if func_let_vars.contains(&expr.syntax().text().to_string()) { if is_self_param { - rewrite_self_to_this(&mut editor); + rewrite_self_to_this(&editor); } insert_let_stmt(); continue; } - let inline_direct = - |editor: &mut SyntaxEditor, usage: &PathExpr, replacement: &ast::Expr| { - if let Some(field) = path_expr_as_record_field(usage) { - cov_mark::hit!(inline_call_inline_direct_field); - let field_name = field.field_name().unwrap(); - let new_field = factory.record_expr_field( - factory.name_ref(&field_name.text()), - Some(replacement.clone()), - ); - editor.replace(field.syntax(), new_field.syntax()); - } else { - editor.replace(usage.syntax(), replacement.syntax()); - } - }; + let inline_direct = |editor: &SyntaxEditor, usage: &PathExpr, replacement: &ast::Expr| { + if let Some(field) = path_expr_as_record_field(usage) { + cov_mark::hit!(inline_call_inline_direct_field); + let field_name = field.field_name().unwrap(); + let new_field = factory.record_expr_field( + factory.name_ref(&field_name.text()), + Some(replacement.clone()), + ); + editor.replace(field.syntax(), new_field.syntax()); + } else { + editor.replace(usage.syntax(), replacement.syntax()); + } + }; match usages { // inline single use closure arguments @@ -565,23 +563,23 @@ fn inline( { cov_mark::hit!(inline_call_inline_closure); let expr = factory.expr_paren(expr.clone()).into(); - inline_direct(&mut editor, usage, &expr); + inline_direct(&editor, usage, &expr); } // inline single use literals [usage] if matches!(expr, ast::Expr::Literal(_)) => { cov_mark::hit!(inline_call_inline_literal); - inline_direct(&mut editor, usage, expr); + inline_direct(&editor, usage, expr); } // inline direct local arguments [_, ..] if expr_as_name_ref(expr).is_some() => { cov_mark::hit!(inline_call_inline_locals); - usages.iter().for_each(|usage| inline_direct(&mut editor, usage, expr)); + usages.iter().for_each(|usage| inline_direct(&editor, usage, expr)); } // can't inline, emit a let statement _ => { if is_self_param { // Rename all `self` tokens to `this` so the let binding matches. - rewrite_self_to_this(&mut editor); + rewrite_self_to_this(&editor); } insert_let_stmt(); } @@ -628,7 +626,7 @@ fn inline( body = body.dedent(original_body_indent).indent(original_indentation); let no_stmts = body.statements().next().is_none(); - let result = match body.tail_expr() { + match body.tail_expr() { Some(expr) if matches!(expr, ast::Expr::ClosureExpr(_)) && no_stmts => { factory.expr_paren(expr).into() } @@ -644,9 +642,7 @@ fn inline( } _ => ast::Expr::BlockExpr(body), }, - }; - file_editor.add_mappings(factory.finish_with_mappings()); - result + } } fn is_in_type_path(self_tok: &syntax::SyntaxToken) -> bool { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index cf9d80539795c..b4826bcc94398 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -69,7 +69,7 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) let mut inline_refs_for_file = |file_id, refs: Vec| { let source = ctx.sema.parse(file_id); - let editor = builder.make_editor(source.syntax()); + let mut editor = builder.make_editor(source.syntax()); let (path_types, path_type_uses) = split_refs_and_uses(refs, |path_type| { path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast) From c49c514b30331c0615db4e4efc0333a9bb6f7e48 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 27 Apr 2026 06:25:54 +0300 Subject: [PATCH 104/289] hir: diagnose non-exhaustive record expressions AI-assisted: GitHub Copilot used. --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 ++ .../crates/hir-ty/src/infer/expr.rs | 2 +- .../crates/hir/src/diagnostics.rs | 9 ++++ .../handlers/non_exhaustive_record_expr.rs | 46 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 4 ++ 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 4aeb5ec71cab2..9b35a97fb7832 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -368,6 +368,10 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] bad_value_break: bool, }, + NonExhaustiveRecordExpr { + #[type_visitable(ignore)] + expr: ExprId, + }, MismatchedArgCount { #[type_visitable(ignore)] call_expr: ExprId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index a6c8cda404a07..11309df7e68b1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -997,7 +997,7 @@ impl<'db> InferenceContext<'_, 'db> { // Prohibit struct expressions when non-exhaustive flag is set. if self.has_applicable_non_exhaustive(variant.into()) { - // FIXME: Emit an error. + self.push_diagnostic(InferenceDiagnostic::NonExhaustiveRecordExpr { expr }); } self.check_record_expr_fields(adt_ty, expected, expr, variant, fields, base_expr); diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 18f28541af66f..afafede6b9290 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -77,6 +77,7 @@ diagnostics![AnyDiagnostic<'db> -> MovedOutOfRef<'db>, NeedMut, NonExhaustiveLet, + NonExhaustiveRecordExpr, NoSuchField, PrivateAssocItem, PrivateField, @@ -315,6 +316,11 @@ pub struct NonExhaustiveLet { pub uncovered_patterns: String, } +#[derive(Debug)] +pub struct NonExhaustiveRecordExpr { + pub expr: InFile, +} + #[derive(Debug)] pub struct TypeMismatch<'db> { pub expr_or_pat: InFile, @@ -726,6 +732,9 @@ impl<'db> AnyDiagnostic<'db> { let expr = expr_syntax(expr)?; BreakOutsideOfLoop { expr, is_break, bad_value_break }.into() } + &InferenceDiagnostic::NonExhaustiveRecordExpr { expr } => { + NonExhaustiveRecordExpr { expr: expr_syntax(expr)? }.into() + } InferenceDiagnostic::TypedHole { expr, expected } => { let expr = expr_syntax(*expr)?; TypedHole { expr, expected: Type::new(db, def, expected.as_ref()) }.into() diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs new file mode 100644 index 0000000000000..9dbce4d1f4215 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs @@ -0,0 +1,46 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: non-exhaustive-record-expr +// +// This diagnostic is triggered if a struct expression constructs a `#[non_exhaustive]` +// struct from another crate. +pub(crate) fn non_exhaustive_record_expr( + ctx: &DiagnosticsContext<'_>, + d: &hir::NonExhaustiveRecordExpr, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0639"), + "cannot create non-exhaustive struct using struct expression", + d.expr.map(|it| it.into()), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn reports_external_non_exhaustive_struct_literal() { + check_diagnostics( + r#" +//- /lib.rs crate:lib +#[non_exhaustive] +pub struct S { + pub field: u32, +} + +fn local_ok() { + let _ = S { field: 0 }; +} + +//- /main.rs crate:main deps:lib +fn main() { + let _ = lib::S { field: 0 }; + //^^^^^^^^^^^^^^^^^^^ error: cannot create non-exhaustive struct using struct expression +} +"#, + ); + } +} 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 a083447335d05..300a6e6c7fd57 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -55,6 +55,7 @@ mod handlers { pub(crate) mod mutability_errors; pub(crate) mod no_such_field; pub(crate) mod non_exhaustive_let; + pub(crate) mod non_exhaustive_record_expr; pub(crate) mod parenthesized_generic_args_without_fn_trait; pub(crate) mod private_assoc_item; pub(crate) mod private_field; @@ -431,6 +432,9 @@ pub fn semantic_diagnostics( None => continue, }, AnyDiagnostic::NonExhaustiveLet(d) => handlers::non_exhaustive_let::non_exhaustive_let(&ctx, &d), + AnyDiagnostic::NonExhaustiveRecordExpr(d) => { + handlers::non_exhaustive_record_expr::non_exhaustive_record_expr(&ctx, &d) + } AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d), AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), AnyDiagnostic::PrivateField(d) => handlers::private_field::private_field(&ctx, &d), From ff8a233dff4a16134165a0eb748d79fea22aa2cf Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 27 Apr 2026 09:26:43 +0300 Subject: [PATCH 105/289] Convert `InternedClosure` to a named struct --- .../rust-analyzer/crates/hir-ty/src/db.rs | 17 ++++++++++------- .../rust-analyzer/crates/hir-ty/src/display.rs | 2 +- .../crates/hir-ty/src/infer/callee.rs | 4 ++-- .../crates/hir-ty/src/infer/closure.rs | 14 +++++++++----- .../crates/hir-ty/src/infer/coerce.rs | 2 +- .../crates/hir-ty/src/mir/lower.rs | 4 ++-- .../crates/hir-ty/src/next_solver/interner.rs | 18 +++++++++--------- .../crates/hir-ty/src/next_solver/ty.rs | 2 +- .../rust-analyzer/crates/hir/src/has_source.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 8 ++++---- 10 files changed, 40 insertions(+), 33 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 821ab5fc042ef..65b2d53398d5e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -230,7 +230,10 @@ pub struct InternedOpaqueTyId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct InternedClosure(pub ExpressionStoreOwnerId, pub ExprId); +pub struct InternedClosure { + pub owner: ExpressionStoreOwnerId, + pub expr: ExprId, +} #[salsa_macros::interned(constructor = new_impl, no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] @@ -242,8 +245,8 @@ impl InternedClosureId { #[inline] pub fn new(db: &dyn HirDatabase, loc: InternedClosure) -> Self { if cfg!(debug_assertions) { - let store = ExpressionStore::of(db, loc.0); - let expr = &store[loc.1]; + let store = ExpressionStore::of(db, loc.owner); + let expr = &store[loc.expr]; assert!( matches!( expr, @@ -270,8 +273,8 @@ impl InternedCoroutineId { #[inline] pub fn new(db: &dyn HirDatabase, loc: InternedClosure) -> Self { if cfg!(debug_assertions) { - let store = ExpressionStore::of(db, loc.0); - let expr = &store[loc.1]; + let store = ExpressionStore::of(db, loc.owner); + let expr = &store[loc.expr]; assert!( matches!( expr, @@ -299,8 +302,8 @@ impl InternedCoroutineClosureId { #[inline] pub fn new(db: &dyn HirDatabase, loc: InternedClosure) -> Self { if cfg!(debug_assertions) { - let store = ExpressionStore::of(db, loc.0); - let expr = &store[loc.1]; + let store = ExpressionStore::of(db, loc.owner); + let expr = &store[loc.expr]; assert!( matches!( expr, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 8b00bdf3918e0..0b06c10e1c385 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1726,7 +1726,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } TyKind::Infer(..) => write!(f, "_")?, TyKind::Coroutine(coroutine_id, subst) => { - let InternedClosure(owner, expr_id) = coroutine_id.0.loc(db); + let InternedClosure { owner, expr: expr_id } = coroutine_id.0.loc(db); let CoroutineArgsParts { resume_ty, yield_ty, return_ty, .. } = subst.split_coroutine_args(); let body = ExpressionStore::of(db, owner); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs index 6c86b6720f7c5..237c9177f8790 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs @@ -136,7 +136,7 @@ impl<'db> InferenceContext<'_, 'db> { ); let adjust_steps = autoderef.adjust_steps_as_infer_ok(); let adjustments = autoderef.ctx().table.register_infer_ok(adjust_steps); - let def_id = def_id.0.loc(autoderef.ctx().db).1; + let def_id = def_id.0.loc(autoderef.ctx().db).expr; autoderef.ctx().record_deferred_call_resolution( def_id, DeferredCallResolution { @@ -187,7 +187,7 @@ impl<'db> InferenceContext<'_, 'db> { ); let adjust_steps = autoderef.adjust_steps_as_infer_ok(); let adjustments = autoderef.ctx().table.register_infer_ok(adjust_steps); - let def_id = def_id.0.loc(autoderef.ctx().db).1; + let def_id = def_id.0.loc(autoderef.ctx().db).expr; autoderef.ctx().record_deferred_call_resolution( def_id, DeferredCallResolution { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index f5b974b1dbf5e..86e82eb22250b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -132,8 +132,10 @@ impl<'db> InferenceContext<'_, 'db> { }, ); - let closure_id = - InternedClosureId::new(self.db, InternedClosure(self.owner, closure_expr)); + let closure_id = InternedClosureId::new( + self.db, + InternedClosure { owner: self.owner, expr: closure_expr }, + ); (Ty::new_closure(interner, closure_id.into(), closure_args.args), None) } @@ -183,8 +185,10 @@ impl<'db> InferenceContext<'_, 'db> { }, ); - let coroutine_id = - InternedCoroutineId::new(self.db, InternedClosure(self.owner, closure_expr)); + let coroutine_id = InternedCoroutineId::new( + self.db, + InternedClosure { owner: self.owner, expr: closure_expr }, + ); ( Ty::new_coroutine(interner, coroutine_id.into(), coroutine_args.args), @@ -258,7 +262,7 @@ impl<'db> InferenceContext<'_, 'db> { let coroutine_closure_id = InternedCoroutineClosureId::new( self.db, - InternedClosure(self.owner, closure_expr), + InternedClosure { owner: self.owner, expr: closure_expr }, ); // We need to turn the liberated signature that we got from HIR, which diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 55e02a6933b65..1c355c2065667 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -1601,7 +1601,7 @@ fn coerce<'db>( } fn is_capturing_closure(db: &dyn HirDatabase, closure: InternedClosureId) -> bool { - let InternedClosure(owner, expr) = closure.loc(db); + let InternedClosure { owner, expr } = closure.loc(db); upvars_mentioned(db, owner) .is_some_and(|upvars| upvars.get(&expr).is_some_and(|upvars| !upvars.is_empty())) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 49fb6f530586d..11972e64d0d7b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -1254,7 +1254,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { not_supported!("closure with non closure type"); }; self.result.closures.push(id.0); - let closure_data = &self.infer.closures_data[&id.0.loc(self.db).1]; + let closure_data = &self.infer.closures_data[&id.0.loc(self.db).expr]; let span = |sources: &[CaptureSourceStack]| match sources .first() @@ -2144,7 +2144,7 @@ pub fn mir_body_for_closure_query<'db>( db: &'db dyn HirDatabase, closure: InternedClosureId, ) -> Result<'db, Arc> { - let InternedClosure(owner, expr) = closure.loc(db); + let InternedClosure { owner, expr } = closure.loc(db); let body_owner = owner.as_def_with_body().expect("MIR lowering should only happen for body-owned closures"); let body = Body::of(db, body_owner); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 14e20dfe80530..46ad4488a64cd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1269,13 +1269,13 @@ impl<'db> Interner for DbInterner<'db> { SolverDefId::TypeAliasId(it) => it.lookup(self.db()).container, SolverDefId::ConstId(it) => it.lookup(self.db()).container, SolverDefId::InternedClosureId(it) => { - return it.loc(self.db).0.generic_def(self.db()).into(); + return it.loc(self.db).owner.generic_def(self.db()).into(); } SolverDefId::InternedCoroutineId(it) => { - return it.loc(self.db).0.generic_def(self.db()).into(); + return it.loc(self.db).owner.generic_def(self.db()).into(); } SolverDefId::InternedCoroutineClosureId(it) => { - return it.loc(self.db).0.generic_def(self.db()).into(); + return it.loc(self.db).owner.generic_def(self.db()).into(); } SolverDefId::StaticId(_) | SolverDefId::AdtId(_) @@ -1313,7 +1313,7 @@ impl<'db> Interner for DbInterner<'db> { fn coroutine_movability(self, def_id: Self::CoroutineId) -> rustc_ast_ir::Movability { // FIXME: Make this a query? I don't believe this can be accessed from bodies other than // the current infer query, except with revealed opaques - is it rare enough to not matter? - let InternedClosure(owner, expr_id) = def_id.0.loc(self.db); + let InternedClosure { owner, expr: expr_id } = def_id.0.loc(self.db); let store = ExpressionStore::of(self.db, owner); let expr = &store[expr_id]; match *expr { @@ -1330,9 +1330,9 @@ impl<'db> Interner for DbInterner<'db> { } fn coroutine_for_closure(self, def_id: Self::CoroutineClosureId) -> Self::CoroutineId { - let InternedClosure(owner, coroutine_closure_expr) = def_id.0.loc(self.db); + let InternedClosure { owner, expr: coroutine_closure_expr } = def_id.0.loc(self.db); let coroutine_expr = ExpressionStore::coroutine_for_closure(coroutine_closure_expr); - InternedCoroutineId::new(self.db, InternedClosure(owner, coroutine_expr)).into() + InternedCoroutineId::new(self.db, InternedClosure { owner, expr: coroutine_expr }).into() } fn generics_require_sized_self(self, def_id: Self::DefId) -> bool { @@ -1938,7 +1938,7 @@ impl<'db> Interner for DbInterner<'db> { fn is_general_coroutine(self, def_id: Self::CoroutineId) -> bool { // FIXME: Make this a query? I don't believe this can be accessed from bodies other than // the current infer query, except with revealed opaques - is it rare enough to not matter? - let InternedClosure(owner, expr_id) = def_id.0.loc(self.db); + let InternedClosure { owner, expr: expr_id } = def_id.0.loc(self.db); let store = ExpressionStore::of(self.db, owner); matches!( store[expr_id], @@ -1952,7 +1952,7 @@ impl<'db> Interner for DbInterner<'db> { fn coroutine_is_async(self, def_id: Self::CoroutineId) -> bool { // FIXME: Make this a query? I don't believe this can be accessed from bodies other than // the current infer query, except with revealed opaques - is it rare enough to not matter? - let InternedClosure(owner, expr_id) = def_id.0.loc(self.db); + let InternedClosure { owner, expr: expr_id } = def_id.0.loc(self.db); let store = ExpressionStore::of(self.db, owner); matches!( store[expr_id], @@ -2114,7 +2114,7 @@ impl<'db> Interner for DbInterner<'db> { ) { let coroutine = InternedCoroutineId::new( self.db, - InternedClosure(ExpressionStoreOwnerId::Body(def_id), expr_id), + InternedClosure { owner: ExpressionStoreOwnerId::Body(def_id), expr: expr_id }, ); result.push(coroutine.into()); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 3bd20e9064523..e8945a77c3865 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -757,7 +757,7 @@ impl<'db> Ty<'db> { } } TyKind::Coroutine(coroutine_id, _args) => { - let InternedClosure(owner, _) = coroutine_id.0.loc(db); + let InternedClosure { owner, expr: _ } = coroutine_id.0.loc(db); let krate = owner.krate(db); if let Some(future_trait) = hir_def::lang_item::lang_items(db, krate).Future { // This is only used by type walking. 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 45c9811cc0158..36e6bdf3fb7d3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs @@ -293,7 +293,7 @@ impl HasSource for Param<'_> { .map(|value| InFile { file_id, value }) } Callee::Closure(closure, _) => { - let InternedClosure(owner, expr_id) = closure.loc(db); + let InternedClosure { owner, expr: expr_id } = closure.loc(db); let (_, source_map) = ExpressionStore::with_source_map(db, owner); let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?; let root = db.parse_or_expand(file_id); diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 8cc82113b3934..150f90fa977ec 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2975,10 +2975,10 @@ impl<'db> Param<'db> { } Callee::Closure(closure, _) => { let c = closure.loc(db); - let body_owner = c.0; - let store = ExpressionStore::of(db, c.0); + let body_owner = c.owner; + let store = ExpressionStore::of(db, c.owner); - if let Expr::Closure { args, .. } = &store[c.1] + if let Expr::Closure { args, .. } = &store[c.expr] && let Pat::Bind { id, .. } = &store[args[self.idx]] { return Some(Local { parent: body_owner, binding_id: *id }); @@ -5165,7 +5165,7 @@ impl<'db> Closure<'db> { AnyClosureId::ClosureId(it) => it.loc(db), AnyClosureId::CoroutineClosureId(it) => it.loc(db), }; - let InternedClosure(owner, closure) = closure; + let InternedClosure { owner, expr: closure } = closure; let infer = InferenceResult::of(db, owner); let param_env = body_param_env_from_has_crate(db, owner); infer.closures_data[&closure] From f07f57c903ba634ab20f91aac6fb7e1ee7949f77 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 27 Apr 2026 09:40:04 +0300 Subject: [PATCH 106/289] Add `ClosureKind` to `InternedClosure` It doesn't add much weight, and it's useful for retrieval. --- .../rust-analyzer/crates/hir-def/src/hir.rs | 6 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 11 +- .../crates/hir-ty/src/display.rs | 44 ++----- .../crates/hir-ty/src/infer/closure.rs | 17 +-- .../crates/hir-ty/src/infer/coerce.rs | 2 +- .../crates/hir-ty/src/mir/lower.rs | 2 +- .../crates/hir-ty/src/next_solver/interner.rs | 111 +++++++----------- .../crates/hir-ty/src/next_solver/ty.rs | 4 +- .../crates/hir/src/has_source.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- 10 files changed, 76 insertions(+), 125 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 6bea505757fa8..125a393bd929b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -524,14 +524,14 @@ pub enum InlineAsmRegOrRegClass { RegClass(Symbol), } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum CoroutineKind { Async, Gen, AsyncGen, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ClosureKind { Closure, OldCoroutine(Movability), @@ -564,7 +564,7 @@ pub enum CaptureBy { Ref, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Movability { Static, Movable, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 65b2d53398d5e..2e130c1028ff0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -6,8 +6,12 @@ use either::Either; use hir_def::{ AdtId, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, - StaticId, TraitId, TypeAliasId, VariantId, builtin_derive::BuiltinDeriveImplMethod, - db::DefDatabase, expr_store::ExpressionStore, hir::ExprId, layout::TargetDataLayout, + StaticId, TraitId, TypeAliasId, VariantId, + builtin_derive::BuiltinDeriveImplMethod, + db::DefDatabase, + expr_store::ExpressionStore, + hir::{ClosureKind, ExprId}, + layout::TargetDataLayout, }; use la_arena::ArenaMap; use salsa::plumbing::AsId; @@ -229,10 +233,11 @@ pub struct InternedOpaqueTyId { pub loc: ImplTraitId, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct InternedClosure { pub owner: ExpressionStoreOwnerId, pub expr: ExprId, + pub kind: ClosureKind, } #[salsa_macros::interned(constructor = new_impl, no_lifetime, debug, revisions = usize::MAX)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 0b06c10e1c385..ca3723f8ef29d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -51,7 +51,7 @@ use stdx::never; use crate::{ CallableDefId, FnAbi, ImplTraitId, MemoryMap, ParamEnvAndCrate, consteval, - db::{HirDatabase, InternedClosure}, + db::HirDatabase, generics::generics, layout::Layout, lower::GenericPredicates, @@ -1552,16 +1552,9 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } TyKind::CoroutineClosure(id, args) => { let id = id.0; - let closure_kind = match id.loc(db) { - InternedClosure(owner, expr_id) => { - match &ExpressionStore::of(db, owner)[expr_id] { - hir_def::hir::Expr::Closure { - closure_kind: HirClosureKind::CoroutineClosure(kind), - .. - } => *kind, - expr => panic!("invalid expr for coroutine closure: {expr:?}"), - } - } + let closure_kind = match id.loc(db).kind { + HirClosureKind::CoroutineClosure(kind) => kind, + kind => panic!("invalid kind for coroutine closure: {kind:?}"), }; let closure_label = match closure_kind { CoroutineKind::Async => "async closure", @@ -1726,16 +1719,11 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } TyKind::Infer(..) => write!(f, "_")?, TyKind::Coroutine(coroutine_id, subst) => { - let InternedClosure { owner, expr: expr_id } = coroutine_id.0.loc(db); + let kind = coroutine_id.0.loc(db).kind; let CoroutineArgsParts { resume_ty, yield_ty, return_ty, .. } = subst.split_coroutine_args(); - let body = ExpressionStore::of(db, owner); - let expr = &body[expr_id]; - match expr { - hir_def::hir::Expr::Closure { - closure_kind: HirClosureKind::Coroutine { kind: CoroutineKind::Async, .. }, - .. - } => { + match kind { + HirClosureKind::Coroutine { kind: CoroutineKind::Async, .. } => { let future_trait = f.lang_items().Future; let output = future_trait.and_then(|t| { t.trait_items(db) @@ -1761,10 +1749,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { return_ty.hir_fmt(f)?; write!(f, ">")?; } - hir_def::hir::Expr::Closure { - closure_kind: HirClosureKind::Coroutine { kind: CoroutineKind::Gen, .. }, - .. - } => { + HirClosureKind::Coroutine { kind: CoroutineKind::Gen, .. } => { let iterator_trait = f.lang_items().Iterator; let item = iterator_trait.and_then(|t| { t.trait_items(db) @@ -1790,11 +1775,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { yield_ty.hir_fmt(f)?; write!(f, ">")?; } - hir_def::hir::Expr::Closure { - closure_kind: - HirClosureKind::Coroutine { kind: CoroutineKind::AsyncGen, .. }, - .. - } => { + HirClosureKind::Coroutine { kind: CoroutineKind::AsyncGen, .. } => { let async_iterator_trait = f.lang_items().AsyncIterator; let item = async_iterator_trait.and_then(|t| { t.trait_items(db) @@ -1822,10 +1803,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { item_ty.hir_fmt(f)?; write!(f, ">")?; } - hir_def::hir::Expr::Closure { - closure_kind: HirClosureKind::OldCoroutine(..), - .. - } => { + HirClosureKind::OldCoroutine(..) => { if f.display_kind.is_source_code() { return Err(HirDisplayError::DisplaySourceCodeError( DisplaySourceCodeError::Coroutine, @@ -1841,7 +1819,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { write!(f, " -> ")?; return_ty.hir_fmt(f)?; } - _ => panic!("invalid expr for coroutine: {expr:?}"), + _ => panic!("invalid kind for coroutine: {kind:?}"), } } TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 86e82eb22250b..f90f135919460 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -95,6 +95,8 @@ impl<'db> InferenceContext<'_, 'db> { let tupled_upvars_ty = self.table.next_ty_var(closure_expr.into()); + let closure_loc = + InternedClosure { owner: self.owner, expr: closure_expr, kind: closure_kind }; // FIXME: We could probably actually just unify this further -- // instead of having a `FnSig` and a `Option`, // we can have a `ClosureSignature { Coroutine { .. }, Closure { .. } }`, @@ -132,10 +134,7 @@ impl<'db> InferenceContext<'_, 'db> { }, ); - let closure_id = InternedClosureId::new( - self.db, - InternedClosure { owner: self.owner, expr: closure_expr }, - ); + let closure_id = InternedClosureId::new(self.db, closure_loc); (Ty::new_closure(interner, closure_id.into(), closure_args.args), None) } @@ -185,10 +184,7 @@ impl<'db> InferenceContext<'_, 'db> { }, ); - let coroutine_id = InternedCoroutineId::new( - self.db, - InternedClosure { owner: self.owner, expr: closure_expr }, - ); + let coroutine_id = InternedCoroutineId::new(self.db, closure_loc); ( Ty::new_coroutine(interner, coroutine_id.into(), coroutine_args.args), @@ -260,10 +256,7 @@ impl<'db> InferenceContext<'_, 'db> { let coroutine_upvars_ty = self.table.next_ty_var(closure_expr.into()); - let coroutine_closure_id = InternedCoroutineClosureId::new( - self.db, - InternedClosure { owner: self.owner, expr: closure_expr }, - ); + let coroutine_closure_id = InternedCoroutineClosureId::new(self.db, closure_loc); // We need to turn the liberated signature that we got from HIR, which // looks something like `|Args...| -> T`, into a signature that is suitable diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 1c355c2065667..ddff23e840430 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -1601,7 +1601,7 @@ fn coerce<'db>( } fn is_capturing_closure(db: &dyn HirDatabase, closure: InternedClosureId) -> bool { - let InternedClosure { owner, expr } = closure.loc(db); + let InternedClosure { owner, expr, .. } = closure.loc(db); upvars_mentioned(db, owner) .is_some_and(|upvars| upvars.get(&expr).is_some_and(|upvars| !upvars.is_empty())) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 11972e64d0d7b..7dcc00858e25d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -2144,7 +2144,7 @@ pub fn mir_body_for_closure_query<'db>( db: &'db dyn HirDatabase, closure: InternedClosureId, ) -> Result<'db, Arc> { - let InternedClosure { owner, expr } = closure.loc(db); + let InternedClosure { owner, expr, .. } = closure.loc(db); let body_owner = owner.as_def_with_body().expect("MIR lowering should only happen for body-owned closures"); let body = Body::of(db, body_owner); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 46ad4488a64cd..b8b8d15347dfb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -14,6 +14,7 @@ use hir_def::{ ItemContainerId, StructId, UnionId, VariantId, attrs::AttrFlags, expr_store::{Body, ExpressionStore}, + hir::{ClosureKind as HirClosureKind, CoroutineKind as HirCoroutineKind}, lang_item::LangItems, signatures::{ EnumSignature, FieldData, FnFlags, FunctionSignature, ImplFlags, ImplSignature, @@ -1311,28 +1312,35 @@ impl<'db> Interner for DbInterner<'db> { } fn coroutine_movability(self, def_id: Self::CoroutineId) -> rustc_ast_ir::Movability { - // FIXME: Make this a query? I don't believe this can be accessed from bodies other than - // the current infer query, except with revealed opaques - is it rare enough to not matter? - let InternedClosure { owner, expr: expr_id } = def_id.0.loc(self.db); - let store = ExpressionStore::of(self.db, owner); - let expr = &store[expr_id]; - match *expr { - hir_def::hir::Expr::Closure { closure_kind, .. } => match closure_kind { - hir_def::hir::ClosureKind::OldCoroutine(movability) => match movability { - hir_def::hir::Movability::Static => rustc_ast_ir::Movability::Static, - hir_def::hir::Movability::Movable => rustc_ast_ir::Movability::Movable, - }, - hir_def::hir::ClosureKind::Coroutine { .. } => rustc_ast_ir::Movability::Static, - _ => panic!("unexpected expression for a coroutine: {expr:?}"), + match def_id.0.loc(self.db).kind { + hir_def::hir::ClosureKind::OldCoroutine(movability) => match movability { + hir_def::hir::Movability::Static => rustc_ast_ir::Movability::Static, + hir_def::hir::Movability::Movable => rustc_ast_ir::Movability::Movable, }, - _ => panic!("unexpected expression for a coroutine: {expr:?}"), + hir_def::hir::ClosureKind::Coroutine { .. } => rustc_ast_ir::Movability::Static, + kind => panic!("unexpected kind for a coroutine: {kind:?}"), } } fn coroutine_for_closure(self, def_id: Self::CoroutineClosureId) -> Self::CoroutineId { - let InternedClosure { owner, expr: coroutine_closure_expr } = def_id.0.loc(self.db); + let InternedClosure { owner, expr: coroutine_closure_expr, kind: coroutine_closure_kind } = + def_id.0.loc(self.db); + let coroutine_closure_kind = match coroutine_closure_kind { + HirClosureKind::CoroutineClosure(it) => it, + _ => { + panic!("invalid kind closure kind {coroutine_closure_kind:?} for coroutine closure") + } + }; let coroutine_expr = ExpressionStore::coroutine_for_closure(coroutine_closure_expr); - InternedCoroutineId::new(self.db, InternedClosure { owner, expr: coroutine_expr }).into() + let coroutine_kind = hir_def::hir::ClosureKind::Coroutine { + kind: coroutine_closure_kind, + source: hir_def::hir::CoroutineSource::Closure, + }; + InternedCoroutineId::new( + self.db, + InternedClosure { owner, expr: coroutine_expr, kind: coroutine_kind }, + ) + .into() } fn generics_require_sized_self(self, def_id: Self::DefId) -> bool { @@ -1936,63 +1944,27 @@ impl<'db> Interner for DbInterner<'db> { } fn is_general_coroutine(self, def_id: Self::CoroutineId) -> bool { - // FIXME: Make this a query? I don't believe this can be accessed from bodies other than - // the current infer query, except with revealed opaques - is it rare enough to not matter? - let InternedClosure { owner, expr: expr_id } = def_id.0.loc(self.db); - let store = ExpressionStore::of(self.db, owner); - matches!( - store[expr_id], - hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::OldCoroutine(_), - .. - } - ) + matches!(def_id.0.loc(self.db).kind, HirClosureKind::OldCoroutine(_)) } fn coroutine_is_async(self, def_id: Self::CoroutineId) -> bool { - // FIXME: Make this a query? I don't believe this can be accessed from bodies other than - // the current infer query, except with revealed opaques - is it rare enough to not matter? - let InternedClosure { owner, expr: expr_id } = def_id.0.loc(self.db); - let store = ExpressionStore::of(self.db, owner); matches!( - store[expr_id], - hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Coroutine { - kind: hir_def::hir::CoroutineKind::Async, - .. - }, - .. - } + def_id.0.loc(self.db).kind, + HirClosureKind::Coroutine { kind: HirCoroutineKind::Async, .. } ) } fn coroutine_is_gen(self, def_id: Self::CoroutineId) -> bool { - let InternedClosure(owner, expr_id) = def_id.0.loc(self.db); - let store = ExpressionStore::of(self.db, owner); matches!( - store[expr_id], - hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Coroutine { - kind: hir_def::hir::CoroutineKind::Gen, - .. - }, - .. - } + def_id.0.loc(self.db).kind, + HirClosureKind::Coroutine { kind: HirCoroutineKind::Gen, .. } ) } fn coroutine_is_async_gen(self, def_id: Self::CoroutineId) -> bool { - let InternedClosure(owner, expr_id) = def_id.0.loc(self.db); - let store = ExpressionStore::of(self.db, owner); matches!( - store[expr_id], - hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Coroutine { - kind: hir_def::hir::CoroutineKind::AsyncGen, - .. - }, - .. - } + def_id.0.loc(self.db).kind, + HirClosureKind::Coroutine { kind: HirCoroutineKind::AsyncGen, .. } ) } @@ -2104,17 +2076,20 @@ impl<'db> Interner for DbInterner<'db> { // Collect coroutines. let body = Body::of(self.db, def_id); body.exprs().for_each(|(expr_id, expr)| { - if matches!( - expr, - hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Coroutine { .. } - | hir_def::hir::ClosureKind::OldCoroutine(_), - .. - } - ) { + if let hir_def::hir::Expr::Closure { + closure_kind: + kind @ (hir_def::hir::ClosureKind::Coroutine { .. } + | hir_def::hir::ClosureKind::OldCoroutine(_)), + .. + } = *expr + { let coroutine = InternedCoroutineId::new( self.db, - InternedClosure { owner: ExpressionStoreOwnerId::Body(def_id), expr: expr_id }, + InternedClosure { + owner: ExpressionStoreOwnerId::Body(def_id), + expr: expr_id, + kind, + }, ); result.push(coroutine.into()); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index e8945a77c3865..22595214b4226 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -27,7 +27,7 @@ use rustc_type_ir::{ use crate::{ FnAbi, - db::{HirDatabase, InternedClosure}, + db::HirDatabase, lower::GenericPredicates, next_solver::{ AdtDef, AliasTy, Binder, CallableIdWrapper, Clause, ClauseKind, ClosureIdWrapper, Const, @@ -757,7 +757,7 @@ impl<'db> Ty<'db> { } } TyKind::Coroutine(coroutine_id, _args) => { - let InternedClosure { owner, expr: _ } = coroutine_id.0.loc(db); + let owner = coroutine_id.0.loc(db).owner; let krate = owner.krate(db); if let Some(future_trait) = hir_def::lang_item::lang_items(db, krate).Future { // This is only used by type walking. 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 36e6bdf3fb7d3..3e7745c090279 100644 --- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs @@ -293,7 +293,7 @@ impl HasSource for Param<'_> { .map(|value| InFile { file_id, value }) } Callee::Closure(closure, _) => { - let InternedClosure { owner, expr: expr_id } = closure.loc(db); + let InternedClosure { owner, expr: expr_id, .. } = closure.loc(db); let (_, source_map) = ExpressionStore::with_source_map(db, owner); let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?; let root = db.parse_or_expand(file_id); diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 150f90fa977ec..df9f743224a4e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -5165,7 +5165,7 @@ impl<'db> Closure<'db> { AnyClosureId::ClosureId(it) => it.loc(db), AnyClosureId::CoroutineClosureId(it) => it.loc(db), }; - let InternedClosure { owner, expr: closure } = closure; + let InternedClosure { owner, expr: closure, .. } = closure; let infer = InferenceResult::of(db, owner); let param_env = body_param_env_from_has_crate(db, owner); infer.closures_data[&closure] From 8b34cd63bf4fd4a912235a2d1443ac872a0e03b5 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 27 Apr 2026 14:57:32 +0300 Subject: [PATCH 107/289] Make `InferenceResult::binding_mode()` fallible The pattern can be missing a binding mode when there was a cycle, and the `InferenceResult` is empty. --- src/tools/rust-analyzer/crates/hir-ty/src/infer.rs | 4 ++-- .../hir-ty/src/infer/closure/analysis/expr_use_visitor.rs | 4 ++-- src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 4aeb5ec71cab2..3a0f9f8048e4a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1110,8 +1110,8 @@ impl InferenceResult { self.expr_adjustments.get(&id).map(|it| &**it) } - pub fn binding_mode(&self, id: PatId) -> BindingMode { - self.binding_modes[id] + pub fn binding_mode(&self, id: PatId) -> Option { + self.binding_modes.get(id).copied() } // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index 0fd3cda31dc13..16b84217b8608 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -949,7 +949,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { // In a cases of pattern like `let pat = upvar`, don't use the span // of the pattern, as this just looks confusing, instead use the span // of the discriminant. - match this.cx.result.binding_mode(pat).0 { + match this.cx.result.binding_mode(pat).ok_or(ErrorGuaranteed)?.0 { ByRef::Yes(m) => { let bk = BorrowKind::from_mutbl(m); this.delegate.borrow(place, bk, this.cx); @@ -1220,7 +1220,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { // and if so, figures out what the type *being borrowed* is. match self.cx.store[pat] { Pat::Bind { .. } => { - let bm = self.cx.result.binding_mode(pat); + let bm = self.cx.result.binding_mode(pat).ok_or(ErrorGuaranteed)?; if let ByRef::Yes(_) = bm.0 { // a bind-by-ref means that the base_ty will be the type of the ident itself, 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 d2658e06dc834..e5d674cb75543 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -449,7 +449,7 @@ impl<'db> SourceAnalyzer<'db> { ) -> Option { let id = self.pat_id(&pat.clone().into())?; let infer = self.infer()?; - Some(match infer.binding_mode(id.as_pat()?) { + Some(match infer.binding_mode(id.as_pat()?)? { hir_ty::BindingMode(hir_ty::ByRef::No, _) => BindingMode::Move, hir_ty::BindingMode(hir_ty::ByRef::Yes(hir_ty::next_solver::Mutability::Mut), _) => { BindingMode::Ref(Mutability::Mut) From f1e321664d0623922ed8a9a5d5b05ab6fdd1e1dd Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 27 Apr 2026 22:54:42 +0200 Subject: [PATCH 108/289] debugprint `MiniCore()` instead of `MiniCore("")` `""` could make it seem that that was the _literal_ contents of `MiniCore` --- src/tools/rust-analyzer/crates/ide-db/src/lib.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 190a754ab53a8..6180e3186cab2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -402,10 +402,16 @@ impl<'a> MiniCore<'a> { impl std::fmt::Debug for MiniCore<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("MiniCore") - // don't print the whole contents if they correspond to the default - .field(if self.0 == test_utils::MiniCore::RAW_SOURCE { &"" } else { &self.0 }) - .finish() + let mut d = f.debug_tuple("MiniCore"); + if self.0 == test_utils::MiniCore::RAW_SOURCE { + // Don't print the whole contents if they correspond to the default. + // The `format_args!` makes it so that the output is + // `MiniCore()` and not `MiniCore(""). + d.field(&format_args!("")); + } else { + d.field(&self.0); + }; + d.finish() } } From 78887626f20bda1ef095ba7d13beacd246aa7bc5 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 23 Apr 2026 23:03:07 +0200 Subject: [PATCH 109/289] misc improvements --- src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs | 4 ++-- src/tools/rust-analyzer/crates/hir/src/diagnostics.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 2b19c445ed7a5..0441d2ec32478 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -2007,7 +2007,7 @@ impl<'db> InferenceContext<'_, 'db> { }, None => None, }; - (arg_types.iter().collect(), expected_input_tys) + (arg_types.to_vec(), expected_input_tys) } _ => { // Otherwise, there's a mismatch, so clear out what we're expecting, and set @@ -2017,7 +2017,7 @@ impl<'db> InferenceContext<'_, 'db> { } } } else { - (formal_input_tys.to_vec(), expected_input_tys) + (formal_input_tys, expected_input_tys) }; // If there are no external expectations at the call site, just use the types from the function defn diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 18f28541af66f..32f0ac3dad128 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -62,6 +62,8 @@ diagnostics![AnyDiagnostic<'db> -> InactiveCode, IncoherentImpl, IncorrectCase, + IncorrectGenericsLen, + IncorrectGenericsOrder, InvalidCast<'db>, InvalidDeriveTarget, InvalidLhsOfAssignment, @@ -105,8 +107,6 @@ diagnostics![AnyDiagnostic<'db> -> GenericArgsProhibited, ParenthesizedGenericArgsWithoutFnTrait, BadRtn, - IncorrectGenericsLen, - IncorrectGenericsOrder, MissingLifetime, ElidedLifetimesInPath, TypeMustBeKnown<'db>, From ff09f9925c1370b44a8ed89217402b8950a10693 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 28 Apr 2026 12:59:40 +0800 Subject: [PATCH 110/289] Join whitespaces for make::block_expr --- src/tools/rust-analyzer/crates/syntax/src/ast/make.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 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 ac02cc9e43fdf..e8f5c9537df7a 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -541,9 +541,10 @@ pub fn block_expr( quote! { BlockExpr { StmtList { - ['{'] "\n" - #(" " #stmts "\n")* - #(" " #tail_expr "\n")* + ['{'] + #("\n " #stmts)* + #("\n " #tail_expr)* + "\n" ['}'] } } From 801b7e34aa32313dcc3253ba1bcd28f46a268baa Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 28 Apr 2026 12:29:34 +0800 Subject: [PATCH 111/289] feat: offer on if-expr with else-if for convert_to_guarded_return Example --- ```rust fn main() { for n in ns { if$0 let Some(n) = n { foo(n); bar(); } else if cond() { return } else { break } } } ``` **Before this PR** Assist not applicable **After this PR** ```rust fn main() { for n in ns { let Some(n) = n else { if cond() { return } else { break } }; foo(n); bar(); } } ``` --- .../src/handlers/convert_to_guarded_return.rs | 200 ++++++++++++++++-- 1 file changed, 183 insertions(+), 17 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index 791a6a26af381..44ca57281eca4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -72,12 +72,6 @@ fn if_expr_to_guarded_return( ctx: &AssistContext<'_>, ) -> Option<()> { let make = SyntaxFactory::without_mappings(); - let else_block = match if_expr.else_branch() { - Some(ast::ElseBranch::Block(block_expr)) => Some(block_expr), - Some(_) => return None, - _ => None, - }; - let cond = if_expr.condition()?; let if_token_range = if_expr.if_token()?.text_range(); @@ -102,7 +96,7 @@ fn if_expr_to_guarded_return( } let container = container_of(&parent_block)?; - let else_block = ElseBlock::new(&ctx.sema, else_block, &container)?; + let else_block = ElseBlock::new(&ctx.sema, if_expr.else_branch(), &container)?; if parent_block.tail_expr() != Some(if_expr.clone().into()) && !(else_block.is_never_block @@ -237,7 +231,7 @@ fn container_of(block: &ast::BlockExpr) -> Option { } struct ElseBlock<'db> { - exist_else_block: Option, + exist_else_branch: Option, is_never_block: bool, kind: EarlyKind<'db>, } @@ -245,13 +239,23 @@ struct ElseBlock<'db> { impl<'db> ElseBlock<'db> { fn new( sema: &Semantics<'db, RootDatabase>, - exist_else_block: Option, + exist_else_branch: Option, parent_container: &SyntaxNode, ) -> Option { - let is_never_block = exist_else_block.as_ref().is_some_and(|it| is_never_block(sema, it)); + let is_never_block = + exist_else_branch.as_ref().is_some_and(|it| is_never_else_branch(sema, it)); let kind = EarlyKind::from_node(parent_container, sema)?; - Some(Self { exist_else_block, is_never_block, kind }) + Some(Self { exist_else_branch, is_never_block, kind }) + } + + fn make_else_block_from_exist_branch(&self, make: &SyntaxFactory) -> Option { + match self.exist_else_branch.as_ref()? { + ast::ElseBranch::Block(block_expr) => Some(block_expr.reset_indent()), + ast::ElseBranch::IfExpr(if_expr) => { + Some(make.block_expr(None, Some(if_expr.reset_indent().indent(1.into()).into()))) + } + } } fn make_early_block( @@ -259,15 +263,15 @@ impl<'db> ElseBlock<'db> { sema: &Semantics<'_, RootDatabase>, make: &SyntaxFactory, ) -> ast::BlockExpr { - let Some(block_expr) = self.exist_else_block else { + let Some(block_expr) = self.make_else_block_from_exist_branch(make) else { return make.tail_only_block_expr(self.kind.make_early_expr(sema, make, None)); }; if self.is_never_block { - return block_expr.reset_indent(); + return block_expr; } - let (editor, block_expr) = SyntaxEditor::with_ast_node(&block_expr.reset_indent()); + let (editor, block_expr) = SyntaxEditor::with_ast_node(&block_expr); let make = editor.make(); let last_stmt = block_expr.statements().last().map(|it| it.syntax().clone()); @@ -284,8 +288,8 @@ impl<'db> ElseBlock<'db> { editor.replace(tail_expr.syntax(), early_expr.syntax()); } else { let last_stmt = match block_expr.tail_expr() { - Some(expr) => make.expr_stmt(expr).syntax().clone(), - None => last_element.clone(), + Some(expr) if !expr.is_block_like() => make.expr_stmt(expr).syntax().clone(), + _ => last_element.clone(), }; let whitespace = make.whitespace(&whitespace.map_or(String::new(), |it| it.to_string())); @@ -401,6 +405,27 @@ fn is_early_block(then_block: &ast::StmtList) -> bool { || then_block.statements().filter_map(into_expr).any(is_early_expr) } +fn is_never_else_branch(sema: &Semantics<'_, RootDatabase>, it: &ast::ElseBranch) -> bool { + match it { + ast::ElseBranch::Block(block_expr) => is_never_block(sema, block_expr), + ast::ElseBranch::IfExpr(if_expr) => { + let mut if_exprs = + std::iter::successors(Some(if_expr.clone()), |it| match it.else_branch()? { + ast::ElseBranch::Block(_) => None, + ast::ElseBranch::IfExpr(if_expr) => Some(if_expr), + }); + if_exprs.all(|it| { + let else_is_never = match it.else_branch() { + None => false, + Some(ast::ElseBranch::IfExpr(_)) => true, + Some(ast::ElseBranch::Block(block)) => is_never_block(sema, &block), + }; + else_is_never && it.then_branch().is_some_and(|it| is_never_block(sema, &it)) + }) + } + } +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -1024,6 +1049,42 @@ fn main() { ); } + #[test] + fn convert_inside_loop_with_else_if() { + check_assist( + convert_to_guarded_return, + r#" +fn main() { + loop { + if$0 guard() { + foo(); + bar(); + } else if cond() { + break; + } else { + return + } + } +} +"#, + r#" +fn main() { + loop { + if !guard() { + if cond() { + break; + } else { + return + } + } + foo(); + bar(); + } +} +"#, + ); + } + #[test] fn convert_let_inside_loop() { check_assist( @@ -1055,6 +1116,7 @@ fn main() { check_assist( convert_to_guarded_return, r#" +//- minicore: iterator fn main() { for n in ns { if$0 let Some(n) = n { @@ -1107,6 +1169,7 @@ fn main() { check_assist( convert_to_guarded_return, r#" +//- minicore: iterator fn main() { for n in ns { if$0 let Some(n) = n { @@ -1133,6 +1196,109 @@ fn main() { ); } + #[test] + fn convert_let_inside_for_with_else_if() { + check_assist( + convert_to_guarded_return, + r#" +//- minicore: iterator +fn main() { + for n in ns { + if$0 let Some(n) = n { + foo(n); + bar(); + } else if cond() { + return + } else { + baz() + } + } +} +"#, + r#" +fn main() { + for n in ns { + let Some(n) = n else { + if cond() { + return + } else { + baz() + } + continue + }; + foo(n); + bar(); + } +} +"#, + ); + + check_assist( + convert_to_guarded_return, + r#" +//- minicore: iterator +fn main() { + for n in ns { + if$0 let Some(n) = n { + foo(n); + bar(); + } else if cond() { + return + } + } +} +"#, + r#" +fn main() { + for n in ns { + let Some(n) = n else { + if cond() { + return + } + continue + }; + foo(n); + bar(); + } +} +"#, + ); + + check_assist( + convert_to_guarded_return, + r#" +//- minicore: iterator +fn main() { + for n in ns { + if$0 let Some(n) = n { + foo(n); + bar(); + } else if cond() { + return + } else { + break + } + } +} +"#, + r#" +fn main() { + for n in ns { + let Some(n) = n else { + if cond() { + return + } else { + break + } + }; + foo(n); + bar(); + } +} +"#, + ); + } + #[test] fn convert_let_stmt_inside_fn() { check_assist( @@ -1415,7 +1581,7 @@ fn main() { } #[test] - fn ignore_else_if() { + fn ignore_on_else_if() { check_assist_not_applicable( convert_to_guarded_return, r#" From 13914de2bef6cd307978829baa934a232bd3506b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 28 Apr 2026 10:55:19 +0530 Subject: [PATCH 112/289] migrate generate function to SyntaxEditor --- .../src/handlers/generate_function.rs | 438 +++++++++++------- 1 file changed, 281 insertions(+), 157 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 6ef492619b50c..10717f07fe48e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -16,9 +16,11 @@ use syntax::{ Edition, SyntaxKind, SyntaxNode, T, TextRange, ast::{ self, AstNode, BlockExpr, CallExpr, HasArgList, HasGenericParams, HasModuleItem, - HasTypeBounds, edit::IndentLevel, edit_in_place::Indent, make, + HasTypeBounds, + edit::{AstNodeEdit, IndentLevel}, + syntax_factory::SyntaxFactory, }, - ted, + syntax_editor::{Position, SyntaxEditor}, }; use crate::{ @@ -74,9 +76,10 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { { return None; } + let make = SyntaxFactory::without_mappings(); let function_builder = - FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target, &adt_info)?; + FunctionBuilder::from_call(&make, ctx, &call, fn_name, target_module, target, &adt_info)?; let text_range = call.syntax().text_range(); let label = format!("Generate {} function", function_builder.fn_name); add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_info, label) @@ -158,7 +161,10 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { }; let target = get_method_target(ctx, &impl_, &adt)?; + let make = SyntaxFactory::without_mappings(); + let function_builder = FunctionBuilder::from_method_call( + &make, ctx, &call, &fn_name, @@ -182,28 +188,15 @@ fn add_func_to_accumulator( label: String, ) -> Option<()> { acc.add(AssistId::generate("generate_function"), label, text_range, |edit| { - edit.edit_file(file); - let target = function_builder.target.clone(); - let edition = function_builder.target_edition; - let func = function_builder.render(ctx.config.snippet_cap, edit); + let snippet_cap = ctx.config.snippet_cap; if let Some(adt) = adt_info .and_then(|adt_info| if adt_info.impl_exists { None } else { Some(adt_info.adt) }) { - let name = make::ty_path(make::ext::ident_path(&format!( - "{}", - adt.name(ctx.db()).display(ctx.db(), edition) - ))); - - // FIXME: adt may have generic params. - let impl_ = make::impl_(None, None, None, name, None, None).clone_for_update(); - - func.indent(IndentLevel(1)); - impl_.get_or_create_assoc_item_list().add_item(func.into()); - target.insert_impl_at(edit, impl_); + target.insert_impl_at(edit, file, ctx, &function_builder, adt, snippet_cap); } else { - target.insert_fn_at(edit, func); + target.insert_fn_at(edit, file, &function_builder, snippet_cap); } }) } @@ -240,6 +233,7 @@ impl FunctionBuilder { /// Prepares a generated function that matches `call`. /// The function is generated in `target_module` or next to `call` fn from_call( + make: &SyntaxFactory, ctx: &AssistContext<'_>, call: &ast::CallExpr, fn_name: &str, @@ -253,9 +247,10 @@ impl FunctionBuilder { let current_module = ctx.sema.scope(call.syntax())?.module(); let visibility = calculate_necessary_visibility(current_module, target_module, ctx); - let fn_name = make::name(fn_name); + let fn_name = make.name(fn_name); let mut necessary_generic_params = FxHashSet::default(); let params = fn_args( + make, ctx, target_module, ast::CallableExpr::Call(call.clone()), @@ -272,25 +267,26 @@ impl FunctionBuilder { // If generated function has the name "new" and is an associated function, we generate fn body // as a constructor and assume a "Self" return type. if let Some(body) = - make_fn_body_as_new_function(ctx, &fn_name.text(), adt_info, target_edition) + make_fn_body_as_new_function(make, ctx, &fn_name.text(), adt_info, target_edition) { - ret_type = Some(make::ret_type(make::ty_path(make::ext::ident_path("Self")))); + ret_type = Some(make.ret_type(make.ty_path(make.ident_path("Self")).into())); should_focus_return_type = false; fn_body = body; } else { let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into()); (ret_type, should_focus_return_type) = make_return_type( + make, ctx, &expr_for_ret_ty, target_module, &mut necessary_generic_params, ); let placeholder_expr = expr_fill_default(ctx.config); - fn_body = make::block_expr(vec![], Some(placeholder_expr)); + fn_body = make.block_expr(vec![], Some(placeholder_expr)); }; let (generic_param_list, where_clause) = - fn_generic_params(ctx, necessary_generic_params, &target)?; + fn_generic_params(make, ctx, necessary_generic_params, &target)?; Some(Self { target, @@ -308,6 +304,7 @@ impl FunctionBuilder { } fn from_method_call( + make: &SyntaxFactory, ctx: &AssistContext<'_>, call: &ast::MethodCallExpr, name: &ast::NameRef, @@ -320,10 +317,11 @@ impl FunctionBuilder { let current_module = ctx.sema.scope(call.syntax())?.module(); let visibility = calculate_necessary_visibility(current_module, target_module, ctx); - let fn_name = make::name(name.ident_token()?.text()); + let fn_name = make.name(name.ident_token()?.text()); let mut necessary_generic_params = FxHashSet::default(); necessary_generic_params.extend(receiver_ty.generic_params(ctx.db())); let params = fn_args( + make, ctx, target_module, ast::CallableExpr::MethodCall(call.clone()), @@ -334,14 +332,19 @@ impl FunctionBuilder { let is_async = await_expr.is_some(); let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into()); - let (ret_type, should_focus_return_type) = - make_return_type(ctx, &expr_for_ret_ty, target_module, &mut necessary_generic_params); + let (ret_type, should_focus_return_type) = make_return_type( + make, + ctx, + &expr_for_ret_ty, + target_module, + &mut necessary_generic_params, + ); let (generic_param_list, where_clause) = - fn_generic_params(ctx, necessary_generic_params, &target)?; + fn_generic_params(make, ctx, necessary_generic_params, &target)?; let placeholder_expr = expr_fill_default(ctx.config); - let fn_body = make::block_expr(vec![], Some(placeholder_expr)); + let fn_body = make.block_expr(vec![], Some(placeholder_expr)); Some(Self { target, @@ -358,55 +361,28 @@ impl FunctionBuilder { }) } - fn render(self, cap: Option, edit: &mut SourceChangeBuilder) -> ast::Fn { + fn render(&self, make: &SyntaxFactory) -> ast::Fn { let visibility = match self.visibility { Visibility::None => None, - Visibility::Crate => Some(make::visibility_pub_crate()), - Visibility::Pub => Some(make::visibility_pub()), + Visibility::Crate => Some(make.visibility_pub_crate()), + Visibility::Pub => Some(make.visibility_pub()), }; let type_params = - self.generic_param_list.filter(|list| list.generic_params().next().is_some()); - let fn_def = make::fn_( + self.generic_param_list.clone().filter(|list| list.generic_params().next().is_some()); + make.fn_( None, visibility, - self.fn_name, + self.fn_name.clone(), type_params, - self.where_clause, - self.params, - self.fn_body, - self.ret_type, + self.where_clause.clone(), + self.params.clone(), + self.fn_body.clone(), + self.ret_type.clone(), self.is_async, false, // FIXME : const and unsafe are not handled yet. false, false, ) - .clone_for_update(); - - let ret_type = fn_def.ret_type(); - // PANIC: we guarantee we always create a function body with a tail expr - let tail_expr = fn_def - .body() - .expect("generated function should have a body") - .tail_expr() - .expect("function body should have a tail expression"); - - if let Some(cap) = cap { - if self.should_focus_return_type { - // Focus the return type if there is one - match ret_type { - Some(ret_type) => { - edit.add_placeholder_snippet(cap, ret_type); - } - None => { - edit.add_placeholder_snippet(cap, tail_expr); - } - } - } else { - edit.add_placeholder_snippet(cap, tail_expr); - } - } - - fn_def } } @@ -420,31 +396,32 @@ impl FunctionBuilder { /// * If we could infer the return type, don't focus it (and thus focus the function body) so the /// user can change the `todo!` function body. fn make_return_type( + make: &SyntaxFactory, ctx: &AssistContext<'_>, expr: &ast::Expr, target_module: Module, necessary_generic_params: &mut FxHashSet, ) -> (Option, bool) { - let (ret_ty, should_focus_return_type) = { + let (ret_ty, should_focus_return_type) = match ctx.sema.type_of_expr(expr).map(TypeInfo::original) { - Some(ty) if ty.is_unknown() => (Some(make::ty_placeholder()), true), - None => (Some(make::ty_placeholder()), true), + Some(ty) if ty.is_unknown() => (Some(make.ty_placeholder()), true), + None => (Some(make.ty_placeholder()), true), Some(ty) if ty.is_unit() => (None, false), Some(ty) => { necessary_generic_params.extend(ty.generic_params(ctx.db())); let rendered = ty.display_source_code(ctx.db(), target_module.into(), true); match rendered { - Ok(rendered) => (Some(make::ty(&rendered)), false), - Err(_) => (Some(make::ty_placeholder()), true), + Ok(rendered) => (Some(make.ty(&rendered)), false), + Err(_) => (Some(make.ty_placeholder()), true), } } - } - }; - let ret_type = ret_ty.map(make::ret_type); + }; + let ret_type = ret_ty.map(|ty| make.ret_type(ty)); (ret_type, should_focus_return_type) } fn make_fn_body_as_new_function( + make: &SyntaxFactory, ctx: &AssistContext<'_>, fn_name: &str, adt_info: &Option, @@ -455,7 +432,7 @@ fn make_fn_body_as_new_function( }; let adt_info = adt_info.as_ref()?; - let path_self = make::ext::ident_path("Self"); + let path_self = make.ident_path("Self"); let placeholder_expr = expr_fill_default(ctx.config); let tail_expr = if let Some(strukt) = adt_info.adt.as_struct() { match strukt.kind(ctx.db()) { @@ -464,8 +441,8 @@ fn make_fn_body_as_new_function( .fields(ctx.db()) .iter() .map(|field| { - make::record_expr_field( - make::name_ref(&format!( + make.record_expr_field( + make.name_ref(&format!( "{}", field.name(ctx.db()).display(ctx.db(), edition) )), @@ -474,7 +451,7 @@ fn make_fn_body_as_new_function( }) .collect::>(); - make::record_expr(path_self, make::record_expr_field_list(fields)).into() + make.record_expr(path_self, make.record_expr_field_list(fields)).into() } StructKind::Tuple => { let args = strukt @@ -483,15 +460,15 @@ fn make_fn_body_as_new_function( .map(|_| placeholder_expr.clone()) .collect::>(); - make::expr_call(make::expr_path(path_self), make::arg_list(args)).into() + make.expr_call(make.expr_path(path_self), make.arg_list(args)).into() } - StructKind::Unit => make::expr_path(path_self), + StructKind::Unit => make.expr_path(path_self), } } else { placeholder_expr }; - let fn_body = make::block_expr(vec![], Some(tail_expr)); + let fn_body = make.block_expr(vec![], Some(tail_expr)); Some(fn_body) } @@ -575,92 +552,236 @@ impl GeneratedFunctionTarget { } } - fn insert_impl_at(&self, edit: &mut SourceChangeBuilder, impl_: ast::Impl) { + fn insert_impl_at( + &self, + edit: &mut SourceChangeBuilder, + file: FileId, + ctx: &AssistContext<'_>, + function_builder: &FunctionBuilder, + adt: Adt, + cap: Option, + ) { + let editor = edit.make_editor(self.syntax()); + match self { GeneratedFunctionTarget::AfterItem(item) => { - let item = edit.make_syntax_mut(item.clone()); let position = if item.parent().is_some() { - ted::Position::after(&item) + Position::after(item) } else { - ted::Position::first_child_of(&item) + Position::first_child_of(item) }; - let indent = IndentLevel::from_node(&item); - let leading_ws = make::tokens::whitespace(&format!("\n{indent}")); - impl_.indent(indent); - - ted::insert_all(position, vec![leading_ws.into(), impl_.syntax().clone().into()]); + let indent = IndentLevel::from_node(item); + insert_rendered_impl( + &editor, + edit, + ctx, + function_builder, + adt, + position, + indent, + indent, + cap, + ); } GeneratedFunctionTarget::InEmptyItemList(item_list) => { - let item_list = edit.make_syntax_mut(item_list.clone()); let insert_after = item_list.children_with_tokens().find_or_first(|child| child.kind() == T!['{']); let position = match insert_after { - Some(child) => ted::Position::after(child), - None => ted::Position::first_child_of(&item_list), + Some(child) => Position::after(child), + None => Position::first_child_of(item_list), }; - let indent = IndentLevel::from_node(&item_list); + let indent = IndentLevel::from_node(item_list); let leading_indent = indent + 1; - let leading_ws = make::tokens::whitespace(&format!("\n{leading_indent}")); - impl_.indent(indent); - - ted::insert_all(position, vec![leading_ws.into(), impl_.syntax().clone().into()]); + insert_rendered_impl( + &editor, + edit, + ctx, + function_builder, + adt, + position, + indent, + leading_indent, + cap, + ); } GeneratedFunctionTarget::InImpl(_) => { unreachable!("can't insert an impl inside an impl") } } + edit.add_file_edits(file, editor); } - fn insert_fn_at(&self, edit: &mut SourceChangeBuilder, func: ast::Fn) { + fn insert_fn_at( + &self, + edit: &mut SourceChangeBuilder, + file: FileId, + function_builder: &FunctionBuilder, + cap: Option, + ) { + let editor = edit.make_editor(self.syntax()); + let make = editor.make(); + match self { GeneratedFunctionTarget::AfterItem(item) => { - let item = edit.make_syntax_mut(item.clone()); let position = if item.parent().is_some() { - ted::Position::after(&item) + Position::after(item) } else { - ted::Position::first_child_of(&item) + Position::first_child_of(item) }; - let indent = IndentLevel::from_node(&item); - let leading_ws = make::tokens::whitespace(&format!("\n\n{indent}")); - func.indent(indent); - - ted::insert_all_raw( + let indent = IndentLevel::from_node(item); + insert_rendered_fn( + &editor, + edit, + function_builder, position, - vec![leading_ws.into(), func.syntax().clone().into()], + indent, + format!("\n\n{indent}"), + None, + cap, ); } GeneratedFunctionTarget::InEmptyItemList(item_list) => { - let item_list = edit.make_syntax_mut(item_list.clone()); let insert_after = item_list.children_with_tokens().find_or_first(|child| child.kind() == T!['{']); let position = match insert_after { - Some(child) => ted::Position::after(child), - None => ted::Position::first_child_of(&item_list), + Some(child) => Position::after(child), + None => Position::first_child_of(item_list), }; - let indent = IndentLevel::from_node(&item_list); + let indent = IndentLevel::from_node(item_list); let leading_indent = indent + 1; - let leading_ws = make::tokens::whitespace(&format!("\n{leading_indent}")); - let trailing_ws = make::tokens::whitespace(&format!("\n{indent}")); - func.indent(leading_indent); - - ted::insert_all( + insert_rendered_fn( + &editor, + edit, + function_builder, position, - vec![leading_ws.into(), func.syntax().clone().into(), trailing_ws.into()], + leading_indent, + format!("\n{leading_indent}"), + Some(format!("\n{indent}")), + cap, ); } GeneratedFunctionTarget::InImpl(impl_) => { - let impl_ = edit.make_mut(impl_.clone()); - let leading_indent = impl_.indent_level() + 1; - func.indent(leading_indent); - impl_.get_or_create_assoc_item_list().add_item(func.into()); + if let Some(item_list) = impl_.assoc_item_list() { + let insert_after_item = item_list.assoc_items().last(); + let insert_after = item_list + .assoc_items() + .last() + .map(|it| it.syntax().clone().into()) + .or_else(|| { + item_list + .syntax() + .children_with_tokens() + .find_or_first(|child| child.kind() == T!['{']) + }); + let position = match insert_after { + Some(child) => Position::after(child), + None => Position::first_child_of(item_list.syntax()), + }; + let indent = impl_.indent_level(); + let leading_ws = if insert_after_item.is_some() { + format!("\n\n{leading_indent}") + } else { + format!("\n{leading_indent}") + }; + let trailing_ws = insert_after_item.is_none().then(|| format!("\n{indent}")); + insert_rendered_fn( + &editor, + edit, + function_builder, + position, + leading_indent, + leading_ws, + trailing_ws, + cap, + ); + } else { + let func = function_builder.render(make).indent(leading_indent); + let item_list = make.assoc_item_list([func.into()]); + if let Some(fn_) = item_list.syntax().descendants().find_map(ast::Fn::cast) { + add_generated_fn_annotation(&editor, edit, function_builder, &fn_, cap); + } + editor.insert(Position::last_child_of(impl_.syntax()), item_list.syntax()); + } } } + edit.add_file_edits(file, editor); + } +} + +fn insert_rendered_impl( + editor: &SyntaxEditor, + edit: &mut SourceChangeBuilder, + ctx: &AssistContext<'_>, + function_builder: &FunctionBuilder, + adt: Adt, + position: Position, + impl_indent: IndentLevel, + leading_ws_indent: IndentLevel, + cap: Option, +) { + let make = editor.make(); + let leading_ws = make.whitespace(&format!("\n{leading_ws_indent}")); + let name = make.ty_path(make.ident_path(&format!( + "{}", + adt.name(ctx.db()).display(ctx.db(), function_builder.target_edition) + ))); + + // FIXME: adt may have generic params. + let fn_ = function_builder.render(make).indent(IndentLevel(1)); + let impl_ = + make.impl_(None, None, None, name.into(), None, Some(make.assoc_item_list([fn_.into()]))); + let impl_ = impl_.indent(impl_indent); + if let Some(fn_) = impl_.syntax().descendants().find_map(ast::Fn::cast) { + add_generated_fn_annotation(editor, edit, function_builder, &fn_, cap); + } + + editor.insert_all(position, vec![leading_ws.into(), impl_.syntax().clone().into()]); +} + +fn insert_rendered_fn( + editor: &SyntaxEditor, + edit: &mut SourceChangeBuilder, + function_builder: &FunctionBuilder, + position: Position, + indent: IndentLevel, + leading_ws: String, + trailing_ws: Option, + cap: Option, +) { + let make = editor.make(); + let leading_ws = make.whitespace(&leading_ws); + let func = function_builder.render(make).indent(indent); + add_generated_fn_annotation(editor, edit, function_builder, &func, cap); + + let mut elements = vec![leading_ws.into(), func.syntax().clone().into()]; + if let Some(trailing_ws) = trailing_ws { + elements.push(make.whitespace(&trailing_ws).into()); + } + editor.insert_all(position, elements); +} + +fn add_generated_fn_annotation( + editor: &SyntaxEditor, + edit: &mut SourceChangeBuilder, + function_builder: &FunctionBuilder, + fn_: &ast::Fn, + cap: Option, +) { + let Some(cap) = cap else { return }; + + let annotation = edit.make_placeholder_snippet(cap); + if function_builder.should_focus_return_type + && let Some(ret_type) = fn_.ret_type() + { + editor.add_annotation(ret_type.syntax(), annotation); + } else if let Some(tail_expr) = fn_.body().and_then(|body| body.tail_expr()) { + editor.add_annotation(tail_expr.syntax(), annotation); } } @@ -677,6 +798,7 @@ impl AdtInfo { /// Computes parameter list for the generated function. fn fn_args( + make: &SyntaxFactory, ctx: &AssistContext<'_>, target_module: Module, call: ast::CallableExpr, @@ -689,14 +811,15 @@ fn fn_args( arg_types.push(fn_arg_type(ctx, target_module, &arg, necessary_generic_params)); } deduplicate_arg_names(&mut arg_names); - let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| { - make::param(make::ext::simple_ident_pat(make::name(&name)).into(), make::ty(&ty)) - }); + let params = arg_names + .into_iter() + .zip(arg_types) + .map(|(name, ty)| make.param(make.simple_ident_pat(make.name(&name)).into(), make.ty(&ty))); - Some(make::param_list( + Some(make.param_list( match call { ast::CallableExpr::Call(_) => None, - ast::CallableExpr::MethodCall(_) => Some(make::self_param()), + ast::CallableExpr::MethodCall(_) => Some(make.self_param()), }, params, )) @@ -711,6 +834,7 @@ fn fn_args( /// currently do anything about it because it's actually easy to resolve it after the assist: just /// use the Rename functionality. fn fn_generic_params( + make: &SyntaxFactory, ctx: &AssistContext<'_>, necessary_params: FxHashSet, target: &GeneratedFunctionTarget, @@ -737,39 +861,39 @@ fn fn_generic_params( filter_unnecessary_bounds(&mut generic_params, &mut where_preds, necessary_params); filter_bounds_in_scope(&mut generic_params, &mut where_preds, ctx, target); + let source_scope = generic_params.first().and_then(|param| ctx.sema.scope(param.node.syntax())); + let target_scope = source_scope.as_ref().and_then(|_| ctx.sema.scope(&target.parent())); + let generic_params: Vec = - generic_params.into_iter().map(|it| it.node.clone_for_update()).collect(); - let where_preds: Vec = - where_preds.into_iter().map(|it| it.node.clone_for_update()).collect(); - - let (generic_params, where_preds): (Vec, Vec) = - if let Some(param) = generic_params.first() - && let source_scope = ctx.sema.scope(param.syntax())? - && let target_scope = ctx.sema.scope(&target.parent())? - && source_scope.module() != target_scope.module() - { - // 4. Rewrite paths - let transform = PathTransform::generic_transformation(&target_scope, &source_scope); - let generic_params = generic_params.iter().map(|it| it.syntax()); - let where_preds = where_preds.iter().map(|it| it.syntax()); - transform - .apply_all(generic_params.chain(where_preds)) - .into_iter() - .filter_map(|it| { - if let Some(it) = ast::GenericParam::cast(it.clone()) { - Some(either::Either::Left(it)) - } else { - ast::WherePred::cast(it).map(either::Either::Right) - } - }) - .partition_map(|it| it) - } else { - (generic_params, where_preds) - }; + generic_params.into_iter().map(|it| it.node).collect(); + let where_preds: Vec = where_preds.into_iter().map(|it| it.node).collect(); + + let (generic_params, where_preds) = if let Some(source_scope) = source_scope + && let Some(target_scope) = target_scope + && source_scope.module() != target_scope.module() + { + // 4. Rewrite paths + let transform = PathTransform::generic_transformation(&target_scope, &source_scope); + let generic_params = generic_params.iter().map(|it| it.syntax()); + let where_preds = where_preds.iter().map(|it| it.syntax()); + transform + .apply_all(generic_params.chain(where_preds)) + .into_iter() + .filter_map(|it| { + if let Some(it) = ast::GenericParam::cast(it.clone()) { + Some(either::Either::Left(it)) + } else { + ast::WherePred::cast(it).map(either::Either::Right) + } + }) + .partition_map(|it| it) + } else { + (generic_params, where_preds) + }; - let generic_param_list = make::generic_param_list(generic_params); + let generic_param_list = make.generic_param_list(generic_params); let where_clause = - if where_preds.is_empty() { None } else { Some(make::where_clause(where_preds)) }; + if where_preds.is_empty() { None } else { Some(make.where_clause(where_preds)) }; Some((Some(generic_param_list), where_clause)) } From 7a8af411077f6eed500c36752340ac02b3fbd05e Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 28 Apr 2026 15:36:52 +0800 Subject: [PATCH 113/289] fix: handle empty expr in tuple expr Use logic similar to `arg_list` for parsing Example --- ```rust const _=(a,,b) ``` **Before this PR** ```text CONST@0..11 CONST_KW@0..5 "const" WHITESPACE@5..6 " " UNDERSCORE@6..7 "_" EQ@7..8 "=" TUPLE_EXPR@8..11 L_PAREN@8..9 "(" PATH_EXPR@9..10 PATH@9..10 PATH_SEGMENT@9..10 NAME_REF@9..10 IDENT@9..10 "a" COMMA@10..11 "," ERROR@11..12 COMMA@11..12 "," ERROR@12..13 PATH@12..13 PATH_SEGMENT@12..13 NAME_REF@12..13 IDENT@12..13 "b" ERROR@13..14 R_PAREN@13..14 ")" WHITESPACE@14..15 "\n" ``` **After this PR** ```text CONST@0..11 CONST_KW@0..5 "const" WHITESPACE@5..6 " " UNDERSCORE@6..7 "_" EQ@7..8 "=" TUPLE_EXPR L_PAREN "(" PATH_EXPR PATH PATH_SEGMENT NAME_REF IDENT "a" COMMA "," COMMA "," PATH_EXPR PATH PATH_SEGMENT NAME_REF IDENT "b" R_PAREN ")" ``` --- .../parser/src/grammar/expressions/atom.rs | 21 +++++++++-------- .../inline/err/tuple_expr_leading_comma.rast | 23 ++++++++++++++++++- .../inline/err/tuple_expr_leading_comma.rs | 1 + 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index 3620a27c230dd..52dfb049aab10 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -216,16 +216,19 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { let mut saw_comma = false; let mut saw_expr = false; - // test_err tuple_expr_leading_comma - // fn foo() { - // (,); - // } - if p.eat(T![,]) { - p.error("expected expression"); - saw_comma = true; - } - while !p.at(EOF) && !p.at(T![')']) { + // test_err tuple_expr_leading_comma + // fn foo() { + // (,); + // (a, , b); + // } + if p.current() == T![,] { + p.error("expected expression"); + p.bump(T![,]); + saw_comma = true; + continue; + } + saw_expr = true; // test tuple_attrs diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast index 3fbc0da4002e4..2ccd04c321e13 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast @@ -18,7 +18,28 @@ SOURCE_FILE COMMA "," R_PAREN ")" SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + TUPLE_EXPR + L_PAREN "(" + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + COMMA "," + WHITESPACE " " + COMMA "," + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + R_PAREN ")" + SEMICOLON ";" WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" -error 17: expected expression +error 16: expected expression +error 27: expected expression diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs index 12fab59a77656..37f756ffa6db0 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs @@ -1,3 +1,4 @@ fn foo() { (,); + (a, , b); } From fab8e87dde6f3cfa2214715db4a7ebc743497611 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 28 Apr 2026 15:38:35 +0800 Subject: [PATCH 114/289] Rename tuple_expr_leading_comma to tuple_expr_empty_expr --- .../crates/parser/src/grammar/expressions/atom.rs | 2 +- .../rust-analyzer/crates/parser/test_data/generated/runner.rs | 4 ++-- ...ple_expr_leading_comma.rast => tuple_expr_empty_expr.rast} | 0 .../{tuple_expr_leading_comma.rs => tuple_expr_empty_expr.rs} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/{tuple_expr_leading_comma.rast => tuple_expr_empty_expr.rast} (100%) rename src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/{tuple_expr_leading_comma.rs => tuple_expr_empty_expr.rs} (100%) diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index 52dfb049aab10..cabdd74b09ce8 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -217,7 +217,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { let mut saw_expr = false; while !p.at(EOF) && !p.at(T![')']) { - // test_err tuple_expr_leading_comma + // test_err tuple_expr_empty_expr // fn foo() { // (,); // (a, , b); diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index ecb9ece67a0e0..845119d9223a1 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -958,8 +958,8 @@ mod err { #[test] fn top_level_let() { run_and_expect_errors("test_data/parser/inline/err/top_level_let.rs"); } #[test] - fn tuple_expr_leading_comma() { - run_and_expect_errors("test_data/parser/inline/err/tuple_expr_leading_comma.rs"); + fn tuple_expr_empty_expr() { + run_and_expect_errors("test_data/parser/inline/err/tuple_expr_empty_expr.rs"); } #[test] fn tuple_field_list_recovery() { diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rast similarity index 100% rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rast diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rs similarity index 100% rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rs From be070a09fc819e35ec1e129ea99a4516f3833463 Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Tue, 28 Apr 2026 14:20:48 +0600 Subject: [PATCH 115/289] internal: address round 3 review for inline_call SyntaxEditor migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - use `editor.make()` for nodes passed to body editor (Self type replacement, `this` token, record_expr_field, closure paren wrap) so they are tracked by the correct editor's factory; rename `factory` → `make` for file-level nodes (let_stmts, post-finish block construction) per reviewer naming - drop `remove_path_if_in_use_stmt` and `ast_to_remove_for_path_in_use_stmt` from `ide-db` (unused after SyntaxEditor migration) - change `remove_use_tree_if_simple` signature to `&SyntaxEditor` (interior mutability via RefCell makes `&mut` unnecessary) - fix `inline_type_alias` to use `let editor` (not `mut`) and pass `&editor` to `remove_use_tree_if_simple` - filter calls nested inside other calls in `inline_into_callers` to prevent overlapping SyntaxEditor edits; nested calls are implicitly replaced when the outer call is inlined, so they count toward the "all handled" check - add `inline_callers_nested_calls` test covering the above --- .../ide-assists/src/handlers/inline_call.rs | 80 ++++++++++++++----- .../src/handlers/inline_type_alias.rs | 4 +- .../crates/ide-db/src/imports/insert_use.rs | 28 +------ 3 files changed, 63 insertions(+), 49 deletions(-) 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 08302e6d4ceda..9273ad4cc50cc 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 @@ -17,7 +17,7 @@ use ide_db::{ }; use itertools::{Itertools, izip}; use syntax::{ - AstNode, NodeOrToken, SyntaxKind, + AstNode, NodeOrToken, SyntaxKind, TextRange, ast::{ self, HasArgList, HasGenericArgs, Pat, PathExpr, edit::{AstNodeEdit, IndentLevel}, @@ -121,6 +121,18 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> // directly inlining into macros may cause errors. .filter(|call_info| !ctx.sema.hir_file_for(call_info.node.syntax()).is_macro()) .collect(); + + // Skip calls nested inside other calls being inlined to avoid overlapping + // edits. Nested calls are implicitly replaced when the outer call is inlined. + let all_ranges: Vec = + call_infos.iter().map(|ci| ci.node.syntax().text_range()).collect(); + let (call_infos, nested_infos): (Vec<_>, Vec<_>) = + call_infos.into_iter().partition(|ci| { + let r = ci.node.syntax().text_range(); + !all_ranges.iter().any(|&other| other != r && other.contains_range(r)) + }); + let nested_count = nested_infos.len(); + let anchor = call_infos .first() .map(|ci| ci.node.syntax().clone()) @@ -138,7 +150,7 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> editor.replace(call_info.node.syntax(), replacement.syntax()); }) .count(); - if replaced + use_trees.len() == count { + if replaced + nested_count + use_trees.len() == count { // we replaced all usages in this file, so we can remove the imports for use_tree in &use_trees { remove_use_tree_if_simple(use_tree, editor); @@ -332,7 +344,7 @@ fn inline( CallInfo { node, arguments, generic_arg_list, krate }: &CallInfo, file_editor: &SyntaxEditor, ) -> ast::Expr { - let factory = file_editor.make(); + let make = file_editor.make(); 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); @@ -433,7 +445,7 @@ fn inline( "", 1, ); - factory.ty(&stripped).syntax().clone() + editor.make().ty(&stripped).syntax().clone() } else { t.syntax().clone() }; @@ -458,8 +470,12 @@ fn inline( let mut let_stmts: Vec = Vec::new(); - let this_token = - factory.name_ref("this").syntax().first_token().expect("NameRef should have had a token."); + let this_token = editor + .make() + .name_ref("this") + .syntax() + .first_token() + .expect("NameRef should have had a token."); let rewrite_self_to_this = |editor: &SyntaxEditor| { for usage in &self_token_usages { editor.replace(usage.clone(), this_token.clone()); @@ -494,7 +510,7 @@ fn inline( let is_self = param.name(sema.db).is_some_and(|name| name == sym::self_); if is_self { - let mut this_pat = factory.ident_pat(false, false, factory.name("this")); + let mut this_pat = make.ident_pat(false, false, make.name("this")); let mut expr = expr.clone(); if let Pat::IdentPat(pat) = pat { match (pat.ref_token(), pat.mut_token()) { @@ -502,11 +518,11 @@ fn inline( (None, None) => {} // mut self => let mut this = obj (None, Some(_)) => { - this_pat = factory.ident_pat(false, true, factory.name("this")); + this_pat = make.ident_pat(false, true, make.name("this")); } // &self => let this = &obj (Some(_), None) => { - expr = factory.expr_ref(expr, false); + expr = make.expr_ref(expr, false); } // let foo = &mut X; &mut self => let this = &mut obj // let mut foo = X; &mut self => let this = &mut *obj (reborrow) @@ -515,16 +531,16 @@ fn inline( .type_of_expr(&expr) .map(|ty| ty.original.is_mutable_reference()); expr = if let Some(true) = should_reborrow { - factory.expr_reborrow(expr) + make.expr_reborrow(expr) } else { - factory.expr_ref(expr, true) + make.expr_ref(expr, true) }; } } }; - let_stmts.push(factory.let_stmt(this_pat.into(), ty, Some(expr)).into()) + let_stmts.push(make.let_stmt(this_pat.into(), ty, Some(expr)).into()) } else { - let_stmts.push(factory.let_stmt(pat.clone(), ty, Some(expr.clone())).into()); + let_stmts.push(make.let_stmt(pat.clone(), ty, Some(expr.clone())).into()); } }; @@ -545,8 +561,8 @@ fn inline( if let Some(field) = path_expr_as_record_field(usage) { cov_mark::hit!(inline_call_inline_direct_field); let field_name = field.field_name().unwrap(); - let new_field = factory.record_expr_field( - factory.name_ref(&field_name.text()), + let new_field = editor.make().record_expr_field( + editor.make().name_ref(&field_name.text()), Some(replacement.clone()), ); editor.replace(field.syntax(), new_field.syntax()); @@ -562,7 +578,7 @@ fn inline( && usage.syntax().parent().and_then(ast::Expr::cast).is_some() => { cov_mark::hit!(inline_call_inline_closure); - let expr = factory.expr_paren(expr.clone()).into(); + let expr = editor.make().expr_paren(expr.clone()).into(); inline_direct(&editor, usage, &expr); } // inline single use literals @@ -605,18 +621,18 @@ fn inline( let is_async_fn = function.is_async(sema.db); if is_async_fn { cov_mark::hit!(inline_call_async_fn); - body = factory.async_move_block_expr(body.statements(), body.tail_expr()); + body = make.async_move_block_expr(body.statements(), body.tail_expr()); // Arguments should be evaluated outside the async block, and then moved into it. if !let_stmts.is_empty() { cov_mark::hit!(inline_call_async_fn_with_let_stmts); body = body.indent(IndentLevel(1)); - body = factory.block_expr(let_stmts, Some(body.into())); + body = make.block_expr(let_stmts, Some(body.into())); } } else if !let_stmts.is_empty() { // Prepend let statements to the body's existing statements let stmts: Vec = let_stmts.into_iter().chain(body.statements()).collect(); - body = factory.block_expr(stmts, body.tail_expr()); + body = make.block_expr(stmts, body.tail_expr()); } let original_indentation = match node { @@ -628,7 +644,7 @@ fn inline( let no_stmts = body.statements().next().is_none(); match body.tail_expr() { Some(expr) if matches!(expr, ast::Expr::ClosureExpr(_)) && no_stmts => { - factory.expr_paren(expr).into() + make.expr_paren(expr).into() } Some(expr) if !is_async_fn && no_stmts => expr, _ => match node @@ -638,7 +654,7 @@ fn inline( .and_then(|bin_expr| bin_expr.lhs()) { Some(lhs) if lhs.syntax() == node.syntax() => { - factory.expr_paren(ast::Expr::BlockExpr(body)).into() + make.expr_paren(ast::Expr::BlockExpr(body)).into() } _ => ast::Expr::BlockExpr(body), }, @@ -1916,6 +1932,28 @@ fn _hash2(self_: &u64, state: &mut u64) { ) } + #[test] + fn inline_callers_nested_calls() { + check_assist( + inline_into_callers, + r#" +fn id$0(x: u32) -> u32 { x } +fn main() { + let x = id(id(1)); +} +"#, + r#" + +fn main() { + let x = { + let x = id(1); + x + }; +} +"#, + ); + } + #[test] fn inline_into_callers_in_macros_not_applicable() { check_assist_not_applicable( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index b4826bcc94398..9c7d266057d87 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -69,14 +69,14 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) let mut inline_refs_for_file = |file_id, refs: Vec| { let source = ctx.sema.parse(file_id); - let mut editor = builder.make_editor(source.syntax()); + let editor = builder.make_editor(source.syntax()); let (path_types, path_type_uses) = split_refs_and_uses(refs, |path_type| { path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast) }); path_type_uses .iter() - .for_each(|use_tree| remove_use_tree_if_simple(use_tree, &mut editor)); + .for_each(|use_tree| remove_use_tree_if_simple(use_tree, &editor)); for (target, replacement) in path_types.into_iter().filter_map(|path_type| { let replacement = diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index 4c4a01a1571d0..7402ec8f5a7b7 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -7,10 +7,7 @@ use std::cmp::Ordering; use hir::Semantics; use syntax::{ Direction, NodeOrToken, SyntaxKind, SyntaxNode, algo, - ast::{ - self, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind, - edit_in_place::Removable, make, - }, + ast::{self, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind, make}, syntax_editor::{Position, SyntaxEditor}, ted, }; @@ -326,28 +323,7 @@ fn insert_use_with_alias_option_with_editor( insert_use_with_editor_(scope, use_item, cfg.group, syntax_editor); } -pub fn ast_to_remove_for_path_in_use_stmt(path: &ast::Path) -> Option> { - // FIXME: improve this - if path.parent_path().is_some() { - return None; - } - let use_tree = path.syntax().parent().and_then(ast::UseTree::cast)?; - if use_tree.use_tree_list().is_some() || use_tree.star_token().is_some() { - return None; - } - if let Some(use_) = use_tree.syntax().parent().and_then(ast::Use::cast) { - return Some(Box::new(use_)); - } - Some(Box::new(use_tree)) -} - -pub fn remove_path_if_in_use_stmt(path: &ast::Path) { - if let Some(node) = ast_to_remove_for_path_in_use_stmt(path) { - node.remove(); - } -} - -pub fn remove_use_tree_if_simple(use_tree: &ast::UseTree, editor: &mut SyntaxEditor) { +pub fn remove_use_tree_if_simple(use_tree: &ast::UseTree, editor: &SyntaxEditor) { if use_tree.use_tree_list().is_some() || use_tree.star_token().is_some() { return; } From 0090872846bee9b2ab65272d3c1f6760bf155d1d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 28 Apr 2026 00:36:18 +0200 Subject: [PATCH 116/289] feat(diagnostics): add handler for E0130 --- .../crates/hir-def/src/expr_store.rs | 1 + .../crates/hir-def/src/expr_store/lower.rs | 12 +-- .../crates/hir/src/diagnostics.rs | 6 ++ src/tools/rust-analyzer/crates/hir/src/lib.rs | 3 + .../src/handlers/pattern_arg_in_extern_fn.rs | 86 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 6 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index 51951896f2c7d..f72419a3aee24 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -303,6 +303,7 @@ pub enum ExpressionStoreDiagnostics { UnreachableLabel { node: InFile>, name: Name }, AwaitOutsideOfAsync { node: InFile>, location: String }, UndeclaredLabel { node: InFile>, name: Name }, + PatternArgInExternFn { node: InFile> }, } impl ExpressionStoreBuilder { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 5c8e87c0e3007..b86fad0420ec6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -2482,9 +2482,7 @@ impl<'db> ExprCollector<'db> { let Some(pat) = pat else { return self.missing_pat() }; match &pat { - ast::Pat::IdentPat(bp) => { - // FIXME: Emit an error if `!bp.is_simple_ident()`. - + ast::Pat::IdentPat(bp) if bp.is_simple_ident() => { let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); let hygiene = bp .name() @@ -2496,8 +2494,12 @@ impl<'db> ExprCollector<'db> { self.add_definition_to_binding(binding, pat); pat } - // FIXME: Emit an error. - _ => self.missing_pat(), + _ => { + self.store.diagnostics.push(ExpressionStoreDiagnostics::PatternArgInExternFn { + node: self.expander.in_file(AstPtr::new(&pat)), + }); + self.missing_pat() + } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 32f0ac3dad128..b2edac6c3221b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -80,6 +80,7 @@ diagnostics![AnyDiagnostic<'db> -> NeedMut, NonExhaustiveLet, NoSuchField, + PatternArgInExternFn, PrivateAssocItem, PrivateField, RemoveTrailingReturn, @@ -486,6 +487,11 @@ pub struct InvalidLhsOfAssignment { pub lhs: InFile>>, } +#[derive(Debug)] +pub struct PatternArgInExternFn { + pub node: InFile>, +} + impl<'db> AnyDiagnostic<'db> { pub(crate) fn body_validation_diagnostic( db: &'db dyn HirDatabase, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 8cc82113b3934..20fae4fb0d0e7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2388,6 +2388,9 @@ fn expr_store_diagnostics<'db>( ExpressionStoreDiagnostics::UndeclaredLabel { node, name } => { UndeclaredLabel { node: *node, name: name.clone() }.into() } + ExpressionStoreDiagnostics::PatternArgInExternFn { node } => { + PatternArgInExternFn { node: *node }.into() + } }); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs new file mode 100644 index 0000000000000..36031865fca03 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs @@ -0,0 +1,86 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: pattern-arg-in-extern-fn +// +// This diagnostic is triggered if a pattern was declared as an argument in a foreign function declaration. +pub(crate) fn pattern_arg_in_extern_fn( + ctx: &DiagnosticsContext<'_>, + d: &hir::PatternArgInExternFn, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0130"), + "patterns aren't allowed in foreign function declarations", + d.node.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn tuple_pattern() { + check_diagnostics( + r#" +unsafe extern { fn foo((a, b): (u32, u32)); } + // ^^^^^^ error: patterns aren't allowed in foreign function declarations + "#, + ); + } + + #[test] + fn struct_pattern() { + check_diagnostics( + r#" +struct Foo(u32, u32); +unsafe extern { fn foo(Foo(a, b): Foo); } + // ^^^^^^^^^ error: patterns aren't allowed in foreign function declarations + "#, + ); + + check_diagnostics( + r#" +struct Foo{ bar: u32, baz: u32 } +unsafe extern { fn foo(Foo { bar, baz }: Foo); } + // ^^^^^^^^^^^^^^^^ error: patterns aren't allowed in foreign function declarations + "#, + ); + } + + #[test] + fn pattern_is_second_arg() { + check_diagnostics( + r#" +struct Foo(u32, u32); +unsafe extern { fn foo(okay: u32, Foo(a, b): Foo); } + // ^^^^^^^^^ error: patterns aren't allowed in foreign function declarations + "#, + ); + } + + #[test] + fn non_simple_ident() { + check_diagnostics( + r#" +unsafe extern { fn foo(ref a: u32); } + // ^^^^^ error: patterns aren't allowed in foreign function declarations + "#, + ); + + check_diagnostics( + r#" +unsafe extern { fn foo(mut a: u32); } + // ^^^^^ error: patterns aren't allowed in foreign function declarations + "#, + ); + + check_diagnostics( + r#" +unsafe extern { fn foo(a @ _: u32); } + // ^^^^^ error: patterns aren't allowed in foreign function declarations + "#, + ); + } +} 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 a083447335d05..adc32564aa6ce 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -56,6 +56,7 @@ mod handlers { pub(crate) mod no_such_field; pub(crate) mod non_exhaustive_let; pub(crate) mod parenthesized_generic_args_without_fn_trait; + pub(crate) mod pattern_arg_in_extern_fn; pub(crate) mod private_assoc_item; pub(crate) mod private_field; pub(crate) mod remove_trailing_return; @@ -483,6 +484,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::GenericDefaultRefersToSelf(d) => handlers::generic_default_refers_to_self::generic_default_refers_to_self(&ctx, &d), AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d), AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d), + AnyDiagnostic::PatternArgInExternFn(d) => handlers::pattern_arg_in_extern_fn::pattern_arg_in_extern_fn(&ctx, &d), }; res.push(d) } From 8c2882351584456389cb28be393dfdcb02eac9d8 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 28 Apr 2026 22:51:39 +0300 Subject: [PATCH 117/289] Lower coroutine closure arguments like rustc This is important since we rely on this for closure analysis. --- .../crates/hir-def/src/expr_store/lower.rs | 68 ++++++++++++++++--- .../hir-def/src/expr_store/tests/body.rs | 8 ++- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 5c8e87c0e3007..611cc511dde39 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -968,37 +968,83 @@ impl<'db> ExprCollector<'db> { kind: CoroutineKind, coroutine_source: CoroutineSource, ) -> ExprId { + // Async function parameters are lowered into the closure body so that they are + // captured and so that the drop order matches the equivalent non-async functions. + // + // from: + // + // async fn foo(: , : , : ) { + // + // } + // + // into: + // + // fn foo(__arg0: , __arg1: , __arg2: ) { + // async move { + // let __arg2 = __arg2; + // let = __arg2; + // let __arg1 = __arg1; + // let = __arg1; + // let __arg0 = __arg0; + // let = __arg0; + // drop-temps { } // see comments later in fn for details + // } + // } + // + // If `` is a simple ident, then it is lowered to a single + // `let = ;` statement as an optimization. + let mut statements = Vec::new(); for param in params { - let (name, hygiene) = match self.store.pats[*param] { - Pat::Bind { id, .. } + let (name, hygiene, is_simple_parameter) = match self.store.pats[*param] { + // Check if this is a binding pattern, if so, we can optimize and avoid adding a + // `let = __argN;` statement. In this case, we do not rename the parameter. + Pat::Bind { id, subpat: None, .. } if matches!( self.store.bindings[id].mode, BindingAnnotation::Unannotated | BindingAnnotation::Mutable ) => { - // If this is a direct binding, we can leave it as-is, as it'll always be captured anyway. - continue; + (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene, true) } Pat::Bind { id, .. } => { // If this is a `ref` binding, we can't leave it as is but we can at least reuse the name, for better display. - (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene) + (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene, false) } - _ => (self.generate_new_name(), HygieneId::ROOT), + _ => (self.generate_new_name(), HygieneId::ROOT, false), }; - let binding_id = self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene); - let pat_id = self.alloc_pat_desugared(Pat::Bind { id: binding_id, subpat: None }); - let expr = self.alloc_expr_desugared(Expr::Path(name.into())); + let child_binding_id = + self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene); + let child_pat_id = + self.alloc_pat_desugared(Pat::Bind { id: child_binding_id, subpat: None }); + let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into())); if !hygiene.is_root() { self.store.ident_hygiene.insert(expr.into(), hygiene); } statements.push(Statement::Let { - pat: *param, + pat: child_pat_id, type_ref: None, initializer: Some(expr), else_branch: None, }); - *param = pat_id; + if !is_simple_parameter { + let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into())); + if !hygiene.is_root() { + self.store.ident_hygiene.insert(expr.into(), hygiene); + } + statements.push(Statement::Let { + pat: *param, + type_ref: None, + initializer: Some(expr), + else_branch: None, + }); + + let parent_binding_id = + self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene); + let parent_pat_id = + self.alloc_pat_desugared(Pat::Bind { id: parent_binding_id, subpat: None }); + *param = parent_pat_id; + } } let coroutine = self.desugared_coroutine_expr( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs index db12775df95f6..9727d87cf0372 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs @@ -652,9 +652,15 @@ fn async_fn_weird_param_patterns() { async fn main(&self, param1: i32, ref mut param2: i32, _: i32, param4 @ _: i32, 123: i32) {} "#, expect![[r#" - fn main(self, param1, mut param2, mut 0, param4 @ _, mut 1) async { + fn main(self, param1, mut param2, mut 0, mut param4, mut 1) async { + let mut param1 = param1; + let mut param2 = param2; let ref mut param2 = param2; + let mut 0 = 0; let _ = 0; + let mut param4 = param4; + let param4 @ _ = param4; + let mut 1 = 1; let 123 = 1; {} }"#]], From 5bc38cd8191994d40036de157c59cd7530126e9b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 29 Apr 2026 00:54:26 +0300 Subject: [PATCH 118/289] Make coroutine closures the owner of bindings in their arguments Previously the inner coroutine was their owner. --- .../crates/hir-def/src/expr_store/lower.rs | 36 ++++++++++++++----- .../crates/hir-ty/src/tests/simple.rs | 14 ++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 611cc511dde39..05dfe80d106ba 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -961,7 +961,7 @@ impl<'db> ExprCollector<'db> { /// into the body. This is to make sure that the future actually owns the /// arguments that are passed to the function, and to ensure things like /// drop order are stable. - fn lower_coroutine_with_moved_arguments( + fn lower_coroutine_body_with_moved_arguments( &mut self, params: &mut [PatId], body: ExprId, @@ -1101,7 +1101,12 @@ impl<'db> ExprCollector<'db> { (false, true) => CoroutineKind::Gen, (false, false) => unreachable!(), }; - this.lower_coroutine_with_moved_arguments(params, body, kind, CoroutineSource::Fn) + this.lower_coroutine_body_with_moved_arguments( + params, + body, + kind, + CoroutineSource::Fn, + ) } else { body } @@ -1525,11 +1530,11 @@ impl<'db> ExprCollector<'db> { } } ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| { - this.with_binding_owner_and_return(|this| { + let mut is_coroutine_closure = false; + let closure = this.with_binding_owner_and_return(|this| { let mut args = Vec::new(); let mut arg_types = Vec::new(); // For coroutine closures, the body, aka. the coroutine is the bindings owner, and not the closure. - let mut body_is_bindings_owner = false; if let Some(pl) = e.param_list() { let num_params = pl.params().count(); args.reserve_exact(num_params); @@ -1572,13 +1577,13 @@ impl<'db> ExprCollector<'db> { let closure_kind = if let Some(kind) = kind { // It's important that this expr is allocated immediately before the closure. // We rely on it for `coroutine_for_closure()`. - body = this.lower_coroutine_with_moved_arguments( + body = this.lower_coroutine_body_with_moved_arguments( &mut args, body, kind, CoroutineSource::Closure, ); - body_is_bindings_owner = true; + is_coroutine_closure = true; ClosureKind::CoroutineClosure(kind) } else if this.is_lowering_coroutine { @@ -1607,8 +1612,23 @@ impl<'db> ExprCollector<'db> { syntax_ptr, ); - (if body_is_bindings_owner { body } else { closure }, closure) - }) + (if is_coroutine_closure { body } else { closure }, closure) + }); + + if is_coroutine_closure { + let Expr::Closure { args, .. } = &this.store.exprs[closure] else { + unreachable!() + }; + for &arg in args { + let Pat::Bind { id, .. } = this.store.pats[arg] else { + never!("`lower_coroutine_body_with_moved_arguments()` should make sure the coroutine closure only have simple bind args"); + continue; + }; + this.store.binding_owners.insert(id, closure); + } + } + + closure }), ast::Expr::BinExpr(e) => { let op = e.op_kind(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 1e75c31fa190f..a8ace690177d0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -4279,3 +4279,17 @@ union U { "#]], ); } + +#[test] +fn async_closure_with_params() { + check_no_mismatches( + r#" +fn foo() { + let capture = false; + async move |param: i32| { + capture; + }; +} + "#, + ); +} From 03b4551897e682308c0231f22855390d710cc8c2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 29 Apr 2026 01:12:51 +0300 Subject: [PATCH 119/289] Relate the bindings `lower_coroutine_body_with_moved_arguments()` creates with their pattern --- src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 05dfe80d106ba..a9697d8e720cd 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -1017,6 +1017,7 @@ impl<'db> ExprCollector<'db> { self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene); let child_pat_id = self.alloc_pat_desugared(Pat::Bind { id: child_binding_id, subpat: None }); + self.add_definition_to_binding(child_binding_id, child_pat_id); let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into())); if !hygiene.is_root() { self.store.ident_hygiene.insert(expr.into(), hygiene); @@ -1043,6 +1044,7 @@ impl<'db> ExprCollector<'db> { self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene); let parent_pat_id = self.alloc_pat_desugared(Pat::Bind { id: parent_binding_id, subpat: None }); + self.add_definition_to_binding(parent_binding_id, parent_pat_id); *param = parent_pat_id; } } From 93d5d6043f57f45d5e4b5271d0b01736cb28c4d0 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 29 Apr 2026 01:23:20 +0300 Subject: [PATCH 120/289] Map the patterns `lower_coroutine_body_with_moved_arguments()` creates back to source code I'm not sure about the mapping the other way back, though - should we override them for the existing pattern? --- .../rust-analyzer/crates/hir-def/src/expr_store/lower.rs | 7 +++++++ src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs | 1 + src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs | 3 +++ 3 files changed, 11 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index a9697d8e720cd..afd53cca9963e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -1013,11 +1013,15 @@ impl<'db> ExprCollector<'db> { } _ => (self.generate_new_name(), HygieneId::ROOT, false), }; + let pat_syntax = self.store.pat_map_back.get(*param).copied(); let child_binding_id = self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene); let child_pat_id = self.alloc_pat_desugared(Pat::Bind { id: child_binding_id, subpat: None }); self.add_definition_to_binding(child_binding_id, child_pat_id); + if let Some(pat_syntax) = pat_syntax { + self.store.pat_map_back.insert(child_pat_id, pat_syntax); + } let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into())); if !hygiene.is_root() { self.store.ident_hygiene.insert(expr.into(), hygiene); @@ -1045,6 +1049,9 @@ impl<'db> ExprCollector<'db> { let parent_pat_id = self.alloc_pat_desugared(Pat::Bind { id: parent_binding_id, subpat: None }); self.add_definition_to_binding(parent_binding_id, parent_pat_id); + if let Some(pat_syntax) = pat_syntax { + self.store.pat_map_back.insert(parent_pat_id, pat_syntax); + } *param = parent_pat_id; } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index a8ace690177d0..fbe7c3bd37a47 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -4032,6 +4032,7 @@ fn main() { 100..147 'async_... })': () 114..146 'async ... }': impl AsyncFnOnce(i32) 121..124 'arg': i32 + 121..124 'arg': i32 126..146 '{ ... }': () 136..139 'arg': i32 153..160 'closure': fn closure(impl FnOnce(i32)) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index bcb5e5de16ba9..18e4a5b41d1c9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -4929,6 +4929,7 @@ async fn baz i32>(c: T) { } "#, expect![[r#" + 37..38 'a': T 37..38 'a': T 43..83 '{ ...ait; }': () 53..57 'fut1': >::CallRefFuture<'?> @@ -4938,6 +4939,7 @@ async fn baz i32>(c: T) { 70..74 'fut1': >::CallRefFuture<'?> 70..80 'fut1.await': i32 124..129 'mut b': T + 124..129 'mut b': T 134..174 '{ ...ait; }': () 144..148 'fut2': >::CallRefFuture<'?> 151..152 'b': T @@ -4946,6 +4948,7 @@ async fn baz i32>(c: T) { 161..165 'fut2': >::CallRefFuture<'?> 161..171 'fut2.await': i32 216..217 'c': T + 216..217 'c': T 222..262 '{ ...ait; }': () 232..236 'fut3': >::CallOnceFuture 239..240 'c': T From cca56d16a20c62a92d59c1561382225de8967aef Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 29 Apr 2026 01:24:21 +0300 Subject: [PATCH 121/289] Fix inline_call tests Not sure why they regress, but the inserting of variables for parameters isn't that used anyway. --- .../ide-assists/src/handlers/inline_call.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) 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 21f2249a19c9d..767f59c10dc14 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 @@ -1503,8 +1503,11 @@ async fn foo(arg: u32) -> u32 { } fn spawn(_: T) {} fn main() { - spawn(async move { - bar(42).await * 2 + spawn({ + let arg = 42; + async move { + bar(arg).await * 2 + } }); } "#, @@ -1535,9 +1538,12 @@ async fn foo(arg: u32) -> u32 { } fn spawn(_: T) {} fn main() { - spawn(async move { - bar(42).await; - 42 + spawn({ + let arg = 42; + async move { + bar(arg).await; + 42 + } }); } "#, @@ -1572,10 +1578,11 @@ fn spawn(_: T) {} fn main() { let var = 42; spawn({ + let x = var; let y = var + 1; let z: &u32 = &var; async move { - bar(var).await; + bar(x).await; y + y + *z } }); From 3e873b72f895f12a9d4864d46930e89741c091e9 Mon Sep 17 00:00:00 2001 From: vikashsiwach Date: Sun, 12 Apr 2026 04:06:18 +0530 Subject: [PATCH 122/289] fix: restrict whitespace handling to Rust Pattern_White_Space --- .../rust-analyzer/crates/parser/src/frontmatter.rs | 2 +- src/tools/rust-analyzer/crates/parser/src/lib.rs | 7 +++++++ .../crates/rust-analyzer/src/cli/analysis_stats.rs | 11 ++++++----- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/parser/src/frontmatter.rs b/src/tools/rust-analyzer/crates/parser/src/frontmatter.rs index 2747db4327c56..1edcad64cf38a 100644 --- a/src/tools/rust-analyzer/crates/parser/src/frontmatter.rs +++ b/src/tools/rust-analyzer/crates/parser/src/frontmatter.rs @@ -263,7 +263,7 @@ pub fn strip_ws_lines(input: &str) -> Option { /// True if `c` is considered a whitespace according to Rust language definition. /// See [Rust language reference](https://doc.rust-lang.org/reference/whitespace.html) /// for definitions of these classes. -fn is_whitespace(c: char) -> bool { +pub(crate) fn is_whitespace(c: char) -> bool { // This is Pattern_White_Space. // // Note that this set is stable (ie, it doesn't change with different diff --git a/src/tools/rust-analyzer/crates/parser/src/lib.rs b/src/tools/rust-analyzer/crates/parser/src/lib.rs index 4478bf4e37333..c94df0a4ec0e4 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lib.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lib.rs @@ -56,6 +56,13 @@ pub use crate::{ syntax_kind::SyntaxKind, }; +/// True if `c` is whitespace in Rust source (Unicode Pattern_White_Space as in the language reference). +/// +/// See . +pub fn is_rust_whitespace(c: char) -> bool { + frontmatter::is_whitespace(c) +} + /// Parse the whole of the input as a given syntactic construct. /// /// This covers two main use-cases: 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 e56727d39d671..246c2b6bafb5b 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 @@ -587,8 +587,8 @@ impl flags::AnalysisStats { continue; }; - fn trim(s: &str) -> String { - s.chars().filter(|c| !c.is_whitespace()).collect() + fn drop_whitespace(s: &str) -> String { + s.chars().filter(|c| !parser::is_rust_whitespace(*c)).collect() } let todo = syntax::ast::make::ext::expr_todo().to_string(); @@ -608,7 +608,8 @@ impl flags::AnalysisStats { display_target, ) .unwrap(); - syntax_hit_found |= trim(&original_text) == trim(&generated); + syntax_hit_found |= + drop_whitespace(&original_text) == drop_whitespace(&generated); // Validate if type-checks let mut txt = file_txt.text(db).to_string(); @@ -662,7 +663,7 @@ impl flags::AnalysisStats { let msg = move || { format!( "processing: {:<50}", - trim(&original_text).chars().take(50).collect::() + drop_whitespace(&original_text).chars().take(50).collect::() ) }; if verbosity.is_spammy() { @@ -1646,5 +1647,5 @@ impl fmt::Display for PrettyItemStats { // fn syntax_len(node: SyntaxNode) -> usize { // // Macro expanded code doesn't contain whitespace, so erase *all* whitespace // // to make macro and non-macro code comparable. -// node.to_string().replace(|it: char| it.is_ascii_whitespace(), "").len() +// drop_whitespace(&node.to_string()).len() // } From 34a06091ad7ca0aec551620df6098cbc3a70580a Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 29 Apr 2026 22:41:10 +0800 Subject: [PATCH 123/289] fix: qualify .new path and no complete generic params Example --- ```rust mod foo { pub struct OtherThing; pub struct RefCell(T); impl RefCell { pub fn new(t: T) -> Self { RefCell(t) } } } fn main() { let thing: foo::RefCell = foo::OtherThing.$0; } ``` **Before this PR** ```rust fn main() { let thing: foo::RefCell = RefCell::new(foo::OtherThing$0); } ``` **After this PR** ```rust fn main() { let thing: foo::RefCell = foo::RefCell::new(foo::OtherThing$0); } ``` --- .../ide-completion/src/completions/postfix.rs | 45 ++++++++++++++++--- 1 file changed, 39 insertions(+), 6 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 0a2e15edae9d2..70d4a46e1f3c0 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 @@ -3,7 +3,7 @@ mod format_like; use base_db::SourceDatabase; -use hir::{HirDisplay, ItemInNs, Semantics}; +use hir::{ItemInNs, Semantics}; use ide_db::{ RootDatabase, SnippetCap, documentation::{Documentation, HasDocs}, @@ -117,11 +117,13 @@ pub(crate) fn complete_postfix( postfix_snippet("call", "function(expr)", format!("${{1}}({receiver_text})")) .add_to(acc, ctx.db); - if let Some(expected_ty) = ctx.expected_type.as_ref() { + if let Some(expected_ty) = ctx.expected_type.as_ref() + && let Some(adt) = expected_ty.as_adt() + { let is_valid_new = expected_ty .iterate_assoc_items(ctx.db, |item| { if let hir::AssocItem::Function(func) = item - && func.name(ctx.db).as_str() == "new" + && func.name(ctx.db) == hir::sym::new && !func.has_self_param(ctx.db) { let params = func.params_without_self(ctx.db); @@ -133,8 +135,9 @@ pub(crate) fn complete_postfix( }) .is_some(); - if is_valid_new { - let ty_name = expected_ty.display(ctx.db, ctx.display_target).to_smolstr(); + let adt = hir::ModuleDef::from(adt); + if is_valid_new && let Some(path) = ctx.module.find_path(ctx.db, adt, cfg) { + let ty_name = path.display(ctx.db, ctx.display_target.edition).to_smolstr(); postfix_snippet( "new", @@ -1616,7 +1619,37 @@ impl RefCell { fn main() { let other_thing = OtherThing; - let thing: RefCell = RefCell::new(other_thing$0); + let thing: RefCell = RefCell::new(other_thing$0); +} +"#, + ); + + check_edit( + "new", + r#" +mod foo { + pub struct OtherThing; + pub struct RefCell(T); + impl RefCell { + pub fn new(t: T) -> Self { RefCell(t) } + } +} + +fn main() { + let thing: foo::RefCell = foo::OtherThing.$0; +} +"#, + r#" +mod foo { + pub struct OtherThing; + pub struct RefCell(T); + impl RefCell { + pub fn new(t: T) -> Self { RefCell(t) } + } +} + +fn main() { + let thing: foo::RefCell = foo::RefCell::new(foo::OtherThing$0); } "#, ); From c83e05bf932eb66c70a132a0cb6991dfb77cf4ce Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 27 Apr 2026 00:56:25 +0300 Subject: [PATCH 124/289] Do not intern `AdtDef` rustc uses this struct to store all information about an ADT, but we have other types and are mainly using it for the solver. So store only the information we need. --- src/tools/rust-analyzer/Cargo.lock | 1 + .../rust-analyzer/crates/hir-def/src/attrs.rs | 78 ++-- .../crates/hir-def/src/signatures.rs | 24 +- .../rust-analyzer/crates/hir-ty/Cargo.toml | 1 + .../crates/hir-ty/src/diagnostics/expr.rs | 4 +- .../diagnostics/match_check/pat_analysis.rs | 12 +- .../crates/hir-ty/src/display.rs | 14 +- .../rust-analyzer/crates/hir-ty/src/drop.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 6 +- .../crates/hir-ty/src/infer/cast.rs | 4 +- .../hir-ty/src/infer/closure/analysis.rs | 2 +- .../closure/analysis/expr_use_visitor.rs | 6 +- .../crates/hir-ty/src/infer/expr.rs | 6 +- .../crates/hir-ty/src/infer/pat.rs | 4 +- .../crates/hir-ty/src/inhabitedness.rs | 7 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 6 +- .../hir-ty/src/method_resolution/probe.rs | 4 +- .../crates/hir-ty/src/mir/eval.rs | 16 +- .../crates/hir-ty/src/mir/eval/shim.rs | 4 +- .../crates/hir-ty/src/mir/eval/shim/simd.rs | 2 +- .../crates/hir-ty/src/mir/lower.rs | 4 +- .../crates/hir-ty/src/next_solver/interner.rs | 359 +++++++----------- .../crates/hir-ty/src/next_solver/ty.rs | 8 +- .../crates/hir-ty/src/next_solver/util.rs | 2 +- .../crates/hir-ty/src/representability.rs | 6 +- .../hir-ty/src/tests/closure_captures.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/traits.rs | 10 +- .../crates/hir-ty/src/variance.rs | 7 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 14 +- .../crates/hir/src/source_analyzer.rs | 6 +- .../ide-completion/src/tests/flyimport.rs | 2 +- 31 files changed, 270 insertions(+), 355 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 768c3a9aa616a..0b3fd2b8ea56d 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -876,6 +876,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", + "bitflags 2.9.4", "cov-mark", "either", "ena", 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 5cf5a9b6be847..5dc410be27216 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -724,52 +724,56 @@ impl AttrFlags { return None; } - return repr(db, owner); + Self::repr_assume_has(db, owner) + } - #[salsa::tracked] - fn repr(db: &dyn DefDatabase, owner: AdtId) -> Option { - let mut result = None; - collect_attrs::(db, owner.into(), |attr| { - let mut current = None; - if let ast::Meta::TokenTreeMeta(attr) = &attr - && let Some(path) = attr.path() - && let Some(tt) = attr.token_tree() + /// Only call this when you've verified the type indeed has a `#[repr]` attribute! + /// + /// Prefer [`AttrFlags::repr()`] in non-perf-sensitive places as it also has a check that + /// that the ADT has repr. + #[salsa::tracked] + pub fn repr_assume_has(db: &dyn DefDatabase, owner: AdtId) -> Option { + let mut result = None; + collect_attrs::(db, owner.into(), |attr| { + let mut current = None; + if let ast::Meta::TokenTreeMeta(attr) = &attr + && let Some(path) = attr.path() + && let Some(tt) = attr.token_tree() + { + if path.is1("repr") + && let Some(repr) = parse_repr_tt(&tt) { - if path.is1("repr") - && let Some(repr) = parse_repr_tt(&tt) - { - current = Some(repr); - } else if path.is1("rustc_scalable_vector") - && let mut tt = TokenTreeChildren::new(&tt) - && let Some(NodeOrToken::Token(scalable)) = tt.next() - && let Some(scalable) = ast::IntNumber::cast(scalable) - && let Ok(scalable) = scalable.value() - && let Ok(scalable) = scalable.try_into() - { - current = Some(ReprOptions { - scalable: Some(rustc_abi::ScalableElt::ElementCount(scalable)), - ..ReprOptions::default() - }); - } - } else if let ast::Meta::PathMeta(attr) = &attr - && attr.path().is1("rustc_scalable_vector") + current = Some(repr); + } else if path.is1("rustc_scalable_vector") + && let mut tt = TokenTreeChildren::new(&tt) + && let Some(NodeOrToken::Token(scalable)) = tt.next() + && let Some(scalable) = ast::IntNumber::cast(scalable) + && let Ok(scalable) = scalable.value() + && let Ok(scalable) = scalable.try_into() { current = Some(ReprOptions { - scalable: Some(rustc_abi::ScalableElt::Container), + scalable: Some(rustc_abi::ScalableElt::ElementCount(scalable)), ..ReprOptions::default() }); } + } else if let ast::Meta::PathMeta(attr) = &attr + && attr.path().is1("rustc_scalable_vector") + { + current = Some(ReprOptions { + scalable: Some(rustc_abi::ScalableElt::Container), + ..ReprOptions::default() + }); + } - if let Some(current) = current { - match &mut result { - Some(existing) => merge_repr(existing, current), - None => result = Some(current), - } + if let Some(current) = current { + match &mut result { + Some(existing) => merge_repr(existing, current), + None => result = Some(current), } - ControlFlow::Continue(()) - }); - result - } + } + ControlFlow::Continue(()) + }); + result } /// Call this only if there are legacy const generics, to save memory. 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 474b238addc46..8136e11412ab7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -128,13 +128,11 @@ impl StructSignature { source_map, ) } -} -impl StructSignature { #[inline] pub fn repr(&self, db: &dyn DefDatabase, id: StructId) -> Option { if self.flags.contains(StructFlags::HAS_REPR) { - AttrFlags::repr(db, id.into()) + AttrFlags::repr_assume_has(db, id.into()) } else { None } @@ -202,6 +200,15 @@ impl UnionSignature { source_map, ) } + + #[inline] + pub fn repr(&self, db: &dyn DefDatabase, id: UnionId) -> Option { + if self.flags.contains(StructFlags::HAS_REPR) { + AttrFlags::repr_assume_has(db, id.into()) + } else { + None + } + } } bitflags! { @@ -211,6 +218,8 @@ bitflags! { const HAS_REPR = 1 << 0; /// Indicates whether the enum has a `#[rustc_has_incoherent_inherent_impls]` attribute. const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1; + /// Whether this enum has `#[fundamental]`. + const FUNDAMENTAL = 1 << 2; } } @@ -243,6 +252,9 @@ impl EnumSignature { if attrs.contains(AttrFlags::HAS_REPR) { flags |= EnumFlags::HAS_REPR; } + if attrs.contains(AttrFlags::FUNDAMENTAL) { + flags |= EnumFlags::FUNDAMENTAL; + } let InFile { file_id, value: source } = loc.source(db); let (store, generic_params, source_map) = lower_generic_params( @@ -276,7 +288,11 @@ impl EnumSignature { #[inline] pub fn repr(&self, db: &dyn DefDatabase, id: EnumId) -> Option { - if self.flags.contains(EnumFlags::HAS_REPR) { AttrFlags::repr(db, id.into()) } else { None } + if self.flags.contains(EnumFlags::HAS_REPR) { + AttrFlags::repr_assume_has(db, id.into()) + } else { + None + } } } bitflags::bitflags! { diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 18426f3095b11..e8eda74e40017 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -33,6 +33,7 @@ query-group.workspace = true salsa.workspace = true salsa-macros.workspace = true petgraph.workspace = true +bitflags.workspace = true ra-ap-rustc_abi.workspace = true ra-ap-rustc_index.workspace = true 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 99dddf0bf4cde..93772ca452aac 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 @@ -15,7 +15,7 @@ use intern::sym; use itertools::Itertools; use rustc_hash::FxHashSet; use rustc_pattern_analysis::constructor::Constructor; -use rustc_type_ir::inherent::{AdtDef, IntoKind}; +use rustc_type_ir::inherent::IntoKind; use syntax::{ AstNode, ast::{self, UnaryOp}, @@ -311,7 +311,7 @@ impl<'db> ExprValidator<'db> { value_or_partial.is_none_or(|v| !matches!(v, ValueNs::StaticId(_))) } Expr::Field { expr, .. } => match self.infer.expr_ty(*expr).kind() { - TyKind::Adt(adt, ..) if matches!(adt.def_id().0, AdtId::UnionId(_)) => false, + TyKind::Adt(adt, ..) if matches!(adt.def_id(), AdtId::UnionId(_)) => false, _ => self.is_known_valid_scrutinee(*expr), }, Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index 5ed7eb7a66e12..87ffa325448c6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -11,7 +11,7 @@ use rustc_pattern_analysis::{ constructor::{Constructor, ConstructorSet, VariantVisibility}, usefulness::{PlaceValidity, UsefulnessReport, compute_match_usefulness}, }; -use rustc_type_ir::inherent::{AdtDef, IntoKind}; +use rustc_type_ir::inherent::IntoKind; use smallvec::{SmallVec, smallvec}; use stdx::never; @@ -197,7 +197,7 @@ impl<'a, 'db> MatchCheckCtx<'a, 'db> { arity = substs.len(); } TyKind::Adt(adt_def, _) => { - let adt = adt_def.def_id().0; + let adt = adt_def.def_id(); ctor = match pat.kind.as_ref() { PatKind::Leaf { .. } if matches!(adt, hir_def::AdtId::UnionId(_)) => { UnionField @@ -265,7 +265,7 @@ impl<'a, 'db> MatchCheckCtx<'a, 'db> { }, TyKind::Adt(adt, substs) => { let variant = - Self::variant_id_for_adt(self.db, pat.ctor(), adt.def_id().0).unwrap(); + Self::variant_id_for_adt(self.db, pat.ctor(), adt.def_id()).unwrap(); let subpatterns = self .list_variant_fields(*pat.ty(), variant) .zip(subpatterns) @@ -325,7 +325,7 @@ impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> { TyKind::Tuple(tys) => tys.len(), TyKind::Adt(adt_def, ..) => { let variant = - Self::variant_id_for_adt(self.db, ctor, adt_def.def_id().0).unwrap(); + Self::variant_id_for_adt(self.db, ctor, adt_def.def_id()).unwrap(); variant.fields(self.db).fields().len() } _ => { @@ -359,7 +359,7 @@ impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> { } TyKind::Ref(_, rty, _) => single(rty), TyKind::Adt(adt_def, ..) => { - let adt = adt_def.def_id().0; + let adt = adt_def.def_id(); let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap(); let visibilities = @@ -428,7 +428,7 @@ impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> { TyKind::Int(..) | TyKind::Uint(..) => unhandled(), TyKind::Array(..) | TyKind::Slice(..) => unhandled(), TyKind::Adt(adt_def, subst) => { - let adt = adt_def.def_id().0; + let adt = adt_def.def_id(); match adt { hir_def::AdtId::EnumId(enum_id) => { let enum_data = enum_id.enum_variants(cx.db); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index ca3723f8ef29d..6bc55bc0e4731 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -43,7 +43,7 @@ use rustc_ast_ir::FloatTy; use rustc_hash::FxHashSet; use rustc_type_ir::{ AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, RegionKind, Upcast, - inherent::{AdtDef, GenericArgs as _, IntoKind, Term as _, Ty as _, Tys as _}, + inherent::{GenericArgs as _, IntoKind, Term as _, Ty as _, Tys as _}, }; use smallvec::SmallVec; use span::Edition; @@ -78,7 +78,7 @@ fn async_gen_item_ty_from_yield_ty<'db>( let TyKind::Adt(poll_def, poll_args) = yield_ty.kind() else { return None; }; - if poll_def.inner().id != poll_id { + if poll_def.def_id() != poll_id { return None; } let [poll_inner] = poll_args.as_slice() else { @@ -89,7 +89,7 @@ fn async_gen_item_ty_from_yield_ty<'db>( let TyKind::Adt(option_def, option_args) = poll_inner.kind() else { return None; }; - if option_def.inner().id != option_id { + if option_def.def_id() != option_id { return None; } let [item] = option_args.as_slice() else { @@ -890,7 +890,7 @@ fn render_const_scalar_inner<'db>( f.write_str("&")?; render_const_scalar(f, bytes, memory_map, t) } - TyKind::Adt(adt, _) if b.len() == 2 * size_of::() => match adt.def_id().0 { + TyKind::Adt(adt, _) if b.len() == 2 * size_of::() => match adt.def_id() { hir_def::AdtId::StructId(s) => { let data = StructSignature::of(f.db, s); write!(f, "&{}", data.name.display(f.db, f.edition()))?; @@ -944,7 +944,7 @@ fn render_const_scalar_inner<'db>( f.write_str(")") } TyKind::Adt(def, args) => { - let def = def.def_id().0; + let def = def.def_id(); let Ok(layout) = f.db.layout_of_adt(def, args.store(), param_env.store()) else { return f.write_str(""); }; @@ -1420,7 +1420,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } } TyKind::Adt(def, parameters) => { - let def_id = def.def_id().0; + let def_id = def.def_id(); f.start_location_link(def_id.into()); match f.display_kind { DisplayKind::Diagnostics | DisplayKind::Test => { @@ -1458,7 +1458,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } f.end_location_link(); - hir_fmt_generics(f, parameters.as_slice(), Some(def.def_id().0.into()), None)?; + hir_fmt_generics(f, parameters.as_slice(), Some(def.def_id().into()), None)?; } TyKind::Alias(alias_ty @ AliasTy { kind: AliasTyKind::Projection { .. }, .. }) => { write_projection(f, &alias_ty, trait_bounds_need_parens)? diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index 0d25d7dbd1d13..07c705ee2f731 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -70,7 +70,7 @@ fn has_drop_glue_impl<'db>( let db = infcx.interner.db; match ty.kind() { TyKind::Adt(adt_def, subst) => { - let adt_id = adt_def.def_id().0; + let adt_id = adt_def.def_id(); if adt_def.destructor(infcx.interner).is_some() { return DropGlue::HasDropGlue; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 5cccba15848be..333219c5af870 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -55,7 +55,7 @@ use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ AliasTyKind, TypeFoldable, - inherent::{AdtDef, Const as _, IntoKind, Ty as _}, + inherent::{Const as _, IntoKind, Ty as _}, }; use smallvec::SmallVec; use span::Edition; @@ -1875,7 +1875,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { return self.err_ty(); } match ty.kind() { - TyKind::Adt(adt_def, substs) => match adt_def.def_id().0 { + TyKind::Adt(adt_def, substs) => match adt_def.def_id() { AdtId::StructId(struct_id) => { match self .db @@ -2197,7 +2197,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { // If we can resolve to an enum variant, it takes priority over associated type // of the same name. if let TyKind::Adt(adt_def, _) = ty.kind() - && let AdtId::EnumId(id) = adt_def.def_id().0 + && let AdtId::EnumId(id) = adt_def.def_id() { let enum_data = id.enum_variants(self.db); if let Some(variant) = enum_data.variant(current_segment.name) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index d23a32d81b64f..daf954c21798a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -10,7 +10,7 @@ use rustc_hash::FxHashSet; use rustc_type_ir::{ InferTy, TypeVisitableExt, UintTy, elaborate, error::TypeError, - inherent::{AdtDef, BoundExistentialPredicates as _, IntoKind, Ty as _}, + inherent::{BoundExistentialPredicates as _, IntoKind, Ty as _}, }; use stdx::never; @@ -529,7 +529,7 @@ fn pointer_kind<'db>( TyKind::Slice(_) | TyKind::Str => Ok(Some(PointerKind::Length)), TyKind::Dynamic(bounds, _) => Ok(Some(PointerKind::VTable(bounds))), TyKind::Adt(adt_def, subst) => { - let id = adt_def.def_id().0; + let id = adt_def.def_id(); let AdtId::StructId(id) = id else { never!("`{:?}` should be sized but is not?", ty); return Err(()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs index 79bdc6cea1042..eb4eb8cce3a85 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -1132,7 +1132,7 @@ fn restrict_repr_packed_field_ref_capture( // Return true for fields of packed structs. match p.kind { ProjectionKind::Field { .. } => match ty.kind() { - TyKind::Adt(def, _) if def.repr().packed() => { + TyKind::Adt(def, _) if def.is_packed() => { // We stop here regardless of field alignment. Field alignment can change as // types change, including the types of private fields in other crates, and that // shouldn't affect how we compute our captures. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index 16b84217b8608..b234e69197b47 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -17,7 +17,7 @@ use hir_def::{ use rustc_ast_ir::{try_visit, visit::VisitorResult}; use rustc_type_ir::{ FallibleTypeFolder, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, - inherent::{AdtDef, IntoKind, Ty as _}, + inherent::{IntoKind, Ty as _}, }; use smallvec::{SmallVec, smallvec}; use syntax::ast::{BinaryOp, UnaryOp}; @@ -799,7 +799,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { // expression that will actually be used match self.cx.structurally_resolve_type(with_expr.into(), with_place.place.ty()).kind() { TyKind::Adt(adt, args) if adt.is_struct() => { - let AdtId::StructId(adt) = adt.def_id().0 else { unreachable!() }; + let AdtId::StructId(adt) = adt.def_id() else { unreachable!() }; let adt_fields = VariantId::from(adt).fields(self.cx.db).fields(); let adt_field_types = self.cx.db.field_types(adt.into()); // Consume those fields of the with expression that are needed. @@ -1701,7 +1701,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { // to assume that more cases will be added to the variant in the future. This mean // that we should handle non-exhaustive SingleVariant the same way we would handle // a MultiVariant. - match def.def_id().0 { + match def.def_id() { AdtId::StructId(_) | AdtId::UnionId(_) => false, AdtId::EnumId(did) => { let has_foreign_non_exhaustive = || { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 0e3716eaa6427..782c6be7c39f1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -20,7 +20,7 @@ use rustc_ast_ir::Mutability; use rustc_hash::FxHashMap; use rustc_type_ir::{ InferTy, Interner, - inherent::{AdtDef, GenericArgs as _, IntoKind, Ty as _}, + inherent::{GenericArgs as _, IntoKind, Ty as _}, }; use stdx::never; use syntax::ast::RangeOp; @@ -1039,7 +1039,7 @@ impl<'db> InferenceContext<'_, 'db> { never!("non-ADT passed to check_struct_expr_fields"); return; }; - let adt_id = adt.def_id().0; + let adt_id = adt.def_id(); let variant_fields = variant.fields(self.db); let variant_field_tys = self.db.field_types(variant); @@ -1623,7 +1623,7 @@ impl<'db> InferenceContext<'_, 'db> { }) }); } - TyKind::Adt(adt, parameters) => match adt.def_id().0 { + TyKind::Adt(adt, parameters) => match adt.def_id() { hir_def::AdtId::StructId(s) => { let local_id = s.fields(self.db).field(name)?; let field = FieldId { parent: s.into(), local_id }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index ac209adef8708..a7b82dad4fc38 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -20,7 +20,7 @@ use rustc_ast_ir::Mutability; use rustc_hash::FxHashMap; use rustc_type_ir::{ TypeVisitableExt as _, - inherent::{AdtDef as _, IntoKind as _, Ty as _}, + inherent::{IntoKind as _, Ty as _}, }; use span::Edition; use tracing::{debug, instrument, trace}; @@ -725,7 +725,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { && let TyKind::Adt(scrutinee_adt, _) = expected.kind() // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern. - && until_adt != Some(scrutinee_adt.def_id().0) + && until_adt != Some(scrutinee_adt.def_id()) // At this point, the pattern isn't able to match `expected` without peeling. Check // that it implements `Deref` before assuming it's a smart pointer, to get a normal // type error instead of a missing impl error if not. This only checks for `Deref`, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs index 74d66123ea53a..41470c54e5e79 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -5,10 +5,7 @@ use hir_def::{ AdtId, EnumVariantId, ModuleId, VariantId, signatures::VariantFields, visibility::Visibility, }; use rustc_hash::FxHashSet; -use rustc_type_ir::{ - TypeSuperVisitable, TypeVisitable, TypeVisitor, - inherent::{AdtDef, IntoKind}, -}; +use rustc_type_ir::{TypeSuperVisitable, TypeVisitable, TypeVisitor, inherent::IntoKind}; use crate::{ consteval::try_const_usize, @@ -85,7 +82,7 @@ impl<'db> TypeVisitor> for UninhabitedFrom<'_, 'db> { } let r = match ty.kind() { - TyKind::Adt(adt, subst) => self.visit_adt(adt.def_id().0, subst), + TyKind::Adt(adt, subst) => self.visit_adt(adt.def_id(), subst), TyKind::Never => BREAK_VISIBLY_UNINHABITED, TyKind::Tuple(..) => ty.super_visit_with(self), TyKind::Array(item_ty, len) => match try_const_usize(self.infcx.interner.db, len) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 798c62c192405..d2759ddeeb197 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -177,7 +177,7 @@ pub fn layout_of_ty_query( .unwrap_or(ty.as_ref()); let result = match ty.kind() { TyKind::Adt(def, args) => { - match def.inner().id { + match def.def_id() { hir_def::AdtId::StructId(s) => { let repr = AttrFlags::repr(db, s.into()).unwrap_or_default(); if repr.simd() { @@ -193,7 +193,7 @@ pub fn layout_of_ty_query( } _ => {} } - return db.layout_of_adt(def.inner().id, args.store(), trait_env); + return db.layout_of_adt(def.def_id(), args.store(), trait_env); } TyKind::Bool => Layout::scalar( dl, @@ -374,7 +374,7 @@ pub(crate) fn layout_of_ty_cycle_result( fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> { match pointee.kind() { TyKind::Adt(def, args) => { - let struct_id = match def.inner().id { + let struct_id = match def.def_id() { AdtId::StructId(id) => id, _ => return pointee, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index 8a28b16724548..6eeec6cb41c89 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs @@ -15,7 +15,7 @@ use rustc_type_ir::{ InferTy, TypeVisitableExt, Upcast, Variance, elaborate::{self, supertrait_def_ids}, fast_reject::{DeepRejectCtxt, TreatParams, simplify_type}, - inherent::{AdtDef as _, BoundExistentialPredicates as _, IntoKind, Ty as _}, + inherent::{BoundExistentialPredicates as _, IntoKind, Ty as _}, }; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -937,7 +937,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { } } TyKind::Adt(def, _) => { - let def_id = def.def_id().0; + let def_id = def.def_id(); self.assemble_inherent_impl_candidates_for_type( &SimplifiedType::Adt(def_id.into()), receiver_steps, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 80e429c4c8232..87d64e567e7fc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -30,7 +30,7 @@ use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ AliasTyKind, - inherent::{AdtDef, GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _}, + inherent::{GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _}, }; use span::FileId; use stdx::never; @@ -1651,7 +1651,7 @@ impl<'db> Evaluator<'db> { let TyKind::Adt(adt_def, _) = ty.kind() else { return Ok(0); }; - let AdtId::EnumId(e) = adt_def.def_id().0 else { + let AdtId::EnumId(e) = adt_def.def_id() else { return Ok(0); }; match &layout.variants { @@ -1701,7 +1701,7 @@ impl<'db> Evaluator<'db> { return Ok(it); } if let TyKind::Adt(adt_ef, subst) = kind - && let AdtId::StructId(struct_id) = adt_ef.def_id().0 + && let AdtId::StructId(struct_id) = adt_ef.def_id() { let field_types = self.db.field_types(struct_id.into()); if let Some(ty) = @@ -1768,8 +1768,8 @@ impl<'db> Evaluator<'db> { } TyKind::Adt(adt_def, target_subst) => match ¤t_ty.kind() { TyKind::Adt(current_adt_def, current_subst) => { - let id = adt_def.def_id().0; - let current_id = current_adt_def.def_id().0; + let id = adt_def.def_id(); + let current_id = current_adt_def.def_id(); if id != current_id { not_supported!("unsizing struct with different type"); } @@ -2409,7 +2409,7 @@ impl<'db> Evaluator<'db> { )?; } } - TyKind::Adt(adt, subst) => match adt.def_id().0 { + TyKind::Adt(adt, subst) => match adt.def_id() { AdtId::StructId(s) => { let data = s.fields(this.db); let layout = this.layout(ty)?; @@ -2527,7 +2527,7 @@ impl<'db> Evaluator<'db> { let new_id = self.vtable_map.id(ty); self.write_memory(addr, &new_id.to_le_bytes())?; } - TyKind::Adt(id, args) => match id.def_id().0 { + TyKind::Adt(id, args) => match id.def_id() { AdtId::StructId(s) => { for (i, (_, ty)) in self.db.field_types(s.into()).iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); @@ -3046,7 +3046,7 @@ impl<'db> Evaluator<'db> { } match ty.kind() { TyKind::Adt(adt_def, subst) => { - let id = adt_def.def_id().0; + let id = adt_def.def_id(); match id { AdtId::StructId(s) => { let data = StructSignature::of(self.db, s); 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 9586d38abc517..4154760f6e16f 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 @@ -6,7 +6,7 @@ use std::cmp::{self, Ordering}; use hir_def::{attrs::AttrFlags, signatures::FunctionSignature}; use hir_expand::name::Name; use intern::sym; -use rustc_type_ir::inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Ty as _}; +use rustc_type_ir::inherent::{GenericArgs as _, IntoKind, SliceLike, Ty as _}; use stdx::never; use crate::{ @@ -1360,7 +1360,7 @@ impl<'db> Evaluator<'db> { "dyn concrete type", )?, TyKind::Adt(adt_def, subst) => { - let id = adt_def.def_id().0; + let id = adt_def.def_id(); let layout = self.layout_adt(id, subst)?; let id = match id { AdtId::StructId(s) => s, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs index e0b3e571b8567..6e20562b4ed8b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs @@ -13,7 +13,7 @@ impl<'db> Evaluator<'db> { let len = match subst.as_slice().get(1).and_then(|it| it.konst()) { Some(len) => len, _ => { - if let AdtId::StructId(id) = adt_def.def_id().0 { + if let AdtId::StructId(id) = adt_def.def_id() { let struct_data = id.fields(self.db); let fields = struct_data.fields(); let Some((first_field, _)) = fields.iter().next() else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 7dcc00858e25d..d16e1b0d5964e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -22,7 +22,7 @@ use itertools::{EitherOrBoth, Itertools}; use la_arena::ArenaMap; use rustc_apfloat::Float; use rustc_hash::FxHashMap; -use rustc_type_ir::inherent::{AdtDef, Const as _, GenericArgs as _, IntoKind, Ty as _}; +use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _}; use span::{Edition, FileId}; use syntax::TextRange; use triomphe::Arc; @@ -2097,7 +2097,7 @@ fn convert_closure_capture_projections( } TyKind::Adt(adt_def, _) => { let local_field_id = LocalFieldId::from_raw(RawIdx::from_u32(field_idx)); - let field = match adt_def.def_id().0 { + let field = match adt_def.def_id() { AdtId::StructId(id) => { FieldId { parent: id.into(), local_id: local_field_id } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index b8b8d15347dfb..fc83ebc625805 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -4,25 +4,24 @@ use std::{fmt, ops::ControlFlow}; use intern::{Interned, InternedRef, InternedSliceRef, impl_internable}; use macros::GenericTypeVisitable; +use rustc_abi::ReprOptions; use rustc_ast_ir::{FloatTy, IntTy, UintTy}; pub use tls_cache::clear_tls_solver_cache; pub use tls_db::{attach_db, attach_db_allow_change, with_attached_db}; use base_db::Crate; use hir_def::{ - AdtId, CallableDefId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, HasModule, + AdtId, CallableDefId, DefWithBodyId, EnumId, ExpressionStoreOwnerId, HasModule, ItemContainerId, StructId, UnionId, VariantId, attrs::AttrFlags, expr_store::{Body, ExpressionStore}, hir::{ClosureKind as HirClosureKind, CoroutineKind as HirCoroutineKind}, lang_item::LangItems, signatures::{ - EnumSignature, FieldData, FnFlags, FunctionSignature, ImplFlags, ImplSignature, + EnumFlags, EnumSignature, FnFlags, FunctionSignature, ImplFlags, ImplSignature, StructFlags, StructSignature, TraitFlags, TraitSignature, UnionSignature, }, }; -use la_arena::Idx; -use rustc_abi::{ReprFlags, ReprOptions}; use rustc_hash::FxHashSet; use rustc_index::bit_set::DenseBitSet; use rustc_type_ir::{ @@ -419,266 +418,167 @@ pub struct AllocId; interned_slice!(VariancesOfStorage, VariancesOf, StoredVariancesOf, variances, Variance, Variance); -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct VariantIdx(usize); - -// FIXME: could/should store actual data? -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub enum VariantDef { - Struct(StructId), - Union(UnionId), - Enum(EnumVariantId), -} - -impl VariantDef { - pub fn id(&self) -> VariantId { - match self { - VariantDef::Struct(struct_id) => VariantId::StructId(*struct_id), - VariantDef::Union(union_id) => VariantId::UnionId(*union_id), - VariantDef::Enum(enum_variant_id) => VariantId::EnumVariantId(*enum_variant_id), - } - } - - pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Idx, FieldData)> { - let id: VariantId = match self { - VariantDef::Struct(it) => (*it).into(), - VariantDef::Union(it) => (*it).into(), - VariantDef::Enum(it) => (*it).into(), - }; - id.fields(db).fields().iter().map(|(id, data)| (id, data.clone())).collect() +bitflags::bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + struct AdtFlags: u8 { + const IS_FUNDAMENTAL = 1 << 0; + const IS_PACKED = 1 << 1; + const HAS_REPR = 1 << 2; + const IS_PHANTOM_DATA = 1 << 3; + const IS_MANUALLY_DROP = 1 << 4; + const IS_BOX = 1 << 5; } } -/* -/// Definition of a variant -- a struct's fields or an enum variant. -#[derive(Debug, HashStable, TyEncodable, TyDecodable)] -pub struct VariantDef { - /// `DefId` that identifies the variant itself. - /// If this variant belongs to a struct or union, then this is a copy of its `DefId`. - pub def_id: DefId, - /// `DefId` that identifies the variant's constructor. - /// If this variant is a struct variant, then this is `None`. - pub ctor: Option<(CtorKind, DefId)>, - /// Variant or struct name, maybe empty for anonymous adt (struct or union). - pub name: Symbol, - /// Discriminant of this variant. - pub discr: VariantDiscr, - /// Fields of this variant. - pub fields: IndexVec, - /// The error guarantees from parser, if any. - tainted: Option, - /// Flags of the variant (e.g. is field list non-exhaustive)? - flags: VariantFlags, +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum AdtDefInner { + Struct { id: StructId, flags: AdtFlags }, + Union { id: UnionId, flags: AdtFlags }, + Enum { id: EnumId, flags: AdtFlags }, } -*/ -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct AdtFlags { - is_enum: bool, - is_union: bool, - is_struct: bool, - is_phantom_data: bool, - is_fundamental: bool, - is_box: bool, - is_manually_drop: bool, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct AdtDefInner { - pub id: AdtId, - variants: Vec<(VariantIdx, VariantDef)>, - flags: AdtFlags, - repr: ReprOptions, -} - -// We're gonna cheat a little bit and implement `Hash` on only the `DefId` and -// accept there might be collisions for def ids from different crates (or across -// different tests, oh my). -impl std::hash::Hash for AdtDefInner { - #[inline] - fn hash(&self, s: &mut H) { - self.id.hash(s) - } -} +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct AdtDef(AdtDefInner); -#[salsa::interned(no_lifetime, constructor = new_)] -pub struct AdtDef { - #[returns(ref)] - data_: AdtDefInner, -} +const _: () = assert!(size_of::() == 12); impl AdtDef { pub fn new<'db>(def_id: AdtId, interner: DbInterner<'db>) -> Self { let db = interner.db(); - let (flags, variants, repr) = match def_id { - AdtId::StructId(struct_id) => { - let data = StructSignature::of(db, struct_id); - - let flags = AdtFlags { - is_enum: false, - is_union: false, - is_struct: true, - is_phantom_data: data.flags.contains(StructFlags::IS_PHANTOM_DATA), - is_fundamental: data.flags.contains(StructFlags::FUNDAMENTAL), - is_box: data.flags.contains(StructFlags::IS_BOX), - is_manually_drop: data.flags.contains(StructFlags::IS_MANUALLY_DROP), - }; - - let variants = vec![(VariantIdx(0), VariantDef::Struct(struct_id))]; - - let data_repr = data.repr(db, struct_id); - let mut repr_flags = ReprFlags::empty(); - if flags.is_box { - repr_flags.insert(ReprFlags::IS_LINEAR); + let inner = match def_id { + AdtId::StructId(id) => { + let data = StructSignature::of(db, id); + let mut flags = AdtFlags::empty(); + if data.flags.contains(StructFlags::FUNDAMENTAL) { + flags.insert(AdtFlags::IS_FUNDAMENTAL); } - if data_repr.is_some_and(|r| r.c()) { - repr_flags.insert(ReprFlags::IS_C); + if data.flags.contains(StructFlags::IS_PHANTOM_DATA) { + flags.insert(AdtFlags::IS_PHANTOM_DATA); } - if data_repr.is_some_and(|r| r.simd()) { - repr_flags.insert(ReprFlags::IS_SIMD); + if data.flags.contains(StructFlags::IS_MANUALLY_DROP) { + flags.insert(AdtFlags::IS_MANUALLY_DROP); } - let repr = ReprOptions { - align: data_repr.and_then(|r| r.align), - pack: data_repr.and_then(|r| r.pack), - int: data_repr.and_then(|r| r.int), - flags: repr_flags, - ..ReprOptions::default() - }; - - (flags, variants, repr) - } - AdtId::UnionId(union_id) => { - let flags = AdtFlags { - is_enum: false, - is_union: true, - is_struct: false, - is_phantom_data: false, - is_fundamental: false, - is_box: false, - is_manually_drop: false, - }; - - let variants = vec![(VariantIdx(0), VariantDef::Union(union_id))]; - - let data_repr = AttrFlags::repr(db, union_id.into()); - let mut repr_flags = ReprFlags::empty(); - if flags.is_box { - repr_flags.insert(ReprFlags::IS_LINEAR); + if data.flags.contains(StructFlags::IS_BOX) { + flags.insert(AdtFlags::IS_BOX); } - if data_repr.is_some_and(|r| r.c()) { - repr_flags.insert(ReprFlags::IS_C); + if data.flags.contains(StructFlags::HAS_REPR) { + flags.insert(AdtFlags::HAS_REPR); + if data.repr(db, id).is_some_and(|repr| repr.packed()) { + flags.insert(AdtFlags::IS_PACKED); + } } - if data_repr.is_some_and(|r| r.simd()) { - repr_flags.insert(ReprFlags::IS_SIMD); + AdtDefInner::Struct { id, flags } + } + AdtId::UnionId(id) => { + let data = UnionSignature::of(db, id); + let mut flags = AdtFlags::empty(); + if data.flags.contains(StructFlags::FUNDAMENTAL) { + flags.insert(AdtFlags::IS_FUNDAMENTAL); } - let repr = ReprOptions { - align: data_repr.and_then(|r| r.align), - pack: data_repr.and_then(|r| r.pack), - int: data_repr.and_then(|r| r.int), - flags: repr_flags, - ..ReprOptions::default() - }; - - (flags, variants, repr) - } - AdtId::EnumId(enum_id) => { - let flags = AdtFlags { - is_enum: true, - is_union: false, - is_struct: false, - is_phantom_data: false, - is_fundamental: false, - is_box: false, - is_manually_drop: false, - }; - - let variants = enum_id - .enum_variants(db) - .variants - .iter() - .enumerate() - .map(|(idx, v)| (VariantIdx(idx), v)) - .map(|(idx, v)| (idx, VariantDef::Enum(v.0))) - .collect(); - - let data_repr = AttrFlags::repr(db, enum_id.into()); - - let mut repr_flags = ReprFlags::empty(); - if flags.is_box { - repr_flags.insert(ReprFlags::IS_LINEAR); + if data.flags.contains(StructFlags::HAS_REPR) { + flags.insert(AdtFlags::HAS_REPR); + if data.repr(db, id).is_some_and(|repr| repr.packed()) { + flags.insert(AdtFlags::IS_PACKED); + } } - if data_repr.is_some_and(|r| r.c()) { - repr_flags.insert(ReprFlags::IS_C); + AdtDefInner::Union { id, flags } + } + AdtId::EnumId(id) => { + let data = EnumSignature::of(db, id); + let mut flags = AdtFlags::empty(); + if data.flags.contains(EnumFlags::FUNDAMENTAL) { + flags.insert(AdtFlags::IS_FUNDAMENTAL); } - if data_repr.is_some_and(|r| r.simd()) { - repr_flags.insert(ReprFlags::IS_SIMD); + if data.flags.contains(EnumFlags::HAS_REPR) { + flags.insert(AdtFlags::HAS_REPR); + if data.repr(db, id).is_some_and(|repr| repr.packed()) { + flags.insert(AdtFlags::IS_PACKED); + } } - - let repr = ReprOptions { - align: data_repr.and_then(|r| r.align), - pack: data_repr.and_then(|r| r.pack), - int: data_repr.and_then(|r| r.int), - flags: repr_flags, - ..ReprOptions::default() - }; - - (flags, variants, repr) + AdtDefInner::Enum { id, flags } } }; + AdtDef(inner) + } - AdtDef::new_(db, AdtDefInner { id: def_id, variants, flags, repr }) + #[inline] + pub fn def_id(self) -> AdtId { + match self.0 { + AdtDefInner::Struct { id, .. } => AdtId::StructId(id), + AdtDefInner::Union { id, .. } => AdtId::UnionId(id), + AdtDefInner::Enum { id, .. } => AdtId::EnumId(id), + } } - pub fn inner(&self) -> &AdtDefInner { - crate::with_attached_db(|db| { - let inner = self.data_(db); - // SAFETY: ¯\_(ツ)_/¯ - unsafe { std::mem::transmute(inner) } - }) + #[inline] + fn flags(self) -> AdtFlags { + match self.0 { + AdtDefInner::Struct { flags, .. } + | AdtDefInner::Union { flags, .. } + | AdtDefInner::Enum { flags, .. } => flags, + } } - pub fn is_enum(&self) -> bool { - self.inner().flags.is_enum + #[inline] + pub fn is_struct(self) -> bool { + matches!(self.0, AdtDefInner::Struct { .. }) + } + + #[inline] + pub fn is_union(self) -> bool { + matches!(self.0, AdtDefInner::Union { .. }) } - pub fn is_box(&self) -> bool { - self.inner().flags.is_box + #[inline] + pub fn is_enum(self) -> bool { + matches!(self.0, AdtDefInner::Enum { .. }) } #[inline] - pub fn repr(self) -> ReprOptions { - self.inner().repr + pub fn is_box(self) -> bool { + matches!(self.0, AdtDefInner::Struct { flags, .. } if flags.contains(AdtFlags::IS_BOX)) } - /// Asserts this is a struct or union and returns its unique variant. - pub fn non_enum_variant(self) -> VariantDef { - assert!(self.inner().flags.is_struct || self.inner().flags.is_union); - self.inner().variants[0].1.clone() + #[inline] + pub fn repr(self, db: &dyn HirDatabase) -> ReprOptions { + if self.flags().contains(AdtFlags::HAS_REPR) { + AttrFlags::repr_assume_has(db, self.def_id()).unwrap_or_default() + } else { + ReprOptions::default() + } } } impl<'db> inherent::AdtDef> for AdtDef { fn def_id(self) -> AdtIdWrapper { - self.inner().id.into() + self.def_id().into() } fn is_struct(self) -> bool { - self.inner().flags.is_struct + self.is_struct() } fn is_phantom_data(self) -> bool { - self.inner().flags.is_phantom_data + matches!(self.0, AdtDefInner::Struct { flags, .. } if flags.contains(AdtFlags::IS_PHANTOM_DATA)) + } + + fn is_manually_drop(self) -> bool { + matches!(self.0, AdtDefInner::Struct { flags, .. } if flags.contains(AdtFlags::IS_MANUALLY_DROP)) + } + + fn is_packed(self) -> bool { + self.flags().contains(AdtFlags::IS_PACKED) } fn is_fundamental(self) -> bool { - self.inner().flags.is_fundamental + self.flags().contains(AdtFlags::IS_FUNDAMENTAL) } fn struct_tail_ty( self, interner: DbInterner<'db>, ) -> Option, Ty<'db>>> { - let hir_def::AdtId::StructId(struct_id) = self.inner().id else { + let hir_def::AdtId::StructId(struct_id) = self.def_id() else { return None; }; let id: VariantId = struct_id.into(); @@ -697,7 +597,7 @@ impl<'db> inherent::AdtDef> for AdtDef { db.field_types(id).iter().map(|(_, ty)| ty.get().skip_binder()).collect::>() }; let field_tys = |_id: VariantId| vec![]; - let tys: Vec<_> = match self.inner().id { + let tys: Vec<_> = match self.def_id() { hir_def::AdtId::StructId(id) => field_tys(id.into()), hir_def::AdtId::UnionId(id) => field_tys(id.into()), hir_def::AdtId::EnumId(id) => id @@ -723,15 +623,7 @@ impl<'db> inherent::AdtDef> for AdtDef { } fn destructor(self, interner: DbInterner<'db>) -> Option { - crate::drop::destructor(interner.db, self.def_id().0).map(|_| AdtDestructorKind::NotConst) - } - - fn is_manually_drop(self) -> bool { - self.inner().flags.is_manually_drop - } - - fn is_packed(self) -> bool { - self.repr().packed() + crate::drop::destructor(interner.db, self.def_id()).map(|_| AdtDestructorKind::NotConst) } fn field_representing_type_info( @@ -746,17 +638,17 @@ impl<'db> inherent::AdtDef> for AdtDef { impl fmt::Debug for AdtDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - crate::with_attached_db(|db| match self.inner().id { - AdtId::StructId(struct_id) => { - let data = StructSignature::of(db, struct_id); + crate::with_attached_db(|db| match self.0 { + AdtDefInner::Struct { id, .. } => { + let data = StructSignature::of(db, id); f.write_str(data.name.as_str()) } - AdtId::UnionId(union_id) => { - let data = UnionSignature::of(db, union_id); + AdtDefInner::Union { id, .. } => { + let data = UnionSignature::of(db, id); f.write_str(data.name.as_str()) } - AdtId::EnumId(enum_id) => { - let data = EnumSignature::of(db, enum_id); + AdtDefInner::Enum { id, .. } => { + let data = EnumSignature::of(db, id); f.write_str(data.name.as_str()) } }) @@ -1985,13 +1877,18 @@ impl<'db> Interner for DbInterner<'db> { }; // The last field of the structure has to exist and contain type/const parameters. - let variant = def.non_enum_variant(); + let variant = match def.def_id() { + AdtId::StructId(id) => VariantId::from(id), + AdtId::UnionId(id) => id.into(), + AdtId::EnumId(_) => panic!("expected a struct or a union"), + }; let fields = variant.fields(self.db()); - let Some((tail_field, prefix_fields)) = fields.split_last() else { + let mut prefix_fields = fields.fields().iter(); + let Some(tail_field) = prefix_fields.next_back() else { return UnsizingParams(DenseBitSet::new_empty(num_params)); }; - let field_types = self.db().field_types(variant.id()); + let field_types = self.db().field_types(variant); let mut unsizing_params = DenseBitSet::new_empty(num_params); let ty = field_types[tail_field.0].get(); for arg in ty.instantiate_identity().walk() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 22595214b4226..8a3866d5acb88 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -504,7 +504,7 @@ impl<'db> Ty<'db> { #[inline] pub fn as_adt(self) -> Option<(AdtId, GenericArgs<'db>)> { match self.kind() { - TyKind::Adt(adt_def, args) => Some((adt_def.def_id().0, args)), + TyKind::Adt(adt_def, args) => Some((adt_def.def_id(), args)), _ => None, } } @@ -1346,9 +1346,11 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { false } - fn discriminant_ty(self, interner: DbInterner<'db>) -> as Interner>::Ty { + fn discriminant_ty(self, interner: DbInterner<'db>) -> Ty<'db> { match self.kind() { - TyKind::Adt(adt, _) if adt.is_enum() => adt.repr().discr_type().to_ty(interner), + TyKind::Adt(adt, _) if adt.is_enum() => { + adt.repr(interner.db).discr_type().to_ty(interner) + } TyKind::Coroutine(_, args) => args.as_coroutine().discr_ty(interner), TyKind::Param(_) | TyKind::Alias(..) | TyKind::Infer(InferTy::TyVar(_)) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index 858233cb2c900..3c5126cc4a3a3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -423,7 +423,7 @@ pub fn sizedness_constraint_for_ty<'db>( .and_then(|ty| sizedness_constraint_for_ty(interner, sizedness, ty)), Adt(adt, args) => { - if crate::representability::representability(interner.db, adt.def_id().0) + if crate::representability::representability(interner.db, adt.def_id()) == Representability::Infinite { return None; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs index bae204c4ef7ca..6c8e890be59e5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs @@ -1,7 +1,7 @@ //! Detecting whether a type is infinitely-sized. use hir_def::{AdtId, VariantId, hir::generics::GenericParams}; -use rustc_type_ir::inherent::{AdtDef, IntoKind}; +use rustc_type_ir::inherent::IntoKind; use crate::{ db::HirDatabase, @@ -54,7 +54,7 @@ fn variant_representability(db: &dyn HirDatabase, id: VariantId) -> Representabi fn representability_ty<'db>(db: &'db dyn HirDatabase, ty: Ty<'db>) -> Representability { match ty.kind() { - TyKind::Adt(adt_id, args) => representability_adt_ty(db, adt_id.def_id().0, args), + TyKind::Adt(adt_id, args) => representability_adt_ty(db, adt_id.def_id(), args), // FIXME(#11924) allow zero-length arrays? TyKind::Array(ty, _) => representability_ty(db, ty), TyKind::Tuple(tys) => { @@ -112,7 +112,7 @@ fn params_in_repr(db: &dyn HirDatabase, def_id: AdtId) -> Box<[bool]> { fn params_in_repr_ty<'db>(db: &'db dyn HirDatabase, ty: Ty<'db>, params_in_repr: &mut [bool]) { match ty.kind() { TyKind::Adt(adt, args) => { - let inner_params_in_repr = self::params_in_repr(db, adt.def_id().0); + let inner_params_in_repr = self::params_in_repr(db, adt.def_id()); for (i, arg) in args.iter().enumerate() { if let GenericArgKind::Type(ty) = arg.kind() && inner_params_in_repr[i] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index 5324d8c605495..b01f44acbc55f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -6,7 +6,7 @@ use hir_def::{ }; use hir_expand::{HirFileId, files::InFileWrapper}; use itertools::Itertools; -use rustc_type_ir::inherent::{AdtDef as _, IntoKind}; +use rustc_type_ir::inherent::IntoKind; use span::{Edition, TextRange}; use stdx::{format_to, never}; use syntax::{AstNode, AstPtr}; @@ -42,7 +42,7 @@ fn display_place(db: &TestDB, store: &ExpressionStore, place: &Place, local: Bin match ty.kind() { TyKind::Tuple(_) => format_to!(result, ".{field_idx}"), TyKind::Adt(adt_def, _) => { - let variant = match adt_def.def_id().0 { + let variant = match adt_def.def_id() { AdtId::StructId(id) => VariantId::from(id), AdtId::UnionId(id) => id.into(), AdtId::EnumId(id) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index ad668f2eee0b1..14611d80af587 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -17,7 +17,7 @@ use hir_expand::name::Name; use intern::sym; use rustc_type_ir::{ TypingMode, - inherent::{AdtDef, BoundExistentialPredicates, IntoKind}, + inherent::{BoundExistentialPredicates, IntoKind}, }; use crate::{ @@ -171,7 +171,7 @@ pub fn is_inherent_impl_coherent(db: &dyn HirDatabase, def_map: &DefMap, impl_id | TyKind::Uint(_) | TyKind::Float(_) => def_map.is_rustc_coherence_is_core(), - TyKind::Adt(adt_def, _) => adt_def.def_id().0.module(db).krate(db) == def_map.krate(), + TyKind::Adt(adt_def, _) => adt_def.def_id().module(db).krate(db) == def_map.krate(), TyKind::Dynamic(it, _) => it .principal_def_id() .is_some_and(|trait_id| trait_id.0.module(db).krate(db) == def_map.krate()), @@ -194,7 +194,7 @@ pub fn is_inherent_impl_coherent(db: &dyn HirDatabase, def_map: &DefMap, impl_id | TyKind::Uint(_) | TyKind::Float(_) => true, - TyKind::Adt(adt_def, _) => match adt_def.def_id().0 { + TyKind::Adt(adt_def, _) => match adt_def.def_id() { hir_def::AdtId::StructId(id) => StructSignature::of(db, id) .flags .contains(StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS), @@ -259,7 +259,7 @@ pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool match ty.kind() { TyKind::Ref(_, referenced, _) => ty = referenced, TyKind::Adt(adt_def, subs) => { - let AdtId::StructId(s) = adt_def.def_id().0 else { + let AdtId::StructId(s) = adt_def.def_id() else { break ty; }; let struct_signature = StructSignature::of(db, s); @@ -282,7 +282,7 @@ pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool // FIXME: param coverage // - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`) let is_not_orphan = trait_ref.args.types().any(|ty| match unwrap_fundamental(ty).kind() { - TyKind::Adt(adt_def, _) => is_local(adt_def.def_id().0.module(db).krate(db)), + TyKind::Adt(adt_def, _) => is_local(adt_def.def_id().module(db).krate(db)), TyKind::Error(_) => true, TyKind::Dynamic(it, _) => { it.principal_def_id().is_some_and(|trait_id| is_local(trait_id.0.module(db).krate(db))) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index a88457e3c745b..a89a9386399df 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -18,10 +18,7 @@ use hir_def::{ signatures::{StructFlags, StructSignature}, }; use rustc_ast_ir::Mutability; -use rustc_type_ir::{ - Variance, - inherent::{AdtDef, IntoKind}, -}; +use rustc_type_ir::{Variance, inherent::IntoKind}; use stdx::never; use crate::{ @@ -214,7 +211,7 @@ impl<'db> Context<'db> { } } TyKind::Adt(def, args) => { - self.add_constraints_from_args(def.def_id().0.into(), args, variance); + self.add_constraints_from_args(def.def_id().into(), args, variance); } TyKind::Alias(alias) => { // FIXME: Probably not correct wrt. opaques. diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 60de6349965c3..9b36f8ab8100f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -105,7 +105,7 @@ use rustc_hash::FxHashSet; use rustc_type_ir::{ AliasTyKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, fast_reject, - inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _}, + inherent::{GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _}, }; use span::{AstIdNode, Edition, FileId}; use stdx::{format_to, impl_from, never}; @@ -5313,7 +5313,7 @@ impl<'db> ClosureCapture<'db> { match ty.kind() { TyKind::Tuple(_) => format_to!(result, "_{field_idx}"), TyKind::Adt(adt_def, _) => { - let variant = match adt_def.def_id().0 { + let variant = match adt_def.def_id() { AdtId::StructId(id) => VariantId::from(id), AdtId::UnionId(id) => id.into(), AdtId::EnumId(id) => { @@ -5347,7 +5347,7 @@ impl<'db> ClosureCapture<'db> { match ty.kind() { TyKind::Tuple(_) => format_to!(result, ".{field_idx}"), TyKind::Adt(adt_def, _) => { - let variant = match adt_def.def_id().0 { + let variant = match adt_def.def_id() { AdtId::StructId(id) => VariantId::from(id), AdtId::UnionId(id) => id.into(), AdtId::EnumId(id) => { @@ -5593,7 +5593,7 @@ impl<'db> Type<'db> { // For non-phantom_data adts we check variants/fields as well as generic parameters TyKind::Adt(adt_def, args) - if !is_phantom_data(self.interner.db(), adt_def.def_id().0) => + if !is_phantom_data(self.interner.db(), adt_def.def_id()) => { let _variant_id_to_fields = |id: VariantId| { let variant_data = &id.fields(self.interner.db()); @@ -5613,7 +5613,7 @@ impl<'db> Type<'db> { }; let variant_id_to_fields = |_: VariantId| vec![]; - let variants: Vec>> = match adt_def.def_id().0 { + let variants: Vec>> = match adt_def.def_id() { AdtId::StructId(id) => { vec![variant_id_to_fields(id.into())] } @@ -5891,7 +5891,7 @@ impl<'db> Type<'db> { pub fn is_packed(&self, db: &'db dyn HirDatabase) -> bool { let adt_id = match self.ty.kind() { - TyKind::Adt(adt_def, ..) => adt_def.def_id().0, + TyKind::Adt(adt_def, ..) => adt_def.def_id(), _ => return false, }; @@ -5930,7 +5930,7 @@ impl<'db> Type<'db> { let interner = DbInterner::new_no_crate(db); let (variant_id, substs) = match self.ty.kind() { TyKind::Adt(adt_def, substs) => { - let id = match adt_def.def_id().0 { + let id = match adt_def.def_id() { AdtId::StructId(id) => id.into(), AdtId::UnionId(id) => id.into(), AdtId::EnumId(_) => return Vec::new(), 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 e5d674cb75543..1aec56a0e0bce 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -47,7 +47,7 @@ use itertools::Itertools; use rustc_hash::FxHashSet; use rustc_type_ir::{ AliasTyKind, - inherent::{AdtDef, IntoKind, Ty as _}, + inherent::{IntoKind, Ty as _}, }; use smallvec::SmallVec; use stdx::never; @@ -956,7 +956,7 @@ impl<'db> SourceAnalyzer<'db> { handle_variants(VariantId::from(variant_id), subst, &mut container)? } Either::Right(container_ty) => match container_ty.kind() { - TyKind::Adt(adt_def, subst) => match adt_def.def_id().0 { + TyKind::Adt(adt_def, subst) => match adt_def.def_id() { AdtId::StructId(id) => { handle_variants(id.into(), subst, &mut container)? } @@ -1286,7 +1286,7 @@ impl<'db> SourceAnalyzer<'db> { let env = self.trait_environment(db); let (subst, expected_resolution) = match ty.kind() { TyKind::Adt(adt_def, subst) => { - let adt_id = adt_def.def_id().0; + let adt_id = adt_def.def_id(); ( GenericSubstitution::new(adt_id.into(), subst, env), PathResolution::Def(ModuleDef::Adt(adt_id.into())), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 60ae077d01426..896b132e151b9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -781,9 +781,9 @@ fn main() { } "#, expect![[r#" + me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED - me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED "#]], ); } From d604e14950bae667eef59ed26f02e36e95fde2a2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 30 Apr 2026 02:26:54 +0300 Subject: [PATCH 125/289] Add missing exprs to visiting There was a note in the docs that const blocks aren't visited, but most people don't read the docs and expect them to be visited. If you don't want them to, you can handle them specially. --- .../crates/hir-def/src/expr_store.rs | 407 ++++++++---------- .../hir-ty/src/diagnostics/unsafe_check.rs | 1 - .../crates/hir/src/source_analyzer.rs | 4 +- .../src/handlers/type_must_be_known.rs | 13 + 4 files changed, 191 insertions(+), 234 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index f72419a3aee24..2c2b477115c74 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -9,7 +9,10 @@ pub mod scope; #[cfg(test)] mod tests; -use std::ops::{Deref, Index}; +use std::{ + borrow::Borrow, + ops::{Deref, Index}, +}; use cfg::{CfgExpr, CfgOptions}; use either::Either; @@ -27,8 +30,8 @@ use crate::{ db::DefDatabase, expr_store::path::Path, hir::{ - Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat, - PatId, RecordFieldPat, RecordSpread, Statement, + Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, InlineAsm, Label, + LabelId, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement, }, nameres::{DefMap, block_def_map}, signatures::VariantFields, @@ -554,31 +557,35 @@ impl ExpressionStore { } pub fn walk_pats_shallow(&self, pat_id: PatId, mut f: impl FnMut(PatId)) { + // Do not use `..` patterns or field accesses here, only destructuring, to ensure we cover all cases + // (we've had multiple bugs with this in the past). let pat = &self[pat_id]; match pat { - Pat::Range { .. } - | Pat::Lit(..) - | Pat::Path(..) - | Pat::ConstBlock(..) + Pat::Range { start: _, end: _, range_type: _ } + | Pat::Lit(_) + | Pat::Path(_) + | Pat::ConstBlock(_) | Pat::Wild | Pat::Missing | Pat::Rest | Pat::Expr(_) => {} - &Pat::Bind { subpat, .. } => { + &Pat::Bind { subpat, id: _ } => { if let Some(subpat) = subpat { f(subpat); } } - Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => { + Pat::Or(args) + | Pat::Tuple { args, ellipsis: _ } + | Pat::TupleStruct { args, ellipsis: _, path: _ } => { args.iter().copied().for_each(f); } - Pat::Ref { pat, .. } => f(*pat), + Pat::Ref { pat, mutability: _ } => f(*pat), Pat::Slice { prefix, slice, suffix } => { let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter()); total_iter.copied().for_each(f); } - Pat::Record { args, .. } => { - args.iter().for_each(|RecordFieldPat { pat, .. }| f(*pat)); + Pat::Record { args, ellipsis: _, path: _ } => { + args.iter().for_each(|RecordFieldPat { pat, name: _ }| f(*pat)); } Pat::Box { inner } => f(*inner), } @@ -606,273 +613,197 @@ impl ExpressionStore { self.expr_only.as_ref()?.binding_owners.get(&id).copied() } - /// Walks the immediate children expressions and calls `f` for each child expression. - /// - /// Note that this does not walk const blocks. - pub fn walk_child_exprs(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) { - let expr = &self[expr_id]; - match expr { - Expr::Continue { .. } - | Expr::Const(_) + fn walk_child_exprs_impl(&self, expr_id: ExprId, mut visitor: impl ExprVisitor) { + // Do not use `..` patterns or field accesses here, only destructuring, to ensure we cover all cases + // (we've had multiple bugs with this in the past). + match &self[expr_id] { + Expr::Continue { label: _ } | Expr::Missing | Expr::Path(_) | Expr::OffsetOf(_) | Expr::Literal(_) | Expr::Underscore => {} - Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op { - AsmOperand::In { expr, .. } - | AsmOperand::Out { expr: Some(expr), .. } - | AsmOperand::InOut { expr, .. } - | AsmOperand::Const(expr) - | AsmOperand::Label(expr) => f(*expr), - AsmOperand::SplitInOut { in_expr, out_expr, .. } => { - f(*in_expr); - if let Some(out_expr) = out_expr { - f(*out_expr); + Expr::InlineAsm(InlineAsm { operands, options: _, kind: _ }) => { + operands.iter().for_each(|(_, op)| match op { + AsmOperand::In { expr, reg: _ } + | AsmOperand::Out { expr: Some(expr), late: _, reg: _ } + | AsmOperand::InOut { expr, late: _, reg: _ } + | AsmOperand::Const(expr) + | AsmOperand::Label(expr) => visitor.on_expr(*expr), + AsmOperand::SplitInOut { in_expr, out_expr, late: _, reg: _ } => { + visitor.on_expr(*in_expr); + visitor.on_expr_opt(*out_expr); } - } - AsmOperand::Out { expr: None, .. } | AsmOperand::Sym(_) => (), - }), + AsmOperand::Out { expr: None, late: _, reg: _ } | AsmOperand::Sym(_) => (), + }) + } Expr::If { condition, then_branch, else_branch } => { - f(*condition); - f(*then_branch); - if let &Some(else_branch) = else_branch { - f(else_branch); - } + visitor.on_expr(*condition); + visitor.on_expr(*then_branch); + visitor.on_expr_opt(*else_branch); } Expr::Let { expr, pat } => { - self.walk_exprs_in_pat(*pat, &mut f); - f(*expr); + visitor.on_pat(*pat); + visitor.on_expr(*expr); } - Expr::Block { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } => { - for stmt in statements.iter() { + Expr::Block { statements, tail, id: _, label: _ } + | Expr::Unsafe { statements, tail, id: _ } => { + for stmt in statements { match stmt { - Statement::Let { initializer, else_branch, pat, .. } => { - if let &Some(expr) = initializer { - f(expr); - } - if let &Some(expr) = else_branch { - f(expr); - } - self.walk_exprs_in_pat(*pat, &mut f); + Statement::Let { initializer, else_branch, pat, type_ref: _ } => { + visitor.on_expr_opt(*initializer); + visitor.on_expr_opt(*else_branch); + visitor.on_pat(*pat); + } + Statement::Expr { expr: expression, has_semi: _ } => { + visitor.on_expr(*expression) } - Statement::Expr { expr: expression, .. } => f(*expression), Statement::Item(_) => (), } } - if let &Some(expr) = tail { - f(expr); - } + visitor.on_expr_opt(*tail); } - Expr::Loop { body, .. } => f(*body), - Expr::Call { callee, args, .. } => { - f(*callee); - args.iter().copied().for_each(f); + Expr::Loop { body, label: _ } => visitor.on_expr(*body), + Expr::Call { callee, args } => { + visitor.on_expr(*callee); + visitor.on_exprs(args); } - Expr::MethodCall { receiver, args, .. } => { - f(*receiver); - args.iter().copied().for_each(f); + Expr::MethodCall { receiver, args, generic_args: _, method_name: _ } => { + visitor.on_expr(*receiver); + visitor.on_exprs(args); } Expr::Match { expr, arms } => { - f(*expr); - arms.iter().for_each(|arm| { - f(arm.expr); - if let Some(guard) = arm.guard { - f(guard); - } - self.walk_exprs_in_pat(arm.pat, &mut f); + visitor.on_expr(*expr); + arms.iter().for_each(|MatchArm { pat, guard, expr }| { + visitor.on_expr(*expr); + visitor.on_expr_opt(*guard); + visitor.on_pat(*pat); }); } - Expr::Break { expr, .. } + Expr::Break { expr, label: _ } | Expr::Return { expr } | Expr::Yield { expr } - | Expr::Yeet { expr } => { - if let &Some(expr) = expr { - f(expr); + | Expr::Yeet { expr } => visitor.on_expr_opt(*expr), + Expr::Become { expr } => visitor.on_expr(*expr), + Expr::RecordLit { fields, spread, path: _ } => { + for RecordLitField { name: _, expr } in fields.iter() { + visitor.on_expr(*expr); } - } - Expr::Become { expr } => f(*expr), - Expr::RecordLit { fields, spread, .. } => { - for field in fields.iter() { - f(field.expr); - } - if let RecordSpread::Expr(expr) = spread { - f(*expr); + match spread { + RecordSpread::Expr(expr) => visitor.on_expr(*expr), + RecordSpread::None | RecordSpread::FieldDefaults => {} } } - Expr::Closure { body, .. } => { - f(*body); + Expr::Closure { + body, + args, + arg_types: _, + capture_by: _, + closure_kind: _, + ret_type: _, + } => { + visitor.on_expr(*body); + visitor.on_pats(args); } - Expr::BinaryOp { lhs, rhs, .. } => { - f(*lhs); - f(*rhs); + Expr::BinaryOp { lhs, rhs, op: _ } => { + visitor.on_expr(*lhs); + visitor.on_expr(*rhs); } - Expr::Range { lhs, rhs, .. } => { - if let &Some(lhs) = rhs { - f(lhs); - } - if let &Some(rhs) = lhs { - f(rhs); - } + Expr::Range { lhs, rhs, range_type: _ } => { + visitor.on_expr_opt(*lhs); + visitor.on_expr_opt(*rhs); } - Expr::Index { base, index, .. } => { - f(*base); - f(*index); + Expr::Index { base, index } => { + visitor.on_expr(*base); + visitor.on_expr(*index); } - Expr::Field { expr, .. } + Expr::Field { expr, name: _ } | Expr::Await { expr } - | Expr::Cast { expr, .. } - | Expr::Ref { expr, .. } - | Expr::UnaryOp { expr, .. } - | Expr::Box { expr } => { - f(*expr); + | Expr::Cast { expr, type_ref: _ } + | Expr::Ref { expr, mutability: _, rawness: _ } + | Expr::UnaryOp { expr, op: _ } + | Expr::Box { expr } + | Expr::Const(expr) => { + visitor.on_expr(*expr); } - Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f), + Expr::Tuple { exprs } => visitor.on_exprs(exprs), Expr::Array(a) => match a { - Array::ElementList { elements, .. } => elements.iter().copied().for_each(f), + Array::ElementList { elements } => visitor.on_exprs(elements), Array::Repeat { initializer, repeat } => { - f(*initializer); - f(*repeat) + visitor.on_expr(*initializer); + visitor.on_expr(*repeat) } }, &Expr::Assignment { target, value } => { - self.walk_exprs_in_pat(target, &mut f); - f(value); + visitor.on_pat(target); + visitor.on_expr(value); } } } - /// Walks the immediate children expressions and calls `f` for each child expression but does - /// not walk expressions within patterns. - /// - /// Note that this does not walk const blocks. - pub fn walk_child_exprs_without_pats(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) { - let expr = &self[expr_id]; - match expr { - Expr::Continue { .. } - | Expr::Const(_) - | Expr::Missing - | Expr::Path(_) - | Expr::OffsetOf(_) - | Expr::Literal(_) - | Expr::Underscore => {} - Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op { - AsmOperand::In { expr, .. } - | AsmOperand::Out { expr: Some(expr), .. } - | AsmOperand::InOut { expr, .. } - | AsmOperand::Const(expr) - | AsmOperand::Label(expr) => f(*expr), - AsmOperand::SplitInOut { in_expr, out_expr, .. } => { - f(*in_expr); - if let Some(out_expr) = out_expr { - f(*out_expr); - } - } - AsmOperand::Out { expr: None, .. } | AsmOperand::Sym(_) => (), - }), - Expr::If { condition, then_branch, else_branch } => { - f(*condition); - f(*then_branch); - if let &Some(else_branch) = else_branch { - f(else_branch); - } - } - Expr::Let { expr, .. } => { - f(*expr); - } - Expr::Block { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } => { - for stmt in statements.iter() { - match stmt { - Statement::Let { initializer, else_branch, .. } => { - if let &Some(expr) = initializer { - f(expr); - } - if let &Some(expr) = else_branch { - f(expr); - } - } - Statement::Expr { expr: expression, .. } => f(*expression), - Statement::Item(_) => (), - } - } - if let &Some(expr) = tail { - f(expr); - } - } - Expr::Loop { body, .. } => f(*body), - Expr::Call { callee, args, .. } => { - f(*callee); - args.iter().copied().for_each(f); - } - Expr::MethodCall { receiver, args, .. } => { - f(*receiver); - args.iter().copied().for_each(f); - } - Expr::Match { expr, arms } => { - f(*expr); - arms.iter().map(|arm| arm.expr).for_each(f); - } - Expr::Break { expr, .. } - | Expr::Return { expr } - | Expr::Yield { expr } - | Expr::Yeet { expr } => { - if let &Some(expr) = expr { - f(expr); - } - } - Expr::Become { expr } => f(*expr), - Expr::RecordLit { fields, spread, .. } => { - for field in fields.iter() { - f(field.expr); - } - if let RecordSpread::Expr(expr) = spread { - f(*expr); - } - } - Expr::Closure { body, .. } => { - f(*body); - } - Expr::BinaryOp { lhs, rhs, .. } => { - f(*lhs); - f(*rhs); - } - Expr::Range { lhs, rhs, .. } => { - if let &Some(lhs) = rhs { - f(lhs); - } - if let &Some(rhs) = lhs { - f(rhs); - } + /// Walks the immediate children expressions and calls `f` for each child expression. + pub fn walk_child_exprs(&self, expr_id: ExprId, callback: impl FnMut(ExprId)) { + return self.walk_child_exprs_impl(expr_id, Visitor { callback, store: self }); + + struct Visitor<'a, F> { + callback: F, + store: &'a ExpressionStore, + } + + impl ExprVisitor for Visitor<'_, F> { + fn on_expr(&mut self, expr: ExprId) { + (self.callback)(expr); } - Expr::Index { base, index, .. } => { - f(*base); - f(*index); + + fn on_pat(&mut self, pat: PatId) { + self.store.walk_exprs_in_pat(pat, &mut self.callback); } - Expr::Field { expr, .. } - | Expr::Await { expr } - | Expr::Cast { expr, .. } - | Expr::Ref { expr, .. } - | Expr::UnaryOp { expr, .. } - | Expr::Box { expr } => { - f(*expr); + } + } + + /// Walks the immediate children expressions and calls `f` for each child expression but does + /// not walk expressions within patterns. + pub fn walk_child_exprs_without_pats(&self, expr_id: ExprId, callback: impl FnMut(ExprId)) { + return self.walk_child_exprs_impl(expr_id, Visitor { callback }); + + struct Visitor { + callback: F, + } + + impl ExprVisitor for Visitor { + fn on_expr(&mut self, expr: ExprId) { + (self.callback)(expr); } - Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f), - Expr::Array(a) => match a { - Array::ElementList { elements, .. } => elements.iter().copied().for_each(f), - Array::Repeat { initializer, repeat } => { - f(*initializer); - f(*repeat) - } - }, - &Expr::Assignment { target: _, value } => f(value), + + fn on_pat(&mut self, _pat: PatId) {} } } - pub fn walk_exprs_in_pat(&self, pat_id: PatId, f: &mut impl FnMut(ExprId)) { - self.walk_pats(pat_id, &mut |pat| { - if let Pat::Expr(expr) | Pat::ConstBlock(expr) = self[pat] { + pub fn walk_exprs_in_pat(&self, pat_id: PatId, mut f: impl FnMut(ExprId)) { + self.walk_pats(pat_id, &mut |pat| match self[pat] { + Pat::Expr(expr) | Pat::ConstBlock(expr) | Pat::Lit(expr) => { f(expr); } + Pat::Range { start, end, range_type: _ } => { + if let Some(start) = start { + f(start); + } + if let Some(end) = end { + f(end); + } + } + Pat::Missing + | Pat::Rest + | Pat::Wild + | Pat::Tuple { .. } + | Pat::Or(_) + | Pat::Record { .. } + | Pat::Slice { .. } + | Pat::Path(_) + | Pat::Bind { .. } + | Pat::TupleStruct { .. } + | Pat::Ref { .. } + | Pat::Box { .. } => {} }); } @@ -940,6 +871,22 @@ impl ExpressionStore { } } +trait ExprVisitor { + fn on_expr(&mut self, expr: ExprId); + fn on_pat(&mut self, pat: PatId); + fn on_expr_opt(&mut self, expr: Option) { + if let Some(expr) = expr { + self.on_expr(expr); + } + } + fn on_exprs(&mut self, exprs: impl IntoIterator>) { + exprs.into_iter().for_each(|expr| self.on_expr(*expr.borrow())); + } + fn on_pats(&mut self, exprs: impl IntoIterator>) { + exprs.into_iter().for_each(|expr| self.on_pat(*expr.borrow())); + } +} + impl Index for ExpressionStore { type Output = Expr; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 4893d72a5c731..ad9909a204842 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -424,7 +424,6 @@ impl<'db> UnsafeVisitor<'db> { Expr::Closure { args, .. } => { self.walk_pats_top(args.iter().copied(), current); } - Expr::Const(e) => self.walk_expr(*e), _ => {} } 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 1aec56a0e0bce..783faa9ac86fe 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -1455,9 +1455,7 @@ impl<'db> SourceAnalyzer<'db> { }; match expanded_expr { ExprOrPatId::ExprId(expanded_expr) => walk_expr(expanded_expr), - ExprOrPatId::PatId(expanded_pat) => { - body.walk_exprs_in_pat(expanded_pat, &mut walk_expr) - } + ExprOrPatId::PatId(expanded_pat) => body.walk_exprs_in_pat(expanded_pat, walk_expr), } return is_unsafe; } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs index 5363f4a5cec58..08bcc738cb902 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs @@ -99,6 +99,19 @@ fn foo() { let _x = any(); // ^^^^^ error: type annotations needed // | full type: `X<{unknown}>` +} + "#, + ); + } + + #[test] + fn const_block_does_not_cause_error() { + check_diagnostics( + r#" +fn bar(_inner: fn() -> *const T) {} + +fn foo() { + bar(const { || 0 as *const i32 }) } "#, ); From 8ad308e836a506cc6578f1fe291bfb4b68cea790 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 30 Apr 2026 14:03:52 +0800 Subject: [PATCH 126/289] Add a parentheses --- .../crates/syntax-bridge/src/prettify_macro_expansion.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs index 83c92f5eb1c94..017aa10b6c6c6 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs @@ -54,7 +54,7 @@ pub fn prettify_macro_expansion( EXPR_STMT if Some(R_CURLY) == node.last_token().map(|it| it.kind()) => true, _ => false, }; - if !is_last_child && is_non_last_newline || is_always_newline { + if (!is_last_child && is_non_last_newline) || is_always_newline { if indent > 0 { mods.push((Position::after(node.clone()), PrettifyWsKind::Indent(indent))); } From 9cae0fede2b55a0c6b73ebb8fd4cbc1b344c8a6e Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 30 Apr 2026 14:06:10 +0800 Subject: [PATCH 127/289] Open empty braces before else kw --- src/tools/rust-analyzer/crates/ide/src/expand_macro.rs | 4 +++- .../crates/syntax-bridge/src/prettify_macro_expansion.rs | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) 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 fe58786991688..cc322d2b9ebcc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -433,7 +433,9 @@ fn main() { expect![[r#" match_ast! { - if let Some(it) = ast::TraitDef::cast(container.clone()){}else if let Some(it) = ast::ImplDef::cast(container.clone()){}else { + if let Some(it) = ast::TraitDef::cast(container.clone()){ + }else if let Some(it) = ast::ImplDef::cast(container.clone()){ + }else { { continue } diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs index 017aa10b6c6c6..ba1b4f5ae4c91 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs @@ -99,6 +99,10 @@ pub fn prettify_macro_expansion( } mods.push(do_nl(before, tok)); } + R_CURLY if is_next(|it| it == T![else], false) => { + mods.push(do_indent(before, tok, indent)); + mods.push(do_nl(before, tok)); + } LIFETIME_IDENT if is_next(is_text, true) => { mods.push(do_ws(after, tok)); } @@ -358,6 +362,7 @@ mod tests { } else if true { bar() } else {} + if true {} else if true {} else {} fun() } }; @@ -378,6 +383,9 @@ mod tests { }else if true { bar() }else {} + if true { + }else if true { + }else {} fun() } }; From 6f154849eb58f25b792006031a165bd3cc974dc2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 30 Apr 2026 09:44:05 +0300 Subject: [PATCH 128/289] Show running tests on CI So we can debug the test we have that gets stuck. --- src/tools/rust-analyzer/.github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index c27d84fb0bc06..b9427e1927f6c 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -120,7 +120,7 @@ jobs: run: cargo codegen --check - name: Run tests - run: cargo nextest run --no-fail-fast --hide-progress-bar --status-level fail + run: cargo nextest run --no-fail-fast --hide-progress-bar - name: Install cargo-machete uses: taiki-e/install-action@cargo-machete From a4cf63a305917738b964c02fc7d64db16137f6c0 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 30 Apr 2026 14:45:14 +0800 Subject: [PATCH 129/289] Handle indent(0) in editing --- .../syntax-bridge/src/prettify_macro_expansion.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs index ba1b4f5ae4c91..678dd143f9e09 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs @@ -55,9 +55,7 @@ pub fn prettify_macro_expansion( _ => false, }; if (!is_last_child && is_non_last_newline) || is_always_newline { - if indent > 0 { - mods.push((Position::after(node.clone()), PrettifyWsKind::Indent(indent))); - } + mods.push((Position::after(node.clone()), PrettifyWsKind::Indent(indent))); if node.parent().is_some() { mods.push((Position::after(node), PrettifyWsKind::Newline)); } @@ -94,9 +92,7 @@ pub fn prettify_macro_expansion( R_CURLY if is_last(|it| it != L_CURLY, true) => { indent = indent.saturating_sub(1); - if indent > 0 { - mods.push(do_indent(before, tok, indent)); - } + mods.push(do_indent(before, tok, indent)); mods.push(do_nl(before, tok)); } R_CURLY if is_next(|it| it == T![else], false) => { @@ -110,9 +106,7 @@ pub fn prettify_macro_expansion( mods.push(do_ws(after, tok)); } T![;] if is_next(|it| it != R_CURLY, true) => { - if indent > 0 { - mods.push(do_indent(after, tok, indent)); - } + mods.push(do_indent(after, tok, indent)); if tok.text_range().end() != syn.text_range().end() { mods.push(do_nl(after, tok)); } @@ -148,6 +142,7 @@ pub fn prettify_macro_expansion( pos, match insert { PrettifyWsKind::Space => editor.make().whitespace(" "), + PrettifyWsKind::Indent(0) => continue, PrettifyWsKind::Indent(indent) => editor.make().whitespace(&" ".repeat(4 * indent)), PrettifyWsKind::Newline => editor.make().whitespace("\n"), }, From ad31babc65e9f051ce393893489f1ba148f3926b Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 30 Apr 2026 16:27:40 +0800 Subject: [PATCH 130/289] fix: add semicolon after expr in stmt for unwrap_branch Example --- ```rust fn main() { $0{ 92 } () } ``` **Before this PR** ```rust fn main() { 92 () } ``` **After this PR** ```rust fn main() { 92; () } ``` --- .../crates/ide-assists/src/handlers/unwrap_branch.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs index 0c94b165eec5a..dba8c0336782c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs @@ -78,9 +78,16 @@ pub(crate) fn unwrap_branch(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio acc.add(AssistId::refactor_rewrite("unwrap_branch"), label, target, |builder| { let replacement = replacement.dedent(from_indent).indent(into_indent); + let mut replacement = extract_statements(replacement); let container = prefer_container.unwrap_or(container); - editor.replace_with_many(&container, extract_statements(replacement)); + if ast::ExprStmt::can_cast(container.kind()) + && block.tail_expr().is_some_and(|it| !it.is_block_like()) + { + replacement.push(editor.make().token(T![;]).into()); + } + + editor.replace_with_many(&container, replacement); delete_else_before(container, &editor); builder.add_file_edits(ctx.vfs_file_id(), editor); @@ -253,7 +260,6 @@ fn main() { } "#, ); - // Pedantically, we should add an `;` here... check_assist( unwrap_branch, r#" @@ -266,7 +272,7 @@ fn main() { "#, r#" fn main() { - 92 + 92; () } "#, From 5b5b93ced240db6938af00c6304003bfc65b141d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 28 Apr 2026 11:17:37 +0200 Subject: [PATCH 131/289] misc: fix outdated docs --- src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index b86fad0420ec6..97c25f3f50a75 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -2819,7 +2819,7 @@ impl<'db> ExprCollector<'db> { // endregion: patterns - /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when + /// Returns `false` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `true` when /// not. fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> bool { let enabled = self.expander.is_cfg_enabled(owner, self.cfg_options); From a3b21ed4d4f6c4e4a83513f6bde30a02c9cbf070 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 30 Apr 2026 11:19:39 +0200 Subject: [PATCH 132/289] decide not to resolve a FIXME --- .../rust-analyzer/crates/hir-def/src/expr_store/lower.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 97c25f3f50a75..0de980bdf03d4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -1806,7 +1806,9 @@ impl<'db> ExprCollector<'db> { }; let record_field_list = e.record_expr_field_list()?; let ellipsis = record_field_list.dotdot_token().is_some(); - // FIXME: Report an error here if `record_field_list.spread().is_some()`. + // We wanted to emit an error here if `record_field_list.spread().is_some()`, + // but that's already a syntax error in rustc, so we decided not to. + // See https://github.com/rust-lang/rust-analyzer/pull/22206#discussion_r3156097370 let args = record_field_list .fields() .filter_map(|f| { From 7f7e961b87a9e39575eafe371f116a9f359aa53a Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Thu, 30 Apr 2026 16:58:33 +0600 Subject: [PATCH 133/289] internal: add SyntaxEditor-based replace_expr for RecordExprField --- .../ide-assists/src/handlers/inline_call.rs | 7 +------ .../crates/syntax/src/ast/edit_in_place.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) 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 9273ad4cc50cc..6e45f537e4490 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 @@ -560,12 +560,7 @@ fn inline( let inline_direct = |editor: &SyntaxEditor, usage: &PathExpr, replacement: &ast::Expr| { if let Some(field) = path_expr_as_record_field(usage) { cov_mark::hit!(inline_call_inline_direct_field); - let field_name = field.field_name().unwrap(); - let new_field = editor.make().record_expr_field( - editor.make().name_ref(&field_name.text()), - Some(replacement.clone()), - ); - editor.replace(field.syntax(), new_field.syntax()); + field.replace_expr_with_editor(editor, replacement.clone()); } else { editor.replace(usage.syntax(), replacement.syntax()); } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 46ea4daba82ea..5396826394c7e 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -10,6 +10,7 @@ use crate::{ SyntaxNode, SyntaxToken, algo::{self, neighbor}, ast::{self, edit::IndentLevel, make}, + syntax_editor::SyntaxEditor, ted, }; @@ -451,6 +452,24 @@ impl ast::RecordExprField { ted::insert_all_raw(ted::Position::last_child_of(self.syntax()), children); } } + + /// [`SyntaxEditor`]-based equivalent of [`replace_expr`](Self::replace_expr). + pub fn replace_expr_with_editor(&self, editor: &SyntaxEditor, expr: ast::Expr) { + if self.name_ref().is_some() { + if let Some(prev) = self.expr() { + editor.replace(prev.syntax(), expr.syntax()); + } + } else if let Some(ast::Expr::PathExpr(path_expr)) = self.expr() + && let Some(path) = path_expr.path() + && let Some(name_ref) = path.as_single_name_ref() + { + // shorthand `{ x }` → expand to `{ x: expr }` + let new_field = editor + .make() + .record_expr_field(editor.make().name_ref(&name_ref.text()), Some(expr)); + editor.replace(self.syntax(), new_field.syntax()); + } + } } impl ast::RecordPatFieldList { From 2d34d2408053811ccb425ad8e5bc2b775c4253a6 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 30 Apr 2026 21:55:20 +0800 Subject: [PATCH 134/289] internal: fix incorrect offset on multiple indel --- src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs b/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs index f93b2cc74eb29..fecd3fb0966a2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs @@ -133,7 +133,7 @@ impl TextEdit { let mut res = offset; for indel in &self.indels { if indel.delete.start() >= offset { - break; + continue; } if offset < indel.delete.end() { return None; From 10454b4b0662ea94cca2c59312fef68d06751a5d Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 30 Apr 2026 22:18:16 +0800 Subject: [PATCH 135/289] feat: support if-else in value on postfix completions Example --- ```rust fn main() { let s = cond.is_some().if$0; } ``` **Before this PR** ```rust fn main() { let s = if cond.is_some() { $0 }; } ``` **After this PR** ```rust fn main() { let s = if cond.is_some() { $1 } else { $0 }; } ``` --- .../ide-completion/src/completions/postfix.rs | 87 ++++++++++++++++++- .../ide-completion/src/context/analysis.rs | 23 +---- 2 files changed, 86 insertions(+), 24 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 70d4a46e1f3c0..8a754a6b27b5b 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 @@ -150,6 +150,7 @@ pub(crate) fn complete_postfix( let try_enum = TryEnum::from_ty(&ctx.sema, receiver_ty); let is_in_cond = is_in_condition(&dot_receiver_including_refs); + let is_in_value = is_in_value(&dot_receiver_including_refs); if let Some(parent) = dot_receiver_including_refs.syntax().parent() { let placeholder = suggest_receiver_name(dot_receiver, "0", &ctx.sema); match &try_enum { @@ -244,12 +245,14 @@ pub(crate) fn complete_postfix( } if let Some(try_enum) = &try_enum { let placeholder = suggest_receiver_name(dot_receiver, "1", &ctx.sema); + let if_then_snip = + if is_in_value { "{\n $2\n} else {\n $0\n}" } else { "{\n $0\n}" }; match try_enum { TryEnum::Result => { postfix_snippet( "ifl", "if let Ok {}", - format!("if let Ok({placeholder}) = {receiver_text} {{\n $0\n}}"), + format!("if let Ok({placeholder}) = {receiver_text} {if_then_snip}"), ) .add_to(acc, ctx.db); @@ -271,7 +274,7 @@ pub(crate) fn complete_postfix( postfix_snippet( "ifl", "if let Some {}", - format!("if let Some({placeholder}) = {receiver_text} {{\n $0\n}}"), + format!("if let Some({placeholder}) = {receiver_text} {if_then_snip}"), ) .add_to(acc, ctx.db); @@ -293,7 +296,9 @@ pub(crate) fn complete_postfix( } } } else if receiver_ty.is_bool() || receiver_ty.is_unknown() { - postfix_snippet("if", "if expr {}", format!("if {receiver_text} {{\n $0\n}}")) + let if_then_snip = + if is_in_value { "{\n $1\n} else {\n $0\n}" } else { "{\n $0\n}" }; + postfix_snippet("if", "if expr {}", format!("if {receiver_text} {if_then_snip}")) .add_to(acc, ctx.db); postfix_snippet( "while", @@ -563,6 +568,22 @@ pub(crate) fn is_in_condition(it: &ast::Expr) -> bool { .unwrap_or(false) } +pub(crate) fn is_in_value(it: &ast::Expr) -> bool { + let Some(node) = it.syntax().parent() else { return false }; + let kind = node.kind(); + ast::LetStmt::can_cast(kind) + || ast::ArgList::can_cast(kind) + || ast::ArrayExpr::can_cast(kind) + || ast::ParenExpr::can_cast(kind) + || ast::BreakExpr::can_cast(kind) + || ast::ReturnExpr::can_cast(kind) + || ast::PrefixExpr::can_cast(kind) + || ast::FormatArgsArg::can_cast(kind) + || ast::RecordExprField::can_cast(kind) + || ast::BinExpr::cast(node.clone()).is_some_and(|expr| expr.rhs().as_ref() == Some(it)) + || ast::IndexExpr::cast(node).is_some_and(|expr| expr.index().as_ref() == Some(it)) +} + #[cfg(test)] mod tests { use expect_test::expect; @@ -1177,6 +1198,66 @@ fn main() { ) } + #[test] + fn postfix_completion_if_else_in_value() { + check_edit( + "if", + r#" +fn main() { + let s = cond.is_some().$0; +} +"#, + r#" +fn main() { + let s = if cond.is_some() { + $1 +} else { + $0 +}; +} +"#, + ); + + check_edit( + "ifl", + r#" +//- minicore: option +fn main() { + let cond = Some("x"); + let s = cond.$0; +} +"#, + r#" +fn main() { + let cond = Some("x"); + let s = if let Some(${1:cond}) = cond { + $2 +} else { + $0 +}; +} +"#, + ); + + check_edit( + "if", + r#" +fn main() { + 2 + true.$0; +} +"#, + r#" +fn main() { + 2 + if true { + $1 +} else { + $0 +}; +} +"#, + ); + } + #[test] fn postfix_completion_for_unsafe() { postfix_completion_for_block("unsafe"); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index c8068d6fc4da1..faeb97f93f7d1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -22,7 +22,7 @@ use syntax::{ }; use crate::{ - completions::postfix::is_in_condition, + completions::postfix::{is_in_condition, is_in_value}, context::{ AttrCtx, BreakableKind, COMPLETION_MARKER, CompletionAnalysis, DotAccess, DotAccessExprCtx, DotAccessKind, ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, @@ -1097,25 +1097,6 @@ fn classify_name_ref<'db>( .and_then(|next| next.first_token()) .is_some_and(|token| token.kind() == SyntaxKind::ELSE_KW) }; - let is_in_value = |it: &SyntaxNode| { - let Some(node) = it.parent() else { return false }; - let kind = node.kind(); - ast::LetStmt::can_cast(kind) - || ast::ArgList::can_cast(kind) - || ast::ArrayExpr::can_cast(kind) - || ast::ParenExpr::can_cast(kind) - || ast::BreakExpr::can_cast(kind) - || ast::ReturnExpr::can_cast(kind) - || ast::PrefixExpr::can_cast(kind) - || ast::FormatArgsArg::can_cast(kind) - || ast::RecordExprField::can_cast(kind) - || ast::BinExpr::cast(node.clone()) - .and_then(|expr| expr.rhs()) - .is_some_and(|expr| expr.syntax() == it) - || ast::IndexExpr::cast(node) - .and_then(|expr| expr.index()) - .is_some_and(|expr| expr.syntax() == it) - }; // We do not want to generate path completions when we are sandwiched between an item decl signature and its body. // ex. trait Foo $0 {} @@ -1429,7 +1410,7 @@ fn classify_name_ref<'db>( .find_map(ast::LetStmt::cast) .is_some_and(|it| it.semicolon_token().is_none()) || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw; - let in_value = is_in_value(it); + let in_value = is_in_value(&expr); let impl_ = fetch_immediate_impl_or_trait(sema, original_file, expr.syntax()) .and_then(Either::left); From 270e965c4d21736fec67d2c5fc0a492bf21bacc4 Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Thu, 30 Apr 2026 22:28:26 +0600 Subject: [PATCH 136/289] fix: correct indentation when inlining self-param with let stmts --- .../crates/ide-assists/src/handlers/inline_call.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 6e45f537e4490..c6a0b22d478bf 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 @@ -357,7 +357,7 @@ fn inline( }; // Capture before `with_ast_node` re-roots and loses the source-relative position. - let original_body_indent = IndentLevel::from_node(body_to_clone.syntax()); + let mut original_body_indent = IndentLevel::from_node(body_to_clone.syntax()); let body_offset = body_to_clone.syntax().text_range().start(); let (editor, body) = SyntaxEditor::with_ast_node(&body_to_clone); @@ -628,6 +628,7 @@ fn inline( // Prepend let statements to the body's existing statements let stmts: Vec = let_stmts.into_iter().chain(body.statements()).collect(); body = make.block_expr(stmts, body.tail_expr()); + original_body_indent = IndentLevel(0); } let original_indentation = match node { From f025ea09b215d9293f1e4591502be7665e43454b Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 1 May 2026 01:30:36 +0800 Subject: [PATCH 137/289] Readable .contains(offset) --- src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs b/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs index fecd3fb0966a2..2dd558b0b721e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs @@ -135,7 +135,7 @@ impl TextEdit { if indel.delete.start() >= offset { continue; } - if offset < indel.delete.end() { + if indel.delete.contains(offset) { return None; } res += TextSize::of(&indel.insert); From c209b4f81ffd4d3f5a93abdbf0d8de5005b225eb Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 1 May 2026 04:23:50 +0800 Subject: [PATCH 138/289] internal: add vendor/ to gitignore --- src/tools/rust-analyzer/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/.gitignore b/src/tools/rust-analyzer/.gitignore index 7192e685e29bf..3beabe39bc7a3 100644 --- a/src/tools/rust-analyzer/.gitignore +++ b/src/tools/rust-analyzer/.gitignore @@ -1,4 +1,5 @@ target/ +vendor/ /dist/ **/*.rs.bk **/*.rs.pending-snap From bd95b5c6783b9126a2725c851039c0557ad4a01f Mon Sep 17 00:00:00 2001 From: Enyium <123484196+Enyium@users.noreply.github.com> Date: Thu, 30 Apr 2026 22:34:38 +0200 Subject: [PATCH 139/289] docs: clarify that `SmolStr` doesn't allocate when cloning --- src/tools/rust-analyzer/lib/smol_str/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/lib/smol_str/src/lib.rs b/src/tools/rust-analyzer/lib/smol_str/src/lib.rs index 55ede286c245d..b76c1c7d1486b 100644 --- a/src/tools/rust-analyzer/lib/smol_str/src/lib.rs +++ b/src/tools/rust-analyzer/lib/smol_str/src/lib.rs @@ -15,8 +15,8 @@ use core::{ /// A `SmolStr` is a string type that has the following properties: /// -/// * `size_of::() == 24` (therefor `== size_of::()` on 64 bit platforms) -/// * `Clone` is `O(1)` +/// * `size_of::() == 24` (therefore `== size_of::()` on 64 bit platforms) +/// * `Clone` is `O(1)` (no allocation) /// * Strings are stack-allocated if they are: /// * Up to 23 bytes long /// * Longer than 23 bytes, but substrings of `WS` (see below). Such strings consist From 1bb6487eb89152c71d9dad962480178f9b42c9db Mon Sep 17 00:00:00 2001 From: Enyium <123484196+Enyium@users.noreply.github.com> Date: Thu, 30 Apr 2026 22:37:07 +0200 Subject: [PATCH 140/289] docs: same in readme --- src/tools/rust-analyzer/lib/smol_str/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/lib/smol_str/README.md b/src/tools/rust-analyzer/lib/smol_str/README.md index 56296fb53f7dc..7b52a6c5b373c 100644 --- a/src/tools/rust-analyzer/lib/smol_str/README.md +++ b/src/tools/rust-analyzer/lib/smol_str/README.md @@ -8,7 +8,7 @@ A `SmolStr` is a string type that has the following properties: * `size_of::() == 24` (therefore `== size_of::()` on 64 bit platforms) -* `Clone` is `O(1)` +* `Clone` is `O(1)` (no allocation) * Strings are stack-allocated if they are: * Up to 23 bytes long * Longer than 23 bytes, but substrings of `WS` (see `src/lib.rs`). Such strings consist From 654ba74e209e7cd653faeb12f438aa394fb4271d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 1 May 2026 00:04:49 +0300 Subject: [PATCH 141/289] Wrap top level or patterns in parens in convert_match_to_let_else I really hate the way it was done, we should improve the editor. --- .../src/handlers/convert_match_to_let_else.rs | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index bc49acc1ef356..6ff6b4b736ade 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -61,9 +61,17 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<' |builder| { let extracting_arm_pat = rename_variable(&extracting_arm_pat, &extracted_variable_positions, pat); + let (open_paren, close_paren) = if ast::OrPat::can_cast(extracting_arm_pat.kind()) { + // Or patterns cannot put put directly under let statements. + // FIXME: Do this with `SyntaxEditor` in `rename_variable()`, it's just difficult right now + // since it re-roots nodes. + ("(", ")") + } else { + ("", "") + }; builder.replace( let_stmt.syntax().text_range(), - format!("let {extracting_arm_pat} = {initializer_expr} else {diverging_arm_expr};"), + format!("let {open_paren}{extracting_arm_pat}{close_paren} = {initializer_expr} else {diverging_arm_expr};"), ) }, ) @@ -543,4 +551,38 @@ fn f() { "#, ); } + + #[test] + fn top_level_or_pat() { + check_assist( + convert_match_to_let_else, + r#" +enum E { + A(u32), + B(u32), + C, +} + +fn foo() { + let e = E::A(0); + let _$0 = match e { + E::A(v) | E::B(v) => v, + _ => return, + }; +} + "#, + r#" +enum E { + A(u32), + B(u32), + C, +} + +fn foo() { + let e = E::A(0); + let (E::A(_) | E::B(_)) = e else { return }; +} + "#, + ); + } } From f9d017eb9d79c24b65e6ed3fb518dba018b667e2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 1 May 2026 03:35:22 +0300 Subject: [PATCH 142/289] Refactor `hir_ty::generics::Generics` Store the chain of parents in an `ArrayVec` and not as `Option`. This should help perf and it simplifies the code. --- .../crates/hir-ty/src/display.rs | 18 +- .../crates/hir-ty/src/generics.rs | 366 ++++++++++-------- .../crates/hir-ty/src/infer/callee.rs | 11 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 14 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 99 ++--- .../crates/hir-ty/src/lower/path.rs | 29 +- .../crates/hir-ty/src/mir/lower.rs | 5 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 21 +- 8 files changed, 269 insertions(+), 294 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 6bc55bc0e4731..2df190bb7a862 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -52,7 +52,7 @@ use stdx::never; use crate::{ CallableDefId, FnAbi, ImplTraitId, MemoryMap, ParamEnvAndCrate, consteval, db::HirDatabase, - generics::generics, + generics::{ProvenanceSplit, generics}, layout::Layout, lower::GenericPredicates, mir::pad16, @@ -743,7 +743,7 @@ impl<'db> HirDisplay<'db> for Const<'db> { } ConstKind::Infer(..) => write!(f, "#c#"), ConstKind::Param(param) => { - let generics = generics(f.db, param.id.parent()); + let generics = GenericParams::of(f.db, param.id.parent()); let param_data = &generics[param.id.local_id()]; f.start_location_link_generic(param.id.into()); write!(f, "{}", param_data.name().unwrap().display(f.db, f.edition()))?; @@ -1364,8 +1364,14 @@ impl<'db> HirDisplay<'db> for Ty<'db> { if !args.is_empty() { let generic_def_id = GenericDefId::from_callable(db, def); let generics = generics(db, generic_def_id); - let (parent_len, self_param, type_, const_, impl_, lifetime) = - generics.provenance_split(); + let ProvenanceSplit { + parent_total: parent_len, + has_self_param: self_param, + non_impl_trait_type_params: type_, + const_params: const_, + impl_trait_type_params: impl_, + lifetimes: lifetime, + } = generics.provenance_split(); let parameters = args.as_slice(); debug_assert_eq!( parameters.len(), @@ -1631,7 +1637,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { TyKind::Param(param) => { // FIXME: We should not access `param.id`, it should be removed, and we should know the // parent from the formatted type. - let generics = generics(db, param.id.parent()); + let generics = GenericParams::of(db, param.id.parent()); let param_data = &generics[param.id.local_id()]; match param_data { TypeOrConstParamData::TypeParamData(p) => match p.provenance { @@ -2273,7 +2279,7 @@ impl<'db> HirDisplay<'db> for Region<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { match self.kind() { RegionKind::ReEarlyParam(param) => { - let generics = generics(f.db, param.id.parent); + let generics = GenericParams::of(f.db, param.id.parent); let param_data = &generics[param.id.local_id]; f.start_location_link_generic(param.id.into()); write!(f, "{}", param_data.name.display(f.db, f.edition()))?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index b04128184413e..354acb1501e54 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -7,51 +7,56 @@ //! - Type or Const parameters //! //! where parent follows the same scheme. -use std::ops; +use arrayvec::ArrayVec; use hir_def::{ ConstParamId, GenericDefId, GenericParamId, ItemContainerId, LifetimeParamId, Lookup, TypeOrConstParamId, TypeParamId, db::DefDatabase, expr_store::ExpressionStore, hir::generics::{ - GenericParamDataRef, GenericParams, LifetimeParamData, LocalLifetimeParamId, - LocalTypeOrConstParamId, TypeOrConstParamData, TypeParamProvenance, WherePredicate, + GenericParamDataRef, GenericParams, LifetimeParamData, TypeOrConstParamData, + TypeParamProvenance, WherePredicate, }, }; -use itertools::chain; -pub fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics<'_> { - let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def))); +pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics<'_> { + let mut chain = ArrayVec::new(); + let mut parent_params_len = 0; + if let Some(parent_def) = parent_generic_def(db, def) { + let (parent_params, parent_store) = GenericParams::with_store(db, parent_def); + chain.push(SingleGenerics { + def: parent_def, + params: parent_params, + store: parent_store, + preceding_params_len: 0, + }); + parent_params_len = parent_params.len() as u32; + } let (params, store) = GenericParams::with_store(db, def); - let has_trait_self_param = params.trait_self_param().is_some(); - Generics { def, params, parent_generics, has_trait_self_param, store } + chain.push(SingleGenerics { def, params, store, preceding_params_len: parent_params_len }); + Generics { chain } +} + +#[derive(Debug)] +pub(crate) struct Generics<'db> { + chain: ArrayVec, 2>, } -#[derive(Clone, Debug)] -pub struct Generics<'db> { + +#[derive(Debug)] +pub(crate) struct SingleGenerics<'db> { def: GenericDefId, + preceding_params_len: u32, params: &'db GenericParams, store: &'db ExpressionStore, - parent_generics: Option>>, - has_trait_self_param: bool, } -impl ops::Index for Generics<'_> -where - GenericParams: ops::Index, -{ - type Output = >::Output; - fn index(&self, index: T) -> &Self::Output { - &self.params[index] - } -} - -impl<'db> Generics<'db> { +impl<'db> SingleGenerics<'db> { pub(crate) fn def(&self) -> GenericDefId { self.def } - pub(crate) fn store(&self) -> &ExpressionStore { + pub(crate) fn store(&self) -> &'db ExpressionStore { self.store } @@ -59,165 +64,236 @@ impl<'db> Generics<'db> { self.params.where_predicates().iter() } - pub(crate) fn is_empty(&self) -> bool { - self.params.is_empty() && self.parent_generics.as_ref().is_none_or(|g| g.params.is_empty()) + pub(crate) fn has_no_params(&self) -> bool { + self.params.is_empty() } - pub(crate) fn iter_id(&self) -> impl Iterator + '_ { - self.iter_parent_id().chain(self.iter_self_id()) + pub(crate) fn len_lifetimes(&self) -> usize { + self.params.len_lifetimes() } - pub(crate) fn iter_self_id(&self) -> impl Iterator + '_ { - self.iter_self().map(|(id, _)| id) + pub(crate) fn len(&self) -> usize { + self.params.len() } - pub(crate) fn iter_parent_id(&self) -> impl Iterator + '_ { - self.iter_parent().map(|(id, _)| id) + fn iter_lifetimes(&self) -> impl Iterator { + let parent = self.def; + self.params + .iter_lt() + .map(move |(local_id, data)| (LifetimeParamId { parent, local_id }, data)) } - pub(crate) fn iter_self_type_or_consts( + pub(crate) fn iter_type_or_consts( &self, - ) -> impl DoubleEndedIterator + '_ - { - let mut toc = self.params.iter_type_or_consts(); - let trait_self_param = self.has_trait_self_param.then(|| toc.next()).flatten(); - chain!(trait_self_param, toc) + ) -> impl Iterator { + let parent = self.def; + self.params + .iter_type_or_consts() + .map(move |(local_id, data)| (TypeOrConstParamId { parent, local_id }, data)) } - /// Iterate over the parent params followed by self params. - pub(crate) fn iter( + fn iter_type_or_consts_as_generic( + &self, + ) -> impl Iterator)> { + self.iter_type_or_consts().map(|(id, data)| match data { + TypeOrConstParamData::TypeParamData(data) => ( + GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)), + GenericParamDataRef::TypeParamData(data), + ), + TypeOrConstParamData::ConstParamData(data) => ( + GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)), + GenericParamDataRef::ConstParamData(data), + ), + }) + } + + fn trait_self_and_others( &self, - ) -> impl DoubleEndedIterator)> + '_ { - self.iter_parent().chain(self.iter_self()) + ) -> ( + Option<(GenericParamId, GenericParamDataRef<'db>)>, + impl Iterator)>, + ) { + let mut iter = self.iter_type_or_consts_as_generic(); + let trait_self = if let GenericDefId::TraitId(_) = self.def { iter.next() } else { None }; + (trait_self, iter) } - pub(crate) fn iter_parents_with_store( + pub(crate) fn iter(&self) -> impl Iterator)> { + let lifetimes = self.iter_lifetimes().map(|(id, data)| { + (GenericParamId::LifetimeParamId(id), GenericParamDataRef::LifetimeParamData(data)) + }); + let (trait_self, type_and_consts) = self.trait_self_and_others(); + trait_self.into_iter().chain(lifetimes).chain(type_and_consts) + } + + pub(crate) fn iter_with_idx( &self, - ) -> impl Iterator), &ExpressionStore)> + '_ - { - self.iter_parent() - .zip(self.parent_generics().into_iter().flat_map(|it| std::iter::repeat(it.store))) + ) -> impl Iterator)> { + std::iter::zip(self.preceding_params_len.., self.iter()) + .map(|(index, (id, data))| (index, id, data)) + } + + pub(crate) fn iter_id(&self) -> impl Iterator { + self.iter().map(|(id, _)| id) + } +} + +impl<'db> Generics<'db> { + pub(crate) fn iter_owners(&self) -> impl DoubleEndedIterator> { + self.chain.iter() + } + + fn owner(&self) -> &SingleGenerics<'db> { + self.chain.last().expect("must have an owner params") + } + + pub(crate) fn parent(&self) -> Option<&SingleGenerics<'db>> { + match &*self.chain { + [parent, _owner] => Some(parent), + _ => None, + } + } + + pub(crate) fn has_no_params(&self) -> bool { + self.iter_owners().all(|owner| owner.has_no_params()) + } + + pub(crate) fn def(&self) -> GenericDefId { + self.owner().def + } + + pub(crate) fn store(&self) -> &'db ExpressionStore { + self.owner().store } - /// Iterate over the params without parent params. pub(crate) fn iter_self( &self, - ) -> impl DoubleEndedIterator)> + '_ { - let mut toc = self.params.iter_type_or_consts().map(from_toc_id(self)); - let trait_self_param = self.has_trait_self_param.then(|| toc.next()).flatten(); - chain!(trait_self_param, self.params.iter_lt().map(from_lt_id(self)), toc) + ) -> impl Iterator)> { + self.owner().iter() } - /// Iterator over types and const params of parent. - pub(crate) fn iter_parent( + pub(crate) fn iter_self_with_idx( &self, - ) -> impl DoubleEndedIterator)> + '_ { - self.parent_generics().into_iter().flat_map(|it| { - let mut toc = it.params.iter_type_or_consts().map(from_toc_id(it)); - let trait_self_param = it.has_trait_self_param.then(|| toc.next()).flatten(); - chain!(trait_self_param, it.params.iter_lt().map(from_lt_id(it)), toc) - }) + ) -> impl Iterator)> { + self.owner().iter_with_idx() + } + + pub(crate) fn iter_parent_id(&self) -> impl Iterator { + self.parent().into_iter().flat_map(|parent| parent.iter_id()) + } + + pub(crate) fn iter_self_type_or_consts( + &self, + ) -> impl Iterator { + self.owner().iter_type_or_consts() + } + + /// Iterate over the parent params followed by self params. + #[cfg(test)] + pub(crate) fn iter(&self) -> impl Iterator)> { + self.iter_owners().flat_map(|owner| owner.iter()) + } + + pub(crate) fn iter_id(&self) -> impl Iterator { + self.iter_owners().flat_map(|owner| owner.iter_id()) } /// Returns total number of generic parameters in scope, including those from parent. pub(crate) fn len(&self) -> usize { - let parent = self.len_parent(); - let child = self.params.len(); - parent + child + match &*self.chain { + [parent, owner] => parent.len() + owner.len(), + [owner] => owner.len(), + _ => unreachable!(), + } } #[inline] pub(crate) fn len_parent(&self) -> usize { - self.parent_generics().map_or(0, Generics::len) + self.parent().map_or(0, SingleGenerics::len) } pub(crate) fn len_lifetimes_self(&self) -> usize { - self.params.len_lifetimes() + self.owner().len_lifetimes() } - /// (parent total, self param, type params, const params, impl trait list, lifetimes) - pub(crate) fn provenance_split(&self) -> (usize, bool, usize, usize, usize, usize) { - let mut self_param = false; - let mut type_params = 0; - let mut impl_trait_params = 0; + pub(crate) fn provenance_split(&self) -> ProvenanceSplit { + let parent_total = self.len_parent(); + + let owner = self.owner(); + let lifetimes = owner.params.len_lifetimes(); + + let mut has_self_param = false; + let mut non_impl_trait_type_params = 0; + let mut impl_trait_type_params = 0; let mut const_params = 0; - self.params.iter_type_or_consts().for_each(|(_, data)| match data { + owner.params.iter_type_or_consts().for_each(|(_, data)| match data { TypeOrConstParamData::TypeParamData(p) => match p.provenance { - TypeParamProvenance::TypeParamList => type_params += 1, - TypeParamProvenance::TraitSelf => self_param |= true, - TypeParamProvenance::ArgumentImplTrait => impl_trait_params += 1, + TypeParamProvenance::TypeParamList => non_impl_trait_type_params += 1, + TypeParamProvenance::TraitSelf => has_self_param |= true, + TypeParamProvenance::ArgumentImplTrait => impl_trait_type_params += 1, }, TypeOrConstParamData::ConstParamData(_) => const_params += 1, }); - let lifetime_params = self.params.len_lifetimes(); - - let parent_len = self.parent_generics().map_or(0, Generics::len); - (parent_len, self_param, type_params, const_params, impl_trait_params, lifetime_params) - } - - pub(crate) fn type_or_const_param( - &self, - param: TypeOrConstParamId, - ) -> Option<(usize, TypeOrConstParamData)> { - let idx = self.find_type_or_const_param(param)?; - self.iter().nth(idx).and_then(|p| { - let data = match p.1 { - GenericParamDataRef::TypeParamData(p) => p.clone().into(), - GenericParamDataRef::ConstParamData(p) => p.clone().into(), - _ => return None, - }; - Some((idx, data)) - }) - } - - pub fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option { - self.find_type_or_const_param(param) + ProvenanceSplit { + parent_total, + has_self_param, + non_impl_trait_type_params, + const_params, + impl_trait_type_params, + lifetimes, + } } - fn find_type_or_const_param(&self, param: TypeOrConstParamId) -> Option { - if param.parent == self.def { - let idx = param.local_id.into_raw().into_u32() as usize; - debug_assert!( - idx < self.params.len_type_or_consts(), - "idx: {} len: {}", - idx, - self.params.len_type_or_consts() - ); - if self.params.trait_self_param() == Some(param.local_id) { - return Some(idx); + fn find_owner(&self, def: GenericDefId) -> &SingleGenerics<'db> { + match &*self.chain { + [parent, owner] => { + if parent.def == def { + parent + } else { + debug_assert_eq!(def, owner.def); + owner + } } - Some(self.parent_generics().map_or(0, |g| g.len()) + self.params.len_lifetimes() + idx) - } else { - debug_assert_eq!(self.parent_generics().map(|it| it.def), Some(param.parent)); - self.parent_generics().and_then(|g| g.find_type_or_const_param(param)) + [owner] => { + debug_assert_eq!(def, owner.def); + owner + } + _ => unreachable!(), } } - pub fn lifetime_idx(&self, lifetime: LifetimeParamId) -> Option { - self.find_lifetime(lifetime) - } - - fn find_lifetime(&self, lifetime: LifetimeParamId) -> Option { - if lifetime.parent == self.def { - let idx = lifetime.local_id.into_raw().into_u32() as usize; - debug_assert!(idx <= self.params.len_lifetimes()); - Some( - self.parent_generics().map_or(0, |g| g.len()) - + self.params.trait_self_param().is_some() as usize - + idx, - ) + pub(crate) fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> u32 { + let owner = self.find_owner(param.parent); + let has_trait_self = matches!(owner.def, GenericDefId::TraitId(_)); + if has_trait_self && param.local_id == GenericParams::SELF_PARAM_ID_IN_SELF { + owner.preceding_params_len } else { - debug_assert_eq!(self.parent_generics().map(|it| it.def), Some(lifetime.parent)); - self.parent_generics().and_then(|g| g.find_lifetime(lifetime)) + owner.preceding_params_len + + owner.len_lifetimes() as u32 + + param.local_id.into_raw().into_u32() } } - pub(crate) fn parent_generics(&self) -> Option<&Generics<'db>> { - self.parent_generics.as_deref() + pub(crate) fn lifetime_param_idx(&self, param: LifetimeParamId) -> u32 { + let owner = self.find_owner(param.parent); + let has_trait_self = matches!(owner.def, GenericDefId::TraitId(_)); + owner.preceding_params_len + + u32::from(has_trait_self) + + param.local_id.into_raw().into_u32() } } +pub(crate) struct ProvenanceSplit { + pub(crate) parent_total: usize, + // The rest are about self. + pub(crate) has_self_param: bool, + pub(crate) non_impl_trait_type_params: usize, + pub(crate) const_params: usize, + pub(crate) impl_trait_type_params: usize, + pub(crate) lifetimes: usize, +} + pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option { let container = match def { GenericDefId::FunctionId(it) => it.lookup(db).container, @@ -235,35 +311,3 @@ pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Opt ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, } } - -fn from_toc_id<'a>( - it: &'a Generics<'a>, -) -> impl Fn( - (LocalTypeOrConstParamId, &'a TypeOrConstParamData), -) -> (GenericParamId, GenericParamDataRef<'a>) { - move |(local_id, p): (_, _)| { - let id = TypeOrConstParamId { parent: it.def, local_id }; - match p { - TypeOrConstParamData::TypeParamData(p) => ( - GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)), - GenericParamDataRef::TypeParamData(p), - ), - TypeOrConstParamData::ConstParamData(p) => ( - GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)), - GenericParamDataRef::ConstParamData(p), - ), - } - } -} - -fn from_lt_id<'a>( - it: &'a Generics<'a>, -) -> impl Fn((LocalLifetimeParamId, &'a LifetimeParamData)) -> (GenericParamId, GenericParamDataRef<'a>) -{ - move |(local_id, p): (_, _)| { - ( - GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }), - GenericParamDataRef::LifetimeParamData(p), - ) - } -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs index 237c9177f8790..9fe566b8fe068 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs @@ -5,9 +5,7 @@ use std::iter; use intern::sym; use tracing::debug; -use hir_def::{ - CallableDefId, ConstParamId, TypeOrConstParamId, hir::ExprId, signatures::FunctionSignature, -}; +use hir_def::{CallableDefId, ConstParamId, hir::ExprId, signatures::FunctionSignature}; use rustc_type_ir::{ InferTy, Interner, inherent::{GenericArgs as _, IntoKind, Ty as _}, @@ -365,12 +363,7 @@ impl<'db> InferenceContext<'_, 'db> { let const_params = generics .iter_self_type_or_consts() .filter(|(_, param_data)| param_data.const_param().is_some()) - .map(|(idx, _)| { - ConstParamId::from_unchecked(TypeOrConstParamId { - parent: func.into(), - local_id: idx, - }) - }) + .map(|(id, _)| ConstParamId::from_unchecked(id)) .collect::>(); let data = FunctionSignature::of(self.db, func); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 4433dd6425ed6..133447dff3f18 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -26,6 +26,7 @@ extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver; extern crate self as hir_ty; pub mod builtin_derive; +mod generics; mod infer; mod inhabitedness; mod lower; @@ -44,7 +45,6 @@ pub mod diagnostics; pub mod display; pub mod drop; pub mod dyn_compatibility; -pub mod generics; pub mod lang_items; pub mod layout; pub mod method_resolution; @@ -61,8 +61,8 @@ mod tests; use std::{hash::Hash, ops::ControlFlow}; use hir_def::{ - CallableDefId, ExpressionStoreOwnerId, GenericDefId, TypeAliasId, TypeOrConstParamId, - TypeParamId, + CallableDefId, ExpressionStoreOwnerId, GenericDefId, LifetimeParamId, TypeAliasId, + TypeOrConstParamId, TypeParamId, hir::{ExprId, ExprOrPatId, PatId}, resolver::TypeNs, type_ref::{Rawness, TypeRefId}, @@ -210,10 +210,14 @@ impl<'db> MemoryMap<'db> { } /// Return an index of a parameter in the generic type parameter list by it's id. -pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option { +pub fn type_or_const_param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> u32 { generics::generics(db, id.parent).type_or_const_param_idx(id) } +pub fn lifetime_param_idx(db: &dyn HirDatabase, id: LifetimeParamId) -> u32 { + generics::generics(db, id.parent).lifetime_param_idx(id) +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum FnAbi { Aapcs, @@ -504,7 +508,7 @@ pub fn associated_type_shorthand_candidates( }; let mut dedup_map = FxHashSet::default(); - let param_ty = Ty::new_param(interner, param, param_idx(db, param.into()).unwrap() as u32); + let param_ty = Ty::new_param(interner, param, type_or_const_param_idx(db, param.into())); // We use the ParamEnv and not the predicates because the ParamEnv elaborates bounds. let param_env = db.trait_environment(ExpressionStoreOwnerId::from(def)); for clause in param_env.clauses { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 7325cd0ef8a5d..7066d40813460 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -10,7 +10,6 @@ pub(crate) mod path; use std::{cell::OnceCell, iter, mem}; -use arrayvec::ArrayVec; use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, EnumId, EnumVariantId, @@ -55,7 +54,7 @@ use crate::{ FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, consteval::intern_const_ref, db::{HirDatabase, InternedOpaqueTyId}, - generics::{Generics, generics}, + generics::{Generics, SingleGenerics, generics}, next_solver::{ AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FxIndexMap, GenericArg, @@ -376,18 +375,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { match self.resolver.resolve_path_in_value_ns_fully(self.db, path, HygieneId::ROOT) { Some(ValueNs::GenericParam(p)) => { let args = self.generics(); - match args.type_or_const_param_idx(p.into()) { - Some(idx) => Some(self.const_param(p, idx as u32)), - None => { - never!( - "Generic list doesn't contain this param: {:?}, {:?}, {:?}", - args, - path, - p - ); - None - } - } + let idx = args.type_or_const_param_idx(p.into()); + Some(self.const_param(p, idx)) } Some(ValueNs::ConstId(c)) => { let args = GenericArgs::empty(self.interner); @@ -465,9 +454,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { res = Some(TypeNs::GenericParam(type_param_id)); let generics = self.generics(); - let (idx, _data) = - generics.type_or_const_param(type_param_id.into()).expect("matching generics"); - self.type_param(type_param_id, idx as u32) + let idx = generics.type_or_const_param_idx(type_param_id.into()); + self.type_param(type_param_id, idx) } &TypeRef::RawPtr(inner, mutability) => { let inner_ty = self.lower_ty(inner); @@ -1094,11 +1082,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { Some(resolution) => match resolution { LifetimeNs::Static => Region::new_static(self.interner), LifetimeNs::LifetimeParam(id) => { - let idx = match self.generics().lifetime_idx(id) { - None => return Region::error(self.interner), - Some(idx) => idx, - }; - self.region_param(id, idx as u32) + let idx = self.generics().lifetime_param_idx(id); + self.region_param(id, idx) } }, None => Region::error(self.interner), @@ -1785,11 +1770,7 @@ fn resolve_type_param_assoc_type_shorthand( LifetimeElisionKind::AnonymousReportError, ); let interner = ctx.interner; - let param_ty = Ty::new_param( - interner, - param, - generics.type_or_const_param_idx(param.into()).unwrap() as u32, - ); + let param_ty = Ty::new_param(interner, param, generics.type_or_const_param_idx(param.into())); let mut this_trait_resolution = None; if let GenericDefId::TraitId(containing_trait) = param.parent() @@ -1805,9 +1786,7 @@ fn resolve_type_param_assoc_type_shorthand( } let mut supertraits_resolution = None; - for maybe_parent_generics in - std::iter::successors(Some(&generics), |generics| generics.parent_generics()) - { + for maybe_parent_generics in generics.iter_owners().rev() { ctx.store = maybe_parent_generics.store(); for pred in maybe_parent_generics.where_predicates() { let (WherePredicate::TypeBound { target, bound } @@ -2211,16 +2190,13 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic let mut parent_predicates = Vec::new(); let mut own_assoc_ty_bounds = Vec::new(); let mut parent_assoc_ty_bounds = Vec::new(); - let all_generics = - std::iter::successors(Some(&generics), |generics| generics.parent_generics()) - .collect::>(); let own_implicit_trait_predicate = implicit_trait_predicate(interner, def); - let parent_implicit_trait_predicate = if all_generics.len() > 1 { - implicit_trait_predicate(interner, all_generics.last().unwrap().def()) + let parent_implicit_trait_predicate = if let Some(parent) = generics.parent() { + implicit_trait_predicate(interner, parent.def()) } else { None }; - for &maybe_parent_generics in all_generics.iter().rev() { + for maybe_parent_generics in generics.iter_owners() { // Collect only diagnostics from the child, not including parents. ctx.diagnostics.clear(); @@ -2291,12 +2267,9 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic parent_predicates.push(clause); } }; - let parent_params_len = maybe_parent_generics.len_parent(); - maybe_parent_generics.iter_self().enumerate().for_each( - |(param_idx, (param_id, param_data))| { - add_sized_clause((param_idx + parent_params_len) as u32, param_id, param_data); - }, - ); + maybe_parent_generics.iter_with_idx().for_each(|(param_idx, param_id, param_data)| { + add_sized_clause(param_idx, param_id, param_data); + }); } // We do not clear `ctx.unsized_types`, as the `?Sized` clause of a child (e.g. an associated type) can @@ -2360,25 +2333,14 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic fn push_const_arg_has_type_predicates<'db>( db: &'db dyn HirDatabase, predicates: &mut Vec>, - generics: &Generics<'db>, + single_generics: &SingleGenerics<'db>, ) { let interner = DbInterner::new_no_crate(db); - let const_params_offset = generics.len_parent() + generics.len_lifetimes_self(); - for (param_index, (param_idx, param_data)) in generics.iter_self_type_or_consts().enumerate() { - if !matches!(param_data, TypeOrConstParamData::ConstParamData(_)) { - continue; - } - - let param_id = ConstParamId::from_unchecked(TypeOrConstParamId { - parent: generics.def(), - local_id: param_idx, - }); + for (param_index, param_id, _) in single_generics.iter_with_idx() { + let GenericParamId::ConstParamId(param_id) = param_id else { continue }; predicates.push(Clause( ClauseKind::ConstArgHasType( - Const::new_param( - interner, - ParamConst { id: param_id, index: (param_index + const_params_offset) as u32 }, - ), + Const::new_param(interner, ParamConst { id: param_id, index: param_index }), db.const_param_ty(param_id), ) .upcast(interner), @@ -2408,7 +2370,7 @@ pub(crate) fn generic_defaults_with_diagnostics_query( def: GenericDefId, ) -> (GenericDefaults, Diagnostics) { let generic_params = generics(db, def); - if generic_params.is_empty() { + if generic_params.has_no_params() { return (GenericDefaults(None), None); } let resolver = def.resolver(db); @@ -2422,24 +2384,21 @@ pub(crate) fn generic_defaults_with_diagnostics_query( LifetimeElisionKind::AnonymousReportError, ) .with_impl_trait_mode(ImplTraitLoweringMode::Disallowed); - let mut idx = 0; let mut has_any_default = false; - let mut defaults = generic_params - .iter_parents_with_store() - .map(|((_id, p), store)| { - ctx.store = store; + let mut defaults = Vec::new(); + if let Some(parent) = generic_params.parent() { + ctx.store = parent.store(); + defaults.extend(parent.iter_with_idx().map(|(idx, _id, p)| { let (result, has_default) = handle_generic_param(&mut ctx, idx, p); has_any_default |= has_default; - idx += 1; result - }) - .collect::>(); + })); + } ctx.diagnostics.clear(); // Don't include diagnostics from the parent. ctx.store = store_for_self; - defaults.extend(generic_params.iter_self().map(|(_id, p)| { + defaults.extend(generic_params.iter_self_with_idx().map(|(idx, _id, p)| { let (result, has_default) = handle_generic_param(&mut ctx, idx, p); has_any_default |= has_default; - idx += 1; result })); let diagnostics = create_diagnostics(mem::take(&mut ctx.diagnostics)); @@ -2452,10 +2411,10 @@ pub(crate) fn generic_defaults_with_diagnostics_query( fn handle_generic_param<'db>( ctx: &mut TyLoweringContext<'db, '_>, - idx: usize, + idx: u32, p: GenericParamDataRef<'_>, ) -> (Option>, bool) { - ctx.lowering_param_default(idx as u32); + ctx.lowering_param_default(idx); match p { GenericParamDataRef::TypeParamData(p) => { let ty = p.default.map(|ty| ctx.lower_ty(ty)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index d6b9c375fc427..c81e4647d87dd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -22,7 +22,6 @@ use rustc_type_ir::{ inherent::{GenericArgs as _, Region as _, Ty as _}, }; use smallvec::SmallVec; -use stdx::never; use crate::{ GenericArgsProhibitedReason, IncorrectGenericsLenKind, PathGenericsSource, @@ -243,17 +242,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { TypeNs::GenericParam(param_id) => { let generics = self.ctx.generics(); let idx = generics.type_or_const_param_idx(param_id.into()); - match idx { - None => { - never!("no matching generics"); - Ty::new_error(self.ctx.interner, ErrorGuaranteed) - } - Some(idx) => { - let (pidx, _param) = generics.iter().nth(idx).unwrap(); - assert_eq!(pidx, param_id.into()); - self.ctx.type_param(param_id, idx as u32) - } - } + self.ctx.type_param(param_id, idx) } TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty(impl_id).skip_binder(), TypeNs::AdtSelfType(adt) => { @@ -640,7 +629,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, ); - return unknown_subst(self.ctx.interner, def); + return GenericArgs::error_for_item(self.ctx.interner, def.into()); } // `Fn()`-style generics are treated like functions for the purpose of lifetime elision. @@ -1332,17 +1321,3 @@ fn type_looks_like_const( _ => None, } } - -fn unknown_subst<'db>(interner: DbInterner<'db>, def: impl Into) -> GenericArgs<'db> { - let params = generics(interner.db(), def.into()); - GenericArgs::new_from_iter( - interner, - params.iter_id().map(|id| match id { - GenericParamId::TypeParamId(_) => Ty::new_error(interner, ErrorGuaranteed).into(), - GenericParamId::ConstParamId(id) => { - unknown_const_as_generic(const_param_ty_query(interner.db(), id)) - } - GenericParamId::LifetimeParamId(_) => Region::error(interner).into(), - }), - ) -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index d16e1b0d5964e..9c9cf0ac0b500 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -567,10 +567,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { not_supported!("owner without generic def id"); }; let generics = generics(self.db, def); - let index = generics - .type_or_const_param_idx(p.into()) - .ok_or(MirLowerError::TypeError("fail to lower const generic param"))? - as u32; + let index = generics.type_or_const_param_idx(p.into()); self.push_assignment( current, place, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 9b36f8ab8100f..8488d6931e0f4 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4633,13 +4633,12 @@ impl GenericParam { GenericParam::ConstParam(_) => return None, GenericParam::LifetimeParam(it) => it.id.parent, }; - let generics = hir_ty::generics::generics(db, parent); let index = match self { - GenericParam::TypeParam(it) => generics.type_or_const_param_idx(it.id.into())?, + GenericParam::TypeParam(it) => hir_ty::type_or_const_param_idx(db, it.id.into()), GenericParam::ConstParam(_) => return None, - GenericParam::LifetimeParam(it) => generics.lifetime_idx(it.id)?, + GenericParam::LifetimeParam(it) => hir_ty::lifetime_param_idx(db, it.id), }; - db.variances_of(parent).get(index).map(Into::into) + db.variances_of(parent).get(index as usize).map(Into::into) } } @@ -4711,8 +4710,8 @@ impl TypeParam { pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { let resolver = self.id.parent().resolver(db); let interner = DbInterner::new_no_crate(db); - let index = hir_ty::param_idx(db, self.id.into()).unwrap(); - let ty = Ty::new_param(interner, self.id, index as u32); + let index = hir_ty::type_or_const_param_idx(db, self.id.into()); + let ty = Ty::new_param(interner, self.id, index); Type::new_with_resolver_inner(db, &resolver, ty) } @@ -4812,9 +4811,9 @@ impl ConstParam { } fn generic_arg_from_param(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option> { - let local_idx = hir_ty::param_idx(db, id)?; + let local_idx = hir_ty::type_or_const_param_idx(db, id); let defaults = db.generic_defaults(id.parent); - let ty = defaults.get(local_idx)?; + let ty = defaults.get(local_idx as usize)?; // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. Some(ty.instantiate_identity()) } @@ -7344,10 +7343,8 @@ fn has_non_default_type_params(db: &dyn HirDatabase, generic_def: GenericDefId) .filter(|(_, param)| matches!(param, TypeOrConstParamData::TypeParamData(_))) .map(|(local_id, _)| TypeOrConstParamId { parent: generic_def, local_id }) .any(|param| { - let Some(param) = hir_ty::param_idx(db, param) else { - return false; - }; - defaults.get(param).is_none() + let param = hir_ty::type_or_const_param_idx(db, param); + defaults.get(param as usize).is_none() }) } From 1698ed074534ebdde7b2a9e5e28ac36c78a18fcd Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 1 May 2026 04:05:59 +0300 Subject: [PATCH 143/289] Refactor the new solver's `Generics` --- .../crates/hir-ty/src/builtin_derive.rs | 14 ++- .../crates/hir-ty/src/generics.rs | 2 +- .../hir-ty/src/next_solver/generic_arg.rs | 82 +++++++----- .../crates/hir-ty/src/next_solver/generics.rs | 119 +++++------------- .../crates/hir-ty/src/next_solver/interner.rs | 16 +-- 5 files changed, 98 insertions(+), 135 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs index 6a9b1671e7be1..f14e6e4e4d0f3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs @@ -12,7 +12,7 @@ use hir_def::{ use itertools::Itertools; use la_arena::ArenaMap; use rustc_type_ir::{ - AliasTyKind, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, + AliasTyKind, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, inherent::{GenericArgs as _, IntoKind}, }; @@ -53,7 +53,10 @@ fn trait_args(trait_: BuiltinDeriveImplTrait, self_ty: Ty<'_>) -> GenericArgs<'_ } } -pub(crate) fn generics_of<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> Generics { +pub(crate) fn generics_of<'db>( + interner: DbInterner<'db>, + id: BuiltinDeriveImplId, +) -> Generics<'db> { let db = interner.db; let loc = id.loc(db); match loc.trait_ { @@ -65,15 +68,14 @@ pub(crate) fn generics_of<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplI | BuiltinDeriveImplTrait::Ord | BuiltinDeriveImplTrait::PartialOrd | BuiltinDeriveImplTrait::Eq - | BuiltinDeriveImplTrait::PartialEq => interner.generics_of(loc.adt.into()), + | BuiltinDeriveImplTrait::PartialEq => Generics::from_generic_def(db, loc.adt.into()), BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => { - let mut generics = interner.generics_of(loc.adt.into()); let trait_id = loc .trait_ .get_id(interner.lang_items()) .expect("we don't pass the impl to the solver if we can't resolve the trait"); - generics.push_param(coerce_pointee_new_type_param(trait_id).into()); - generics + let additional_param = coerce_pointee_new_type_param(trait_id).into(); + Generics::from_generic_def_plus_one(db, loc.adt.into(), additional_param) } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index 354acb1501e54..32482ca94b711 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -294,7 +294,7 @@ pub(crate) struct ProvenanceSplit { pub(crate) lifetimes: usize, } -pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option { +fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option { let container = match def { GenericDefId::FunctionId(it) => it.lookup(db).container, GenericDefId::TypeAliasId(it) => it.lookup(db).container, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 05955d060bd0e..75df2b541ebc0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -8,6 +8,7 @@ use std::{hint::unreachable_unchecked, marker::PhantomData, ptr::NonNull}; +use arrayvec::ArrayVec; use hir_def::{GenericDefId, GenericParamId}; use intern::InternedRef; use rustc_type_ir::{ @@ -18,7 +19,6 @@ use rustc_type_ir::{ relate::{Relate, VarianceDiagInfo}, walk::TypeWalker, }; -use smallvec::SmallVec; use crate::next_solver::{ ConstInterned, RegionInterned, TyInterned, impl_foldable_for_interned_slice, interned_slice, @@ -495,7 +495,47 @@ impl<'db> TypeFoldable> for StoredGenericArgs { } } +trait GenericArgsBuilder<'db>: AsRef<[GenericArg<'db>]> { + fn push(&mut self, arg: GenericArg<'db>); +} + +impl<'db, const N: usize> GenericArgsBuilder<'db> for ArrayVec, N> { + fn push(&mut self, arg: GenericArg<'db>) { + self.push(arg); + } +} + +impl<'db> GenericArgsBuilder<'db> for Vec> { + fn push(&mut self, arg: GenericArg<'db>) { + self.push(arg); + } +} + impl<'db> GenericArgs<'db> { + #[inline(always)] + fn fill_builder( + args: &mut impl GenericArgsBuilder<'db>, + defs: &Generics<'db>, + mut mk_kind: F, + ) where + F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + { + defs.iter_id().enumerate().for_each(|(idx, param_id)| { + let new_arg = mk_kind(idx as u32, param_id, args.as_ref()); + args.push(new_arg); + }); + } + + #[cold] + fn fill_vec_builder(defs: &Generics<'db>, count: usize, mk_kind: F) -> GenericArgs<'db> + where + F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + { + let mut args = Vec::with_capacity(count); + Self::fill_builder(&mut args, defs, mk_kind); + GenericArgs::new_from_slice(&args) + } + /// Creates an `GenericArgs` for generic parameter definitions, /// by calling closures to obtain each kind. /// The closures get to observe the `GenericArgs` as they're @@ -504,7 +544,7 @@ impl<'db> GenericArgs<'db> { pub fn for_item( interner: DbInterner<'db>, def_id: SolverDefId, - mut mk_kind: F, + mk_kind: F, ) -> GenericArgs<'db> where F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, @@ -513,12 +553,14 @@ impl<'db> GenericArgs<'db> { let count = defs.count(); if count == 0 { - return Default::default(); + GenericArgs::default() + } else if count <= 10 { + let mut args = ArrayVec::<_, 10>::new(); + Self::fill_builder(&mut args, &defs, mk_kind); + GenericArgs::new_from_slice(&args) + } else { + Self::fill_vec_builder(&defs, count, mk_kind) } - - let mut args = SmallVec::with_capacity(count); - Self::fill_item(&mut args, interner, defs, &mut mk_kind); - interner.mk_args(&args) } /// Creates an all-error `GenericArgs`. @@ -577,32 +619,6 @@ impl<'db> GenericArgs<'db> { }) } - fn fill_item( - args: &mut SmallVec<[GenericArg<'db>; 8]>, - interner: DbInterner<'_>, - defs: Generics, - mk_kind: &mut F, - ) where - F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, - { - if let Some(def_id) = defs.parent { - let parent_defs = interner.generics_of(def_id.into()); - Self::fill_item(args, interner, parent_defs, mk_kind); - } - Self::fill_single(args, &defs, mk_kind); - } - - fn fill_single(args: &mut SmallVec<[GenericArg<'db>; 8]>, defs: &Generics, mk_kind: &mut F) - where - F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, - { - args.reserve(defs.own_params.len()); - for param in &defs.own_params { - let kind = mk_kind(args.len() as u32, param.id, args); - args.push(kind); - } - } - pub fn types(self) -> impl Iterator> { self.iter().filter_map(|it| it.as_type()) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs index f31de21796fe3..570e24592280c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs @@ -1,117 +1,60 @@ //! Things related to generics in the next-trait-solver. -use hir_def::{ - ConstParamId, GenericDefId, GenericParamId, LifetimeParamId, TypeOrConstParamId, TypeParamId, - hir::generics::{GenericParams, TypeOrConstParamData}, -}; -use rustc_type_ir::inherent::GenericsOf; +use hir_def::{GenericDefId, GenericParamId}; -use crate::generics::parent_generic_def; +use crate::db::HirDatabase; use super::SolverDefId; use super::DbInterner; -pub(crate) fn generics(interner: DbInterner<'_>, def: SolverDefId) -> Generics { - let mk_lt = |parent, index, local_id| { - let id = GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id }); - GenericParamDef { index, id } - }; - let mk_ty = |parent, index, local_id, p: &TypeOrConstParamData| { - let id = TypeOrConstParamId { parent, local_id }; - let id = match p { - TypeOrConstParamData::TypeParamData(_) => { - GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)) - } - TypeOrConstParamData::ConstParamData(_) => { - GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)) - } - }; - GenericParamDef { index, id } - }; - let own_params_for_generic_params = |parent, params: &GenericParams| { - let mut result = Vec::with_capacity(params.len()); - let mut type_and_consts = params.iter_type_or_consts(); - let mut index = 0; - if let Some(self_param) = params.trait_self_param() { - result.push(mk_ty(parent, 0, self_param, ¶ms[self_param])); - type_and_consts.next(); - index += 1; - } - result.extend(params.iter_lt().map(|(local_id, _data)| { - let lt = mk_lt(parent, index, local_id); - index += 1; - lt - })); - result.extend(type_and_consts.map(|(local_id, data)| { - let ty = mk_ty(parent, index, local_id, data); - index += 1; - ty - })); - result - }; - +pub(crate) fn generics(interner: DbInterner<'_>, def: SolverDefId) -> Generics<'_> { let db = interner.db; - let (parent, own_params) = match (def.try_into(), def) { - (Ok(def), _) => ( - parent_generic_def(db, def), - own_params_for_generic_params(def, GenericParams::of(db, def)), - ), - (_, SolverDefId::InternedOpaqueTyId(id)) => { - match db.lookup_intern_impl_trait_id(id) { - crate::ImplTraitId::ReturnTypeImplTrait(function_id, _) => { - // The opaque type itself does not have generics - only the parent function - (Some(GenericDefId::FunctionId(function_id)), vec![]) - } - crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => { - (Some(type_alias_id.into()), Vec::new()) - } - } - } + let def = match (def.try_into(), def) { + (Ok(def), _) => def, + (_, SolverDefId::InternedOpaqueTyId(id)) => match db.lookup_intern_impl_trait_id(id) { + crate::ImplTraitId::ReturnTypeImplTrait(function_id, _) => function_id.into(), + crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => type_alias_id.into(), + }, (_, SolverDefId::BuiltinDeriveImplId(id)) => { return crate::builtin_derive::generics_of(interner, id); } _ => panic!("No generics for {def:?}"), }; - let parent_generics = parent.map(|def| Box::new(generics(interner, def.into()))); - Generics { - parent, - parent_count: parent_generics.map_or(0, |g| g.parent_count + g.own_params.len()), - own_params, - } + Generics::from_generic_def(db, def) } #[derive(Debug)] -pub struct Generics { - pub parent: Option, - pub parent_count: usize, - pub own_params: Vec, +pub struct Generics<'db> { + generics: crate::generics::Generics<'db>, + /// This is used for builtin derives, specifically `CoercePointee`. + additional_param: Option, } -impl Generics { - pub(crate) fn push_param(&mut self, id: GenericParamId) { - let index = self.count() as u32; - self.own_params.push(GenericParamDef { index, id }); +impl<'db> Generics<'db> { + pub(crate) fn from_generic_def(db: &'db dyn HirDatabase, def: GenericDefId) -> Generics<'db> { + Generics { generics: crate::generics::generics(db, def), additional_param: None } } -} -#[derive(Debug)] -pub struct GenericParamDef { - index: u32, - pub(crate) id: GenericParamId, -} + pub(crate) fn from_generic_def_plus_one( + db: &'db dyn HirDatabase, + def: GenericDefId, + additional_param: GenericParamId, + ) -> Generics<'db> { + Generics { + generics: crate::generics::generics(db, def), + additional_param: Some(additional_param), + } + } -impl GenericParamDef { - /// Returns the index of the param on the self generics only - /// (i.e. not including parent generics) - pub fn index(&self) -> u32 { - self.index + pub(super) fn iter_id(&self) -> impl Iterator { + self.generics.iter_id().chain(self.additional_param) } } -impl<'db> rustc_type_ir::inherent::GenericsOf> for Generics { +impl<'db> rustc_type_ir::inherent::GenericsOf> for Generics<'db> { fn count(&self) -> usize { - self.parent_count + self.own_params.len() + self.generics.len() + usize::from(self.additional_param.is_some()) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index fc83ebc625805..bdbcaee4f6192 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -967,7 +967,7 @@ impl<'db> Interner for DbInterner<'db> { type Clause = Clause<'db>; type Clauses = Clauses<'db>; - type GenericsOf = Generics; + type GenericsOf = Generics<'db>; type VariancesOf = VariancesOf<'db>; @@ -1124,12 +1124,14 @@ impl<'db> Interner for DbInterner<'db> { def_id: Self::DefId, args: Self::GenericArgs, ) -> (rustc_type_ir::TraitRef, Self::GenericArgsSlice) { - let trait_def_id = self.parent(def_id); - let trait_generics = self.generics_of(trait_def_id); - let trait_args = - GenericArgs::new_from_slice(&args.as_slice()[0..trait_generics.own_params.len()]); - let alias_args = &args.as_slice()[trait_generics.own_params.len()..]; - (TraitRef::new_from_args(self, trait_def_id.try_into().unwrap(), trait_args), alias_args) + let SolverDefId::TraitId(trait_def_id) = self.parent(def_id) else { + panic!("expected a trait"); + }; + let trait_generics = crate::generics::generics(self.db, trait_def_id.into()); + let trait_generics_len = trait_generics.len(); + let trait_args = GenericArgs::new_from_slice(&args.as_slice()[..trait_generics_len]); + let alias_args = &args.as_slice()[trait_generics_len..]; + (TraitRef::new_from_args(self, trait_def_id.into(), trait_args), alias_args) } fn check_args_compatible(self, _def_id: Self::DefId, _args: Self::GenericArgs) -> bool { From f1e0bc0b85fbdf5deb8973fd826fabb8618b8757 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 01:56:47 +0300 Subject: [PATCH 144/289] Remove some `Arc`s Unfortunately, this causes `Semantics` to become invariant over its lifetime, which necessitates large changes, but the changes from `hir` downwards are mechanical and could use a skim review. --- .../crates/hir-def/src/attrs/docs.rs | 4 +- .../crates/hir-def/src/expr_store/expander.rs | 24 +++-- .../crates/hir-def/src/expr_store/lower.rs | 4 +- .../crates/hir-def/src/find_path.rs | 2 +- .../crates/hir-def/src/item_tree/lower.rs | 13 +-- .../hir-def/src/macro_expansion_tests/mod.rs | 16 ++-- .../crates/hir-def/src/nameres/assoc.rs | 29 +++--- .../hir-def/src/nameres/tests/incremental.rs | 72 +++++++-------- .../crates/hir-expand/src/attrs.rs | 4 +- .../crates/hir-expand/src/cfg_process.rs | 6 +- .../rust-analyzer/crates/hir-expand/src/db.rs | 32 +++---- .../crates/hir-expand/src/declarative.rs | 6 +- .../crates/hir-expand/src/eager.rs | 18 ++-- .../crates/hir-expand/src/files.rs | 20 ++--- .../crates/hir-expand/src/fixup.rs | 15 ++-- .../crates/hir-expand/src/lib.rs | 18 ++-- .../crates/hir-expand/src/span_map.rs | 58 +++--------- .../crates/hir-ty/src/tests/incremental.rs | 28 +++--- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- .../rust-analyzer/crates/hir/src/semantics.rs | 6 +- .../crates/hir/src/semantics/source_to_def.rs | 12 +-- .../crates/hir/src/term_search.rs | 8 +- .../crates/hir/src/term_search/tactics.rs | 18 ++-- .../crates/ide-assists/src/assist_context.rs | 14 +-- .../ide-assists/src/handlers/add_braces.rs | 4 +- .../src/handlers/add_explicit_dot_deref.rs | 2 +- .../add_explicit_enum_discriminant.rs | 2 +- .../src/handlers/add_explicit_type.rs | 2 +- .../src/handlers/add_label_to_loop.rs | 4 +- .../src/handlers/add_lifetime_to_type.rs | 2 +- .../src/handlers/add_missing_impl_members.rs | 11 ++- .../src/handlers/add_missing_match_arms.rs | 12 +-- .../src/handlers/add_return_type.rs | 4 +- .../src/handlers/add_turbo_fish.rs | 2 +- .../src/handlers/apply_demorgan.rs | 9 +- .../ide-assists/src/handlers/auto_import.rs | 8 +- .../src/handlers/bind_unused_param.rs | 2 +- .../src/handlers/change_visibility.rs | 4 +- .../src/handlers/convert_bool_then.rs | 10 ++- .../src/handlers/convert_bool_to_enum.rs | 10 +-- .../src/handlers/convert_char_literal.rs | 2 +- .../src/handlers/convert_closure_to_fn.rs | 10 +-- .../src/handlers/convert_comment_block.rs | 2 +- .../convert_comment_from_or_to_doc.rs | 2 +- .../src/handlers/convert_for_to_while_let.rs | 2 +- .../src/handlers/convert_from_to_tryfrom.rs | 5 +- .../src/handlers/convert_integer_literal.rs | 5 +- .../src/handlers/convert_into_to_from.rs | 2 +- .../handlers/convert_iter_for_each_to_for.rs | 6 +- .../src/handlers/convert_let_else_to_match.rs | 5 +- .../src/handlers/convert_match_to_let_else.rs | 9 +- .../convert_named_struct_to_tuple_struct.rs | 12 +-- .../convert_nested_function_to_closure.rs | 2 +- .../handlers/convert_range_for_to_while.rs | 5 +- .../src/handlers/convert_to_guarded_return.rs | 9 +- .../convert_tuple_return_type_to_struct.rs | 8 +- .../convert_tuple_struct_to_named_struct.rs | 10 +-- ...ert_two_arm_bool_match_to_matches_macro.rs | 2 +- .../src/handlers/convert_while_to_loop.rs | 2 +- .../handlers/destructure_struct_binding.rs | 22 +++-- .../src/handlers/destructure_tuple_binding.rs | 29 +++--- .../src/handlers/desugar_doc_comment.rs | 2 +- .../src/handlers/desugar_try_expr.rs | 2 +- .../src/handlers/expand_glob_import.rs | 24 ++--- .../src/handlers/expand_rest_pattern.rs | 12 +-- .../extract_expressions_from_format_string.rs | 2 +- .../src/handlers/extract_function.rs | 90 +++++++++---------- .../src/handlers/extract_module.rs | 12 +-- .../extract_struct_from_enum_variant.rs | 4 +- .../src/handlers/extract_type_alias.rs | 2 +- .../src/handlers/extract_variable.rs | 10 +-- .../src/handlers/fix_visibility.rs | 4 +- .../ide-assists/src/handlers/flip_binexpr.rs | 4 +- .../ide-assists/src/handlers/flip_comma.rs | 2 +- .../src/handlers/flip_or_pattern.rs | 2 +- .../src/handlers/flip_trait_bound.rs | 2 +- .../handlers/generate_blanket_trait_impl.rs | 2 +- .../src/handlers/generate_constant.rs | 4 +- .../generate_default_from_enum_variant.rs | 2 +- .../src/handlers/generate_default_from_new.rs | 7 +- .../src/handlers/generate_delegate_methods.rs | 5 +- .../src/handlers/generate_delegate_trait.rs | 17 ++-- .../src/handlers/generate_deref.rs | 6 +- .../src/handlers/generate_derive.rs | 2 +- .../generate_documentation_template.rs | 22 ++--- .../src/handlers/generate_enum_is_method.rs | 5 +- .../generate_enum_projection_method.rs | 9 +- .../src/handlers/generate_enum_variant.rs | 6 +- .../src/handlers/generate_fn_type_alias.rs | 2 +- .../handlers/generate_from_impl_for_enum.rs | 7 +- .../src/handlers/generate_function.rs | 59 ++++++------ .../src/handlers/generate_getter_or_setter.rs | 16 ++-- .../ide-assists/src/handlers/generate_impl.rs | 6 +- .../handlers/generate_is_empty_from_len.rs | 7 +- .../src/handlers/generate_mut_trait_impl.rs | 5 +- .../ide-assists/src/handlers/generate_new.rs | 2 +- .../generate_single_field_struct_from.rs | 4 +- .../src/handlers/generate_trait_from_impl.rs | 5 +- .../ide-assists/src/handlers/inline_call.rs | 8 +- .../src/handlers/inline_const_as_literal.rs | 7 +- .../src/handlers/inline_local_variable.rs | 2 +- .../ide-assists/src/handlers/inline_macro.rs | 4 +- .../src/handlers/inline_type_alias.rs | 6 +- .../src/handlers/into_to_qualified_from.rs | 2 +- .../src/handlers/introduce_named_lifetime.rs | 5 +- .../introduce_named_type_parameter.rs | 2 +- .../ide-assists/src/handlers/invert_if.rs | 2 +- .../ide-assists/src/handlers/merge_imports.rs | 2 +- .../src/handlers/merge_match_arms.rs | 8 +- .../src/handlers/merge_nested_if.rs | 2 +- .../ide-assists/src/handlers/move_bounds.rs | 2 +- .../src/handlers/move_const_to_impl.rs | 2 +- .../src/handlers/move_from_mod_rs.rs | 2 +- .../ide-assists/src/handlers/move_guard.rs | 4 +- .../src/handlers/move_module_to_file.rs | 2 +- .../src/handlers/move_to_mod_rs.rs | 2 +- .../src/handlers/normalize_import.rs | 2 +- .../src/handlers/number_representation.rs | 5 +- .../src/handlers/promote_local_to_const.rs | 2 +- .../src/handlers/pull_assignment_up.rs | 8 +- .../src/handlers/qualify_method_call.rs | 2 +- .../ide-assists/src/handlers/qualify_path.rs | 2 +- .../ide-assists/src/handlers/raw_string.rs | 10 +-- .../ide-assists/src/handlers/remove_dbg.rs | 2 +- .../src/handlers/remove_else_branches.rs | 2 +- .../ide-assists/src/handlers/remove_mut.rs | 2 +- .../src/handlers/remove_parentheses.rs | 2 +- .../src/handlers/remove_underscore.rs | 2 +- .../src/handlers/remove_unused_imports.rs | 10 +-- .../src/handlers/remove_unused_param.rs | 4 +- .../src/handlers/reorder_fields.rs | 4 +- .../src/handlers/reorder_impl_items.rs | 4 +- .../src/handlers/replace_arith_op.rs | 15 ++-- .../replace_derive_with_manual_impl.rs | 4 +- .../src/handlers/replace_if_let_with_match.rs | 18 ++-- .../replace_is_method_with_if_let_method.rs | 2 +- .../src/handlers/replace_let_with_if_let.rs | 5 +- .../src/handlers/replace_method_eager_lazy.rs | 10 ++- .../replace_named_generic_with_impl.rs | 2 +- .../replace_qualified_name_with_use.rs | 4 +- .../src/handlers/replace_string_with_char.rs | 10 ++- .../replace_turbofish_with_explicit_type.rs | 2 +- .../ide-assists/src/handlers/sort_items.rs | 4 +- .../ide-assists/src/handlers/split_import.rs | 2 +- .../ide-assists/src/handlers/term_search.rs | 2 +- .../src/handlers/toggle_async_sugar.rs | 4 +- .../ide-assists/src/handlers/toggle_ignore.rs | 2 +- .../src/handlers/toggle_macro_delimiter.rs | 2 +- .../src/handlers/unmerge_imports.rs | 2 +- .../src/handlers/unmerge_match_arm.rs | 2 +- .../src/handlers/unnecessary_async.rs | 6 +- .../src/handlers/unqualify_method_call.rs | 4 +- .../ide-assists/src/handlers/unwrap_branch.rs | 6 +- .../src/handlers/unwrap_return_type.rs | 2 +- .../ide-assists/src/handlers/unwrap_tuple.rs | 2 +- .../handlers/unwrap_type_to_generic_arg.rs | 5 +- .../src/handlers/wrap_return_type.rs | 4 +- .../src/handlers/wrap_unwrap_cfg_attr.rs | 12 ++- .../crates/ide-assists/src/lib.rs | 2 +- .../crates/ide-assists/src/utils.rs | 15 ++-- .../ide-assists/src/utils/ref_field_expr.rs | 6 +- .../crates/ide-completion/src/completions.rs | 80 ++++++++--------- .../src/completions/attribute.rs | 4 +- .../src/completions/attribute/cfg.rs | 2 +- .../src/completions/attribute/derive.rs | 2 +- .../src/completions/attribute/diagnostic.rs | 2 +- .../src/completions/attribute/feature.rs | 2 +- .../src/completions/attribute/lint.rs | 2 +- .../src/completions/attribute/macro_use.rs | 2 +- .../src/completions/attribute/repr.rs | 2 +- .../ide-completion/src/completions/dot.rs | 14 +-- .../src/completions/env_vars.rs | 2 +- .../ide-completion/src/completions/expr.rs | 10 +-- .../src/completions/extern_abi.rs | 2 +- .../src/completions/extern_crate.rs | 2 +- .../ide-completion/src/completions/field.rs | 4 +- .../src/completions/flyimport.rs | 42 ++++----- .../src/completions/fn_param.rs | 8 +- .../src/completions/format_string.rs | 2 +- .../src/completions/item_list.rs | 10 ++- .../src/completions/item_list/trait_impl.rs | 32 +++---- .../ide-completion/src/completions/keyword.rs | 2 +- .../src/completions/lifetime.rs | 4 +- .../src/completions/macro_def.rs | 2 +- .../ide-completion/src/completions/mod_.rs | 2 +- .../ide-completion/src/completions/pattern.rs | 4 +- .../ide-completion/src/completions/postfix.rs | 8 +- .../src/completions/postfix/format_like.rs | 2 +- .../src/completions/ra_fixture.rs | 2 +- .../ide-completion/src/completions/record.rs | 8 +- .../ide-completion/src/completions/snippet.rs | 13 ++- .../ide-completion/src/completions/type.rs | 4 +- .../ide-completion/src/completions/use_.rs | 2 +- .../ide-completion/src/completions/vis.rs | 2 +- .../crates/ide-completion/src/context.rs | 20 ++--- .../crates/ide-completion/src/item.rs | 2 +- .../crates/ide-completion/src/render.rs | 50 +++++------ .../ide-completion/src/render/const_.rs | 7 +- .../ide-completion/src/render/function.rs | 20 ++--- .../ide-completion/src/render/literal.rs | 10 +-- .../ide-completion/src/render/macro_.rs | 8 +- .../ide-completion/src/render/pattern.rs | 8 +- .../ide-completion/src/render/type_alias.rs | 6 +- .../src/render/union_literal.rs | 2 +- .../ide-completion/src/render/variant.rs | 6 +- .../crates/ide-completion/src/snippet.rs | 7 +- .../crates/ide-db/src/active_parameter.rs | 2 +- .../crates/ide-db/src/path_transform.rs | 2 +- .../rust-analyzer/crates/ide-db/src/search.rs | 10 +-- .../src/handlers/await_outside_of_async.rs | 2 +- .../ide-diagnostics/src/handlers/bad_rtn.rs | 2 +- .../src/handlers/break_outside_of_loop.rs | 2 +- .../src/handlers/elided_lifetimes_in_path.rs | 2 +- .../src/handlers/expected_function.rs | 2 +- .../src/handlers/generic_args_prohibited.rs | 4 +- .../generic_default_refers_to_self.rs | 2 +- .../src/handlers/inactive_code.rs | 2 +- .../src/handlers/incoherent_impl.rs | 5 +- .../src/handlers/incorrect_case.rs | 7 +- .../src/handlers/incorrect_generics_len.rs | 2 +- .../src/handlers/incorrect_generics_order.rs | 2 +- .../src/handlers/invalid_cast.rs | 7 +- .../src/handlers/invalid_derive_target.rs | 2 +- .../src/handlers/invalid_lhs_of_assignment.rs | 2 +- .../src/handlers/macro_error.rs | 7 +- .../src/handlers/malformed_derive.rs | 2 +- .../src/handlers/mismatched_arg_count.rs | 6 +- .../src/handlers/missing_fields.rs | 9 +- .../src/handlers/missing_lifetime.rs | 2 +- .../src/handlers/missing_match_arms.rs | 2 +- .../src/handlers/missing_unsafe.rs | 7 +- .../src/handlers/moved_out_of_ref.rs | 2 +- .../src/handlers/mutability_errors.rs | 7 +- .../src/handlers/no_such_field.rs | 4 +- .../src/handlers/non_exhaustive_let.rs | 2 +- .../handlers/non_exhaustive_record_expr.rs | 2 +- ...nthesized_generic_args_without_fn_trait.rs | 2 +- .../src/handlers/pattern_arg_in_extern_fn.rs | 2 +- .../src/handlers/private_assoc_item.rs | 2 +- .../src/handlers/private_field.rs | 2 +- .../src/handlers/remove_trailing_return.rs | 4 +- .../src/handlers/remove_unnecessary_else.rs | 4 +- .../replace_filter_map_next_with_find_map.rs | 4 +- .../handlers/trait_impl_incorrect_safety.rs | 2 +- .../handlers/trait_impl_missing_assoc_item.rs | 2 +- .../src/handlers/trait_impl_orphan.rs | 2 +- .../trait_impl_redundant_assoc_item.rs | 4 +- .../src/handlers/type_mismatch.rs | 14 +-- .../src/handlers/type_must_be_known.rs | 2 +- .../src/handlers/typed_hole.rs | 7 +- .../src/handlers/undeclared_label.rs | 2 +- .../handlers/unimplemented_builtin_macro.rs | 2 +- .../src/handlers/unlinked_file.rs | 4 +- .../src/handlers/unreachable_label.rs | 2 +- .../src/handlers/unresolved_assoc_item.rs | 2 +- .../src/handlers/unresolved_extern_crate.rs | 2 +- .../src/handlers/unresolved_field.rs | 12 +-- .../src/handlers/unresolved_ident.rs | 2 +- .../src/handlers/unresolved_import.rs | 2 +- .../src/handlers/unresolved_macro_call.rs | 2 +- .../src/handlers/unresolved_method.rs | 11 ++- .../src/handlers/unresolved_module.rs | 4 +- .../src/handlers/unused_variables.rs | 2 +- .../crates/ide-diagnostics/src/lib.rs | 12 +-- .../crates/ide/src/expand_macro.rs | 4 +- .../crates/ide/src/highlight_related.rs | 8 +- .../crates/ide/src/hover/render.rs | 4 +- .../crates/ide/src/inlay_hints.rs | 28 +++--- src/tools/rust-analyzer/crates/mbe/src/lib.rs | 7 ++ 269 files changed, 1079 insertions(+), 949 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 9a715b19688e6..0d01d54786a1f 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 @@ -333,7 +333,7 @@ struct DocExprSourceCtx<'db> { resolver: Resolver<'db>, file_id: HirFileId, ast_id_map: &'db AstIdMap, - span_map: SpanMap, + span_map: SpanMap<'db>, } fn expand_doc_expr_via_macro_pipeline<'db>( @@ -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; + let parse = expander.db.parse_macro_expansion(call_id).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 2fffa02c13145..c79a1db8472ba 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 @@ -5,10 +5,8 @@ use std::mem; use base_db::Crate; use cfg::CfgOptions; use drop_bomb::DropBomb; -use hir_expand::AstId; -use hir_expand::span_map::SpanMapRef; use hir_expand::{ - ExpandError, ExpandErrorKind, ExpandResult, HirFileId, InFile, Lookup, MacroCallId, + AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, InFile, Lookup, MacroCallId, eager::EagerCallBackFn, mod_path::ModPath, span_map::SpanMap, }; use span::{AstIdMap, SyntaxContext}; @@ -23,7 +21,7 @@ use crate::{ #[derive(Debug)] pub(super) struct Expander<'db> { - span_map: SpanMap, + span_map: SpanMap<'db>, current_file_id: HirFileId, ast_id_map: &'db AstIdMap, /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached. @@ -58,11 +56,11 @@ impl<'db> Expander<'db> { } pub(super) fn hygiene_for_range(&self, db: &dyn DefDatabase, range: TextRange) -> HygieneId { - match self.span_map.as_ref() { - hir_expand::span_map::SpanMapRef::ExpansionSpanMap(span_map) => { + match self.span_map { + SpanMap::ExpansionSpanMap(span_map) => { HygieneId::new(span_map.span_at(range.start()).ctx.opaque_and_semiopaque(db)) } - hir_expand::span_map::SpanMapRef::RealSpanMap(_) => HygieneId::ROOT, + SpanMap::RealSpanMap(_) => HygieneId::ROOT, } } @@ -193,15 +191,15 @@ impl<'db> Expander<'db> { let res = db.parse_macro_expansion(call_id); - let err = err.or(res.err); + let err = err.or_else(|| res.err.clone()); ExpandResult { value: { - let parse = res.value.0.cast::(); + let parse = res.value.0.clone().cast::(); self.recursion_depth += 1; let old_file_id = std::mem::replace(&mut self.current_file_id, call_id.into()); let old_span_map = - std::mem::replace(&mut self.span_map, db.span_map(self.current_file_id)); + 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)); let mark = Mark { @@ -222,15 +220,15 @@ impl<'db> Expander<'db> { } #[inline] - pub(super) fn span_map(&self) -> SpanMapRef<'_> { - self.span_map.as_ref() + pub(super) fn span_map(&self) -> SpanMap<'_> { + self.span_map } } #[derive(Debug)] pub(super) struct Mark<'db> { file_id: HirFileId, - span_map: SpanMap, + span_map: SpanMap<'db>, ast_id_map: &'db AstIdMap, bomb: DropBomb, } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 0de980bdf03d4..76c65361e53d5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -15,7 +15,7 @@ use hir_expand::{ HirFileId, InFile, MacroDefId, mod_path::ModPath, name::{AsName, Name}, - span_map::SpanMapRef, + span_map::SpanMap, }; use intern::{Symbol, sym}; use rustc_hash::FxHashMap; @@ -597,7 +597,7 @@ impl<'db> ExprCollector<'db> { } #[inline] - pub(crate) fn span_map(&self) -> SpanMapRef<'_> { + pub(crate) fn span_map(&self) -> SpanMap<'_> { self.expander.span_map() } 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 78d0a2f0af5fb..2a1b7f7cb0cf8 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 @@ -675,7 +675,7 @@ mod tests { 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()).as_ref().span_for_range(range).ctx + db.span_map(pos.file_id.into()).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 68b6cd79aea3b..91c2e60fd7c4c 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 @@ -4,12 +4,7 @@ use std::cell::OnceCell; use base_db::{Crate, FxIndexSet}; use cfg::CfgOptions; -use hir_expand::{ - HirFileId, - mod_path::PathKind, - name::AsName, - span_map::{SpanMap, SpanMapRef}, -}; +use hir_expand::{HirFileId, mod_path::PathKind, name::AsName, span_map::SpanMap}; use la_arena::Arena; use span::{AstIdMap, FileAstId, SyntaxContext}; use syntax::{ @@ -32,7 +27,7 @@ pub(super) struct Ctx<'db> { pub(super) db: &'db dyn DefDatabase, tree: ItemTree, source_ast_id_map: &'db AstIdMap, - span_map: OnceCell, + span_map: OnceCell>, file: HirFileId, cfg_options: OnceCell<&'db CfgOptions>, krate: Crate, @@ -60,8 +55,8 @@ impl<'db> Ctx<'db> { self.cfg_options.get_or_init(|| self.krate.cfg_options(self.db)) } - pub(super) fn span_map(&self) -> SpanMapRef<'_> { - self.span_map.get_or_init(|| self.db.span_map(self.file)).as_ref() + pub(super) fn span_map(&self) -> SpanMap<'_> { + *self.span_map.get_or_init(|| self.db.span_map(self.file)) } 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 eabdada67c2a7..357da4e672a5e 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 @@ -22,7 +22,7 @@ use hir_expand::{ builtin::quote::quote, db::ExpandDatabase, proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind}, - span_map::SpanMapRef, + span_map::SpanMap, }; use intern::{Symbol, sym}; use itertools::Itertools; @@ -142,10 +142,10 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream } let mut expn_text = String::new(); - if let Some(err) = exp.err { + if let Some(err) = &exp.err { format_to!(expn_text, "/* error: {} */", err.render_to_string(&db).message); } - let (parse, token_map) = exp.value; + let (parse, token_map) = &exp.value; if expect_errors { assert!(!parse.errors().is_empty(), "no parse errors in expansion"); for e in parse.errors() { @@ -161,7 +161,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream } let pp = pretty_print_macro_expansion( parse.syntax_node(), - SpanMapRef::ExpansionSpanMap(&token_map), + SpanMap::ExpansionSpanMap(token_map), show_spans, show_ctxt, ); @@ -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).as_ref(), + db.span_map(src.file_id), 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()).as_ref(), + db.span_map(macro_file.into()), 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()).as_ref(), + db.span_map(macro_file.into()), false, false, ); @@ -309,7 +309,7 @@ fn reindent(indent: IndentLevel, pp: String) -> String { fn pretty_print_macro_expansion( expn: SyntaxNode, - map: SpanMapRef<'_>, + map: SpanMap<'_>, show_spans: bool, show_ctxt: bool, ) -> String { 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 f5a852b39c8d8..a1f1de90efc1c 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 @@ -138,7 +138,7 @@ struct AssocItemCollector<'db> { def_map: &'db DefMap, local_def_map: &'db LocalDefMap, ast_id_map: &'db AstIdMap, - span_map: SpanMap, + span_map: SpanMap<'db>, cfg_options: &'db CfgOptions, file_id: HirFileId, diagnostics: Vec, @@ -191,19 +191,18 @@ impl<'db> AssocItemCollector<'db> { fn collect_item(&mut self, item: ast::AssocItem) { let ast_id = self.ast_id_map.ast_id(&item); - let attrs = - match AttrsOrCfg::lower(self.db, &item, &|| self.cfg_options, self.span_map.as_ref()) { - AttrsOrCfg::Enabled { attrs } => attrs, - AttrsOrCfg::CfgDisabled(cfg) => { - self.diagnostics.push(DefDiagnostic::unconfigured_code( - self.module_id, - InFile::new(self.file_id, ast_id.erase()), - cfg.0, - self.cfg_options.clone(), - )); - return; - } - }; + let attrs = match AttrsOrCfg::lower(self.db, &item, &|| self.cfg_options, self.span_map) { + AttrsOrCfg::Enabled { attrs } => attrs, + AttrsOrCfg::CfgDisabled(cfg) => { + self.diagnostics.push(DefDiagnostic::unconfigured_code( + self.module_id, + InFile::new(self.file_id, ast_id.erase()), + cfg.0, + self.cfg_options.clone(), + )); + return; + } + }; let ast_id = InFile::new(self.file_id, ast_id.upcast()); 'attrs: for (attr_id, attr) in attrs.as_ref().iter() { @@ -357,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) = &self.db.parse_macro_expansion(macro_call_id).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 0f1828abceadb..60ec3035faed6 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 @@ -168,15 +168,15 @@ fn no() {} "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "EnumVariants::of_", ] "#]], @@ -185,7 +185,7 @@ fn no() {} "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "EnumVariants::of_", ] "#]], @@ -226,20 +226,20 @@ pub struct S {} "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "decl_macro_expander_shim", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", + "parse_macro_expansion", "macro_arg_shim", ] "#]], @@ -248,9 +248,9 @@ pub struct S {} "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "macro_arg_shim", - "parse_macro_expansion_shim", + "parse_macro_expansion", "ast_id_map", "file_item_tree_query", ] @@ -284,25 +284,25 @@ fn f() { foo } "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "crate_local_def_map", "proc_macros_for_crate_shim", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", + "parse_macro_expansion", "expand_proc_macro_shim", "macro_arg_shim", "proc_macro_span_shim", @@ -313,10 +313,10 @@ fn f() { foo } "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "macro_arg_shim", "expand_proc_macro_shim", - "parse_macro_expansion_shim", + "parse_macro_expansion", "ast_id_map", "file_item_tree_query", ] @@ -408,37 +408,37 @@ pub struct S {} "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "crate_local_def_map", "proc_macros_for_crate_shim", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "decl_macro_expander_shim", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", + "parse_macro_expansion", "macro_arg_shim", "decl_macro_expander_shim", "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", + "parse_macro_expansion", "macro_arg_shim", "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", + "parse_macro_expansion", "expand_proc_macro_shim", "macro_arg_shim", "proc_macro_span_shim", @@ -449,7 +449,7 @@ pub struct S {} "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "macro_arg_shim", "decl_macro_expander_shim", "macro_arg_shim", @@ -518,35 +518,35 @@ m!(Z); [crate_def_map.modules_for_file(&db, pos.file_id.file_id(&db)).next().unwrap()]; assert_eq!(module_data.scope.resolutions().count(), 4); }, - &[("file_item_tree_query", 6), ("parse_macro_expansion_shim", 3)], + &[("file_item_tree_query", 6), ("parse_macro_expansion", 3)], expect![[r#" [ "crate_local_def_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "decl_macro_expander_shim", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", + "parse_macro_expansion", "macro_arg_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", + "parse_macro_expansion", "macro_arg_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", + "parse_macro_expansion", "macro_arg_shim", ] "#]], @@ -568,13 +568,13 @@ m!(Z); [crate_def_map.modules_for_file(&db, pos.file_id.file_id(&db)).next().unwrap()]; assert_eq!(module_data.scope.resolutions().count(), 4); }, - &[("file_item_tree_query", 1), ("parse_macro_expansion_shim", 0)], + &[("file_item_tree_query", 1), ("parse_macro_expansion", 0)], expect![[r#" [ "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "macro_arg_shim", "macro_arg_shim", "macro_arg_shim", @@ -612,7 +612,7 @@ pub type Ty = (); "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", ] "#]], ); @@ -632,7 +632,7 @@ pub type Ty = (); "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", ] "#]], ); 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 d1f962f7ffd38..395b997972965 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -32,7 +32,7 @@ use crate::{ AstId, db::ExpandDatabase, mod_path::ModPath, - span_map::SpanMapRef, + span_map::SpanMap, tt::{self, TopSubtree}, }; @@ -422,7 +422,7 @@ impl AttrId { )); let tt = syntax_bridge::syntax_node_to_token_tree( tt.syntax(), - SpanMapRef::RealSpanMap(&span_map), + SpanMap::RealSpanMap(&span_map), span_map.span_for_range(tt.syntax().text_range()), DocCommentDesugarMode::ProcMacro, ); 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 6258fac0e9923..81edc9f2cffaf 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 @@ -16,7 +16,7 @@ use crate::{ attrs::{AstPathExt, AttrId, expand_cfg_attr, is_item_tree_filtered_attr}, db::ExpandDatabase, fixup::{self, SyntaxFixupUndoInfo}, - span_map::SpanMapRef, + span_map::SpanMap, tt::{self, DelimSpan, Span}, }; @@ -51,7 +51,7 @@ fn macro_input_callback( censor_item_tree_attr_ids: &[AttrId], krate: Crate, default_span: Span, - span_map: SpanMapRef<'_>, + span_map: SpanMap<'_>, ) -> impl FnMut(&mut PreorderWithTokens, &WalkEvent) -> (bool, Vec) { let cfg_options = OnceCell::new(); let cfg_options = move || *cfg_options.get_or_init(|| krate.cfg_options(db)); @@ -295,7 +295,7 @@ fn macro_input_callback( pub(crate) fn attr_macro_input_to_token_tree( db: &dyn ExpandDatabase, node: &SyntaxNode, - span_map: SpanMapRef<'_>, + span_map: SpanMap<'_>, span: Span, is_derive: bool, censor_item_tree_attr_ids: &[AttrId], 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 8dddddfabb7a2..57c78748f8437 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -17,7 +17,7 @@ use crate::{ fixup::{self, SyntaxFixupUndoInfo}, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt}, proc_macro::{CrateProcMacros, CustomProcMacroExpander, ProcMacros}, - span_map::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef}, + span_map::{ExpansionSpanMap, RealSpanMap, SpanMap}, tt, }; /// This is just to ensure the types of smart_macro_arg and macro_arg are the same @@ -67,21 +67,22 @@ pub trait ExpandDatabase: SourceDatabase { fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode; /// Implementation for the macro case. - #[salsa::lru(512)] + #[salsa::transparent] fn parse_macro_expansion( &self, macro_file: MacroCallId, - ) -> ExpandResult<(Parse, Arc)>; + ) -> &ExpandResult<(Parse, ExpansionSpanMap)>; #[salsa::transparent] #[salsa::invoke(SpanMap::new)] - fn span_map(&self, file_id: HirFileId) -> SpanMap; + 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) -> Arc; + fn expansion_span_map(&self, file_id: MacroCallId) -> &ExpansionSpanMap; #[salsa::invoke(crate::span_map::real_span_map)] - fn real_span_map(&self, file_id: EditionedFileId) -> Arc; + #[salsa::transparent] + fn real_span_map(&self, file_id: EditionedFileId) -> &RealSpanMap; /// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the /// reason why we use salsa at all. @@ -181,7 +182,7 @@ pub fn expand_speculative( let (_, _, span) = db.macro_arg_considering_derives(actual_macro_call, &loc.kind); let span_map = RealSpanMap::absolute(span.anchor.file_id); - let span_map = SpanMapRef::RealSpanMap(&span_map); + 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 { @@ -358,10 +359,11 @@ fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode { // 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, Arc)> { +) -> ExpandResult<(Parse, ExpansionSpanMap)> { let _p = tracing::info_span!("parse_macro_expansion").entered(); let loc = db.lookup_intern_macro_call(macro_file); let expand_to = loc.expand_to(); @@ -377,7 +379,7 @@ fn parse_macro_expansion( ); rev_token_map.matched_arm = matched_arm; - ExpandResult { value: (parse, Arc::new(rev_token_map)), err } + ExpandResult { value: (parse, rev_token_map), err } } fn parse_macro_expansion_error( @@ -385,21 +387,21 @@ fn parse_macro_expansion_error( macro_call_id: MacroCallId, ) -> Option>>> { let e: ExpandResult> = - db.parse_macro_expansion(macro_call_id).map(|it| Arc::from(it.0.errors())); + 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(Arc::new(e)) } } pub(crate) fn parse_with_map( db: &dyn ExpandDatabase, file_id: HirFileId, -) -> (Parse, SpanMap) { +) -> (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) = db.parse_macro_expansion(macro_file).value; - (parse, SpanMap::ExpansionSpanMap(map)) + let (parse, map) = &db.parse_macro_expansion(macro_file).value; + (parse.clone(), SpanMap::ExpansionSpanMap(map)) } } } @@ -487,7 +489,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let mut tt = syntax_bridge::syntax_node_to_token_tree( tt.syntax(), - map.as_ref(), + map, span, if loc.def.is_proc_macro() { DocCommentDesugarMode::ProcMacro @@ -522,7 +524,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let (mut tt, undo_info) = attr_macro_input_to_token_tree( db, item_node.syntax(), - map.as_ref(), + map, span, is_derive, censor_item_tree_attr_ids, 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 4b2c6e73517b8..aa745a2ccdc6a 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -128,7 +128,7 @@ impl DeclarativeMacroExpander { Some(arg) => { let tt = syntax_bridge::syntax_node_to_token_tree( arg.syntax(), - map.as_ref(), + map, map.span_for_range( macro_rules.macro_rules_token().unwrap().text_range(), ), @@ -152,14 +152,14 @@ impl DeclarativeMacroExpander { let args = macro_def.args().map(|args| { syntax_bridge::syntax_node_to_token_tree( args.syntax(), - map.as_ref(), + map, span, DocCommentDesugarMode::Mbe, ) }); let body = syntax_bridge::syntax_node_to_token_tree( body.syntax(), - map.as_ref(), + map, span, DocCommentDesugarMode::Mbe, ); 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 0b6124ebf3598..6e95ff01c3415 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -70,7 +70,7 @@ pub fn expand_eager_macro_input( let ExpandResult { value: expanded_eager_input, err } = { eager_macro_recur( db, - &arg_exp_map, + arg_exp_map, &mut arg_map, TextSize::new(0), InFile::new(arg_id.into(), arg_exp.syntax_node()), @@ -80,7 +80,7 @@ pub fn expand_eager_macro_input( eager_callback, ) }; - let err = parse_err.or(err); + let err = parse_err.clone().or(err); if cfg!(debug_assertions) { arg_map.finish(); } @@ -117,15 +117,15 @@ pub fn expand_eager_macro_input( ExpandResult { value: Some(db.intern_macro_call(loc)), err } } -fn lazy_expand( - db: &dyn ExpandDatabase, +fn lazy_expand<'db>( + db: &'db dyn ExpandDatabase, def: &MacroDefId, macro_call: &ast::MacroCall, ast_id: AstId, krate: Crate, call_site: SyntaxContext, eager_callback: EagerCallBackFn<'_>, -) -> ExpandResult<(InFile>, Arc)> { +) -> ExpandResult<(InFile>, &'db ExpansionSpanMap)> { let expand_to = ExpandTo::from_call_site(macro_call); let id = def.make_call( db, @@ -135,7 +135,9 @@ fn lazy_expand( ); eager_callback(ast_id.map(|ast_id| (AstPtr::new(macro_call), ast_id)), id); - db.parse_macro_expansion(id).map(|parse| (InFile::new(id.into(), parse.0), parse.1)) + db.parse_macro_expansion(id) + .as_ref() + .map(|parse| (InFile::new(id.into(), parse.0.clone()), &parse.1)) } fn eager_macro_recur( @@ -232,7 +234,7 @@ fn eager_macro_recur( syntax_node.clone_for_update(), offset + syntax_node.text_range().len(), )), - err: err.or(err2), + err: err.clone().or_else(|| err2.clone()), } } None => ExpandResult { value: None, err }, @@ -256,7 +258,7 @@ fn eager_macro_recur( // replace macro inside let ExpandResult { value, err: error } = eager_macro_recur( db, - &tm, + tm, expanded_map, offset, // FIXME: We discard parse errors here 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 71da560b15f1a..09a5c9326e058 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs @@ -328,7 +328,7 @@ impl> InFile { let FileRange { file_id: editioned_file_id, range } = map_node_range_up_rooted( db, - &db.expansion_span_map(file_id), + db.expansion_span_map(file_id), self.value.borrow().text_range(), )?; @@ -371,7 +371,7 @@ impl InFile { HirFileId::MacroFile(mac_file) => { let (range, ctxt) = span_for_offset( db, - &db.expansion_span_map(mac_file), + db.expansion_span_map(mac_file), self.value.text_range().start(), ); @@ -397,7 +397,7 @@ impl InFile { HirFileId::MacroFile(mac_file) => { let (range, ctxt) = span_for_offset( db, - &db.expansion_span_map(mac_file), + db.expansion_span_map(mac_file), self.value.text_range().start(), ); @@ -411,7 +411,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, db.expansion_span_map(self.file_id), self.value) } } @@ -425,7 +425,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, db.expansion_span_map(mac_file), self.value) { Some(it) => it, None => { let loc = db.lookup_intern_macro_call(mac_file); @@ -443,7 +443,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, db.expansion_span_map(mac_file), self.value) { Some(it) => it, _ => { let loc = db.lookup_intern_macro_call(mac_file); @@ -461,7 +461,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, db.expansion_span_map(mac_file), self.value) { Some(it) => it, _ => { let loc = db.lookup_intern_macro_call(mac_file); @@ -482,7 +482,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, db.expansion_span_map(mac_file), self.value) } } } @@ -494,7 +494,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, db.expansion_span_map(mac_file), self.value) } } } @@ -516,7 +516,7 @@ impl InFile { let FileRange { file_id: editioned_file_id, range } = map_node_range_up_rooted( db, - &db.expansion_span_map(file_id), + db.expansion_span_map(file_id), self.value.syntax().text_range(), )?; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs index 424655ed651bd..b51ec39ce7b4e 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -18,7 +18,7 @@ use triomphe::Arc; use tt::{Spacing, TransformTtAction, transform_tt}; use crate::{ - span_map::SpanMapRef, + span_map::SpanMap, tt::{self, Ident, Leaf, Punct, TopSubtree}, }; @@ -51,7 +51,7 @@ const FIXUP_DUMMY_RANGE: TextRange = TextRange::empty(TextSize::new(0)); const FIXUP_DUMMY_RANGE_END: TextSize = TextSize::new(!0); pub(crate) fn fixup_syntax( - span_map: SpanMapRef<'_>, + span_map: SpanMap<'_>, node: &SyntaxNode, call_site: Span, mode: DocCommentDesugarMode, @@ -402,7 +402,6 @@ mod tests { use span::{Edition, EditionedFileId, FileId}; use syntax::TextRange; use syntax_bridge::DocCommentDesugarMode; - use triomphe::Arc; use crate::{ fixup::reverse_fixups, @@ -440,19 +439,19 @@ mod tests { #[track_caller] fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, mut expect: Expect) { let parsed = syntax::SourceFile::parse(ra_fixture, span::Edition::CURRENT); - let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(EditionedFileId::new( + let span_map = SpanMap::RealSpanMap(&RealSpanMap::absolute(EditionedFileId::new( FileId::from_raw(0), Edition::CURRENT, - )))); + ))); let fixups = super::fixup_syntax( - span_map.as_ref(), + span_map, &parsed.syntax_node(), span_map.span_for_range(TextRange::empty(0.into())), DocCommentDesugarMode::Mbe, ); let mut tt = syntax_bridge::syntax_node_to_token_tree_modified( &parsed.syntax_node(), - span_map.as_ref(), + span_map, fixups.append, fixups.remove, span_map.span_for_range(TextRange::empty(0.into())), @@ -494,7 +493,7 @@ mod tests { // modulo token IDs and `Punct`s' spacing. let original_as_tt = syntax_bridge::syntax_node_to_token_tree( &parsed.syntax_node(), - span_map.as_ref(), + span_map, span_map.span_for_range(TextRange::empty(0.into())), DocCommentDesugarMode::Mbe, ); 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 8d42a24e2fae8..fc2a07435bf3e 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -491,7 +491,7 @@ impl MacroCallId { } /// 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 ExpandDatabase) -> ExpansionInfo<'_> { ExpansionInfo::new(db, self) } @@ -797,16 +797,16 @@ impl MacroCallKind { // FIXME: can be expensive to create, we should check the use sites and maybe replace them with // simpler function calls if the map is only used once #[derive(Clone, Debug, PartialEq, Eq)] -pub struct ExpansionInfo { +pub struct ExpansionInfo<'db> { expanded: InMacroFile, /// The argument TokenTree or item for attributes arg: InFile>, - exp_map: Arc, - arg_map: SpanMap, + exp_map: &'db ExpansionSpanMap, + arg_map: SpanMap<'db>, loc: MacroCallLoc, } -impl ExpansionInfo { +impl<'db> ExpansionInfo<'db> { pub fn expanded(&self) -> InMacroFile { self.expanded.clone() } @@ -872,7 +872,7 @@ impl ExpansionInfo { offset: TextSize, ) -> (FileRange, SyntaxContext) { debug_assert!(self.expanded.value.text_range().contains(offset)); - span_for_offset(db, &self.exp_map, offset) + span_for_offset(db, self.exp_map, offset) } /// Maps up the text range out of the expansion hierarchy back into the original file its from. @@ -882,7 +882,7 @@ impl ExpansionInfo { range: TextRange, ) -> Option<(FileRange, SyntaxContext)> { debug_assert!(self.expanded.value.text_range().contains_range(range)); - map_node_range_up(db, &self.exp_map, range) + map_node_range_up(db, self.exp_map, range) } /// Maps up the text range out of the expansion into its macro call. @@ -918,14 +918,14 @@ impl ExpansionInfo { } } - pub fn new(db: &dyn ExpandDatabase, macro_file: MacroCallId) -> ExpansionInfo { + pub fn new(db: &'db dyn ExpandDatabase, macro_file: MacroCallId) -> ExpansionInfo<'db> { let _p = tracing::info_span!("ExpansionInfo::new").entered(); let loc = db.lookup_intern_macro_call(macro_file); 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) = &db.parse_macro_expansion(macro_file).value; let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() }; ExpansionInfo { expanded, loc, arg: arg_tt, exp_map, arg_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 aa8603341b3b2..6a94df8b5a554 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 @@ -2,7 +2,6 @@ use span::Span; use syntax::{AstNode, TextRange, ast}; -use triomphe::Arc; pub use span::RealSpanMap; @@ -11,35 +10,21 @@ use crate::{HirFileId, MacroCallId, db::ExpandDatabase}; pub type ExpansionSpanMap = span::SpanMap; /// Spanmap for a macro file or a real file -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum SpanMap { +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SpanMap<'db> { /// Spanmap for a macro file - ExpansionSpanMap(Arc), + ExpansionSpanMap(&'db ExpansionSpanMap), /// Spanmap for a real file - RealSpanMap(Arc), + RealSpanMap(&'db RealSpanMap), } -#[derive(Copy, Clone)] -pub enum SpanMapRef<'a> { - /// Spanmap for a macro file - ExpansionSpanMap(&'a ExpansionSpanMap), - /// Spanmap for a real file - RealSpanMap(&'a RealSpanMap), -} - -impl syntax_bridge::SpanMapper for SpanMap { - fn span_for(&self, range: TextRange) -> Span { - self.span_for_range(range) - } -} - -impl syntax_bridge::SpanMapper for SpanMapRef<'_> { +impl syntax_bridge::SpanMapper for SpanMap<'_> { fn span_for(&self, range: TextRange) -> Span { self.span_for_range(range) } } -impl SpanMap { +impl<'db> SpanMap<'db> { pub fn span_for_range(&self, range: TextRange) -> Span { match self { // FIXME: Is it correct for us to only take the span at the start? This feels somewhat @@ -51,37 +36,22 @@ impl SpanMap { } } - pub fn as_ref(&self) -> SpanMapRef<'_> { - match self { - Self::ExpansionSpanMap(span_map) => SpanMapRef::ExpansionSpanMap(span_map), - Self::RealSpanMap(span_map) => SpanMapRef::RealSpanMap(span_map), - } - } - #[inline] - pub(crate) fn new(db: &dyn ExpandDatabase, file_id: HirFileId) -> SpanMap { + 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.parse_macro_expansion(m).value.1) + SpanMap::ExpansionSpanMap(&db.parse_macro_expansion(m).value.1) } } } } -impl SpanMapRef<'_> { - pub fn span_for_range(self, range: TextRange) -> Span { - match self { - Self::ExpansionSpanMap(span_map) => span_map.span_at(range.start()), - Self::RealSpanMap(span_map) => span_map.span_for_range(range), - } - } -} - +#[salsa_macros::tracked(returns(ref))] pub(crate) fn real_span_map( db: &dyn ExpandDatabase, editioned_file_id: base_db::EditionedFileId, -) -> Arc { +) -> 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()); @@ -134,16 +104,16 @@ pub(crate) fn real_span_map( _ => (), }); - Arc::new(RealSpanMap::from_file( + RealSpanMap::from_file( editioned_file_id.span_file_id(db), pairs.into_boxed_slice(), tree.syntax().text_range().end(), - )) + ) } pub(crate) fn expansion_span_map( db: &dyn ExpandDatabase, file_id: MacroCallId, -) -> Arc { - db.parse_macro_expansion(file_id).value.1 +) -> &ExpansionSpanMap { + &db.parse_macro_expansion(file_id).value.1 } 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 960155a8e4f93..02d8f6597d0a1 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 @@ -36,7 +36,7 @@ fn foo() -> i32 { "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "InferenceResult::for_body_", "FunctionSignature::of_", "FunctionSignature::with_source_map_", @@ -80,7 +80,7 @@ fn foo() -> i32 { "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "AttrFlags::query_", "FunctionSignature::with_source_map_", "FunctionSignature::of_", @@ -125,7 +125,7 @@ fn baz() -> i32 { "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "InferenceResult::for_body_", "FunctionSignature::of_", "FunctionSignature::with_source_map_", @@ -196,7 +196,7 @@ fn baz() -> i32 { "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "AttrFlags::query_", "FunctionSignature::with_source_map_", "FunctionSignature::of_", @@ -249,7 +249,7 @@ $0", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "TraitImpls::for_crate_", "lang_items", "crate_lang_items", @@ -286,7 +286,7 @@ pub struct NewStruct { "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "crate_local_def_map", "TraitImpls::for_crate_", "crate_lang_items", @@ -324,7 +324,7 @@ $0", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "TraitImpls::for_crate_", "lang_items", "crate_lang_items", @@ -362,7 +362,7 @@ pub enum SomeEnum { "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "crate_local_def_map", "TraitImpls::for_crate_", "crate_lang_items", @@ -400,7 +400,7 @@ $0", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "TraitImpls::for_crate_", "lang_items", "crate_lang_items", @@ -435,7 +435,7 @@ fn bar() -> f32 { "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "crate_local_def_map", "TraitImpls::for_crate_", "crate_lang_items", @@ -477,7 +477,7 @@ $0", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "TraitImpls::for_crate_", "lang_items", "crate_lang_items", @@ -520,7 +520,7 @@ impl SomeStruct { "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "crate_local_def_map", "TraitImpls::for_crate_", "crate_lang_items", @@ -578,7 +578,7 @@ fn main() { "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "TraitItems::query_with_diagnostics_", "Body::of_", "Body::with_source_map_", @@ -674,7 +674,7 @@ fn main() { "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "crate_local_def_map", "TraitItems::query_with_diagnostics_", "Body::with_source_map_", diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 9b36f8ab8100f..bfca6a1e8498b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -196,7 +196,7 @@ use { hir_def::expr_store::path::Path, hir_expand::{ name::AsName, - span_map::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef}, + span_map::{ExpansionSpanMap, RealSpanMap, SpanMap}, }, }; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 252f4ac7f27b3..d2056b1e7cdb8 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -166,7 +166,7 @@ pub struct Semantics<'db, DB: ?Sized> { pub struct SemanticsImpl<'db> { pub db: &'db dyn HirDatabase, - s2d_cache: RefCell, + s2d_cache: RefCell>, /// MacroCall to its expansion's MacroCallId cache macro_call_cache: RefCell, MacroCallId>>, } @@ -548,7 +548,7 @@ impl<'db> SemanticsImpl<'db> { } pub fn expand(&self, file_id: MacroCallId) -> ExpandResult { - let res = self.db.parse_macro_expansion(file_id).map(|it| it.0.syntax_node()); + let res = self.db.parse_macro_expansion(file_id).as_ref().map(|it| it.0.syntax_node()); self.cache(res.value.clone(), file_id.into()); res } @@ -648,7 +648,7 @@ impl<'db> SemanticsImpl<'db> { let ExpandResult { value, err } = self.db.parse_macro_expansion(file_id); let root_node = value.0.syntax_node(); self.cache(root_node.clone(), file_id.into()); - Some(ExpandResult { value: root_node, err }) + Some(ExpandResult { value: root_node, err: err.clone() }) }) .collect(); Some(res) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index d932198b43a7d..a9c3395381aea 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -117,16 +117,16 @@ use tt::TextRange; use crate::{InFile, InlineAsmOperand, db::HirDatabase, semantics::child_by_source::ChildBySource}; #[derive(Default)] -pub(super) struct SourceToDefCache { +pub(super) struct SourceToDefCache<'db> { pub(super) dynmap_cache: FxHashMap<(ChildContainer, HirFileId), DynMap>, - expansion_info_cache: FxHashMap, + expansion_info_cache: FxHashMap>, pub(super) file_to_def_cache: FxHashMap>, pub(super) included_file_cache: FxHashMap>, /// Rootnode to HirFileId cache pub(super) root_to_file_cache: FxHashMap, } -impl SourceToDefCache { +impl<'db> SourceToDefCache<'db> { pub(super) fn cache( root_to_file_cache: &mut FxHashMap, root_node: SyntaxNode, @@ -156,9 +156,9 @@ impl SourceToDefCache { pub(super) fn get_or_insert_expansion( &mut self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, macro_file: MacroCallId, - ) -> &ExpansionInfo { + ) -> &ExpansionInfo<'db> { self.expansion_info_cache.entry(macro_file).or_insert_with(|| { let exp_info = macro_file.expansion_info(db); @@ -172,7 +172,7 @@ impl SourceToDefCache { pub(super) struct SourceToDefCtx<'db, 'cache> { pub(super) db: &'db dyn HirDatabase, - pub(super) cache: &'cache mut SourceToDefCache, + pub(super) cache: &'cache mut SourceToDefCache<'db>, } impl SourceToDefCtx<'_, '_> { diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs index f2dc1ce798ad9..af2371d49349d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search.rs @@ -214,11 +214,11 @@ impl<'db> LookupTable<'db> { /// Context for the `term_search` function #[derive(Debug)] -pub struct TermSearchCtx<'db, DB: HirDatabase> { +pub struct TermSearchCtx<'a, 'db, DB: HirDatabase> { /// Semantics for the program - pub sema: &'db Semantics<'db, DB>, + pub sema: &'a Semantics<'db, DB>, /// Semantic scope, captures context for the term search - pub scope: &'db SemanticsScope<'db>, + pub scope: &'a SemanticsScope<'db>, /// Target / expected output type pub goal: Type<'db>, /// Configuration for term search @@ -263,7 +263,7 @@ impl Default for TermSearchConfig { /// Note that there are usually more ways we can get to the `goal` type but some are discarded to /// reduce the memory consumption. It is also unlikely anyone is willing ti browse through /// thousands of possible responses so we currently take first 10 from every tactic. -pub fn term_search<'db, DB: HirDatabase>(ctx: &'db TermSearchCtx<'db, DB>) -> Vec> { +pub fn term_search<'db, DB: HirDatabase>(ctx: &TermSearchCtx<'_, 'db, DB>) -> Vec> { let module = ctx.scope.module(); let mut defs = FxHashSet::default(); defs.insert(ScopeDef::ModuleDef(ModuleDef::Module(module))); diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index c7ef4e5d5deb0..b11f7a172310f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -41,7 +41,7 @@ use super::{LookupTable, NewTypesKey, TermSearchCtx}; /// _Note that there is no use of calling this tactic in every iteration as the output does not /// depend on the current state of `lookup`_ pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, defs: &'a FxHashSet, lookup: &'lt mut LookupTable<'db>, ) -> impl Iterator> + use<'a, 'db, 'lt, DB> { @@ -105,7 +105,7 @@ pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>( /// _Note that there is no use of calling this tactic in every iteration as the output does not /// depend on the current state of `lookup`_ pub(super) fn assoc_const<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, defs: &'a FxHashSet, lookup: &'lt mut LookupTable<'db>, ) -> impl Iterator> + use<'a, 'db, 'lt, DB> { @@ -153,7 +153,7 @@ pub(super) fn assoc_const<'a, 'lt, 'db, DB: HirDatabase>( /// * `lookup` - Lookup table for types /// * `should_continue` - Function that indicates when to stop iterating pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, _defs: &'a FxHashSet, lookup: &'lt mut LookupTable<'db>, should_continue: &'a dyn std::ops::Fn() -> bool, @@ -302,7 +302,7 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( /// * `lookup` - Lookup table for types /// * `should_continue` - Function that indicates when to stop iterating pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, defs: &'a FxHashSet, lookup: &'lt mut LookupTable<'db>, should_continue: &'a dyn std::ops::Fn() -> bool, @@ -437,7 +437,7 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( /// * `lookup` - Lookup table for types /// * `should_continue` - Function that indicates when to stop iterating pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, _defs: &'a FxHashSet, lookup: &'lt mut LookupTable<'db>, should_continue: &'a dyn std::ops::Fn() -> bool, @@ -556,7 +556,7 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( /// * `lookup` - Lookup table for types /// * `should_continue` - Function that indicates when to stop iterating pub(super) fn struct_projection<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, _defs: &'a FxHashSet, lookup: &'lt mut LookupTable<'db>, should_continue: &'a dyn std::ops::Fn() -> bool, @@ -598,7 +598,7 @@ pub(super) fn struct_projection<'a, 'lt, 'db, DB: HirDatabase>( /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types pub(super) fn famous_types<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, _defs: &'a FxHashSet, lookup: &'lt mut LookupTable<'db>, ) -> impl Iterator> + use<'a, 'db, 'lt, DB> { @@ -632,7 +632,7 @@ pub(super) fn famous_types<'a, 'lt, 'db, DB: HirDatabase>( /// * `lookup` - Lookup table for types /// * `should_continue` - Function that indicates when to stop iterating pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, _defs: &'a FxHashSet, lookup: &'lt mut LookupTable<'db>, should_continue: &'a dyn std::ops::Fn() -> bool, @@ -738,7 +738,7 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( /// * `lookup` - Lookup table for types /// * `should_continue` - Function that indicates when to stop iterating pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, _defs: &'a FxHashSet, lookup: &'lt mut LookupTable<'db>, should_continue: &'a dyn std::ops::Fn() -> bool, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs index 0b8f35bfc3c6c..d8e097f0e27b7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs @@ -45,9 +45,9 @@ pub(crate) use ide_db::source_change::SourceChangeBuilder; /// Note, however, that we don't actually use such two-phase logic at the /// moment, because the LSP API is pretty awkward in this place, and it's much /// easier to just compute the edit eagerly :-) -pub(crate) struct AssistContext<'a> { +pub(crate) struct AssistContext<'a, 'db> { pub(crate) config: &'a AssistConfig, - pub(crate) sema: Semantics<'a, RootDatabase>, + pub(crate) sema: Semantics<'db, RootDatabase>, frange: FileRange, trimmed_range: TextRange, source_file: SourceFile, @@ -57,12 +57,12 @@ pub(crate) struct AssistContext<'a> { covering_element: SyntaxElement, } -impl<'a> AssistContext<'a> { +impl<'a, 'db> AssistContext<'a, 'db> { pub(crate) fn new( - sema: Semantics<'a, RootDatabase>, + sema: Semantics<'db, RootDatabase>, config: &'a AssistConfig, frange: FileRange, - ) -> AssistContext<'a> { + ) -> AssistContext<'a, 'db> { let source_file = sema.parse(frange.file_id); let start = frange.range.start(); @@ -95,7 +95,7 @@ impl<'a> AssistContext<'a> { } } - pub(crate) fn db(&self) -> &'a RootDatabase { + pub(crate) fn db(&self) -> &'db RootDatabase { self.sema.db } @@ -165,7 +165,7 @@ pub(crate) struct Assists { } impl Assists { - pub(crate) fn new(ctx: &AssistContext<'_>, resolve: AssistResolveStrategy) -> Assists { + pub(crate) fn new(ctx: &AssistContext<'_, '_>, resolve: AssistResolveStrategy) -> Assists { Assists { resolve, file: ctx.frange.file_id.file_id(ctx.db()), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs index c5ec88ffb88a3..4bd987a3710ac 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs @@ -44,7 +44,7 @@ use crate::{AssistContext, AssistId, Assists}; // }; // } // ``` -pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (expr_type, expr) = get_replacement_node(ctx)?; acc.add( @@ -74,7 +74,7 @@ enum ParentType { Assignment, } -fn get_replacement_node(ctx: &AssistContext<'_>) -> Option<(ParentType, ast::Expr)> { +fn get_replacement_node(ctx: &AssistContext<'_, '_>) -> Option<(ParentType, ast::Expr)> { let node = ctx.find_node_at_offset::>(); let (parent_type, body) = if let Some(eq_token) = ctx.find_token_syntax_at_offset(T![=]) { let parent = eq_token.parent()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs index 1809b8f305b62..40d48bee3ccfe 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs @@ -29,7 +29,7 @@ use crate::{AssistContext, Assists}; // ``` pub(crate) fn add_explicit_method_call_deref( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { if ctx.has_empty_selection() { return None; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs index 75c5f84b85012..db9f9dc074be4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs @@ -30,7 +30,7 @@ use crate::{AssistContext, Assists, utils::add_group_separators}; // ``` pub(crate) fn add_explicit_enum_discriminant( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let enum_node = ctx.find_node_at_offset::()?; let enum_def = ctx.sema.to_def(&enum_node)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs index 0dd01b67e8f47..53de25c454bce 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs @@ -20,7 +20,7 @@ use crate::{AssistContext, AssistId, Assists}; // let x: i32 = 92; // } // ``` -pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let syntax_node = ctx.find_node_at_offset::>()?; let (ascribed_ty, expr, pat) = if let Either::Left(let_stmt) = syntax_node { let cursor_in_range = { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs index 41e9b6cc84539..b2194ab3dcd71 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs @@ -30,7 +30,7 @@ use crate::{AssistContext, AssistId, Assists}; // } // } // ``` -pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let loop_expr = ctx.find_node_at_offset::()?; let loop_kw = loop_token(&loop_expr)?; if loop_expr.label().is_some() || !loop_kw.text_range().contains_inclusive(ctx.offset()) { @@ -86,7 +86,7 @@ fn loop_token(loop_expr: &ast::AnyHasLoopBody) -> Option { fn insert_label_after_token( editor: &SyntaxEditor, token: &SyntaxToken, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, ) { let make = editor.make(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs index 265ee3d2d4e71..dc847dcdbe7d7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs @@ -22,7 +22,7 @@ use crate::{AssistContext, AssistId, Assists}; // y: u32, // } // ``` -pub(crate) fn add_lifetime_to_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_lifetime_to_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let ref_type_focused = ctx.find_node_at_offset::()?; if ref_type_focused.lifetime().is_some_and(|lifetime| lifetime.text() != "'_") { return None; 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 d1f1f9f123387..20f27dc728037 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 @@ -45,7 +45,10 @@ use crate::{ // } // } // ``` -pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_missing_impl_members( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { add_missing_impl_members_inner( acc, ctx, @@ -89,7 +92,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext<'_ // ``` pub(crate) fn add_missing_default_members( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { add_missing_impl_members_inner( acc, @@ -103,7 +106,7 @@ pub(crate) fn add_missing_default_members( fn add_missing_impl_members_inner( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, mode: DefaultMethods, ignore_items: IgnoreAssocItems, assist_id: &'static str, @@ -224,7 +227,7 @@ fn add_missing_impl_members_inner( fn try_gen_trait_body( make: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, func: &ast::Fn, trait_ref: hir::TraitRef<'_>, impl_def: &ast::Impl, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index f2d71a1dfee47..667a1d7813c5c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -38,7 +38,7 @@ use crate::{AssistContext, AssistId, Assists, utils}; // } // } // ``` -pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let match_expr = ctx.find_node_at_offset_with_descend::()?; let match_arm_list = match_expr.match_arm_list()?; let arm_list_range = ctx.sema.original_range_opt(match_arm_list.syntax())?; @@ -303,7 +303,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) } fn cursor_at_trivial_match_arm_list( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, match_expr: &MatchExpr, match_arm_list: &MatchArmList, ) -> Option<()> { @@ -357,7 +357,7 @@ struct ArmsEdit { } impl ArmsEdit { - fn remove_wildcard_arms(&mut self, ctx: &AssistContext<'_>, editor: &SyntaxEditor) { + fn remove_wildcard_arms(&mut self, ctx: &AssistContext<'_, '_>, editor: &SyntaxEditor) { for arm in self.match_arm_list.arms() { if !matches!(arm.pat(), Some(Pat::WildcardPat(_))) { self.last_arm = Some(arm); @@ -417,7 +417,7 @@ impl ArmsEdit { fn add_comma_after_last_arm( &self, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, editor: &SyntaxEditor, ) { @@ -432,7 +432,7 @@ impl ArmsEdit { fn cover_edit_range( &self, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, node: &impl AstNode, ) -> Option> { let range = ctx.sema.original_range_opt(node.syntax())?; @@ -581,7 +581,7 @@ fn resolve_array_of_enum_def( } fn build_pat( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, module: hir::Module, var: ExtendedVariant, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs index 7934a80bfabbc..e7203a96bb218 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs @@ -16,7 +16,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` // fn foo() -> i32 { 42i32 } // ``` -pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (fn_type, tail_expr, builder_edit_pos) = extract_tail(ctx)?; let module = ctx.sema.scope(tail_expr.syntax())?.module(); let ty = ctx.sema.type_of_expr(&peel_blocks(tail_expr.clone()))?.adjusted(); @@ -133,7 +133,7 @@ fn peel_blocks(mut expr: ast::Expr) -> ast::Expr { expr } -fn extract_tail(ctx: &AssistContext<'_>) -> Option<(FnType, ast::Expr, InsertOrReplace)> { +fn extract_tail(ctx: &AssistContext<'_, '_>) -> Option<(FnType, ast::Expr, InsertOrReplace)> { let node = ctx.find_node_at_offset::>()?; let (fn_type, tail_expr, return_type_range, action) = match node { Either::Left(closure) => { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs index dcd2124f7bebc..53205910c8e35 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs @@ -28,7 +28,7 @@ use crate::{ // let x = make::<${0:_}>(); // } // ``` -pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let turbofish_target = ctx.find_node_at_offset::().map(Either::Left).or_else(|| { let callable_expr = ctx.find_node_at_offset::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index 31e7f1c176c30..10262445a2dfe 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -37,7 +37,7 @@ use crate::{AssistContext, AssistId, Assists, utils::invert_boolean_expression}; // if !(x == 4 && y >= 3.14) {} // } // ``` -pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let mut bin_expr = if let Some(not) = ctx.find_token_syntax_at_offset(T![!]) && let Some(NodeOrToken::Node(next)) = not.next_sibling_or_token() && let Some(paren) = ast::ParenExpr::cast(next) @@ -189,7 +189,10 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti // } // } // ``` -pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn apply_demorgan_iterator( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let method_call: ast::MethodCallExpr = ctx.find_node_at_offset().or_else(|| { let parent = ctx.find_token_syntax_at_offset(T![!])?.parent()?; match ast::PrefixExpr::cast(parent)?.expr()? { @@ -253,7 +256,7 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_> /// Ensures that the method call is to `Iterator::all` or `Iterator::any`. fn validate_method_call_expr( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, method_call: &ast::MethodCallExpr, ) -> Option<(ast::NameRef, ast::Expr)> { let name_ref = method_call.name_ref()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index f9d618790c6e8..9bfb47e69dca1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -91,7 +91,7 @@ use crate::{AssistContext, AssistId, Assists, GroupLabel}; // } // # pub mod std { pub mod collections { pub struct HashMap { } } } // ``` -pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let cfg = ctx.config.import_path_config(); let (import_assets, syntax_under_caret, expected) = find_importable_node(ctx)?; @@ -170,8 +170,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< Some(()) } -pub(super) fn find_importable_node<'a: 'db, 'db>( - ctx: &'a AssistContext<'db>, +pub(super) fn find_importable_node<'db>( + ctx: &AssistContext<'_, 'db>, ) -> Option<(ImportAssets<'db>, SyntaxNode, Option>)> { // Deduplicate this with the `expected_type_and_name` logic for completions let expected = |expr_or_pat: Either| match expr_or_pat { @@ -248,7 +248,7 @@ fn group_label(import_candidate: &ImportCandidate<'_>) -> GroupLabel { /// Determine how relevant a given import is in the current context. Higher scores are more /// relevant. pub(crate) fn relevance_score( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, import: &LocatedImport, expected: Option<&Type<'_>>, current_module: Option<&Module>, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs index 50e4a367e9a1b..5d3d8127baeff 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs @@ -18,7 +18,7 @@ use syntax::{ // let _ = x; // } // ``` -pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let param: ast::Param = ctx.find_node_at_offset()?; let Some(ast::Pat::IdentPat(ident_pat)) = param.pat() else { return None }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs index 7119d5b9c23eb..f17197a75055a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs @@ -21,14 +21,14 @@ use crate::{AssistContext, AssistId, Assists, utils::vis_offset}; // ``` // pub(crate) fn frobnicate() {} // ``` -pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { if let Some(vis) = ctx.find_node_at_offset::() { return change_vis(acc, vis); } add_vis(acc, ctx) } -fn add_vis(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +fn add_vis(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let item_keyword = ctx.token_at_offset().find(|leaf| { matches!( leaf.kind(), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index a2a71bcba6baa..5ecc11ccc526a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -38,7 +38,10 @@ use crate::{ // cond.then(|| val) // } // ``` -pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_if_to_bool_then( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { // FIXME applies to match as well let expr = ctx.find_node_at_offset::()?; if !expr.if_token()?.text_range().contains_inclusive(ctx.offset()) { @@ -153,7 +156,10 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_> // } // } // ``` -pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_bool_then_to_if( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let name_ref = ctx.find_node_at_offset::()?; let mcall = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?; let receiver = mcall.receiver()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs index 231c447ec3816..456a4d1fcf17b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs @@ -52,7 +52,7 @@ use crate::{ // } // } // ``` -pub(crate) fn convert_bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let BoolNodeData { target_node, name, ty_annotation, initializer, definition } = find_bool_node(ctx)?; let target_module = ctx.sema.scope(&target_node)?.module().nearest_non_block_module(ctx.db()); @@ -101,7 +101,7 @@ struct BoolNodeData { } /// Attempts to find an appropriate node to apply the action to. -fn find_bool_node(ctx: &AssistContext<'_>) -> Option { +fn find_bool_node(ctx: &AssistContext<'_, '_>) -> Option { let name = ctx.find_node_at_offset::()?; if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { @@ -208,7 +208,7 @@ fn bool_expr_to_enum_expr(expr: ast::Expr, make: &SyntaxFactory) -> ast::Expr { /// Replaces all usages of the target identifier, both when read and written to. fn replace_usages( edit: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, usages: UsageSearchResult, target_definition: Definition, target_module: &hir::Module, @@ -338,7 +338,7 @@ struct FileReferenceWithImport { } fn augment_references_with_imports( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, references: Vec, target_module: &hir::Module, make: &SyntaxFactory, @@ -469,7 +469,7 @@ fn find_method_call_expr_usage(name: &ast::NameLike) -> Option { /// Adds the definition of the new enum before the target node. fn add_enum_def( edit: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, usages: &UsageSearchResult, target_node: SyntaxNode, target_module: &hir::Module, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_char_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_char_literal.rs index 0a50ba86ba6fb..b23525665a73f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_char_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_char_literal.rs @@ -12,7 +12,7 @@ use crate::{AssistContext, AssistId, Assists, GroupLabel}; // ``` // const _: char = '\x61'; // ``` -pub(crate) fn convert_char_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_char_literal(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { if !ctx.has_empty_selection() { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index acade433978ce..026b139b54956 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -51,7 +51,7 @@ use crate::assist_context::{AssistContext, Assists}; // closure("abc", &mut s); // } // ``` -pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let closure = ctx.find_node_at_offset::()?; if ctx.find_node_at_offset::() != Some(ast::Expr::ClosureExpr(closure.clone())) { // Not inside the parameter list. @@ -346,7 +346,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) } fn compute_closure_type_params( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, mentioned_generic_params: FxHashSet, closure: &ast::ClosureExpr, ) -> (Option, Option) { @@ -527,7 +527,7 @@ fn wrap_capture_in_deref_if_needed( make::expr_prefix(T![*], capture_name).into() } -fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture<'_>) -> ast::Expr { +fn capture_as_arg(ctx: &AssistContext<'_, '_>, capture: &ClosureCapture<'_>) -> ast::Expr { let place = parse_expr_from_str( &capture.display_place_source_code(ctx.db(), ctx.edition()), ctx.edition(), @@ -548,7 +548,7 @@ fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture<'_>) -> ast: fn handle_calls( builder: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, closure_name: Option<&ast::IdentPat>, captures_as_args: &[ast::Expr], closure: &ast::ClosureExpr, @@ -590,7 +590,7 @@ fn handle_calls( fn handle_call( builder: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, closure_ref: ast::Expr, captures_as_args: &[ast::Expr], ) -> Option<()> { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs index f242fe831447a..d950b6df214a3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs @@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists}; // comment // */ // ``` -pub(crate) fn convert_comment_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_comment_block(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let comment = ctx.find_token_at_offset::()?; // Only allow comments which are alone on their line if let Some(prev) = comment.syntax().prev_token() { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs index 187cc74306e25..11a3c64188d49 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs @@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn convert_comment_from_or_to_doc( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let comment = ctx.find_token_at_offset::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs index 9eb4c0584b362..d45c4292b8ed6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs @@ -32,7 +32,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn convert_for_loop_to_while_let( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let for_loop = ctx.find_node_at_offset::()?; let iterable = for_loop.iterable()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs index 18f3ced414026..3e11ee804a6bb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs @@ -33,7 +33,10 @@ use crate::{AssistContext, AssistId, Assists}; // } // } // ``` -pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_from_to_tryfrom( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let impl_ = ctx.find_node_at_offset::()?; let trait_ty = impl_.trait_()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs index bc76ade97f69d..c92ad2a7c95ad 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs @@ -13,7 +13,10 @@ use crate::{AssistContext, AssistId, Assists, GroupLabel}; // ``` // const _: i32 = 0b1010; // ``` -pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_integer_literal( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { if !ctx.has_empty_selection() { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs index 2fcde15874367..a01a66e7b1a52 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs @@ -33,7 +33,7 @@ use crate::{AssistContext, AssistId, Assists}; // } // } // ``` -pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let impl_ = ctx.find_node_at_offset::()?; let src_type = impl_.self_ty()?; let ast_trait = impl_.trait_()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs index cc5cc490f1bc1..9506a963183eb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs @@ -33,7 +33,7 @@ use crate::{AssistContext, AssistId, Assists, utils::wrap_paren}; // ``` pub(crate) fn convert_iter_for_each_to_for( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let method = ctx.find_node_at_offset::()?; @@ -98,7 +98,7 @@ pub(crate) fn convert_iter_for_each_to_for( // ``` pub(crate) fn convert_for_loop_with_for_each( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let for_loop = ctx.find_node_at_offset::()?; let iterable = for_loop.iterable()?; @@ -205,7 +205,7 @@ fn impls_core_iter(sema: &hir::Semantics<'_, ide_db::RootDatabase>, iterable: &a } fn validate_method_call_expr( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, expr: ast::MethodCallExpr, ) -> Option<(ast::Expr, ast::Expr)> { let name_ref = expr.name_ref()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 1ae12390eedbf..721e2d8789109 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -24,7 +24,10 @@ use crate::{AssistContext, AssistId, Assists}; // }; // } // ``` -pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_let_else_to_match( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); // Should focus on the `else` token to trigger let let_stmt = ctx diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index bc49acc1ef356..b41ae6788bc64 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -29,7 +29,10 @@ use crate::{ // let Some(val) = opt else { return }; // } // ``` -pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_match_to_let_else( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let let_stmt: ast::LetStmt = ctx.find_node_at_offset()?; let pat = let_stmt.pat()?; if ctx.offset() > pat.syntax().text_range().end() { @@ -71,7 +74,7 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<' // Given a match expression, find extracting and diverging arms. fn find_arms( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, match_expr: &ast::MatchExpr, ) -> Option<(ast::MatchArm, ast::MatchArm)> { let arms = match_expr.match_arm_list()?.arms().collect::>(); @@ -99,7 +102,7 @@ fn find_arms( } // Given an extracting arm, find the extracted variable. -fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Option> { +fn find_extracted_variable(ctx: &AssistContext<'_, '_>, arm: &ast::MatchArm) -> Option> { match arm.expr()? { ast::Expr::PathExpr(path) => { let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index 5b691dba5ea76..72418f155962e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -54,7 +54,7 @@ use crate::{ // ``` pub(crate) fn convert_named_struct_to_tuple_struct( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { // XXX: We don't currently provide this assist for struct definitions inside macros, but if we // are to lift this limitation, don't forget to make `edit_struct_def()` consider macro files @@ -92,7 +92,7 @@ pub(crate) fn convert_named_struct_to_tuple_struct( } fn edit_struct_def( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, strukt: &Either, record_fields: ast::RecordFieldList, @@ -153,7 +153,7 @@ fn edit_struct_def( } fn edit_struct_references( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, strukt: Either, ) { @@ -174,7 +174,7 @@ fn edit_struct_references( } fn process_struct_name_reference( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, r: FileReference, edit: &SyntaxEditor, source: &ast::SourceFile, @@ -227,7 +227,7 @@ fn process_struct_name_reference( } fn record_to_tuple_struct_like( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, source: &ast::SourceFile, editor: &SyntaxEditor, field_list: T, @@ -282,7 +282,7 @@ where } fn edit_field_references( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, fields: impl Iterator, ) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs index c0fd69779aeae..d0e82d69eb51e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs @@ -29,7 +29,7 @@ use crate::assist_context::{AssistContext, Assists}; // ``` pub(crate) fn convert_nested_function_to_closure( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let name = ctx.find_node_at_offset::()?; let function = name.syntax().parent().and_then(ast::Fn::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs index c83f8b076551b..7026b5bafdc7c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs @@ -35,7 +35,10 @@ use crate::assist_context::{AssistContext, Assists}; // } // } // ``` -pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_range_for_to_while( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); let make = editor.make(); let for_kw = ctx.find_token_syntax_at_offset(T![for])?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index 44ca57281eca4..74392ca7c14f5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -59,7 +59,10 @@ use crate::{ // let Some(x) = foo() else { return }; // } // ``` -pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_to_guarded_return( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { match ctx.find_node_at_offset::>()? { Either::Left(let_stmt) => let_stmt_to_guarded_return(let_stmt, acc, ctx), Either::Right(if_expr) => if_expr_to_guarded_return(if_expr, acc, ctx), @@ -69,7 +72,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<' fn if_expr_to_guarded_return( if_expr: ast::IfExpr, acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let make = SyntaxFactory::without_mappings(); let cond = if_expr.condition()?; @@ -174,7 +177,7 @@ fn if_expr_to_guarded_return( fn let_stmt_to_guarded_return( let_stmt: ast::LetStmt, acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let pat = let_stmt.pat()?; let expr = let_stmt.initializer()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 254ff7280f0d6..46e2917f72f17 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -51,7 +51,7 @@ use crate::assist_context::{AssistContext, Assists}; // ``` pub(crate) fn convert_tuple_return_type_to_struct( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let ret_type = ctx.find_node_at_offset::()?; let type_ref = ret_type.ty()?; @@ -104,7 +104,7 @@ pub(crate) fn convert_tuple_return_type_to_struct( /// Replaces tuple usages with the corresponding tuple struct pattern. fn replace_usages( edit: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, usages: &UsageSearchResult, struct_name: &str, target_module: &hir::Module, @@ -177,7 +177,7 @@ fn node_to_pats(node: SyntaxNode) -> Option> { fn augment_references_with_imports( make: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, references: &[FileReference], struct_name: &str, target_module: &hir::Module, @@ -232,7 +232,7 @@ fn augment_references_with_imports( fn add_tuple_struct_def( edit: &mut SourceChangeBuilder, syntax_factory: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, usages: &UsageSearchResult, parent: &SyntaxNode, tuple_ty: &ast::TupleType, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index afbcf024b9fd4..a6a47d78a8e0c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -58,7 +58,7 @@ use crate::{ // ``` pub(crate) fn convert_tuple_struct_to_named_struct( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let strukt_or_variant = ctx .find_node_at_offset::() @@ -140,7 +140,7 @@ fn edit_struct_def( } fn edit_struct_references( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, edit: &mut SourceChangeBuilder, strukt: Either, names: &[ast::Name], @@ -164,7 +164,7 @@ fn edit_struct_references( } fn process_struct_name_reference( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, r: FileReference, editor: &SyntaxEditor, source: &ast::SourceFile, @@ -229,7 +229,7 @@ fn process_struct_name_reference( } fn process_delimiter( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, source: &ast::SourceFile, editor: &SyntaxEditor, list: &impl AstNode, @@ -270,7 +270,7 @@ fn process_delimiter( } fn edit_field_references( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, edit: &mut SourceChangeBuilder, fields: impl Iterator, names: &[ast::Name], diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs index 1af5db17f0400..73b373dbcb4e1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs @@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn convert_two_arm_bool_match_to_matches_macro( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { use ArmBodyExpression::*; let match_expr = ctx.find_node_at_offset::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs index 793e7465c11ae..94920569a080e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs @@ -38,7 +38,7 @@ use crate::{ // } // } // ``` -pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let while_kw = ctx.find_token_syntax_at_offset(T![while])?; let while_expr = while_kw.parent().and_then(ast::WhileExpr::cast)?; let while_body = while_expr.loop_body()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index 6e6c7fcbfbedc..2b5b2ca13bb49 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -47,7 +47,10 @@ use crate::{ // let baz2 = &baz; // } // ``` -pub(crate) fn destructure_struct_binding(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn destructure_struct_binding( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let target = ctx.find_node_at_offset::()?; let data = collect_data(target, ctx)?; @@ -119,7 +122,7 @@ impl AstNode for Target { } fn destructure_struct_binding_impl( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, data: &StructEditData, ) { @@ -173,7 +176,7 @@ impl StructEditData { } } -fn collect_data(target: Target, ctx: &AssistContext<'_>) -> Option { +fn collect_data(target: Target, ctx: &AssistContext<'_, '_>) -> Option { let ty = target.ty(&ctx.sema)?; let hir::Adt::Struct(struct_type) = ty.strip_references().as_adt()? else { return None }; @@ -246,7 +249,7 @@ fn collect_data(target: Target, ctx: &AssistContext<'_>) -> Option, + ctx: &AssistContext<'_, '_>, target: &Target, usages: &[FileReference], ) -> Option> { @@ -270,7 +273,7 @@ fn get_names_in_scope( } fn destructure_pat( - _ctx: &AssistContext<'_>, + _ctx: &AssistContext<'_, '_>, editor: &SyntaxEditor, data: &StructEditData, field_names: &[(SmolStr, SmolStr)], @@ -313,7 +316,10 @@ fn destructure_pat( data.apply_to_destruct(new_pat, editor); } -fn generate_field_names(ctx: &AssistContext<'_>, data: &StructEditData) -> Vec<(SmolStr, SmolStr)> { +fn generate_field_names( + ctx: &AssistContext<'_, '_>, + data: &StructEditData, +) -> Vec<(SmolStr, SmolStr)> { match data.kind { hir::StructKind::Tuple => data .visible_fields @@ -348,7 +354,7 @@ fn new_field_name(base_name: SmolStr, names_in_scope: &FxHashSet) -> Sm } fn update_usages( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, editor: &SyntaxEditor, data: &StructEditData, field_names: &FxHashMap, @@ -367,7 +373,7 @@ fn update_usages( } fn build_usage_edit( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, data: &StructEditData, usage: &FileReference, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index 09a554234a356..2755962362677 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -34,7 +34,10 @@ use crate::{ // let v = _0; // } // ``` -pub(crate) fn destructure_tuple_binding(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn destructure_tuple_binding( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { destructure_tuple_binding_impl(acc, ctx, false) } @@ -58,7 +61,7 @@ pub(crate) fn destructure_tuple_binding(acc: &mut Assists, ctx: &AssistContext<' // ``` pub(crate) fn destructure_tuple_binding_impl( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, with_sub_pattern: bool, ) -> Option<()> { let ident_pat = ctx.find_node_at_offset::()?; @@ -84,7 +87,7 @@ pub(crate) fn destructure_tuple_binding_impl( } fn destructure_tuple_edit_impl( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, edit: &mut SourceChangeBuilder, data: &TupleData, in_sub_pattern: bool, @@ -102,7 +105,7 @@ fn destructure_tuple_edit_impl( edit.add_file_edits(ctx.vfs_file_id(), editor); } -fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option { +fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_, '_>) -> Option { if ident_pat.at_token().is_some() { // Cannot destructure pattern with sub-pattern: // Only IdentPat can have sub-pattern, @@ -168,7 +171,7 @@ struct TupleData { usages: Option>, } fn edit_tuple_assignment( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, edit: &mut SourceChangeBuilder, editor: &SyntaxEditor, data: &TupleData, @@ -235,7 +238,7 @@ impl AssignmentEdit { fn edit_tuple_usages( data: &TupleData, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, in_sub_pattern: bool, ) -> Option> { @@ -258,7 +261,7 @@ fn edit_tuple_usages( Some(edits) } fn edit_tuple_usage( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, usage: &FileReference, data: &TupleData, @@ -275,7 +278,7 @@ fn edit_tuple_usage( } fn edit_tuple_field_usage( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, data: &TupleData, index: TupleIndex, @@ -305,7 +308,7 @@ enum EditTupleUsage { impl EditTupleUsage { fn apply( self, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, edit: &mut SourceChangeBuilder, syntax_editor: &SyntaxEditor, ) { @@ -371,7 +374,7 @@ mod tests { // Tests for direct tuple destructure: // `let $0t = (1,2);` -> `let (_0, _1) = (1,2);` - fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + fn assist(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { destructure_tuple_binding_impl(acc, ctx, false) } @@ -1180,10 +1183,10 @@ fn main { use super::*; use crate::tests::check_assist_by_label; - fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + fn assist(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { destructure_tuple_binding_impl(acc, ctx, true) } - fn in_place_assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + fn in_place_assist(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { destructure_tuple_binding_impl(acc, ctx, false) } @@ -1251,7 +1254,7 @@ fn main() { #[test] fn trigger_both_destructure_tuple_assists() { - fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + fn assist(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { destructure_tuple_binding_impl(acc, ctx, true) } let text = r#" diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs index 74bb0ba3f6020..e6784a0c3b397 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs @@ -24,7 +24,7 @@ use crate::{ // #[doc = r"Multi-line // comment"] // ``` -pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let comment = ctx.find_token_at_offset::()?; // Only allow doc comments let placement = comment.kind().doc?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs index fc894f0fe9a0f..5e1cd7700da36 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs @@ -50,7 +50,7 @@ use crate::assist_context::{AssistContext, Assists}; // }; // } // ``` -pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let question_tok = ctx.find_token_syntax_at_offset(T![?])?; let try_expr = question_tok.parent().and_then(ast::TryExpr::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs index 1c8cbf5af5941..24ffc6787dd03 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -41,7 +41,7 @@ use crate::{ // // fn qux(bar: Bar, baz: Baz) {} // ``` -pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let star = ctx.find_token_syntax_at_offset(T![*])?; let use_tree = star.parent().and_then(ast::UseTree::cast)?; let use_item = star.parent_ancestors().find_map(ast::Use::cast)?; @@ -99,7 +99,7 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> // // pub use foo::{Bar, Baz}; // ``` -pub(crate) fn expand_glob_reexport(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn expand_glob_reexport(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let star = ctx.find_token_syntax_at_offset(T![*])?; let use_tree = star.parent().and_then(ast::UseTree::cast)?; let use_item = star.parent_ancestors().find_map(ast::Use::cast)?; @@ -140,7 +140,7 @@ pub(crate) fn expand_glob_reexport(acc: &mut Assists, ctx: &AssistContext<'_>) - } fn build_expanded_import( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, use_tree: UseTree, use_item: Use, @@ -237,7 +237,7 @@ fn find_parent_and_path( } } -fn def_is_referenced_in(def: Definition, ctx: &AssistContext<'_>) -> bool { +fn def_is_referenced_in(def: Definition, ctx: &AssistContext<'_, '_>) -> bool { let search_scope = SearchScope::single_file(ctx.file_id()); def.usages(&ctx.sema).in_scope(&search_scope).at_least_one() } @@ -251,7 +251,11 @@ struct Ref { } impl Ref { - fn from_scope_def(ctx: &AssistContext<'_>, name: Name, scope_def: ScopeDef) -> Option { + fn from_scope_def( + ctx: &AssistContext<'_, '_>, + name: Name, + scope_def: ScopeDef, + ) -> Option { match scope_def { ScopeDef::ModuleDef(def) => Some(Ref { visible_name: name, @@ -267,7 +271,7 @@ impl Ref { struct Refs(Vec); impl Refs { - fn used_refs(&self, ctx: &AssistContext<'_>) -> Refs { + fn used_refs(&self, ctx: &AssistContext<'_, '_>) -> Refs { Refs( self.0 .clone() @@ -297,7 +301,7 @@ impl Refs { } fn find_refs_in_mod( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, expandable: Expandable, visible_from: Module, must_be_pub: bool, @@ -325,8 +329,8 @@ fn find_refs_in_mod( } } -fn is_visible_from(ctx: &AssistContext<'_>, expandable: &Expandable, from: Module) -> bool { - fn is_mod_visible_from(ctx: &AssistContext<'_>, module: Module, from: Module) -> bool { +fn is_visible_from(ctx: &AssistContext<'_, '_>, expandable: &Expandable, from: Module) -> bool { + fn is_mod_visible_from(ctx: &AssistContext<'_, '_>, module: Module, from: Module) -> bool { match module.parent(ctx.db()) { Some(parent) => { module.visibility(ctx.db()).is_visible_from(ctx.db(), from.into()) @@ -366,7 +370,7 @@ fn is_visible_from(ctx: &AssistContext<'_>, expandable: &Expandable, from: Modul // use foo::*$0; // use baz::Baz; // ↑ --------------- -fn find_imported_defs(ctx: &AssistContext<'_>, use_item: Use) -> Vec { +fn find_imported_defs(ctx: &AssistContext<'_, '_>, use_item: Use) -> Vec { [Direction::Prev, Direction::Next] .into_iter() .flat_map(|dir| { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs index dc4976e8c29d7..4aa11b4e03c85 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs @@ -29,7 +29,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` fn expand_record_rest_pattern( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, record_pat: ast::RecordPat, rest_pat: ast::RestPat, ) -> Option<()> { @@ -92,7 +92,7 @@ fn expand_record_rest_pattern( // ``` fn expand_tuple_struct_rest_pattern( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, pat: ast::TupleStructPat, rest_pat: ast::RestPat, ) -> Option<()> { @@ -171,7 +171,7 @@ fn expand_tuple_struct_rest_pattern( // ``` fn expand_tuple_rest_pattern( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, pat: ast::TuplePat, rest_pat: ast::RestPat, ) -> Option<()> { @@ -233,7 +233,7 @@ fn expand_tuple_rest_pattern( // ``` fn expand_slice_rest_pattern( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, pat: ast::SlicePat, rest_pat: ast::RestPat, ) -> Option<()> { @@ -277,7 +277,7 @@ fn expand_slice_rest_pattern( ) } -pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let rest_pat = ctx.find_node_at_offset::()?; let parent = rest_pat.syntax().parent()?; match_ast! { @@ -292,7 +292,7 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> } fn gen_unnamed_pat( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, name_gen: &mut NameGenerator, ty: &hir::Type<'_>, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index c87ded9dc47b7..178477c74605f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -30,7 +30,7 @@ use syntax::{ pub(crate) fn extract_expressions_from_format_string( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let fmt_string = ctx.find_token_at_offset::()?; let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index c5fd0201e959a..a8aa0d6e5de4d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -64,7 +64,7 @@ use crate::{ // let k = m + n; // } // ``` -pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let range = ctx.selection_trimmed(); if range.is_empty() { return None; @@ -452,7 +452,7 @@ struct OutlivedLocal { struct LocalUsages(ide_db::search::UsageSearchResult); impl LocalUsages { - fn find_local_usages(ctx: &AssistContext<'_>, var: Local) -> Self { + fn find_local_usages(ctx: &AssistContext<'_, '_>, var: Local) -> Self { Self( Definition::Local(var) .usages(&ctx.sema) @@ -467,7 +467,7 @@ impl LocalUsages { } impl<'db> Function<'db> { - fn return_type(&self, ctx: &AssistContext<'db>) -> FunType<'db> { + fn return_type(&self, ctx: &AssistContext<'_, 'db>) -> FunType<'db> { match &self.ret_ty { RetType::Expr(ty) if ty.is_unit() => FunType::Unit, RetType::Expr(ty) => FunType::Single(ty.clone()), @@ -482,7 +482,7 @@ impl<'db> Function<'db> { } } - fn self_param_adt(&self, ctx: &AssistContext<'_>) -> Option { + fn self_param_adt(&self, ctx: &AssistContext<'_, '_>) -> Option { let self_param = self.self_param.as_ref()?; let def = ctx.sema.to_def(self_param)?; let adt = def.ty(ctx.db()).strip_references().as_adt()?; @@ -510,7 +510,7 @@ impl<'db> Param<'db> { fn to_arg( &self, make: &SyntaxFactory, - ctx: &AssistContext<'db>, + ctx: &AssistContext<'_, 'db>, edition: Edition, ) -> ast::Expr { let var = path_expr_from_local(make, ctx, self.var, edition); @@ -524,7 +524,7 @@ impl<'db> Param<'db> { fn to_param( &self, make: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, module: hir::Module, edition: Edition, ) -> ast::Param { @@ -551,7 +551,7 @@ impl<'db> Param<'db> { impl<'db> TryKind<'db> { fn of_ty( ty: hir::Type<'db>, - ctx: &AssistContext<'db>, + ctx: &AssistContext<'_, 'db>, edition: Edition, ) -> Option> { if ty.is_unknown() { @@ -587,7 +587,7 @@ impl<'db> FlowKind<'db> { } } - fn expr_ty(&self, ctx: &AssistContext<'db>) -> Option> { + fn expr_ty(&self, ctx: &AssistContext<'_, 'db>) -> Option> { match self { FlowKind::Return(Some(expr)) | FlowKind::Break(_, Some(expr)) => { ctx.sema.type_of_expr(expr).map(TypeInfo::adjusted) @@ -957,7 +957,7 @@ impl FunctionBody { )) } - fn return_ty<'db>(&self, ctx: &AssistContext<'db>) -> Option> { + fn return_ty<'db>(&self, ctx: &AssistContext<'_, 'db>) -> Option> { match self.tail_expr() { Some(expr) => ctx.sema.type_of_expr(&expr).map(TypeInfo::original).map(RetType::Expr), None => Some(RetType::Stmt), @@ -967,7 +967,7 @@ impl FunctionBody { /// Local variables defined inside `body` that are accessed outside of it fn ret_values<'a>( &self, - ctx: &'a AssistContext<'_>, + ctx: &'a AssistContext<'_, '_>, parent: &SyntaxNode, ) -> impl Iterator + 'a { let parent = parent.clone(); @@ -980,7 +980,7 @@ impl FunctionBody { /// Analyses the function body for external control flow. fn external_control_flow<'db>( &self, - ctx: &AssistContext<'db>, + ctx: &AssistContext<'_, 'db>, container_info: &ContainerInfo<'db>, ) -> Option> { let mut ret_expr = None; @@ -1070,7 +1070,7 @@ impl FunctionBody { /// Computes additional info that affects param type and mutability fn extracted_function_params<'db>( &self, - ctx: &AssistContext<'db>, + ctx: &AssistContext<'_, 'db>, container_info: &ContainerInfo<'db>, locals: FxIndexSet, ) -> Vec> { @@ -1167,7 +1167,7 @@ fn generic_parents(parent: &SyntaxNode) -> Vec { /// checks if relevant var is used with `&mut` access inside body fn has_exclusive_usages( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, usages: &LocalUsages, body: &FunctionBody, ) -> bool { @@ -1181,7 +1181,7 @@ fn has_exclusive_usages( fn reference_is_exclusive( reference: &FileReference, node: &dyn HasTokenAtOffset, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> bool { // FIXME: this quite an incorrect way to go about doing this :-) // `FileReference` is an IDE-type --- it encapsulates data communicated to the human, @@ -1204,7 +1204,7 @@ fn reference_is_exclusive( } /// checks if this expr requires `&mut` access, recurses on field access -fn expr_require_exclusive_access(ctx: &AssistContext<'_>, expr: &ast::Expr) -> Option { +fn expr_require_exclusive_access(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> Option { if let ast::Expr::MacroExpr(_) = expr { // FIXME: expand macro and check output for mutable usages of the variable? return None; @@ -1324,7 +1324,7 @@ fn locals_defined_in_body( /// Returns usage details if local variable is used after(outside of) body fn local_outlives_body( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, body_range: TextRange, local: Local, parent: &SyntaxNode, @@ -1349,7 +1349,7 @@ fn local_outlives_body( /// checks if the relevant local was defined before(outside of) body fn is_defined_outside_of_body( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, body: &FunctionBody, src: &LocalSource, ) -> bool { @@ -1417,10 +1417,10 @@ fn impl_type_name(impl_node: &ast::Impl) -> Option { Some(impl_node.self_ty()?.to_string()) } -fn make_call( +fn make_call<'db>( make: &SyntaxFactory, - ctx: &AssistContext<'_>, - fun: &Function<'_>, + ctx: &AssistContext<'_, 'db>, + fun: &Function<'db>, indent: IndentLevel, ) -> SyntaxNode { let ret_ty = fun.return_type(ctx); @@ -1598,7 +1598,7 @@ impl<'db> FlowHandler<'db> { fn path_expr_from_local( make: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, var: Local, edition: Edition, ) -> ast::Expr { @@ -1606,10 +1606,10 @@ fn path_expr_from_local( make.expr_path(make.ident_path(&name)) } -fn format_function( - ctx: &AssistContext<'_>, +fn format_function<'db>( + ctx: &AssistContext<'_, 'db>, module: hir::Module, - fun: &Function<'_>, + fun: &Function<'db>, old_indent: IndentLevel, make: &SyntaxFactory, ) -> ast::Fn { @@ -1635,10 +1635,10 @@ fn format_function( ) } -fn make_generic_params_and_where_clause( - ctx: &AssistContext<'_>, +fn make_generic_params_and_where_clause<'db>( + ctx: &AssistContext<'_, 'db>, make: &SyntaxFactory, - fun: &Function<'_>, + fun: &Function<'db>, ) -> (Option, Option) { let used_type_params = fun.type_params(ctx); @@ -1648,10 +1648,10 @@ fn make_generic_params_and_where_clause( (generic_param_list, where_clause) } -fn make_generic_param_list( - ctx: &AssistContext<'_>, +fn make_generic_param_list<'db>( + ctx: &AssistContext<'_, 'db>, make: &SyntaxFactory, - fun: &Function<'_>, + fun: &Function<'db>, used_type_params: &[TypeParam], ) -> Option { let mut generic_params = fun @@ -1673,7 +1673,7 @@ fn make_generic_param_list( } fn param_is_required( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, param: &ast::GenericParam, used_type_params: &[TypeParam], ) -> bool { @@ -1687,7 +1687,7 @@ fn param_is_required( } fn make_where_clause( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, fun: &Function<'_>, used_type_params: &[TypeParam], @@ -1707,7 +1707,7 @@ fn make_where_clause( } fn pred_is_required( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, pred: &ast::WherePred, used_type_params: &[TypeParam], ) -> bool { @@ -1717,7 +1717,7 @@ fn pred_is_required( } } -fn resolved_type_param(ctx: &AssistContext<'_>, pred: &ast::WherePred) -> Option { +fn resolved_type_param(ctx: &AssistContext<'_, '_>, pred: &ast::WherePred) -> Option { let path = match pred.ty()? { ast::Type::PathType(path_type) => path_type.path(), _ => None, @@ -1731,7 +1731,7 @@ fn resolved_type_param(ctx: &AssistContext<'_>, pred: &ast::WherePred) -> Option impl<'db> Function<'db> { /// Collect all the `TypeParam`s used in the `body` and `params`. - fn type_params(&self, ctx: &AssistContext<'db>) -> Vec { + fn type_params(&self, ctx: &AssistContext<'_, 'db>) -> Vec { let type_params_in_descendant_paths = self.body.descendant_paths().filter_map(|it| match ctx.sema.resolve_path(&it) { Some(PathResolution::TypeParam(type_param)) => Some(type_param), @@ -1744,7 +1744,7 @@ impl<'db> Function<'db> { fn make_param_list( &self, make: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, module: hir::Module, edition: Edition, ) -> ast::ParamList { @@ -1775,7 +1775,7 @@ impl<'db> Function<'db> { fn make_ret_ty( &self, make: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, 'db>, module: hir::Module, ) -> Option { let fun_ty = self.return_type(ctx); @@ -1825,7 +1825,7 @@ impl<'db> FunType<'db> { fn make_ty( &self, make: &SyntaxFactory, - ctx: &AssistContext<'db>, + ctx: &AssistContext<'_, 'db>, module: hir::Module, ) -> ast::Type { match self { @@ -1849,11 +1849,11 @@ impl<'db> FunType<'db> { } } -fn make_body( +fn make_body<'db>( make: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, 'db>, old_indent: IndentLevel, - fun: &Function<'_>, + fun: &Function<'db>, ) -> ast::BlockExpr { let ret_ty = fun.return_type(ctx); let handler = FlowHandler::from_ret_ty(fun, &ret_ty); @@ -2045,7 +2045,7 @@ fn with_tail_expr( make.hacky_block_expr(elements, Some(tail_expr)) } -fn format_type(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> String { +fn format_type(ty: &hir::Type<'_>, ctx: &AssistContext<'_, '_>, module: hir::Module) -> String { ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_owned()) } @@ -2057,7 +2057,7 @@ fn is_inherit_attr(attr: &ast::Attr) -> bool { fn make_ty( make: &SyntaxFactory, ty: &hir::Type<'_>, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, module: hir::Module, ) -> ast::Type { let ty_str = format_type(ty, ctx, module); @@ -2065,7 +2065,7 @@ fn make_ty( } fn rewrite_body_segment( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, to_this_param: Option, params: &[Param<'_>], handler: &FlowHandler<'_>, @@ -2086,7 +2086,7 @@ fn fix_param_usages( editor: &SyntaxEditor, source_syntax: &SyntaxNode, syntax: &SyntaxNode, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, to_this_param: Option, params: &[Param<'_>], ) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index dcbeaefa21f4b..9e06a17337d8e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -52,7 +52,7 @@ use super::remove_unused_param::range_to_remove; // name + 2 // } // ``` -pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { if ctx.has_empty_selection() { return None; } @@ -267,7 +267,7 @@ fn extract_child_target( impl Module { fn get_usages_and_record_fields( &self, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, replace_range: TextRange, ) -> (FxHashMap>, Vec, FxHashMap) { @@ -356,7 +356,7 @@ impl Module { fn expand_and_group_usages_file_wise( &self, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, replace_range: TextRange, node_def: Definition, refs_in_files: &mut FxHashMap>, @@ -449,7 +449,7 @@ impl Module { fn resolve_imports( &mut self, module: Option, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Vec { let mut imports_to_remove = vec![]; let mut node_set = FxHashSet::default(); @@ -491,7 +491,7 @@ impl Module { def: Definition, use_node: &SyntaxNode, curr_parent_module: &Option, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option { //We only need to find in the current file let selection_range = ctx.selection_trimmed(); @@ -689,7 +689,7 @@ fn check_intersection_and_push( fn check_def_in_mod_and_out_sel( def: Definition, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, curr_parent_module: &Option, selection_range: TextRange, curr_file_id: FileId, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 9a884bc1daedf..37867d656c364 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -40,7 +40,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn extract_struct_from_enum_variant( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let variant = ctx.find_node_at_offset::()?; let field_list = extract_field_list_if_applicable(&variant)?; @@ -416,7 +416,7 @@ fn apply_references( } fn process_references( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, visited_modules: &mut FxHashSet, enum_module_def: &ModuleDef, variant_hir_name: &Name, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs index eda35eba45c9a..a654b681d18b7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists}; // field: Type, // } // ``` -pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { if ctx.has_empty_selection() { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index c5c57c76b47ca..d4a0490f16a41 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -65,7 +65,7 @@ use crate::{AssistContext, AssistId, Assists, utils::is_body_const}; // VAR_NAME * 4; // } // ``` -pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let node = if ctx.has_empty_selection() { if let Some(t) = ctx.token_at_offset().find(|it| it.kind() == T![;]) { t.parent().and_then(ast::ExprStmt::cast)?.syntax().clone() @@ -332,7 +332,7 @@ fn peel_parens(mut expr: ast::Expr) -> ast::Expr { /// Check whether the node is a valid expression which can be extracted to a variable. /// In general that's true for any expression, but in some cases that would produce invalid code. -fn valid_target_expr(ctx: &AssistContext<'_>) -> impl Fn(SyntaxNode) -> Option { +fn valid_target_expr(ctx: &AssistContext<'_, '_>) -> impl Fn(SyntaxNode) -> Option { let selection = ctx.selection_trimmed(); move |node| match node.kind() { SyntaxKind::LOOP_EXPR | SyntaxKind::LET_EXPR => None, @@ -383,7 +383,7 @@ impl ExtractionKind { fn get_name_and_expr( &self, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, to_extract: &ast::Expr, ) -> (String, SyntaxNode) { // We only do this sort of extraction for fields because they should have lowercase names @@ -416,7 +416,7 @@ impl ExtractionKind { } } -fn get_literal_name(ctx: &AssistContext<'_>, expr: &ast::Expr) -> Option { +fn get_literal_name(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> Option { let ast::Expr::Literal(literal) = expr else { return None; }; @@ -512,7 +512,7 @@ impl Anchor { } } -fn like_const_value(ctx: &AssistContext<'_>, path_resolution: hir::PathResolution) -> bool { +fn like_const_value(ctx: &AssistContext<'_, '_>, path_resolution: hir::PathResolution) -> bool { let db = ctx.db(); let adt_like_const_value = |adt: Option| matches!(adt, Some(hir::Adt::Struct(s)) if s.kind(db) == hir::StructKind::Unit); match path_resolution { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs index d8714dd49c2db..d0f5c7c5003d4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs @@ -30,11 +30,11 @@ use crate::{AssistContext, AssistId, Assists}; // m::frobnicate(); // } // ``` -pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { add_vis_to_referenced_module_def(acc, ctx) } -fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let path: ast::Path = ctx.find_node_at_offset()?; let qualifier = path.qualifier()?; let name_ref = path.segment()?.name_ref()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs index 17911150f5e7b..d47f5c83cd7ea 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs @@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists}; // let _ = 2 + 90; // } // ``` -pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let expr = ctx.find_node_at_offset::()?; let lhs = expr.lhs()?; let rhs = expr.rhs()?; @@ -114,7 +114,7 @@ impl From for FlipAction { // let _ = ..90; // } // ``` -pub(crate) fn flip_range_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn flip_range_expr(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let range_expr = ctx.find_node_at_offset::()?; let op = range_expr.op_token()?; let start = range_expr.start(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs index 65dc36cdca7ee..00d659adc1300 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs @@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists}; // ((3, 4), (1, 2)); // } // ``` -pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let comma = ctx.find_token_syntax_at_offset(T![,])?; let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?; let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs index bd56331f4128b..c60c6a2a98081 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs @@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists}; // let (b | a) = 1; // } // ``` -pub(crate) fn flip_or_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn flip_or_pattern(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { // Only flip on the `|` token let pipe = ctx.find_token_syntax_at_offset(T![|])?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs index dfd280efa6303..77d5c042c9269 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs @@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` // fn foo() { } // ``` -pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { // Only flip on the `+` token let plus = ctx.find_token_syntax_at_offset(T![+])?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index 0bb90f187c684..e99dd81066645 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -58,7 +58,7 @@ use syntax::{ // ``` pub(crate) fn generate_blanket_trait_impl( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let name = ctx.find_node_at_offset::()?; let traitd = ast::Trait::cast(name.syntax().parent()?)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs index fce0ce399463c..6c5042b14f9e2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs @@ -31,7 +31,7 @@ use syntax::{ // } // ``` -pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let constant_token = ctx.find_node_at_offset::()?; if constant_token.to_string().chars().any(|it| !(it.is_uppercase() || it == '_')) { cov_mark::hit!(not_constant_name); @@ -113,7 +113,7 @@ fn get_text_for_generate_constant( } fn target_data_for_generate_constant( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, current_module: Module, constant_module: Module, ) -> Option<(TextSize, IndentLevel, Option, String)> { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs index b4a17c376ac6b..4470791f4d017 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs @@ -30,7 +30,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn generate_default_from_enum_variant( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let variant = ctx.find_node_at_offset::()?; let variant_name = variant.name()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs index 739b63173694a..34ab3c430412d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs @@ -44,7 +44,10 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_default_from_new( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let fn_node = ctx.find_node_at_offset::()?; let fn_name = fn_node.name()?; @@ -154,7 +157,7 @@ fn generate_default_impl(make: &SyntaxFactory, impl_: &ast::Impl, self_ty: ast:: ) } -fn is_default_implemented(ctx: &AssistContext<'_>, impl_: &Impl) -> bool { +fn is_default_implemented(ctx: &AssistContext<'_, '_>, impl_: &Impl) -> bool { let db = ctx.sema.db; let impl_ = ctx.sema.to_def(impl_); let impl_def = match impl_ { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index 9486aa6f01953..a209e5fc29ecd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -48,7 +48,10 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_delegate_methods( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { if !ctx.config.code_action_grouping { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs index 6639f10c1f360..ed1599821904c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -86,7 +86,10 @@ use syntax::{ // } // } // ``` -pub(crate) fn generate_delegate_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_delegate_trait( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { if !ctx.config.code_action_grouping { return None; } @@ -118,7 +121,7 @@ struct Field { impl Field { pub(crate) fn new( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, f: Either, ) -> Option { let db = ctx.sema.db; @@ -202,7 +205,7 @@ impl Struct { Some(Struct { name, strukt: s }) } - pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_>) { + pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_, '_>) { let db = ctx.db(); for (index, delegee) in field.impls.iter().enumerate() { @@ -249,7 +252,7 @@ impl Struct { } fn generate_impl( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, strukt: &Struct, field_ty: &ast::Type, field_name: &str, @@ -412,7 +415,7 @@ fn generate_impl( } fn transform_impl( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, strukt: &ast::Struct, old_impl: &ast::Impl, args: &Option, @@ -638,7 +641,7 @@ fn generate_args_for_impl( } fn rename_strukt_args( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, strukt: &ast::Struct, item: &N, args: &GenericArgList, @@ -654,7 +657,7 @@ where N::cast(transform.apply(item.syntax())) } -fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_>) -> bool { +fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_, '_>) -> bool { ctx.sema .source(trait_) .and_then(|src| { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs index a5bdf80ac725e..9d1b257af40c1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs @@ -38,11 +38,11 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { generate_record_deref(acc, ctx).or_else(|| generate_tuple_deref(acc, ctx)) } -fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let strukt = ctx.find_node_at_offset::()?; let field = ctx.find_node_at_offset::()?; @@ -84,7 +84,7 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( ) } -fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let strukt = ctx.find_node_at_offset::()?; let field = ctx.find_node_at_offset::()?; let field_list = ctx.find_node_at_offset::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs index 0129b1db396b2..f293e956bca5b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs @@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists}; // y: u32, // } // ``` -pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let cap = ctx.config.snippet_cap?; let nominal = ctx.find_node_at_offset::()?; let target = nominal.syntax().text_range(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs index 77232dfebdfe4..89adda93866f0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs @@ -43,7 +43,7 @@ use crate::assist_context::{AssistContext, Assists}; // ``` pub(crate) fn generate_documentation_template( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let name = ctx.find_node_at_offset::()?; let ast_func = name.syntax().parent().and_then(ast::Fn::cast)?; @@ -95,7 +95,7 @@ pub(crate) fn generate_documentation_template( // /// ``` // pub fn add(a: i32, b: i32) -> i32 { a + b } // ``` -pub(crate) fn generate_doc_example(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_doc_example(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let tok: ast::Comment = ctx.find_token_at_offset()?; let node = tok.syntax().parent()?; let last_doc_token = @@ -127,7 +127,7 @@ pub(crate) fn generate_doc_example(acc: &mut Assists, ctx: &AssistContext<'_>) - ) } -fn make_example_for_fn(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option { +fn make_example_for_fn(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option { if !is_public(ast_func, ctx)? { // Doctests for private items can't actually name the item, so they're pretty useless. return None; @@ -182,7 +182,7 @@ fn make_example_for_fn(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option) -> Option { +fn introduction_builder(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option { let hir_func = ctx.sema.to_def(ast_func)?; let container = hir_func.as_assoc_item(ctx.db())?.container(ctx.db()); if let hir::AssocItemContainer::Impl(imp) = container { @@ -281,7 +281,7 @@ fn safety_builder(ast_func: &ast::Fn) -> Option> { } /// Checks if the function is public / exported -fn is_public(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option { +fn is_public(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option { let hir_func = ctx.sema.to_def(ast_func)?; Some( hir_func.visibility(ctx.db()) == Visibility::Public @@ -290,7 +290,7 @@ fn is_public(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option { } /// Checks that all parent modules of the function are public / exported -fn all_parent_mods_public(hir_func: &hir::Function, ctx: &AssistContext<'_>) -> bool { +fn all_parent_mods_public(hir_func: &hir::Function, ctx: &AssistContext<'_, '_>) -> bool { let mut module = hir_func.module(ctx.db()); loop { if let Some(parent) = module.parent(ctx.db()) { @@ -305,7 +305,7 @@ fn all_parent_mods_public(hir_func: &hir::Function, ctx: &AssistContext<'_>) -> } /// Returns the name of the current crate -fn crate_name(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option { +fn crate_name(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option { let krate = ctx.sema.scope(ast_func.syntax())?.krate(); Some(krate.display_name(ctx.db())?.to_string()) } @@ -378,7 +378,7 @@ fn self_partial_type(ast_func: &ast::Fn) -> Option { } /// Helper function to determine if the function is in a trait implementation -fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool { +fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> bool { ctx.sema .to_def(ast_func) .and_then(|hir_func| hir_func.as_assoc_item(ctx.db())) @@ -387,7 +387,7 @@ fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool { } /// Helper function to determine if the function definition is in a trait definition -fn is_in_trait_def(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool { +fn is_in_trait_def(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> bool { ctx.sema .to_def(ast_func) .and_then(|hir_func| hir_func.as_assoc_item(ctx.db())) @@ -490,7 +490,7 @@ fn string_vec_from(string_array: &[&str]) -> Vec { } /// Helper function to build the path of the module in the which is the node -fn build_path(ast_func: &ast::Fn, ctx: &AssistContext<'_>, edition: Edition) -> Option { +fn build_path(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>, edition: Edition) -> Option { let crate_name = crate_name(ast_func, ctx)?; let leaf = self_partial_type(ast_func) .or_else(|| ast_func.name().map(|n| n.to_string())) @@ -508,7 +508,7 @@ fn return_type(ast_func: &ast::Fn) -> Option { } /// Helper function to determine if the function returns some data -fn returns_a_value(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool { +fn returns_a_value(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> bool { ctx.sema .to_def(ast_func) .map(|hir_func| hir_func.ret_type(ctx.db())) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs index e2783811f743b..867eaf4c2987f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs @@ -40,7 +40,10 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_enum_is_method( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let variant = ctx.find_node_at_offset::()?; let parent_enum = ast::Adt::Enum(variant.parent_enum()); let variants = variant diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs index 9a97ad1e8fe75..4cdc801ec19d6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs @@ -40,7 +40,7 @@ use crate::{ // ``` pub(crate) fn generate_enum_try_into_method( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { generate_enum_projection_method( acc, @@ -85,7 +85,10 @@ pub(crate) fn generate_enum_try_into_method( // } // } // ``` -pub(crate) fn generate_enum_as_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_enum_as_method( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { generate_enum_projection_method( acc, ctx, @@ -113,7 +116,7 @@ struct ProjectionProps { fn generate_enum_projection_method( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, assist_id: &'static str, assist_description: &str, props: ProjectionProps, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs index 9b4d44d8b5177..fb43e3eaa37e0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs @@ -32,7 +32,7 @@ use crate::assist_context::{AssistContext, Assists}; // let country = Countries::Lesotho; // } // ``` -pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let path: ast::Path = ctx.find_node_at_offset()?; let parent = PathParent::new(&path)?; @@ -104,7 +104,7 @@ impl PathParent { fn make_field_list( &self, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, ) -> Option { let scope = ctx.sema.scope(self.syntax())?; @@ -156,7 +156,7 @@ fn name_from_field_shorthand(field: &ast::RecordExprField) -> Option { } fn expr_ty( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, arg: ast::Expr, scope: &hir::SemanticsScope<'_>, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs index 55e5083811347..a9f5ab6976a1e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs @@ -36,7 +36,7 @@ use crate::{AssistContext, Assists}; // unsafe fn foo(n: i32) -> i32 { 42i32 } // ``` -pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let name = ctx.find_node_at_offset::()?; let func = &name.syntax().parent()?; let func_node = ast::Fn::cast(func.clone())?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index 76246c3e8efd0..52df6182ac56f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs @@ -27,7 +27,7 @@ use crate::{ // ``` pub(crate) fn generate_from_impl_for_enum( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let variant = ctx.find_node_at_offset::()?; let adt = ast::Adt::Enum(variant.parent_enum()); @@ -107,7 +107,10 @@ struct VariantInfo { ty: ast::Type, } -fn selected_variants(ctx: &AssistContext<'_>, variant: &ast::Variant) -> Option> { +fn selected_variants( + ctx: &AssistContext<'_, '_>, + variant: &ast::Variant, +) -> Option> { variant .parent_enum() .variant_list()? diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index baf795754f4ce..14dd4061e72f5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -53,11 +53,11 @@ use crate::{ // } // // ``` -pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { gen_fn(acc, ctx).or_else(|| gen_method(acc, ctx)) } -fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; let path = path_expr.path()?; @@ -104,7 +104,7 @@ impl TargetInfo { } fn fn_target_info( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, path: ast::Path, call: &CallExpr, fn_name: &str, @@ -134,7 +134,7 @@ fn fn_target_info( } } -fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let call: ast::MethodCallExpr = ctx.find_node_at_offset()?; if ctx.sema.resolve_method_call(&call).is_some() { return None; @@ -182,7 +182,7 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { fn add_func_to_accumulator( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, text_range: TextRange, function_builder: FunctionBuilder, file: FileId, @@ -204,7 +204,7 @@ fn add_func_to_accumulator( } fn get_adt_source( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, adt: &hir::Adt, fn_name: &str, ) -> Option<(Option, FileId)> { @@ -236,7 +236,7 @@ impl FunctionBuilder { /// The function is generated in `target_module` or next to `call` fn from_call( make: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, call: &ast::CallExpr, fn_name: &str, target_module: Option, @@ -307,7 +307,7 @@ impl FunctionBuilder { fn from_method_call( make: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, call: &ast::MethodCallExpr, name: &ast::NameRef, receiver_ty: Type<'_>, @@ -399,7 +399,7 @@ impl FunctionBuilder { /// user can change the `todo!` function body. fn make_return_type( make: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, expr: &ast::Expr, target_module: Module, necessary_generic_params: &mut FxHashSet, @@ -424,7 +424,7 @@ fn make_return_type( fn make_fn_body_as_new_function( make: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, fn_name: &str, adt_info: &Option, edition: Edition, @@ -475,7 +475,7 @@ fn make_fn_body_as_new_function( } fn get_fn_target_info( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, target_module: Option, call: CallExpr, ) -> Option { @@ -484,7 +484,7 @@ fn get_fn_target_info( } fn get_fn_target( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, target_module: Option, call: CallExpr, ) -> Option<(GeneratedFunctionTarget, FileId)> { @@ -501,7 +501,7 @@ fn get_fn_target( } fn get_method_target( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, impl_: &Option, adt: &Adt, ) -> Option { @@ -513,7 +513,7 @@ fn get_method_target( } fn assoc_fn_target_info( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, call: &CallExpr, adt: hir::Adt, fn_name: &str, @@ -558,7 +558,7 @@ impl GeneratedFunctionTarget { &self, edit: &mut SourceChangeBuilder, file: FileId, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, function_builder: &FunctionBuilder, adt: Adt, cap: Option, @@ -719,7 +719,7 @@ impl GeneratedFunctionTarget { fn insert_rendered_impl( editor: &SyntaxEditor, edit: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, function_builder: &FunctionBuilder, adt: Adt, position: Position, @@ -801,7 +801,7 @@ impl AdtInfo { /// Computes parameter list for the generated function. fn fn_args( make: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, target_module: Module, call: ast::CallableExpr, necessary_generic_params: &mut FxHashSet, @@ -837,7 +837,7 @@ fn fn_args( /// use the Rename functionality. fn fn_generic_params( make: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, necessary_params: FxHashSet, target: &GeneratedFunctionTarget, ) -> Option<(Option, Option)> { @@ -901,7 +901,7 @@ fn fn_generic_params( } fn params_and_where_preds_in_scope( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> (Vec, Vec) { let Some(body) = containing_body(ctx) else { return Default::default(); @@ -942,7 +942,7 @@ fn params_and_where_preds_in_scope( (generic_params, where_clauses) } -fn containing_body(ctx: &AssistContext<'_>) -> Option { +fn containing_body(ctx: &AssistContext<'_, '_>) -> Option { let item: ast::Item = ctx.find_node_at_offset()?; let def = match item { ast::Item::Fn(it) => ctx.sema.to_def(&it)?.into(), @@ -954,7 +954,7 @@ fn containing_body(ctx: &AssistContext<'_>) -> Option { } fn get_bounds_in_scope( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, def: D, ) -> (impl Iterator, impl Iterator) where @@ -1020,7 +1020,7 @@ struct WherePredWithParams { } fn compute_contained_params_in_generic_param( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, node: ast::GenericParam, ) -> Option { match &node { @@ -1049,7 +1049,7 @@ fn compute_contained_params_in_generic_param( } fn compute_contained_params_in_where_pred( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, node: ast::WherePred, ) -> Option { let self_ty = node.ty()?; @@ -1070,7 +1070,10 @@ fn compute_contained_params_in_where_pred( Some(WherePredWithParams { node, self_ty_params, other_params }) } -fn filter_generic_params(ctx: &AssistContext<'_>, node: SyntaxNode) -> Option { +fn filter_generic_params( + ctx: &AssistContext<'_, '_>, + node: SyntaxNode, +) -> Option { let path = ast::Path::cast(node)?; match ctx.sema.resolve_path(&path)? { PathResolution::TypeParam(it) => Some(it.into()), @@ -1165,7 +1168,7 @@ fn filter_unnecessary_bounds( fn filter_bounds_in_scope( generic_params: &mut Vec, where_preds: &mut Vec, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, target: &GeneratedFunctionTarget, ) -> Option<()> { let target_impl = target.parent().ancestors().find_map(ast::Impl::cast)?; @@ -1248,13 +1251,13 @@ fn fn_arg_name(sema: &Semantics<'_, RootDatabase>, arg_expr: &ast::Expr) -> Stri } fn fn_arg_type( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, target_module: Module, fn_arg: &ast::Expr, generic_params: &mut FxHashSet, ) -> String { fn maybe_displayed_type( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, target_module: Module, fn_arg: &ast::Expr, generic_params: &mut FxHashSet, @@ -1347,7 +1350,7 @@ enum Visibility { fn calculate_necessary_visibility( current_module: Module, target_module: Module, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Visibility { let db = ctx.db(); let current_module = current_module.nearest_non_block_module(db); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index b884581041f45..c8ab54474c750 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs @@ -35,7 +35,7 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { // This if condition denotes two modes this assist can work in: // - First is acting upon selection of record fields // - Next is acting upon a single record field @@ -124,7 +124,7 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt // } // } // ``` -pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { generate_getter_impl(acc, ctx, false) } @@ -149,7 +149,7 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt // } // } // ``` -pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { generate_getter_impl(acc, ctx, true) } @@ -175,7 +175,7 @@ enum AssistType { pub(crate) fn generate_getter_impl( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, mutable: bool, ) -> Option<()> { let (strukt, info_of_record_fields, fn_names) = @@ -215,7 +215,7 @@ pub(crate) fn generate_getter_impl( } fn generate_getter_from_info( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, info: &AssistInfo, record_field_info: &RecordFieldInfo, make: &SyntaxFactory, @@ -329,7 +329,7 @@ fn generate_setter_from_info( } fn extract_and_parse( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, assist_type: AssistType, ) -> Option<(ast::Struct, Vec, Vec)> { // This if condition denotes two modes assists can work in: @@ -411,7 +411,7 @@ fn parse_record_field( } fn items( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, info_of_record_fields: Vec, assist_info: &AssistInfo, make: &SyntaxFactory, @@ -432,7 +432,7 @@ fn items( fn build_source_change( builder: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, info_of_record_fields: Vec, assist_info: AssistInfo, ) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index 27a1eb643978a..ab0eb56fcf19d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -45,7 +45,7 @@ fn insert_impl(editor: &SyntaxEditor, impl_: &ast::Impl, nominal: &impl AstNodeE // // impl Ctx {$0} // ``` -pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let nominal = ctx.find_node_at_offset::()?; let name = nominal.name()?; let target = nominal.syntax().text_range(); @@ -93,7 +93,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio // // impl ${1:_} for Ctx {$0} // ``` -pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let nominal = ctx.find_node_at_offset::()?; let name = nominal.name()?; let target = nominal.syntax().text_range(); @@ -149,7 +149,7 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> // } // } // ``` -pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let name = ctx.find_node_at_offset::()?; let trait_ = ast::Trait::cast(name.syntax().parent()?)?; let target_scope = ctx.sema.scope(trait_.syntax())?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs index f10b21b13ecf5..39f304cac9ca3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs @@ -39,7 +39,10 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_is_empty_from_len( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let fn_node = ctx.find_node_at_offset::()?; let fn_name = fn_node.name()?; @@ -86,7 +89,7 @@ pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext< } fn get_impl_method( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, impl_: &ast::Impl, fn_name: &Name, ) -> Option { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index acf0819222d53..fd095dd9b2aab 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -44,7 +44,10 @@ use crate::{AssistContext, AssistId, Assists}; // } // } // ``` -pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_mut_trait_impl( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let impl_def = ctx.find_node_at_offset::()?; let indent = impl_def.indent_level(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 756dc3d0fa073..40adfceaae145 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -33,7 +33,7 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let strukt = ctx.find_node_at_offset::()?; let field_list: Vec<(String, ast::Type)> = match strukt.kind() { StructKind::Record(named) => { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 265a632a8cb9b..23c7b2b7c8cbf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -47,7 +47,7 @@ use crate::{ // ``` pub(crate) fn generate_single_field_struct_from( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let strukt_name = ctx.find_node_at_offset::()?; let adt = ast::Adt::cast(strukt_name.syntax().parent()?)?; @@ -179,7 +179,7 @@ fn make_adt_constructor( } fn make_constructors( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, module: hir::Module, types: &[ast::Type], ) -> Vec> { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index 049398de8c559..d5f0eb234cf7b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -68,7 +68,10 @@ use syntax::{ // const_maker! {i32, 7} // } // ``` -pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_trait_from_impl( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { // Get AST Node let impl_ast = ctx.find_node_at_offset::()?; 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 c6a0b22d478bf..d95456fd3fb58 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 @@ -69,7 +69,7 @@ use crate::{ // }; // } // ``` -pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let def_file = ctx.file_id(); let vfs_def_file = ctx.vfs_file_id(); let name = ctx.find_node_at_offset::()?; @@ -219,7 +219,7 @@ pub(super) fn split_refs_and_uses( // }; // } // ``` -pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let name_ref: ast::NameRef = ctx.find_node_at_offset()?; let call_info = CallInfo::from_name_ref( name_ref.clone(), @@ -350,7 +350,7 @@ fn inline( cov_mark::hit!(inline_call_defined_in_macro); let span_map = sema.db.expansion_span_map(macro_file); let body_prettified = - prettify_macro_expansion(sema.db, fn_body.syntax().clone(), &span_map, *krate); + 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() } } else { fn_body.clone() @@ -496,7 +496,7 @@ fn inline( let param_ty_prettified = prettify_macro_expansion( sema.db, param_ty.syntax().clone(), - &span_map, + span_map, *krate, ); ast::Type::cast(param_ty_prettified).unwrap_or(param_ty) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs index b11d3792bc4c7..46cedea967810 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs @@ -22,7 +22,10 @@ use crate::{AssistContext, AssistId, Assists}; // "Hello, World!" // } // ``` -pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn inline_const_as_literal( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let variable = ctx.find_node_at_offset::()?; if let hir::PathResolution::Def(hir::ModuleDef::Const(konst)) = @@ -57,7 +60,7 @@ pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_> } fn validate_type_recursively( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ty_hir: Option<&hir::Type<'_>>, refed: bool, fuel: i32, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs index bf9bc394b399c..531adf62ba3f5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -32,7 +32,7 @@ use crate::{ // (1 + 2) * 4; // } // ``` -pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let file_id = ctx.file_id(); let range = ctx.selection_trimmed(); let InlineData { let_stmt, delete_let, references, target } = 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 ef9b5d093f364..002791b88df64 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 @@ -35,7 +35,7 @@ use crate::{AssistContext, AssistId, Assists}; // println!("{number}"); // } // ``` -pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let unexpanded = ctx.find_node_at_offset::()?; let macro_call = ctx.sema.to_def(&unexpanded)?; let target_crate_id = ctx.sema.file_to_module_def(ctx.vfs_file_id())?.krate(ctx.db()).into(); @@ -51,7 +51,7 @@ pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let span_map = ctx.sema.db.expansion_span_map(macro_call); // 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); + let expanded = prettify_macro_expansion(ctx.db(), expanded, span_map, target_crate_id); let expanded = ast::edit::indent(&expanded, unexpanded.indent_level()); editor.replace(unexpanded.syntax(), expanded); builder.add_file_edits(ctx.vfs_file_id(), editor); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index 9c7d266057d87..e4a1314f5bb29 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -45,7 +45,7 @@ use super::inline_call::split_refs_and_uses; // let _: i32 = 3; // } // ``` -pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let name = ctx.find_node_at_offset::()?; let ast_alias = name.syntax().parent().and_then(ast::TypeAlias::cast)?; @@ -125,7 +125,7 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) // let a: Vec; // } // ``` -pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let alias_instance = ctx.find_node_at_offset::()?; let concrete_type; let replacement; @@ -412,7 +412,7 @@ fn create_replacement( editor.finish().new_root().clone() } -fn get_type_alias(ctx: &AssistContext<'_>, path: &ast::PathType) -> Option { +fn get_type_alias(ctx: &AssistContext<'_, '_>, path: &ast::PathType) -> Option { let resolved_path = ctx.sema.resolve_path(&path.path()?)?; // We need the generics in the correct order to be able to map any provided diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs index 47b273535a88f..9f8b08f4f80b6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs @@ -36,7 +36,7 @@ use crate::assist_context::{AssistContext, Assists}; // let b: B = B::from(a); // } // ``` -pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?; let nameref = method_call.name_ref()?; let receiver = method_call.receiver()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs index 2cbeae1d19c25..986989952f676 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs @@ -34,7 +34,10 @@ static ASSIST_LABEL: &str = "Introduce named lifetime"; // } // } // ``` -pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn introduce_named_lifetime( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { // FIXME: How can we handle renaming any one of multiple anonymous lifetimes? // FIXME: should also add support for the case fun(f: &Foo) -> &$0Foo let lifetime = diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs index c8ae13a83328c..06023476da334 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs @@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn introduce_named_type_parameter( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let impl_trait_type = ctx.find_node_at_offset::()?; let param = impl_trait_type.syntax().ancestors().find_map(ast::Param::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs index c8cb7bb60f69c..9dda4bbb966b6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs @@ -26,7 +26,7 @@ use crate::{ // if y { B } else { A } // } // ``` -pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let if_keyword = ctx .find_token_syntax_at_offset(T![if]) .or_else(|| ctx.find_token_syntax_at_offset(T![else]))?; 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 1dd0833fad03d..dc40a6a640e00 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 @@ -27,7 +27,7 @@ use Edit::*; // ``` // use std::{fmt::Formatter, io}; // ``` -pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (target, edits) = if ctx.has_empty_selection() { // Merge a neighbor cov_mark::hit!(merge_with_use_item_neighbors); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs index 6e84af5f91299..f41769150c042 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs @@ -33,7 +33,7 @@ use crate::{AssistContext, AssistId, Assists, TextRange}; // } // } // ``` -pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let current_arm = ctx.find_node_at_trimmed_offset::()?; // Don't try to handle arms with guards for now - can add support for this later if current_arm.guard().is_some() { @@ -107,7 +107,7 @@ fn contains_placeholder(a: &ast::MatchArm) -> bool { fn are_same_types( current_arm_types: &FxHashMap>>, arm: &ast::MatchArm, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> bool { let arm_types = get_arm_types(ctx, arm); for (other_arm_type_name, other_arm_type) in arm_types { @@ -122,14 +122,14 @@ fn are_same_types( } fn get_arm_types<'db>( - context: &AssistContext<'db>, + context: &AssistContext<'_, 'db>, arm: &ast::MatchArm, ) -> FxHashMap>> { let mut mapping: FxHashMap>> = FxHashMap::default(); fn recurse<'db>( map: &mut FxHashMap>>, - ctx: &AssistContext<'db>, + ctx: &AssistContext<'_, 'db>, pat: &Option, ) { if let Some(local_pat) = pat { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_nested_if.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_nested_if.rs index e491c043e1c11..bb3725a13388a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_nested_if.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_nested_if.rs @@ -23,7 +23,7 @@ use crate::{ // if x == 3 && y == 4 { 1 } // } // ``` -pub(crate) fn merge_nested_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn merge_nested_if(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let if_keyword = ctx.find_token_syntax_at_offset(T![if])?; let expr = ast::IfExpr::cast(if_keyword.parent()?)?; let if_range = if_keyword.text_range(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs index e044068ff7578..3efc847141c70 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs @@ -24,7 +24,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn move_bounds_to_where_clause( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let type_param_list = ctx.find_node_at_offset::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs index 86bdf3f8b4eb7..4071f16a9fb4e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs @@ -42,7 +42,7 @@ use crate::assist_context::{AssistContext, Assists}; // } // } // ``` -pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let db = ctx.db(); let const_: ast::Const = ctx.find_node_at_offset()?; // Don't show the assist when the cursor is at the const's body. diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs index a36d3136a16da..21a8c53e8c9bc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs @@ -20,7 +20,7 @@ use crate::{ // ``` // fn t() {} // ``` -pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let source_file = ctx.find_node_at_offset::()?; let module = ctx.sema.file_to_module_def(ctx.vfs_file_id())?; // Enable this assist if the user select all "meaningful" content in the source file diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs index 7309cc6d06a5c..7cd0d01238470 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs @@ -38,7 +38,7 @@ use crate::{AssistContext, AssistId, Assists}; // } // } // ``` -pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let match_arm = ctx.find_node_at_offset::()?; let guard = match_arm.guard()?; if ctx.offset() > guard.syntax().text_range().end() { @@ -122,7 +122,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) // ``` pub(crate) fn move_arm_cond_to_match_guard( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let match_arm: MatchArm = ctx.find_node_at_offset::()?; let match_pat = match_arm.pat()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs index 503003bc6bea9..3fec102275355 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs @@ -24,7 +24,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` // mod foo; // ``` -pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let module_ast = ctx.find_node_at_offset::()?; let module_items = module_ast.item_list()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs index 5e95b264fc8e4..7407bd1a0c299 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs @@ -20,7 +20,7 @@ use crate::{ // ``` // fn t() {} // ``` -pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let source_file = ctx.find_node_at_offset::()?; let module = ctx.sema.file_to_module_def(ctx.vfs_file_id())?; // Enable this assist if the user select all "meaningful" content in the source file diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs index 36da1d1788247..f97a3e583f030 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs @@ -17,7 +17,7 @@ use crate::{ // ``` // use std::{fmt::Formatter, io}; // ``` -pub(crate) fn normalize_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn normalize_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let use_item = if ctx.has_empty_selection() { ctx.find_node_at_offset()? } else { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs index fac81aefe0279..a50f443d5d58f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs @@ -15,7 +15,10 @@ const MIN_NUMBER_OF_DIGITS_TO_FORMAT: usize = 5; // ``` // const _: i32 = 1_012_345; // ``` -pub(crate) fn reformat_number_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn reformat_number_literal( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let literal = ctx.find_node_at_offset::()?; let literal = match literal.kind() { ast::LiteralKind::IntNumber(it) => it, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs index ed61d32eb657f..23c88e65d69e1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs @@ -39,7 +39,7 @@ use crate::{ // } // } // ``` -pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let pat = ctx.find_node_at_offset::()?; let name = pat.name()?; if !pat.is_simple_ident() { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs index 082052c9d42dc..4024d51ffc79d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs @@ -33,7 +33,7 @@ use crate::{ // }; // } // ``` -pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let assign_expr = ctx.find_node_at_offset::()?; let op_kind = assign_expr.op_kind()?; @@ -115,13 +115,13 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) -> ) } -struct AssignmentsCollector<'a> { - sema: &'a hir::Semantics<'a, ide_db::RootDatabase>, +struct AssignmentsCollector<'a, 'db> { + sema: &'a hir::Semantics<'db, ide_db::RootDatabase>, common_lhs: ast::Expr, assignments: Vec<(ast::BinExpr, ast::Expr)>, } -impl AssignmentsCollector<'_> { +impl AssignmentsCollector<'_, '_> { fn collect_match(&mut self, match_expr: &ast::MatchExpr) -> Option<()> { for arm in match_expr.match_arm_list()?.arms() { match arm.expr()? { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs index d7885d50651c0..edf0a855c4fe9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -32,7 +32,7 @@ use crate::{ // Foo::foo(&foo); // } // ``` -pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let name: ast::NameRef = ctx.find_node_at_offset()?; let call = name.syntax().parent().and_then(ast::MethodCallExpr::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index 18a31a2b42c10..cb48554083ab4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -34,7 +34,7 @@ use crate::{ // } // # pub mod std { pub mod collections { pub struct HashMap { } } } // ``` -pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (import_assets, syntax_under_caret, expected) = find_importable_node(ctx)?; let cfg = ctx.config.import_path_config(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs index 8234a0374e777..0a745157390f5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs @@ -24,7 +24,7 @@ use crate::{ // r#"Hello, World!"#; // } // ``` -pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let token = ctx.find_token_at_offset::()?; if token.is_raw() { return None; @@ -60,7 +60,7 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt // "Hello, \"World!\""; // } // ``` -pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let token = ctx.find_token_at_offset::()?; if !token.is_raw() { return None; @@ -97,7 +97,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O // r##"Hello, World!"##; // } // ``` -pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let token = ctx.find_token_at_offset::()?; if !token.is_raw() { return None; @@ -128,7 +128,7 @@ pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> // r"Hello, World!"; // } // ``` -pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let token = ctx.find_token_at_offset::()?; if !token.is_raw() { return None; @@ -160,7 +160,7 @@ fn replace_literal( token: &impl AstToken, new: &str, builder: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) { let old_token = token.syntax(); let parent = old_token.parent().expect("no parent token"); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs index 778533be5a005..91977c2b7fa2d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs @@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists}; // let x = 42 * (4 + 2); // } // ``` -pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); let make = editor.make(); let macro_calls = if ctx.has_empty_selection() { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_else_branches.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_else_branches.rs index 0c03856417a0a..a40d691e4cc34 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_else_branches.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_else_branches.rs @@ -35,7 +35,7 @@ use crate::{AssistContext, AssistId, Assists}; // let _x = 2; // } // ``` -pub(crate) fn remove_else_branches(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_else_branches(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let else_token = ctx.find_token_syntax_at_offset(T![else])?; let else_branches = ctx .find_node_at_range::() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_mut.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_mut.rs index 2a6024339f605..db379809c5c8d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_mut.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_mut.rs @@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, Assists}; // fn feed(&self, amount: u32) {} // } // ``` -pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let mut_token = ctx.find_token_syntax_at_offset(T![mut])?; let target = mut_token.text_range(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs index af249c97b9c42..d6606d181ebbc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs @@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, Assists}; // _ = 2 + 2; // } // ``` -pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let parens = ctx.find_node_at_offset::()?; let cursor_in_range = diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_underscore.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_underscore.rs index 1de1c15cf720b..efe5c945bee52 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_underscore.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_underscore.rs @@ -24,7 +24,7 @@ use crate::{AssistContext, Assists}; // foo = 2; // } // ``` -pub(crate) fn remove_underscore(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_underscore(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (text, text_range, def) = if let Some(name_ref) = ctx.find_node_at_offset::() { let text = name_ref.text(); if !text.starts_with('_') { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs index c38bdfdccf5be..c90623ceed26f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs @@ -33,7 +33,7 @@ use crate::{AssistContext, AssistId, Assists}; // mod foo { // } // ``` -pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { // First, grab the uses that intersect with the current selection. let selected_el = match ctx.covering_element() { syntax::NodeOrToken::Node(n) => n, @@ -132,7 +132,7 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) } fn is_path_per_ns_unused_in_scope( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, u: &ast::UseTree, scope: &mut Vec, path: &PathResolutionPerNs, @@ -151,7 +151,7 @@ fn is_path_per_ns_unused_in_scope( } fn is_path_unused_in_scope( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, u: &ast::UseTree, scope: &mut Vec, path: &[Option], @@ -167,7 +167,7 @@ fn is_path_unused_in_scope( } fn is_trait_unused_in_scope( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, u: &ast::UseTree, scope: &mut Vec, t: &hir::Trait, @@ -178,7 +178,7 @@ fn is_trait_unused_in_scope( } fn used_once_in_scope( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, def: Definition, rename: Option, scopes: &Vec, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs index b91d678c9371f..d7b0b5ea01f27 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs @@ -31,7 +31,7 @@ use crate::{ // frobnicate(); // } // ``` -pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let param: ast::Param = ctx.find_node_at_offset()?; let ident_pat = match param.pat()? { ast::Pat::IdentPat(it) => it, @@ -94,7 +94,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> } fn process_usages( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, editioned_file_id: EditionedFileId, references: Vec, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs index facbab8019b2a..9c9224b99704c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs @@ -19,7 +19,7 @@ use crate::{AssistContext, AssistId, Assists}; // struct Foo {foo: i32, bar: i32}; // const test: Foo = Foo {foo: 1, bar: 0} // ``` -pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let path = ctx.find_node_at_offset::()?; let record = path.syntax().parent().and_then(>::cast)?; @@ -97,7 +97,7 @@ fn replace( fn compute_fields_ranks( path: &ast::Path, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option> { let strukt = match ctx.sema.resolve_path(path) { Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Struct(it)))) => it, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs index df5281895abdc..658947abe135f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs @@ -42,7 +42,7 @@ use crate::{AssistContext, AssistId, Assists}; // fn c() {} // } // ``` -pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let impl_ast = ctx.find_node_at_offset::()?; let items = impl_ast.assoc_item_list()?; @@ -113,7 +113,7 @@ pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> fn compute_item_ranks( path: &ast::Path, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option> { let td = trait_definition(path, &ctx.sema)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs index a04f6f38336a9..9fadf1333f7ae 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs @@ -24,7 +24,10 @@ use crate::{ // let x = 1.checked_add(2); // } // ``` -pub(crate) fn replace_arith_with_checked(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_arith_with_checked( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { replace_arith(acc, ctx, ArithKind::Checked) } @@ -45,7 +48,7 @@ pub(crate) fn replace_arith_with_checked(acc: &mut Assists, ctx: &AssistContext< // ``` pub(crate) fn replace_arith_with_saturating( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { replace_arith(acc, ctx, ArithKind::Saturating) } @@ -67,12 +70,12 @@ pub(crate) fn replace_arith_with_saturating( // ``` pub(crate) fn replace_arith_with_wrapping( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { replace_arith(acc, ctx, ArithKind::Wrapping) } -fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) -> Option<()> { +fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_, '_>, kind: ArithKind) -> Option<()> { let (lhs, op, is_assign, rhs) = parse_binary_op(ctx)?; let op_expr = lhs.syntax().parent()?; @@ -103,7 +106,7 @@ fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) -> ) } -fn is_primitive_int(ctx: &AssistContext<'_>, expr: &ast::Expr) -> bool { +fn is_primitive_int(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> bool { match ctx.sema.type_of_expr(expr) { Some(ty) => ty.adjusted().is_int_or_uint(), _ => false, @@ -111,7 +114,7 @@ fn is_primitive_int(ctx: &AssistContext<'_>, expr: &ast::Expr) -> bool { } /// Extract the operands of an arithmetic expression (e.g. `1 + 2` or `1.checked_add(2)`) -fn parse_binary_op(ctx: &AssistContext<'_>) -> Option<(ast::Expr, ArithOp, bool, ast::Expr)> { +fn parse_binary_op(ctx: &AssistContext<'_, '_>) -> Option<(ast::Expr, ArithOp, bool, ast::Expr)> { if !ctx.has_empty_selection() { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index da02d9db404dd..4e85b30b58188 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -44,7 +44,7 @@ use crate::{ // ``` pub(crate) fn replace_derive_with_manual_impl( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let attr = ctx.find_node_at_offset_with_descend::()?; let path = attr.path()?; @@ -128,7 +128,7 @@ pub(crate) fn replace_derive_with_manual_impl( fn add_assist( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, attr: &ast::Attr, old_derives: &[ast::Path], old_tree: &ast::TokenTree, 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 a47e3a4086794..aa3f917f124df 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 @@ -47,7 +47,10 @@ use crate::{ // } // } // ``` -pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_if_let_with_match( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; let available_range = TextRange::new( if_expr.syntax().text_range().start(), @@ -160,7 +163,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' } fn make_else_arm( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, else_expr: Option, conditionals: &[(Option, Option, ast::BlockExpr)], @@ -223,7 +226,10 @@ fn make_else_arm( // } // } // ``` -pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_match_with_if_let( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let match_expr: ast::MatchExpr = ctx.find_node_at_offset()?; let match_arm_list = match_expr.match_arm_list()?; let available_range = TextRange::new( @@ -397,7 +403,7 @@ fn is_sad_pat(sema: &hir::Semantics<'_, RootDatabase>, pat: &ast::Pat) -> bool { fn let_and_guard( cond: &ast::Expr, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<(Option<(ast::Pat, ast::Expr)>, Option)> { if let ast::Expr::ParenExpr(expr) = cond && let Some(sub_expr) = expr.expr() @@ -454,7 +460,7 @@ fn and_bin_expr_left(expr: &ast::BinExpr) -> ast::BinExpr { fn parse_matches_macro( expr: &ast::Expr, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<(ast::Pat, ast::Expr, Option)> { let ast::Expr::MacroExpr(macro_expr) = expr else { return None }; let macro_call = macro_expr.macro_call()?; @@ -504,7 +510,7 @@ fn pretty_pat_inside_macro( let pretty_node = hir::prettify_macro_expansion( db, pat, - &db.expansion_span_map(file_id), + db.expansion_span_map(file_id), scope.module().krate(db).into(), ); ast::Pat::cast(pretty_node) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index 802d5f72b9973..f50614a66fbd6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -29,7 +29,7 @@ use crate::{ // ``` pub(crate) fn replace_is_method_with_if_let_method( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let has_cond = ctx.find_node_at_offset::>()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs index 85e72130e0a6c..29688d9b9704d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs @@ -34,7 +34,10 @@ use crate::{AssistContext, AssistId, Assists}; // // fn compute() -> Option { None } // ``` -pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_let_with_if_let( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let let_kw = ctx.find_token_syntax_at_offset(T![let])?; let let_stmt = let_kw.parent().and_then(ast::LetStmt::cast)?; let init = let_stmt.initializer()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 6e4dd8cb73a6b..7aa9a82109c1e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -25,7 +25,10 @@ use crate::{AssistContext, Assists, utils::wrap_paren_in_call}; // a.unwrap_or_else(|| 2); // } // ``` -pub(crate) fn replace_with_lazy_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_with_lazy_method( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let call: ast::MethodCallExpr = ctx.find_node_at_offset()?; let scope = ctx.sema.scope(call.syntax())?; @@ -113,7 +116,10 @@ fn into_closure(param: &Expr, name_lazy: &str) -> Expr { // a.unwrap_or(2); // } // ``` -pub(crate) fn replace_with_eager_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_with_eager_method( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let call: ast::MethodCallExpr = ctx.find_node_at_offset()?; let scope = ctx.sema.scope(call.syntax())?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index 43726830161e7..979f832978632 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -26,7 +26,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn replace_named_generic_with_impl( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { // finds `>` let type_param = ctx.find_node_at_offset::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index 92fd11918474b..0bd1ec12d06c2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -29,7 +29,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn replace_qualified_name_with_use( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let original_path: ast::Path = ctx.find_node_at_offset()?; // We don't want to mess with use statements @@ -95,7 +95,7 @@ pub(crate) fn replace_qualified_name_with_use( ) } -fn target_path(ctx: &AssistContext<'_>, mut original_path: ast::Path) -> Option { +fn target_path(ctx: &AssistContext<'_, '_>, mut original_path: ast::Path) -> Option { let on_first = original_path.qualifier().is_none(); if on_first { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs index fb5b234d55987..df22c472a4e08 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs @@ -22,7 +22,10 @@ use crate::{AssistContext, AssistId, Assists, utils::string_suffix}; // find('{'); // } // ``` -pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_string_with_char( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let token = ctx.find_token_syntax_at_offset(STRING).and_then(ast::String::cast)?; let value = token.value().ok()?; let target = token.syntax().text_range(); @@ -64,7 +67,10 @@ pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext<'_ // find("{"); // } // ``` -pub(crate) fn replace_char_with_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_char_with_string( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let token = ctx.find_token_syntax_at_offset(CHAR)?; let target = token.text_range(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs index a692259410dc0..5e336523b48e9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs @@ -28,7 +28,7 @@ use crate::{ // ``` pub(crate) fn replace_turbofish_with_explicit_type( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let let_stmt = ctx.find_node_at_offset::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs index 911fa9d14b591..49b3cfb908287 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs @@ -81,7 +81,7 @@ use crate::{AssistContext, AssistId, Assists, utils::get_methods}; // Cat { name: String, weight: f64 }, // } // ``` -pub(crate) fn sort_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn sort_items(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { if ctx.has_empty_selection() { cov_mark::hit!(not_applicable_if_no_selection); return None; @@ -150,7 +150,7 @@ fn add_sort_field_list_assist(acc: &mut Assists, field_list: Option, + ctx: &AssistContext<'_, '_>, item_list: ast::AssocItemList, ) -> Option<()> { let selection = ctx.selection_trimmed(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/split_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/split_import.rs index 1729a0667c0a2..96f4f8e4471f6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/split_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/split_import.rs @@ -13,7 +13,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` // use std::{collections::HashMap}; // ``` -pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let colon_colon = ctx.find_token_syntax_at_offset(T![::])?; let path = ast::Path::cast(colon_colon.parent()?)?.qualifier()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs index 849dfa49ddb01..5642f10a6dbec 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs @@ -10,7 +10,7 @@ use syntax::{AstNode, ast}; use crate::assist_context::{AssistContext, Assists}; -pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let unexpanded = ctx.find_node_at_offset::()?; let syntax = unexpanded.syntax(); let goal_range = syntax.text_range(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs index fa6ccb9a5f128..99be89ece1640 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs @@ -27,7 +27,7 @@ use crate::{AssistContext, Assists}; // ``` pub(crate) fn sugar_impl_future_into_async( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let ret_type: ast::RetType = ctx.find_node_at_offset()?; let function = ret_type.syntax().parent().and_then(ast::Fn::cast)?; @@ -117,7 +117,7 @@ pub(crate) fn sugar_impl_future_into_async( // ``` pub(crate) fn desugar_async_into_impl_future( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let async_token = ctx.find_token_syntax_at_offset(SyntaxKind::ASYNC_KW)?; let function = async_token.parent().and_then(ast::Fn::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_ignore.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_ignore.rs index a088fb178d257..35c304b3870c3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_ignore.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_ignore.rs @@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists, utils::test_related_attribute_syn} // assert_eq!(2 + 2, 5); // } // ``` -pub(crate) fn toggle_ignore(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn toggle_ignore(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let attr: ast::Attr = ctx.find_node_at_offset()?; let func = attr.syntax().parent().and_then(ast::Fn::cast)?; let attr = test_related_attribute_syn(&func)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index 4d375080f50e0..1dec094d9a367 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs @@ -26,7 +26,7 @@ use crate::{AssistContext, Assists}; // // sth!{ } // ``` -pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { #[derive(Debug)] enum MacroDelims { LPar, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs index ab6317ad446d6..ec5c0929b4c37 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs @@ -21,7 +21,7 @@ use crate::{ // use std::fmt::{Debug}; // use std::fmt::Display; // ``` -pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); let make = editor.make(); let tree = ctx.find_node_at_offset::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs index 65300ccefdb91..555d3d2ee9cd7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs @@ -30,7 +30,7 @@ use crate::{AssistContext, AssistId, Assists}; // } // } // ``` -pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let pipe_token = ctx.find_token_syntax_at_offset(T![|])?; let or_pat = ast::OrPat::cast(pipe_token.parent()?)?; if or_pat.leading_pipe().is_some_and(|it| it == pipe_token) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs index b9385775b4765..5542180362e16 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs @@ -28,7 +28,7 @@ use crate::{AssistContext, Assists}; // pub fn foo() {} // pub async fn bar() { foo() } // ``` -pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let function: ast::Fn = ctx.find_node_at_offset()?; // Do nothing if the cursor isn't on the async token. @@ -91,7 +91,7 @@ pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_>) -> O } fn find_all_references( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, def: &Definition, ) -> impl Iterator { def.usages(&ctx.sema).all().into_iter().flat_map(|(file_id, references)| { @@ -101,7 +101,7 @@ fn find_all_references( /// Finds the await expression for the given `NameRef`. /// If no await expression is found, returns None. -fn find_await_expression(ctx: &AssistContext<'_>, nameref: &NameRef) -> Option { +fn find_await_expression(ctx: &AssistContext<'_, '_>, nameref: &NameRef) -> Option { // From the nameref, walk up the tree to the await expression. let await_expr = if let Some(path) = full_path_of_name_ref(nameref) { // Function calls. diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs index 045a27295297e..242ebc4063d0d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs @@ -22,7 +22,7 @@ use crate::{AssistContext, AssistId, Assists}; // } // # mod std { pub mod ops { pub trait Add { fn add(self, _: Self) {} } impl Add for i32 {} } } // ``` -pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let call = ctx.find_node_at_offset::()?; let ast::Expr::PathExpr(path_expr) = call.expr()? else { return None }; let path = path_expr.path()?; @@ -77,7 +77,7 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) fn add_import( qualifier: ast::Path, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, editor: &syntax::syntax_editor::SyntaxEditor, ) { if let Some(path_segment) = qualifier.segment() { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs index dba8c0336782c..a018451f61da5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs @@ -29,7 +29,7 @@ use crate::{AssistContext, AssistId, Assists}; // println!("foo"); // } // ``` -pub(crate) fn unwrap_branch(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unwrap_branch(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); let place = unwrap_branch_place(ctx)?; let target = place.syntax().text_range(); @@ -115,7 +115,7 @@ pub(crate) fn unwrap_branch(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio // } // } // ``` -pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?; let target = block.syntax().text_range(); @@ -182,7 +182,7 @@ fn wrap_let(assign: &ast::LetStmt, replacement: ast::BlockExpr) -> ast::BlockExp try_wrap_assign().unwrap_or(replacement) } -fn unwrap_branch_place(ctx: &AssistContext<'_>) -> Option { +fn unwrap_branch_place(ctx: &AssistContext<'_, '_>) -> Option { if let Some(l_curly_token) = ctx.find_token_syntax_at_offset(T!['{']) { let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?; Some(block.into()) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs index 1fe9ea4eb8759..608c68ea85a6d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs @@ -37,7 +37,7 @@ use crate::{AssistContext, AssistId, Assists}; // fn foo() -> i32 { 42i32 } // ``` -pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let ret_type = ctx.find_node_at_offset::()?; let parent = ret_type.syntax().parent()?; let body_expr = match_ast! { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs index e03274bbb3c9e..7d0205afe359c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs @@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists}; // let bar = "Bar"; // } // ``` -pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let let_kw = ctx.find_token_syntax_at_offset(T![let])?; let let_stmt = let_kw.parent().and_then(Either::::cast)?; let mut indent_level = let_stmt.indent_level(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs index 935ae18905449..0c8e40bca1de7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs @@ -21,7 +21,10 @@ use crate::{AssistContext, Assists}; // todo!() // } // ``` -pub(crate) fn unwrap_type_to_generic_arg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unwrap_type_to_generic_arg( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let path_type = ctx.find_node_at_offset::()?; let path = path_type.path()?; let segment = path.segment()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs index ddc0af31c33f2..f9c103aab8f1f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs @@ -40,7 +40,7 @@ use crate::{AssistContext, AssistId, Assists}; // fn foo() -> Result { Ok(42i32) } // ``` -pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let ret_type = ctx.find_node_at_offset::()?; let parent = ret_type.syntax().parent()?; let body_expr = match_ast! { @@ -212,7 +212,7 @@ impl WrapperKind { // Try to find an wrapper type alias in the current scope (shadowing the default). fn wrapper_alias<'db>( - ctx: &AssistContext<'db>, + ctx: &AssistContext<'_, 'db>, make: &SyntaxFactory, core_wrapper: hir::Enum, ast_ret_type: &ast::Type, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs index 635fab857d087..94bd29049d0fa 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs @@ -106,7 +106,7 @@ fn attempt_get_derive(attr: ast::Attr, ident: SyntaxToken) -> WrapUnwrapOption { attempt_attr().unwrap_or_else(|| WrapUnwrapOption::WrapAttr(vec![attr])) } } -pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let option = if ctx.has_empty_selection() { let ident = ctx.find_token_syntax_at_offset(T![ident]); let attr = ctx.find_node_at_offset::(); @@ -161,7 +161,7 @@ pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) - fn wrap_derive( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, attr: ast::Attr, derive_element: TextRange, ) -> Option<()> { @@ -232,7 +232,11 @@ fn wrap_derive( Some(()) } -fn wrap_cfg_attrs(acc: &mut Assists, ctx: &AssistContext<'_>, attrs: Vec) -> Option<()> { +fn wrap_cfg_attrs( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, + attrs: Vec, +) -> Option<()> { let (first_attr, last_attr) = (attrs.first()?, attrs.last()?); let range = first_attr.syntax().text_range().cover(last_attr.syntax().text_range()); let handle_source_change = |edit: &mut SourceChangeBuilder| { @@ -268,7 +272,7 @@ fn wrap_cfg_attrs(acc: &mut Assists, ctx: &AssistContext<'_>, attrs: Vec, + ctx: &AssistContext<'_, '_>, meta: ast::CfgAttrMeta, ) -> Option<()> { let top_attr = ast::Meta::from(meta.clone()).parent_attr()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 18ff63470d352..eabcb8093e228 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -102,7 +102,7 @@ pub fn assists( mod handlers { use crate::{AssistContext, Assists}; - pub(crate) type Handler = fn(&mut Assists, &AssistContext<'_>) -> Option<()>; + pub(crate) type Handler = fn(&mut Assists, &AssistContext<'_, '_>) -> Option<()>; mod add_braces; mod add_explicit_dot_deref; 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 8efc1bb230f0a..9d3af35ee5942 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -222,7 +222,7 @@ pub fn add_trait_assoc_items_to_impl( let item_prettified = prettify_macro_expansion( sema.db, original_item.syntax().clone(), - &span_map, + span_map, target_scope.krate().into(), ); if let Some(formatted) = ast::AssocItem::cast(item_prettified) { @@ -382,18 +382,21 @@ pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool { pat_head == var_head } -pub(crate) fn does_pat_variant_nested_or_literal(ctx: &AssistContext<'_>, pat: &ast::Pat) -> bool { +pub(crate) fn does_pat_variant_nested_or_literal( + ctx: &AssistContext<'_, '_>, + pat: &ast::Pat, +) -> bool { check_pat_variant_nested_or_literal_with_depth(ctx, pat, 0) } -fn check_pat_variant_from_enum(ctx: &AssistContext<'_>, pat: &ast::Pat) -> bool { +fn check_pat_variant_from_enum(ctx: &AssistContext<'_, '_>, pat: &ast::Pat) -> bool { ctx.sema.type_of_pat(pat).is_none_or(|ty: hir::TypeInfo<'_>| { ty.adjusted().as_adt().is_some_and(|adt| matches!(adt, hir::Adt::Enum(_))) }) } fn check_pat_variant_nested_or_literal_with_depth( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, pat: &ast::Pat, depth_after_refutable: usize, ) -> bool { @@ -480,7 +483,7 @@ pub(crate) fn expr_fill_default(config: &AssistConfig) -> ast::Expr { /// - `Some(None)`: no impl exists. /// - `Some(Some(_))`: an impl exists, with no matching function names. pub(crate) fn find_struct_impl( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, adt: &ast::Adt, names: &[String], ) -> Option> { @@ -1029,7 +1032,7 @@ pub(crate) fn add_group_separators(s: &str, group_size: usize) -> String { /// Replaces the record expression, handling field shorthands including inside macros. pub(crate) fn replace_record_field_expr( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, edit: &mut SourceChangeBuilder, record_field: ast::RecordExprField, initializer: ast::Expr, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs index fc9bf210e4872..d2b02b3373e1f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs @@ -13,7 +13,7 @@ use crate::AssistContext; /// Decides whether the new path expression needs to be dereferenced and/or wrapped in parens. /// Returns the relevant parent expression to replace and the [RefData]. pub(crate) fn determine_ref_and_parens( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, field_expr: &FieldExpr, ) -> (ast::Expr, RefData) { let s = field_expr.syntax(); @@ -62,8 +62,8 @@ pub(crate) fn determine_ref_and_parens( // other combinations (`&value` -> `value`, `&&value` -> `&value`, `&value` -> `&&value`) might or might not be able to auto-ref/deref, // but there might be trait implementations an added `&` might resolve to // -> ONLY handle auto-ref from `value` to `&value` - fn is_auto_ref(ctx: &AssistContext<'_>, call_expr: &MethodCallExpr) -> bool { - fn impl_(ctx: &AssistContext<'_>, call_expr: &MethodCallExpr) -> Option { + fn is_auto_ref(ctx: &AssistContext<'_, '_>, call_expr: &MethodCallExpr) -> bool { + fn impl_(ctx: &AssistContext<'_, '_>, call_expr: &MethodCallExpr) -> Option { let rec = call_expr.receiver()?; let rec_ty = ctx.sema.type_of_expr(&rec)?.original(); // input must be actual value diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index ed972a1e2a2dc..f3190bbbc82e6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -87,7 +87,7 @@ impl Completions { } } - pub(crate) fn add_keyword(&mut self, ctx: &CompletionContext<'_>, keyword: &'static str) { + pub(crate) fn add_keyword(&mut self, ctx: &CompletionContext<'_, '_>, keyword: &'static str) { let item = CompletionItem::new( CompletionItemKind::Keyword, ctx.source_range(), @@ -97,7 +97,7 @@ impl Completions { item.add_to(self, ctx.db); } - pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext<'_>) { + pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext<'_, '_>) { ["self::", "crate::"].into_iter().for_each(|kw| self.add_keyword(ctx, kw)); if ctx.depth_from_crate_root > 0 { @@ -107,7 +107,7 @@ impl Completions { pub(crate) fn add_nameref_keywords_with_type_like( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, ) { let mut add_keyword = |kw| { @@ -120,7 +120,7 @@ impl Completions { } } - pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext<'_>) { + pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext<'_, '_>) { ["self", "crate"].into_iter().for_each(|kw| self.add_keyword(ctx, kw)); if ctx.depth_from_crate_root > 0 { @@ -130,7 +130,7 @@ impl Completions { pub(crate) fn add_type_keywords( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, ) { let mut add_keyword = |kw, snippet| { @@ -145,7 +145,7 @@ impl Completions { pub(crate) fn add_super_keyword( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, super_chain_len: Option, ) { if let Some(len) = super_chain_len @@ -158,7 +158,7 @@ impl Completions { pub(crate) fn add_keyword_snippet_expr( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, incomplete_let: bool, kw: &str, snippet: &str, @@ -185,7 +185,7 @@ impl Completions { pub(crate) fn add_keyword_snippet( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, kw: &str, snippet: &str, ) { @@ -201,7 +201,7 @@ impl Completions { pub(crate) fn add_expr( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, expr: &hir::term_search::Expr<'_>, ) { if let Some(item) = render_expr(ctx, expr) { @@ -211,7 +211,7 @@ impl Completions { pub(crate) fn add_crate_roots( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, ) { ctx.process_all_names(&mut |name, res, doc_aliases| match res { @@ -224,7 +224,7 @@ impl Completions { pub(crate) fn add_path_resolution( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, local_name: hir::Name, resolution: hir::ScopeDef, @@ -246,7 +246,7 @@ impl Completions { pub(crate) fn add_pattern_resolution( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, local_name: hir::Name, resolution: hir::ScopeDef, @@ -267,7 +267,7 @@ impl Completions { pub(crate) fn add_enum_variants( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, e: hir::Enum, ) { @@ -278,7 +278,7 @@ impl Completions { pub(crate) fn add_module( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, module: hir::Module, local_name: hir::Name, @@ -295,7 +295,7 @@ impl Completions { pub(crate) fn add_macro( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, mac: hir::Macro, local_name: hir::Name, @@ -316,7 +316,7 @@ impl Completions { pub(crate) fn add_function( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, func: hir::Function, local_name: Option, @@ -338,7 +338,7 @@ impl Completions { pub(crate) fn add_method( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, dot_access: &DotAccess<'_>, func: hir::Function, receiver: Option, @@ -362,7 +362,7 @@ impl Completions { pub(crate) fn add_method_with_import( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, dot_access: &DotAccess<'_>, func: hir::Function, import: LocatedImport, @@ -386,7 +386,7 @@ impl Completions { .add_to(self, ctx.db); } - pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) { + pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_, '_>, konst: hir::Const) { let is_private_editable = match ctx.is_visible(&konst) { Visible::Yes => false, Visible::Editable => true, @@ -400,7 +400,7 @@ impl Completions { pub(crate) fn add_type_alias( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, type_alias: hir::TypeAlias, ) { let is_private_editable = match ctx.is_visible(&type_alias) { @@ -416,7 +416,7 @@ impl Completions { pub(crate) fn add_type_alias_with_eq( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, type_alias: hir::TypeAlias, ) { if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) { @@ -427,7 +427,7 @@ impl Completions { pub(crate) fn add_qualified_enum_variant( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, variant: hir::EnumVariant, path: hir::ModPath, @@ -444,7 +444,7 @@ impl Completions { pub(crate) fn add_enum_variant( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, variant: hir::EnumVariant, local_name: Option, @@ -467,7 +467,7 @@ impl Completions { pub(crate) fn add_field( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, dot_access: &DotAccess<'_>, receiver: Option, field: hir::Field, @@ -491,7 +491,7 @@ impl Completions { pub(crate) fn add_struct_literal( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, strukt: hir::Struct, path: Option, @@ -515,7 +515,7 @@ impl Completions { pub(crate) fn add_union_literal( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, un: hir::Union, path: Option, local_name: Option, @@ -536,7 +536,7 @@ impl Completions { pub(crate) fn add_tuple_field( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, receiver: Option, field: usize, ty: &hir::Type<'_>, @@ -547,7 +547,7 @@ impl Completions { self.add(item); } - pub(crate) fn add_lifetime(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) { + pub(crate) fn add_lifetime(&mut self, ctx: &CompletionContext<'_, '_>, name: hir::Name) { CompletionItem::new( SymbolKind::LifetimeParam, ctx.source_range(), @@ -557,7 +557,7 @@ impl Completions { .add_to(self, ctx.db) } - pub(crate) fn add_label(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) { + pub(crate) fn add_label(&mut self, ctx: &CompletionContext<'_, '_>, name: hir::Name) { CompletionItem::new( SymbolKind::Label, ctx.source_range(), @@ -569,7 +569,7 @@ impl Completions { pub(crate) fn add_variant_pat( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, path_ctx: Option<&PathCompletionCtx<'_>>, variant: hir::EnumVariant, @@ -590,7 +590,7 @@ impl Completions { pub(crate) fn add_qualified_variant_pat( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, variant: hir::EnumVariant, path: hir::ModPath, @@ -611,7 +611,7 @@ impl Completions { pub(crate) fn add_struct_pat( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, strukt: hir::Struct, local_name: Option, @@ -629,7 +629,7 @@ impl Completions { )); } - pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) { + pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_, '_>, name: &str) { let item = CompletionItem::new( CompletionItemKind::Binding, ctx.source_range(), @@ -644,10 +644,10 @@ impl Completions { /// Skips variants that are visible with single segment paths. fn enum_variants_with_paths( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, enum_: hir::Enum, impl_: Option<&ast::Impl>, - cb: impl Fn(&mut Completions, &CompletionContext<'_>, hir::EnumVariant, hir::ModPath), + cb: impl Fn(&mut Completions, &CompletionContext<'_, '_>, hir::EnumVariant, hir::ModPath), ) { let mut process_variant = |variant: EnumVariant| { let self_path = hir::ModPath::from_segments( @@ -683,7 +683,7 @@ fn enum_variants_with_paths( pub(super) fn complete_name( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, NameContext { name, kind }: &NameContext, ) { match kind { @@ -724,10 +724,10 @@ pub(super) fn complete_name( } } -pub(super) fn complete_name_ref( +pub(super) fn complete_name_ref<'db>( acc: &mut Completions, - ctx: &CompletionContext<'_>, - NameRefContext { nameref, kind }: &NameRefContext<'_>, + ctx: &CompletionContext<'_, 'db>, + NameRefContext { nameref, kind }: &NameRefContext<'db>, ) { match kind { NameRefKind::Path(path_ctx) => { @@ -813,7 +813,7 @@ pub(super) fn complete_name_ref( fn complete_patterns( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, ) { flyimport::import_on_the_fly_pat(acc, ctx, pattern_ctx); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index 0ce0dde2e25c9..df513dd8fdedc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -31,7 +31,7 @@ pub(crate) use self::derive::complete_derive_path; /// Complete inputs to known builtin attributes as well as derive attributes pub(crate) fn complete_known_attribute_input( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, colon_prefix: bool, fake_attribute_under_caret: &ast::TokenTreeMeta, extern_crate: Option<&ast::ExternCrate>, @@ -70,7 +70,7 @@ pub(crate) fn complete_known_attribute_input( pub(crate) fn complete_attribute_path( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, &AttrCtx { kind, annotated_item_kind, ref derive_helpers }: &AttrCtx, ) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index 1bd6e6d9bfdf2..1672e8e7930e3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs @@ -6,7 +6,7 @@ use syntax::{AstToken, Direction, NodeOrToken, SmolStr, SyntaxKind, algo, ast::I use crate::{CompletionItem, completions::Completions, context::CompletionContext}; -pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { +pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_>) { let add_completion = |item: &str| { let mut completion = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), item, ctx.edition); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs index 57ea609a40e06..9e7dabbd01046 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs @@ -12,7 +12,7 @@ use crate::{ pub(crate) fn complete_derive_path( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, existing_derives: &ExistingDerives, ) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs index 0899bbff14641..1a2e5652e1bf6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs @@ -9,7 +9,7 @@ use super::AttrCompletion; pub(super) fn complete_on_unimplemented( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, existing_keys: &[ast::Expr], ) { for attr in ATTRIBUTE_ARGS { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/feature.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/feature.rs index 1e6baca864c78..1bcf05faa8712 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/feature.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/feature.rs @@ -10,7 +10,7 @@ use crate::{Completions, context::CompletionContext, item::CompletionItem}; pub(super) fn complete_feature( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, existing_features: &[ast::Path], ) { for &Lint { label, description, .. } in FEATURES { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs index f810196bfe3f8..2f957b5a55df9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs @@ -10,7 +10,7 @@ use crate::{Completions, context::CompletionContext, item::CompletionItem}; pub(super) fn complete_lint( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, is_qualified: bool, existing_lints: &[ast::Path], ) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs index 9c0fe0055dd4f..618cf6fd01ab8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs @@ -7,7 +7,7 @@ use crate::{Completions, context::CompletionContext, item::CompletionItem}; pub(super) fn complete_macro_use( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, extern_crate: Option<&ast::ExternCrate>, existing_imports: &[ast::Path], ) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs index b04358963ae80..63cddb365e301 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs @@ -7,7 +7,7 @@ use crate::{Completions, context::CompletionContext, item::CompletionItem}; pub(super) fn complete_repr( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, existing_reprs: &[ast::Expr], ) { for &ReprCompletion { label, snippet, lookup, collides } in REPR_COMPLETIONS { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 18c1992afa246..31a8ed2c130dd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -18,7 +18,7 @@ use crate::{ /// Complete dot accesses, i.e. fields or methods. pub(crate) fn complete_dot( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, dot_access: &DotAccess<'_>, ) { let receiver_ty = match dot_access { @@ -126,7 +126,7 @@ pub(crate) fn complete_dot( pub(crate) fn complete_undotted_self( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, expr_ctx: &PathExprCtx<'_>, ) { @@ -198,7 +198,7 @@ pub(crate) fn complete_undotted_self( fn complete_fields( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, receiver: &hir::Type<'_>, mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type<'_>), mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type<'_>), @@ -227,13 +227,13 @@ fn complete_fields( } fn complete_methods( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, receiver: &hir::Type<'_>, traits_in_scope: &FxHashSet, f: impl FnMut(hir::Function), ) { - struct Callback<'a, F> { - ctx: &'a CompletionContext<'a>, + struct Callback<'a, 'db, F> { + ctx: &'a CompletionContext<'a, 'db>, f: F, // We deliberately deduplicate by function ID and not name, because while inherent methods cannot be // duplicated, trait methods can. And it is still useful to show all of them (even when there @@ -241,7 +241,7 @@ fn complete_methods( seen_methods: FxHashSet, } - impl MethodCandidateCallback for Callback<'_, F> + impl MethodCandidateCallback for Callback<'_, '_, F> where F: FnMut(hir::Function), { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs index 885d1a30750f8..30662877d6dca 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs @@ -47,7 +47,7 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ pub(crate) fn complete_cargo_env_vars( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, original: &ast::String, expanded: &ast::String, ) -> Option<()> { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index c15c67173ea3d..a2a4cbac2161b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -12,14 +12,14 @@ use crate::{ context::{PathCompletionCtx, PathExprCtx, Qualified}, }; -struct PathCallback<'a, F> { - ctx: &'a CompletionContext<'a>, +struct PathCallback<'a, 'db, F> { + ctx: &'a CompletionContext<'a, 'db>, acc: &'a mut Completions, add_assoc_item: F, seen: FxHashSet, } -impl PathCandidateCallback for PathCallback<'_, F> +impl PathCandidateCallback for PathCallback<'_, '_, F> where F: FnMut(&mut Completions, hir::AssocItem), { @@ -46,7 +46,7 @@ where pub(crate) fn complete_expr_path( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, expr_ctx: &PathExprCtx<'_>, ) { @@ -458,7 +458,7 @@ pub(crate) fn complete_expr_path( pub(crate) fn complete_expr( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, ) { let _p = tracing::info_span!("complete_expr").entered(); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs index 570d1a0a2db8a..b00b2e1907534 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs @@ -43,7 +43,7 @@ const SUPPORTED_CALLING_CONVENTIONS: &[&str] = &[ pub(crate) fn complete_extern_abi( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, expanded: &ast::String, ) -> Option<()> { if !expanded.syntax().parent().is_some_and(|it| ast::Abi::can_cast(it.kind())) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs index 91202e8b32fcd..819db549d4f71 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs @@ -8,7 +8,7 @@ use crate::{CompletionItem, CompletionItemKind, context::CompletionContext}; use super::Completions; -pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionContext<'_>) { +pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionContext<'_, '_>) { let imported_extern_crates: Vec = ctx.scope.extern_crate_decls().collect(); for (name, module) in ctx.scope.extern_crates() { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/field.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/field.rs index 26afa9c8ad96f..b4f7e1839dee3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/field.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/field.rs @@ -7,7 +7,7 @@ use crate::{ pub(crate) fn complete_field_list_tuple_variant( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, ) { if ctx.qualifier_ctx.vis_node.is_some() { @@ -28,7 +28,7 @@ pub(crate) fn complete_field_list_tuple_variant( pub(crate) fn complete_field_list_record_variant( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, ) { if ctx.qualifier_ctx.vis_node.is_none() { let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 2cf87baf33077..b350647b9a2bd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -108,10 +108,10 @@ use crate::{ // The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag. // Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding // capability enabled. -pub(crate) fn import_on_the_fly_path( +pub(crate) fn import_on_the_fly_path<'db>( acc: &mut Completions, - ctx: &CompletionContext<'_>, - path_ctx: &PathCompletionCtx<'_>, + ctx: &CompletionContext<'_, 'db>, + path_ctx: &PathCompletionCtx<'db>, ) -> Option<()> { if !ctx.config.enable_imports_on_the_fly { return None; @@ -155,7 +155,7 @@ pub(crate) fn import_on_the_fly_path( pub(crate) fn import_on_the_fly_pat( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, ) -> Option<()> { if !ctx.config.enable_imports_on_the_fly { @@ -178,10 +178,10 @@ pub(crate) fn import_on_the_fly_pat( ) } -pub(crate) fn import_on_the_fly_dot( +pub(crate) fn import_on_the_fly_dot<'db>( acc: &mut Completions, - ctx: &CompletionContext<'_>, - dot_access: &DotAccess<'_>, + ctx: &CompletionContext<'_, 'db>, + dot_access: &DotAccess<'db>, ) -> Option<()> { if !ctx.config.enable_imports_on_the_fly { return None; @@ -206,11 +206,11 @@ pub(crate) fn import_on_the_fly_dot( ) } -fn import_on_the_fly( +fn import_on_the_fly<'db>( acc: &mut Completions, - ctx: &CompletionContext<'_>, - path_ctx @ PathCompletionCtx { kind, .. }: &PathCompletionCtx<'_>, - import_assets: ImportAssets<'_>, + ctx: &CompletionContext<'_, 'db>, + path_ctx @ PathCompletionCtx { kind, .. }: &PathCompletionCtx<'db>, + import_assets: ImportAssets<'db>, position: SyntaxNode, potential_import_name: String, ) -> Option<()> { @@ -292,11 +292,11 @@ fn import_on_the_fly( Some(()) } -fn import_on_the_fly_pat_( +fn import_on_the_fly_pat_<'db>( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, 'db>, pattern_ctx: &PatternContext, - import_assets: ImportAssets<'_>, + import_assets: ImportAssets<'db>, position: SyntaxNode, potential_import_name: String, ) -> Option<()> { @@ -338,11 +338,11 @@ fn import_on_the_fly_pat_( Some(()) } -fn import_on_the_fly_method( +fn import_on_the_fly_method<'db>( acc: &mut Completions, - ctx: &CompletionContext<'_>, - dot_access: &DotAccess<'_>, - import_assets: ImportAssets<'_>, + ctx: &CompletionContext<'_, 'db>, + dot_access: &DotAccess<'db>, + import_assets: ImportAssets<'db>, position: SyntaxNode, potential_import_name: String, ) -> Option<()> { @@ -378,7 +378,7 @@ fn import_on_the_fly_method( Some(()) } -fn filter_excluded_flyimport(ctx: &CompletionContext<'_>, import: &LocatedImport) -> bool { +fn filter_excluded_flyimport(ctx: &CompletionContext<'_, '_>, import: &LocatedImport) -> bool { let def = import.item_to_import.into_module_def(); let is_exclude_flyimport = ctx.exclude_flyimport.get(&def).copied(); @@ -400,14 +400,14 @@ fn filter_excluded_flyimport(ctx: &CompletionContext<'_>, import: &LocatedImport true } -fn import_name(ctx: &CompletionContext<'_>) -> String { +fn import_name(ctx: &CompletionContext<'_, '_>) -> String { let token_kind = ctx.token.kind(); if token_kind.is_any_identifier() { ctx.token.to_string() } else { String::new() } } fn import_assets_for_path<'db>( - ctx: &CompletionContext<'db>, + ctx: &CompletionContext<'_, 'db>, path: Option<&ast::Path>, potential_import_name: &str, qualifier: Option, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs index bd0b69215cf7a..f1e8e5f39eaac 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs @@ -22,7 +22,7 @@ use crate::{ /// Also complete parameters for closure or local functions from the surrounding defined locals. pub(crate) fn complete_fn_param( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, ) -> Option<()> { let (ParamContext { param_list, kind, param, .. }, impl_or_trait) = match pattern_ctx { @@ -78,7 +78,7 @@ pub(crate) fn complete_fn_param( } fn fill_fn_params( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, function: &ast::Fn, param_list: &ast::ParamList, current_param: &ast::Param, @@ -139,7 +139,7 @@ fn fill_fn_params( } fn params_from_stmt_list_scope( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, stmt_list: ast::StmtList, mut cb: impl FnMut(hir::Name, String), ) { @@ -196,7 +196,7 @@ fn should_add_self_completions( } } -fn comma_wrapper(ctx: &CompletionContext<'_>) -> Option<(impl Fn(&str) -> SmolStr, TextRange)> { +fn comma_wrapper(ctx: &CompletionContext<'_, '_>) -> Option<(impl Fn(&str) -> SmolStr, TextRange)> { let param = ctx.original_token.parent_ancestors().find(|node| node.kind() == SyntaxKind::PARAM)?; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs index eaacd8d1a8fef..1e5240218571a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs @@ -10,7 +10,7 @@ use crate::{CompletionItem, CompletionItemKind, Completions, context::Completion /// Complete identifiers in format strings. pub(crate) fn format_string( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, original: &ast::String, expanded: &ast::String, ) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs index 39048e44001e7..1b26cab263334 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs @@ -9,7 +9,7 @@ pub(crate) mod trait_impl; pub(crate) fn complete_item_list_in_expr( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, expr_ctx: &PathExprCtx<'_>, ) { @@ -24,7 +24,7 @@ pub(crate) fn complete_item_list_in_expr( pub(crate) fn complete_item_list( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, kind: &ItemListKind, ) { @@ -72,7 +72,11 @@ pub(crate) fn complete_item_list( } } -fn add_keywords(acc: &mut Completions, ctx: &CompletionContext<'_>, kind: Option<&ItemListKind>) { +fn add_keywords( + acc: &mut Completions, + ctx: &CompletionContext<'_, '_>, + kind: Option<&ItemListKind>, +) { let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet); let in_item_list = matches!(kind, Some(ItemListKind::SourceFile | ItemListKind::Module) | None); 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 9bac34885c0cb..99d37b699f3fc 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 @@ -59,7 +59,7 @@ enum ImplCompletionKind { pub(crate) fn complete_trait_impl_const( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, name: &Option, ) -> Option<()> { complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::Const) @@ -67,7 +67,7 @@ pub(crate) fn complete_trait_impl_const( pub(crate) fn complete_trait_impl_type_alias( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, name: &Option, ) -> Option<()> { complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::TypeAlias) @@ -75,7 +75,7 @@ pub(crate) fn complete_trait_impl_type_alias( pub(crate) fn complete_trait_impl_fn( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, name: &Option, ) -> Option<()> { complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::Fn) @@ -83,7 +83,7 @@ pub(crate) fn complete_trait_impl_fn( fn complete_trait_impl_name( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, name: &Option, kind: ImplCompletionKind, ) -> Option<()> { @@ -122,7 +122,7 @@ fn complete_trait_impl_name( pub(crate) fn complete_trait_impl_item_by_name( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, name_ref: &Option, impl_: &Option, @@ -149,7 +149,7 @@ pub(crate) fn complete_trait_impl_item_by_name( fn complete_trait_impl( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, kind: ImplCompletionKind, replacement_range: TextRange, impl_def: &ast::Impl, @@ -178,7 +178,7 @@ fn complete_trait_impl( fn add_function_impl( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, replacement_range: TextRange, func: hir::Function, impl_def: hir::Impl, @@ -198,7 +198,7 @@ fn add_function_impl( fn add_function_impl_( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, replacement_range: TextRange, func: hir::Function, impl_def: hir::Impl, @@ -257,7 +257,7 @@ enum AsyncSugaring { /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. fn get_transformed_assoc_item( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, assoc_item: ast::AssocItem, impl_def: hir::Impl, ) -> Option { @@ -281,7 +281,7 @@ fn get_transformed_assoc_item( /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. fn get_transformed_fn( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, fn_: ast::Fn, impl_def: hir::Impl, async_: AsyncSugaring, @@ -363,7 +363,7 @@ fn get_transformed_fn( fn add_type_alias_impl( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, replacement_range: TextRange, type_alias: hir::TypeAlias, impl_def: hir::Impl, @@ -444,7 +444,7 @@ fn add_type_alias_impl( fn add_const_impl( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, replacement_range: TextRange, const_: hir::Const, impl_def: hir::Impl, @@ -486,13 +486,13 @@ fn add_const_impl( } fn make_const_compl_syntax( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, const_: &ast::Const, macro_file: Option, ) -> SmolStr { let const_ = if let Some(macro_file) = macro_file { let span_map = ctx.db.expansion_span_map(macro_file); - prettify_macro_expansion(ctx.db, const_.syntax().clone(), &span_map, ctx.krate.into()) + prettify_macro_expansion(ctx.db, const_.syntax().clone(), span_map, ctx.krate.into()) } else { const_.syntax().clone() }; @@ -514,13 +514,13 @@ fn make_const_compl_syntax( } fn function_declaration( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, node: &ast::Fn, macro_file: Option, ) -> String { let node = if let Some(macro_file) = macro_file { let span_map = ctx.db.expansion_span_map(macro_file); - prettify_macro_expansion(ctx.db, node.syntax().clone(), &span_map, ctx.krate.into()) + 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-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index fbb3cde968849..f7dd1589ae7d5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs @@ -6,7 +6,7 @@ use crate::{CompletionContext, Completions}; pub(crate) fn complete_for_and_where( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, keyword_item: &ast::Item, ) { let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs index 8902cd09cec0c..6291b42a0368b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs @@ -17,7 +17,7 @@ use crate::{ /// Completes lifetimes. pub(crate) fn complete_lifetime( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, lifetime_ctx: &LifetimeContext, ) { let &LifetimeContext { kind: LifetimeKind::Lifetime { in_lifetime_param_bound, def }, .. } = @@ -44,7 +44,7 @@ pub(crate) fn complete_lifetime( /// Completes labels. pub(crate) fn complete_label( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, lifetime_ctx: &LifetimeContext, ) { if !matches!(lifetime_ctx, LifetimeContext { kind: LifetimeKind::LabelRef, .. }) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/macro_def.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/macro_def.rs index 2c8e7a2e62cc8..884b4dd966a1c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/macro_def.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/macro_def.rs @@ -4,7 +4,7 @@ use ide_db::SymbolKind; use crate::{CompletionItem, Completions, context::CompletionContext}; -pub(crate) fn complete_macro_segment(acc: &mut Completions, ctx: &CompletionContext<'_>) { +pub(crate) fn complete_macro_segment(acc: &mut Completions, ctx: &CompletionContext<'_, '_>) { for &label in MACRO_SEGMENTS { let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), label, ctx.edition); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs index 3333300045773..a8c6c82aac3a0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs @@ -14,7 +14,7 @@ use crate::{CompletionItem, Completions, context::CompletionContext}; /// Complete mod declaration, i.e. `mod $0;` pub(crate) fn complete_mod( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, mod_under_caret: &ast::Module, ) -> Option<()> { if mod_under_caret.item_list().is_some() { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs index e7597bf95c80f..7b887fb7d7b20 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs @@ -12,7 +12,7 @@ use crate::{ /// Completes constants and paths in unqualified patterns. pub(crate) fn complete_pattern( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, ) { let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet); @@ -128,7 +128,7 @@ pub(crate) fn complete_pattern( pub(crate) fn complete_pattern_path( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, ) { match qualified { 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 8a754a6b27b5b..5966bfcd63efb 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 @@ -31,7 +31,7 @@ use crate::{ pub(crate) fn complete_postfix( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, dot_access: &DotAccess<'_>, ) { if !ctx.config.enable_postfix_completions { @@ -479,7 +479,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) { } fn build_postfix_snippet_builder<'ctx>( - ctx: &'ctx CompletionContext<'_>, + ctx: &'ctx CompletionContext<'_, '_>, cap: SnippetCap, receiver: &'ctx ast::Expr, ) -> Option Builder + 'ctx> { @@ -495,7 +495,7 @@ fn build_postfix_snippet_builder<'ctx>( // Wrapping impl Fn in an option ruins lifetime inference for the parameters in a way that // can't be annotated for the closure, hence fix it by constructing it without the Option first fn build<'ctx>( - ctx: &'ctx CompletionContext<'_>, + ctx: &'ctx CompletionContext<'_, '_>, cap: SnippetCap, delete_range: TextRange, ) -> impl Fn(&str, &str, String) -> Builder + 'ctx { @@ -525,7 +525,7 @@ fn build_postfix_snippet_builder<'ctx>( fn add_custom_postfix_completions( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, postfix_snippet: impl Fn(&str, &str, String) -> Builder, receiver_text: &str, ) -> Option<()> { 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 6b0e9f31c4a47..3b22e8a266e73 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 @@ -44,7 +44,7 @@ static SNIPPET_RETURNS_NON_UNIT: &[&str] = &["format"]; pub(crate) fn add_format_like_completions( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, dot_receiver: &ast::Expr, cap: SnippetCap, receiver_text: &ast::String, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/ra_fixture.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/ra_fixture.rs index 5a8881edc73e9..08ad37b7f2035 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/ra_fixture.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/ra_fixture.rs @@ -14,7 +14,7 @@ use crate::{ pub(crate) fn complete_ra_fixture( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, original: &ast::String, expanded: &ast::String, ) -> Option<()> { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs index 12c564af5cba4..1238a91dad871 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs @@ -13,7 +13,7 @@ use crate::{ pub(crate) fn complete_record_pattern_fields( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, ) { if let PatternContext { record_pat: Some(record_pat), .. } = pattern_ctx { @@ -44,7 +44,7 @@ pub(crate) fn complete_record_pattern_fields( pub(crate) fn complete_record_expr_fields( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, record_expr: &ast::RecordExpr, &dot_prefix: &bool, ) { @@ -98,7 +98,7 @@ pub(crate) fn complete_record_expr_fields( pub(crate) fn add_default_update( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, ty: Option<&hir::TypeInfo<'_>>, ) { let default_trait = ctx.famous_defs().core_default_Default(); @@ -127,7 +127,7 @@ pub(crate) fn add_default_update( fn complete_fields( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, missing_fields: Vec<(hir::Field, hir::Type<'_>)>, ) { for (field, ty) in missing_fields { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs index 04450aea75bf7..7432c5226bfad 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs @@ -10,7 +10,7 @@ use crate::{ pub(crate) fn complete_expr_snippet( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, &PathExprCtx { in_block_expr, .. }: &PathExprCtx<'_>, ) { @@ -50,7 +50,7 @@ macro_rules! $1 { pub(crate) fn complete_item_snippet( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, kind: &ItemListKind, ) { @@ -117,7 +117,12 @@ macro_rules! $1 { } } -fn snippet(ctx: &CompletionContext<'_>, cap: SnippetCap, label: &str, snippet: &str) -> Builder { +fn snippet( + ctx: &CompletionContext<'_, '_>, + cap: SnippetCap, + label: &str, + snippet: &str, +) -> Builder { let mut item = CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), label, ctx.edition); item.insert_snippet(cap, snippet); @@ -126,7 +131,7 @@ fn snippet(ctx: &CompletionContext<'_>, cap: SnippetCap, label: &str, snippet: & fn add_custom_completions( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, cap: SnippetCap, scope: SnippetScope, ) -> Option<()> { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs index 20bbf0dd8bacc..0b2b6682aaabe 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs @@ -11,7 +11,7 @@ use crate::{ pub(crate) fn complete_type_path( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, location: &TypeLocation, ) { @@ -217,7 +217,7 @@ pub(crate) fn complete_type_path( pub(crate) fn complete_ascribed_type( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, ascription: &TypeAscriptionTarget, ) -> Option<()> { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs index f39b641649326..1ff7dd6deff04 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs @@ -12,7 +12,7 @@ use crate::{ pub(crate) fn complete_use_path( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, use_tree_parent, .. }: &PathCompletionCtx<'_>, name_ref: &Option, ) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs index 28d906d91ce5a..49a52f298658a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs @@ -7,7 +7,7 @@ use crate::{ pub(crate) fn complete_vis_path( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, &has_in_token: &bool, ) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index b9520e9132143..f7fced3f062e9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -458,10 +458,10 @@ pub(crate) enum ParamKind { /// `CompletionContext` is created early during completion to figure out, where /// exactly is the cursor, syntax-wise. #[derive(Debug)] -pub(crate) struct CompletionContext<'a> { - pub(crate) sema: Semantics<'a, RootDatabase>, - pub(crate) scope: SemanticsScope<'a>, - pub(crate) db: &'a RootDatabase, +pub(crate) struct CompletionContext<'a, 'db> { + pub(crate) sema: Semantics<'db, RootDatabase>, + pub(crate) scope: SemanticsScope<'db>, + pub(crate) db: &'db RootDatabase, pub(crate) config: &'a CompletionConfig<'a>, pub(crate) position: FilePosition, @@ -487,7 +487,7 @@ pub(crate) struct CompletionContext<'a> { /// This is usually the parameter name of the function argument we are completing. pub(crate) expected_name: Option, /// The expected type of what we are completing. - pub(crate) expected_type: Option>, + pub(crate) expected_type: Option>, pub(crate) qualifier_ctx: QualifierCtx, @@ -523,7 +523,7 @@ pub(crate) enum CompleteSemicolon { CompleteComma, } -impl CompletionContext<'_> { +impl<'db> CompletionContext<'_, 'db> { /// The range of the identifier that is being completed. pub(crate) fn source_range(&self) -> TextRange { let kind = self.original_token.kind(); @@ -540,7 +540,7 @@ impl CompletionContext<'_> { } } - pub(crate) fn famous_defs(&self) -> FamousDefs<'_, '_> { + pub(crate) fn famous_defs(&self) -> FamousDefs<'_, 'db> { FamousDefs(&self.sema, self.krate) } @@ -732,13 +732,13 @@ impl CompletionContext<'_> { } // CompletionContext construction -impl<'db> CompletionContext<'db> { +impl<'a, 'db> CompletionContext<'a, 'db> { pub(crate) fn new( db: &'db RootDatabase, position @ FilePosition { file_id, offset }: FilePosition, - config: &'db CompletionConfig<'db>, + config: &'a CompletionConfig<'a>, trigger_character: Option, - ) -> Option<(CompletionContext<'db>, CompletionAnalysis<'db>)> { + ) -> Option<(CompletionContext<'a, 'db>, CompletionAnalysis<'db>)> { let _p = tracing::info_span!("CompletionContext::new").entered(); let sema = Semantics::new(db); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 6b34bf07bb1e7..cfadec6287947 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -535,7 +535,7 @@ pub(crate) struct Builder { impl Builder { pub(crate) fn from_resolution( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, local_name: hir::Name, resolution: hir::ScopeDef, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index d24ebab55da5e..50b44b57c5680 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -35,15 +35,15 @@ use crate::{ }; /// Interface for data and methods required for items rendering. #[derive(Debug, Clone)] -pub(crate) struct RenderContext<'a> { - completion: &'a CompletionContext<'a>, +pub(crate) struct RenderContext<'a, 'db> { + completion: &'a CompletionContext<'a, 'db>, is_private_editable: bool, import_to_add: Option, doc_aliases: Vec, } -impl<'a> RenderContext<'a> { - pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> { +impl<'a, 'db> RenderContext<'a, 'db> { + pub(crate) fn new(completion: &'a CompletionContext<'a, 'db>) -> RenderContext<'a, 'db> { RenderContext { completion, is_private_editable: false, @@ -136,7 +136,7 @@ impl<'a> RenderContext<'a> { } pub(crate) fn render_field( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, dot_access: &DotAccess<'_>, receiver: Option, field: hir::Field, @@ -213,7 +213,7 @@ fn field_with_receiver(receiver: Option<&str>, field_name: &str) -> SmolStr { } pub(crate) fn render_tuple_field( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, receiver: Option, field: usize, ty: &hir::Type<'_>, @@ -235,7 +235,7 @@ pub(crate) fn render_tuple_field( pub(crate) fn render_type_inference( ty_string: String, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, ) -> CompletionItem { let mut builder = CompletionItem::new( @@ -254,7 +254,7 @@ pub(crate) fn render_type_inference( } pub(crate) fn render_path_resolution( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, local_name: hir::Name, resolution: ScopeDef, @@ -263,7 +263,7 @@ pub(crate) fn render_path_resolution( } pub(crate) fn render_pattern_resolution( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, pattern_ctx: &PatternContext, local_name: hir::Name, resolution: ScopeDef, @@ -272,7 +272,7 @@ pub(crate) fn render_pattern_resolution( } pub(crate) fn render_resolution_with_import( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, import_edit: LocatedImport, ) -> Option { @@ -285,7 +285,7 @@ pub(crate) fn render_resolution_with_import( } pub(crate) fn render_resolution_with_import_pat( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, pattern_ctx: &PatternContext, import_edit: LocatedImport, ) -> Option { @@ -295,7 +295,7 @@ pub(crate) fn render_resolution_with_import_pat( } pub(crate) fn render_expr( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, expr: &hir::term_search::Expr<'_>, ) -> Option { let mut i = 1; @@ -358,7 +358,7 @@ pub(crate) fn render_expr( fn get_import_name( resolution: ScopeDef, - ctx: &RenderContext<'_>, + ctx: &RenderContext<'_, '_>, import_edit: &LocatedImport, ) -> Option { // FIXME: Temporary workaround for handling aliased import. @@ -376,7 +376,7 @@ fn get_import_name( fn scope_def_to_name( resolution: ScopeDef, - ctx: &RenderContext<'_>, + ctx: &RenderContext<'_, '_>, import_edit: &LocatedImport, ) -> Option { Some(match resolution { @@ -388,7 +388,7 @@ fn scope_def_to_name( } fn render_resolution_pat( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, pattern_ctx: &PatternContext, local_name: hir::Name, import_to_add: Option, @@ -406,7 +406,7 @@ fn render_resolution_pat( } fn render_resolution_path( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, local_name: hir::Name, import_to_add: Option, @@ -515,7 +515,7 @@ fn render_resolution_path( } fn render_resolution_simple_( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, local_name: &hir::Name, import_to_add: Option, resolution: ScopeDef, @@ -589,7 +589,7 @@ fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option, resolution: ScopeDef) -> bool { +fn scope_def_is_deprecated(ctx: &RenderContext<'_, '_>, resolution: ScopeDef) -> bool { let db = ctx.db(); match resolution { ScopeDef::ModuleDef(hir::ModuleDef::EnumVariant(it)) => ctx.is_variant_deprecated(it), @@ -605,7 +605,7 @@ fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> boo } pub(crate) fn render_type_keyword_snippet( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, label: &str, snippet: &str, @@ -629,7 +629,7 @@ pub(crate) fn render_type_keyword_snippet( } fn adds_ret_type_arrow( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, item: &mut Builder, insert_text: String, @@ -648,7 +648,7 @@ fn adds_ret_type_arrow( // FIXME: This checks types without possible coercions which some completions might want to do fn match_types( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, ty1: &hir::Type<'_>, ty2: &hir::Type<'_>, ) -> Option { @@ -662,7 +662,7 @@ fn match_types( } fn compute_type_match( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, completion_ty: &hir::Type<'_>, ) -> Option { let expected_type = ctx.expected_type.as_ref()?; @@ -696,12 +696,12 @@ fn compute_has_local_inherent_impl( .any(|imp| imp.trait_(db).is_none() && imp.module(db) == curr_module) } -fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) -> bool { +fn compute_exact_name_match(ctx: &CompletionContext<'_, '_>, completion_name: &str) -> bool { ctx.expected_name.as_ref().is_some_and(|name| name.text() == completion_name) } fn compute_ref_match( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, completion_ty: &hir::Type<'_>, ) -> Option { let expected_type = ctx.expected_type.as_ref()?; @@ -736,7 +736,7 @@ fn compute_ref_match( } fn path_ref_match( - completion: &CompletionContext<'_>, + completion: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, ty: &hir::Type<'_>, item: &mut Builder, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs index 134a77a8991e3..c14fc1704c5ba 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs @@ -6,12 +6,15 @@ use syntax::ToSmolStr; use crate::{item::CompletionItem, render::RenderContext}; -pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option { +pub(crate) fn render_const( + ctx: RenderContext<'_, '_>, + const_: hir::Const, +) -> Option { let _p = tracing::info_span!("render_const").entered(); render(ctx, const_) } -fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option { +fn render(ctx: RenderContext<'_, '_>, const_: hir::Const) -> Option { let db = ctx.db(); let name = const_.name(db)?; let (name, escaped_name) = diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 18151cffcd391..97d5a25f493d5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -27,7 +27,7 @@ enum FuncKind<'ctx> { } pub(crate) fn render_fn( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, local_name: Option, func: hir::Function, @@ -37,7 +37,7 @@ pub(crate) fn render_fn( } pub(crate) fn render_method( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, dot_access: &DotAccess<'_>, receiver: Option, local_name: Option, @@ -48,7 +48,7 @@ pub(crate) fn render_method( } fn render( - ctx @ RenderContext { completion, .. }: RenderContext<'_>, + ctx @ RenderContext { completion, .. }: RenderContext<'_, '_>, local_name: Option, func: hir::Function, func_kind: FuncKind<'_>, @@ -183,7 +183,7 @@ fn render( fn compute_return_type_match( db: &dyn HirDatabase, - ctx: &RenderContext<'_>, + ctx: &RenderContext<'_, '_>, self_type: hir::Type<'_>, ret_type: &hir::Type<'_>, ) -> CompletionRelevanceReturnType { @@ -210,7 +210,7 @@ fn compute_return_type_match( pub(super) fn add_call_parens<'b>( builder: &'b mut Builder, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, cap: SnippetCap, name: SmolStr, escaped_name: SmolStr, @@ -286,7 +286,7 @@ pub(super) fn add_call_parens<'b>( builder.label(SmolStr::from_iter([&name, label_suffix])).insert_snippet(cap, snippet) } -fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type<'_>) -> &'static str { +fn ref_of_param(ctx: &CompletionContext<'_, '_>, arg: &str, ty: &hir::Type<'_>) -> &'static str { if let Some(derefed_ty) = ty.remove_ref() { for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) { if name.as_str() == arg { @@ -301,7 +301,7 @@ fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type<'_>) -> & "" } -fn detail(ctx: &CompletionContext<'_>, func: hir::Function) -> String { +fn detail(ctx: &CompletionContext<'_, '_>, func: hir::Function) -> String { let mut ret_ty = func.ret_type(ctx.db); let mut detail = String::new(); @@ -327,7 +327,7 @@ fn detail(ctx: &CompletionContext<'_>, func: hir::Function) -> String { detail } -fn detail_full(ctx: &CompletionContext<'_>, func: hir::Function) -> String { +fn detail_full(ctx: &CompletionContext<'_, '_>, func: hir::Function) -> String { let signature = format!("{}", func.display(ctx.db, ctx.display_target)); let mut detail = String::with_capacity(signature.len()); @@ -342,7 +342,7 @@ fn detail_full(ctx: &CompletionContext<'_>, func: hir::Function) -> String { detail } -fn params_display(ctx: &CompletionContext<'_>, detail: &mut String, func: hir::Function) { +fn params_display(ctx: &CompletionContext<'_, '_>, detail: &mut String, func: hir::Function) { if let Some(self_param) = func.self_param(ctx.db) { format_to!(detail, "{}", self_param.display(ctx.db, ctx.display_target)); let assoc_fn_params = func.assoc_fn_params(ctx.db); @@ -368,7 +368,7 @@ fn params_display(ctx: &CompletionContext<'_>, detail: &mut String, func: hir::F } fn params<'db>( - ctx: &CompletionContext<'db>, + ctx: &CompletionContext<'_, 'db>, func: hir::Function, func_kind: &FuncKind<'_>, has_dot_receiver: bool, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index eb03e27cd9767..9e0cec62e6418 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs @@ -20,7 +20,7 @@ use crate::{ }; pub(crate) fn render_variant_lit( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, local_name: Option, variant: hir::EnumVariant, @@ -34,7 +34,7 @@ pub(crate) fn render_variant_lit( } pub(crate) fn render_struct_literal( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, strukt: hir::Struct, path: Option, @@ -48,7 +48,7 @@ pub(crate) fn render_struct_literal( } fn render( - ctx @ RenderContext { completion, .. }: RenderContext<'_>, + ctx @ RenderContext { completion, .. }: RenderContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, thing: Variant, name: hir::Name, @@ -154,7 +154,7 @@ enum Variant { } impl Variant { - fn fields(self, ctx: &CompletionContext<'_>) -> Option> { + fn fields(self, ctx: &CompletionContext<'_, '_>) -> Option> { let fields = match self { Variant::Struct(it) => it.fields(ctx.db), Variant::EnumVariant(it) => it.fields(ctx.db), @@ -187,7 +187,7 @@ impl Variant { } } - fn is_deprecated(self, ctx: &RenderContext<'_>) -> bool { + fn is_deprecated(self, ctx: &RenderContext<'_, '_>) -> bool { match self { Variant::Struct(it) => { ctx.is_deprecated(it, None /* structs can't be assoc items */) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs index ff4cf9a75b60e..85a0761c17e0b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs @@ -11,7 +11,7 @@ use crate::{ }; pub(crate) fn render_macro( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, PathCompletionCtx { kind, has_macro_bang, has_call_parens, .. }: &PathCompletionCtx<'_>, name: hir::Name, @@ -22,7 +22,7 @@ pub(crate) fn render_macro( } pub(crate) fn render_macro_pat( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, _pattern_ctx: &PatternContext, name: hir::Name, macro_: hir::Macro, @@ -32,7 +32,7 @@ pub(crate) fn render_macro_pat( } fn render( - ctx @ RenderContext { completion, .. }: RenderContext<'_>, + ctx @ RenderContext { completion, .. }: RenderContext<'_, '_>, is_use_path: bool, has_macro_bang: bool, has_call_parens: bool, @@ -91,7 +91,7 @@ fn render( } fn label( - ctx: &RenderContext<'_>, + ctx: &RenderContext<'_, '_>, needs_bang: bool, bra: &str, ket: &str, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index 022e97e4f7600..392ecbc302ae5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -15,7 +15,7 @@ use crate::{ }; pub(crate) fn render_struct_pat( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, pattern_ctx: &PatternContext, strukt: hir::Struct, local_name: Option, @@ -44,7 +44,7 @@ pub(crate) fn render_struct_pat( } pub(crate) fn render_variant_pat( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, pattern_ctx: &PatternContext, path_ctx: Option<&PathCompletionCtx<'_>>, variant: hir::EnumVariant, @@ -104,7 +104,7 @@ pub(crate) fn render_variant_pat( } fn build_completion( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, label: SmolStr, lookup: SmolStr, pat: String, @@ -140,7 +140,7 @@ fn build_completion( } fn render_pat( - ctx: &RenderContext<'_>, + ctx: &RenderContext<'_, '_>, pattern_ctx: &PatternContext, name: &str, kind: StructKind, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs index 2b79ca2deb693..ce1be2d55117d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs @@ -7,7 +7,7 @@ use syntax::{SmolStr, ToSmolStr}; use crate::{item::CompletionItem, render::RenderContext}; pub(crate) fn render_type_alias( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, type_alias: hir::TypeAlias, ) -> Option { let _p = tracing::info_span!("render_type_alias").entered(); @@ -15,7 +15,7 @@ pub(crate) fn render_type_alias( } pub(crate) fn render_type_alias_with_eq( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, type_alias: hir::TypeAlias, ) -> Option { let _p = tracing::info_span!("render_type_alias_with_eq").entered(); @@ -23,7 +23,7 @@ pub(crate) fn render_type_alias_with_eq( } fn render( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, type_alias: hir::TypeAlias, with_eq: bool, ) -> Option { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs index 7164c94fde946..e7ee59d489b95 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs @@ -14,7 +14,7 @@ use crate::{ }; pub(crate) fn render_union_literal( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, un: hir::Union, path: Option, local_name: Option, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs index ce35ab135f3c3..f86af6cdcb7d8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs @@ -17,7 +17,7 @@ pub(crate) struct RenderedLiteral { /// Render a record type (or sub-type) to a `RenderedCompound`. Use `None` for /// the `name` argument for an anonymous type. pub(crate) fn render_record_lit( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, snippet_cap: Option, fields: &[hir::Field], path: &str, @@ -63,7 +63,7 @@ pub(crate) fn render_record_lit( /// Render a tuple type (or sub-type) to a `RenderedCompound`. Use `None` for /// the `name` argument for an anonymous type. pub(crate) fn render_tuple_lit( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, snippet_cap: Option, fields: &[hir::Field], path: &str, @@ -93,7 +93,7 @@ pub(crate) fn render_tuple_lit( /// fields, plus a boolean for whether the list is comprehensive (contains no /// private fields and its item is not marked `#[non_exhaustive]`). pub(crate) fn visible_fields( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, fields: &[hir::Field], item: impl HasAttrs + HasCrate + Copy, ) -> Option<(Vec, bool)> { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index d326098f94071..67ca9db2304fb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -150,7 +150,7 @@ impl Snippet { } /// Returns [`None`] if the required items do not resolve. - pub(crate) fn imports(&self, ctx: &CompletionContext<'_>) -> Option> { + pub(crate) fn imports(&self, ctx: &CompletionContext<'_, '_>) -> Option> { import_edits(ctx, &self.requires) } @@ -163,7 +163,10 @@ impl Snippet { } } -fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option> { +fn import_edits( + ctx: &CompletionContext<'_, '_>, + requires: &[ModPath], +) -> Option> { let import_cfg = ctx.config.find_path_config(ctx.is_nightly); let resolve = |import| { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs index 8bd4c6c46b858..506645261b8d5 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs @@ -27,7 +27,7 @@ impl<'db> ActiveParameter<'db> { /// Returns information about the call argument this token is part of. pub fn at_arg( - sema: &'db Semantics<'db, RootDatabase>, + sema: &Semantics<'db, RootDatabase>, list: ast::ArgList, at: TextSize, ) -> Option { 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 e66f645c00567..83991a85e0f82 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), + db.expansion_span_map(file_id), self.target_scope.module().krate(db).into(), ) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index f41e29307007d..d59df3601fbbb 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -440,7 +440,7 @@ impl Definition { } } - pub fn usages<'a>(self, sema: &'a Semantics<'_, RootDatabase>) -> FindUsages<'a> { + pub fn usages<'a, 'db>(self, sema: &'a Semantics<'db, RootDatabase>) -> FindUsages<'a, 'db> { FindUsages { def: self, rename: None, @@ -456,10 +456,10 @@ impl Definition { } #[derive(Clone)] -pub struct FindUsages<'a> { +pub struct FindUsages<'a, 'db> { def: Definition, rename: Option<&'a Rename>, - sema: &'a Semantics<'a, RootDatabase>, + sema: &'a Semantics<'db, RootDatabase>, scope: Option<&'a SearchScope>, /// The container of our definition should it be an assoc item assoc_item_container: Option, @@ -473,7 +473,7 @@ pub struct FindUsages<'a> { exclude_library_files: bool, } -impl<'a> FindUsages<'a> { +impl<'a, 'db> FindUsages<'a, 'db> { /// Enable searching for `Self` when the definition is a type or `self` for modules. pub fn include_self_refs(mut self) -> Self { self.include_self_kw_refs = def_to_ty(self.sema, &self.def); @@ -858,7 +858,7 @@ impl<'a> FindUsages<'a> { } fn search( - this: &FindUsages<'_>, + this: &FindUsages<'_, '_>, finder: &Finder<'_>, name: &str, files: impl Iterator, EditionedFileId, TextRange)>, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs index 7cadff84fb07f..e6adac9da74fa 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticsContext, adjusted_display_range}; // // This diagnostic is triggered if the `await` keyword is used outside of an async function or block pub(crate) fn await_outside_of_async( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::AwaitOutsideOfAsync, ) -> Diagnostic { let display_range = diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs index ae42a88c313ce..c84b29dbe242d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: bad-rtn // // This diagnostic is shown when a RTN (Return Type Notation, `Type::method(..): Send`) is written in an improper place. -pub(crate) fn bad_rtn(ctx: &DiagnosticsContext<'_>, d: &hir::BadRtn) -> Diagnostic { +pub(crate) fn bad_rtn(ctx: &DiagnosticsContext<'_, '_>, d: &hir::BadRtn) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( ctx, DiagnosticCode::Ra("bad-rtn", Severity::Error), diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs index 02f3cab565080..b7265c47b6fb2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if the `break` keyword is used outside of a loop. pub(crate) fn break_outside_of_loop( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::BreakOutsideOfLoop, ) -> Diagnostic { let message = if d.bad_value_break { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs index b284d9b351031..8df99598590fa 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // This diagnostic is triggered when lifetimes are elided in paths. It is a lint only for some cases, // and a hard error for others. pub(crate) fn elided_lifetimes_in_path( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::ElidedLifetimesInPath, ) -> Diagnostic { if d.hard_error { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs index afd1687ae0736..25e9dc09eb034 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs @@ -6,7 +6,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if a call is made on something that is not callable. pub(crate) fn expected_function( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::ExpectedFunction<'_>, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs index 9ae6f013c70d5..515878fd47adf 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs @@ -12,7 +12,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // This diagnostic is shown when generic arguments are provided for a type that does not accept // generic arguments. pub(crate) fn generic_args_prohibited( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::GenericArgsProhibited, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( @@ -42,7 +42,7 @@ fn describe_reason(reason: GenericArgsProhibitedReason) -> String { format!("generic arguments are not allowed on {kind}") } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::GenericArgsProhibited) -> Option> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::GenericArgsProhibited) -> Option> { let file_id = d.args.file_id.file_id()?; let syntax = d.args.to_node(ctx.sema.db); let range = match &syntax { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs index 3d38159c4f195..926c517bc9bb6 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is shown when a generic default refers to `Self` pub(crate) fn generic_default_refers_to_self( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::GenericDefaultRefersToSelf, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs index be4fe763a0549..09f3e8bfb319b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs @@ -7,7 +7,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // // This diagnostic is shown for code with inactive `#[cfg]` attributes. pub(crate) fn inactive_code( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::InactiveCode, ) -> Option { // If there's inactive code somewhere in a macro, don't propagate to the call-site. diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs index a0c364b00108e..bd8b804af4368 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs @@ -6,7 +6,10 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran // Diagnostic: incoherent-impl // // This diagnostic is triggered if the targe type of an impl is from a foreign crate. -pub(crate) fn incoherent_impl(ctx: &DiagnosticsContext<'_>, d: &hir::IncoherentImpl) -> Diagnostic { +pub(crate) fn incoherent_impl( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::IncoherentImpl, +) -> Diagnostic { let display_range = adjusted_display_range(ctx, InFile::new(d.file_id, d.impl_), &|node| { Some(TextRange::new( node.syntax().text_range().start(), 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 5a456504e121f..bda3f9bf0ad04 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 @@ -13,7 +13,10 @@ use crate::{ // Diagnostic: incorrect-ident-case // // This diagnostic is triggered if an item name doesn't follow [Rust naming convention](https://doc.rust-lang.org/1.0.0/style/style/naming/README.html). -pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic { +pub(crate) fn incorrect_case( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::IncorrectCase, +) -> Diagnostic { let code = match d.expected_case { CaseType::LowerSnakeCase => DiagnosticCode::RustcLint("non_snake_case"), CaseType::UpperSnakeCase => DiagnosticCode::RustcLint("non_upper_case_globals"), @@ -33,7 +36,7 @@ pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCas .with_fixes(fixes(ctx, d)) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::IncorrectCase) -> Option> { let root = ctx.sema.db.parse_or_expand(d.file); 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/incorrect_generics_len.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs index 2a31a41fbc212..9c04d2be8aac0 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs @@ -5,7 +5,7 @@ use hir::IncorrectGenericsLenKind; // // This diagnostic is triggered if the number of generic arguments does not match their declaration. pub(crate) fn incorrect_generics_len( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::IncorrectGenericsLen, ) -> Diagnostic { let owner_description = d.def.description(); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs index b71586d6be0b6..c2b70a204e45b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs @@ -6,7 +6,7 @@ use syntax::SyntaxKind; // // This diagnostic is triggered the order of provided generic arguments does not match their declaration. pub(crate) fn incorrect_generics_order( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::IncorrectGenericsOrder, ) -> Diagnostic { let provided_description = match d.provided_arg.value.kind() { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs index 405d8df6854df..ba7556cd8b909 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -18,7 +18,10 @@ macro_rules! format_ty { // Diagnostic: invalid-cast // // This diagnostic is triggered if the code contains an illegal cast -pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast<'_>) -> Diagnostic { +pub(crate) fn invalid_cast( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::InvalidCast<'_>, +) -> Diagnostic { let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into())); let (code, message) = match d.error { CastError::CastToBool => ( @@ -111,7 +114,7 @@ pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast<'_ // // This diagnostic is triggered when casting to an unsized type pub(crate) fn cast_to_unsized( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::CastToUnsized<'_>, ) -> Diagnostic { let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into())); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs index 9aa7aed16964d..8522041b52985 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // This diagnostic is shown when the derive attribute is used on an item other than a `struct`, // `enum` or `union`. pub(crate) fn invalid_derive_target( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::InvalidDeriveTarget, ) -> Diagnostic { let display_range = ctx.sema.diagnostics_display_range_for_range(d.range); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs index 02716f2b86ac8..225d3e0b46b2c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if the left-hand side of an assignment can't be assigned to. pub(crate) fn invalid_lhs_of_assignment( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::InvalidLhsOfAssignment, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs index a44b043f433c6..b6571e02efbe6 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -11,7 +11,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: proc-macro-disabled // // This diagnostic is shown for proc macros that have been specifically disabled via `rust-analyzer.procMacro.ignored`. -pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic { +pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MacroError) -> Diagnostic { // Use more accurate position if available. let display_range = ctx.sema.diagnostics_display_range_for_range(d.range); Diagnostic::new( @@ -25,7 +25,10 @@ pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> // Diagnostic: macro-def-error // // This diagnostic is shown for macro expansion errors. -pub(crate) fn macro_def_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroDefError) -> Diagnostic { +pub(crate) fn macro_def_error( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MacroDefError, +) -> Diagnostic { // Use more accurate position if available. let display_range = match d.name { Some(name) => ctx.sema.diagnostics_display_range_for_range(d.node.with_value(name)), diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs index 7d0c71f4fa7c1..c7d8991f45a87 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is shown when the derive attribute has invalid input. pub(crate) fn malformed_derive( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::MalformedDerive, ) -> Diagnostic { let display_range = ctx.sema.diagnostics_display_range_for_range(d.range); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index b900a8f5cc10e..179f9dcec5f51 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -12,7 +12,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran // // This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. pub(crate) fn mismatched_tuple_struct_pat_arg_count( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::MismatchedTupleStructPatArgCount, ) -> Diagnostic { let s = if d.found == 1 { "" } else { "s" }; @@ -33,7 +33,7 @@ pub(crate) fn mismatched_tuple_struct_pat_arg_count( // // This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. pub(crate) fn mismatched_arg_count( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::MismatchedArgCount, ) -> Diagnostic { let s = if d.expected == 1 { "" } else { "s" }; @@ -47,7 +47,7 @@ pub(crate) fn mismatched_arg_count( } fn invalid_args_range( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, source: InFile>>, expected: usize, found: usize, 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 117702923b19e..206d8caf90e9b 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 @@ -33,7 +33,10 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // // let a = A { a: 10 }; // ``` -pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic { +pub(crate) fn missing_fields( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MissingFields, +) -> Diagnostic { let mut message = String::from("missing structure fields:\n"); for (field, _) in &d.missed_fields { format_to!(message, "- {}\n", field.display(ctx.sema.db, ctx.edition)); @@ -51,7 +54,7 @@ pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingField .with_fixes(fixes(ctx, d)) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingFields) -> Option> { // Note that although we could add a diagnostics to // fill the missing tuple field, e.g : // `struct A(usize);` @@ -202,7 +205,7 @@ fn make_ty( } fn get_default_constructor( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingFields, ty: &Type<'_>, ) -> Option { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs index b10cdaa14ee61..760bb7309d68b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered when a lifetime argument is missing. pub(crate) fn missing_lifetime( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingLifetime, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 8cd41f7aed90b..7bc7955c4ec85 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if `match` block is missing one or more match arms. pub(crate) fn missing_match_arms( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingMatchArms, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 3351f5dc1cfba..38cf548cc6ad8 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -10,7 +10,10 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // Diagnostic: missing-unsafe // // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. -pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic { +pub(crate) fn missing_unsafe( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MissingUnsafe, +) -> Diagnostic { let code = match d.lint { UnsafeLint::HardError => DiagnosticCode::RustcHardError("E0133"), UnsafeLint::UnsafeOpInUnsafeFn => DiagnosticCode::RustcLint("unsafe_op_in_unsafe_fn"), @@ -38,7 +41,7 @@ fn display_unsafety_reason(reason: UnsafetyReason) -> &'static str { } } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Option> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingUnsafe) -> Option> { // The fixit will not work correctly for macro expansions, so we don't offer it in that case. if d.node.file_id.is_macro() { return None; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index 6331090d9c90a..e61719acf50fd 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs @@ -5,7 +5,7 @@ use hir::HirDisplay; // // This diagnostic is triggered on moving non copy things out of references. pub(crate) fn moved_out_of_ref( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::MovedOutOfRef<'_>, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 9a5ad375dcb67..31becd1d74960 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -8,7 +8,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // Diagnostic: need-mut // // This diagnostic is triggered on mutating an immutable variable. -pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option { +pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_, '_>, d: &hir::NeedMut) -> Option { let root = ctx.sema.db.parse_or_expand(d.span.file_id); let node = d.span.value.to_node(&root); let mut span = d.span; @@ -63,7 +63,10 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option // Diagnostic: unused-mut // // This diagnostic is triggered when a mutable variable isn't actually mutated. -pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Option { +pub(crate) fn unused_mut( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::UnusedMut, +) -> Option { let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); let fixes = (|| { let file_id = ast.file_id.file_id()?; 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 944622bb1d586..7959fddc757f4 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 @@ -15,7 +15,7 @@ use crate::{ // Diagnostic: no-such-field // // This diagnostic is triggered if created structure does not have field provided in record. -pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic { +pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_, '_>, d: &hir::NoSuchField) -> Diagnostic { let (code, message) = if d.private.is_some() { ("E0451", "field is private") } else if let VariantId::EnumVariantId(_) = d.variant { @@ -30,7 +30,7 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) .with_fixes(fixes(ctx, d)) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::NoSuchField) -> Option> { // FIXME: quickfix for pattern let root = ctx.sema.db.parse_or_expand(d.field.file_id); match &d.field.value.to_node(&root) { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs index bc10e82854f5c..ee2f6bf3192d4 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs @@ -12,7 +12,7 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // This diagnostic is triggered if a `let` statement without an `else` branch has a non-exhaustive // pattern. pub(crate) fn non_exhaustive_let( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::NonExhaustiveLet, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs index 9dbce4d1f4215..be9c07b1ac6c2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // This diagnostic is triggered if a struct expression constructs a `#[non_exhaustive]` // struct from another crate. pub(crate) fn non_exhaustive_record_expr( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::NonExhaustiveRecordExpr, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs index 68f2b1965702c..44fc9f482b15e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // This diagnostic is shown when a `Fn`-trait-style generic parameters (`Trait(A, B) -> C`) // was used on non-`Fn` trait/type. pub(crate) fn parenthesized_generic_args_without_fn_trait( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::ParenthesizedGenericArgsWithoutFnTrait, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs index 36031865fca03..459ec175b158f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if a pattern was declared as an argument in a foreign function declaration. pub(crate) fn pattern_arg_in_extern_fn( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::PatternArgInExternFn, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs index 6d33ae0cf9bea..92f3c6961e363 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // This diagnostic is triggered if the referenced associated item is not visible from the current // module. pub(crate) fn private_assoc_item( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::PrivateAssocItem, ) -> Diagnostic { // FIXME: add quickfix diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs index 90c27bdcef794..9515afed763f3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs @@ -10,7 +10,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // Diagnostic: private-field // // This diagnostic is triggered if the accessed field is not visible from the current module. -pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField) -> Diagnostic { +pub(crate) fn private_field(ctx: &DiagnosticsContext<'_, '_>, d: &hir::PrivateField) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( ctx, DiagnosticCode::RustcHardError("E0616"), 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 2ec41d0528496..fb1470b69f87e 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 @@ -10,7 +10,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran // This diagnostic is triggered when there is a redundant `return` at the end of a function // or closure. pub(crate) fn remove_trailing_return( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &RemoveTrailingReturn, ) -> Option { if d.return_expr.file_id.macro_file().is_some() { @@ -36,7 +36,7 @@ pub(crate) fn remove_trailing_return( ) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &RemoveTrailingReturn) -> Option> { let root = ctx.sema.db.parse_or_expand(d.return_expr.file_id); 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 04f48ae3db170..aa7b57e2928f8 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 @@ -19,7 +19,7 @@ use crate::{ // This diagnostic is triggered when there is an `else` block for an `if` expression whose // then branch diverges (e.g. ends with a `return`, `continue`, `break` e.t.c). pub(crate) fn remove_unnecessary_else( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &RemoveUnnecessaryElse, ) -> Option { if d.if_expr.file_id.macro_file().is_some() { @@ -40,7 +40,7 @@ pub(crate) fn remove_unnecessary_else( ) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse) -> Option> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &RemoveUnnecessaryElse) -> Option> { let root = ctx.sema.db.parse_or_expand(d.if_expr.file_id); 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 37ce5f583f93a..f974c55023135 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 @@ -12,7 +12,7 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // // This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`. pub(crate) fn replace_filter_map_next_with_find_map( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::ReplaceFilterMapNextWithFindMap, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( @@ -26,7 +26,7 @@ pub(crate) fn replace_filter_map_next_with_find_map( } fn fixes( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::ReplaceFilterMapNextWithFindMap, ) -> Option> { let root = ctx.sema.db.parse_or_expand(d.file); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs index c5b2f499d3067..9e7393c89c154 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs @@ -7,7 +7,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity, adjusted_d // // Diagnoses incorrect safety annotations of trait impls. pub(crate) fn trait_impl_incorrect_safety( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TraitImplIncorrectSafety, ) -> Diagnostic { Diagnostic::new( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs index 2c05544701872..5f5e155bd79ea 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs @@ -8,7 +8,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran // // Diagnoses missing trait items in a trait impl. pub(crate) fn trait_impl_missing_assoc_item( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TraitImplMissingAssocItems, ) -> Diagnostic { let missing = d.missing.iter().format_with(", ", |(name, item), f| { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs index 96911d4781b8a..a9dc0d5d72841 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs @@ -6,7 +6,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // Only traits defined in the current crate can be implemented for arbitrary types pub(crate) fn trait_impl_orphan( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TraitImplOrphan, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( 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 6a380481d4c13..ee972f2d1dc69 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 @@ -16,7 +16,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // Diagnoses redundant trait items in a trait impl. pub(crate) fn trait_impl_redundant_assoc_item( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TraitImplRedundantAssocItems, ) -> Diagnostic { let db = ctx.sema.db; @@ -74,7 +74,7 @@ pub(crate) fn trait_impl_redundant_assoc_item( /// add assoc item into the trait def body fn quickfix_for_redundant_assoc_item( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TraitImplRedundantAssocItems, redundant_item_def: String, range: TextRange, 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 a845e0b59aca2..d469405d61ad9 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 @@ -20,7 +20,7 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_dis // This diagnostic is triggered when the type of an expression or pattern does not match // the expected type. pub(crate) fn type_mismatch( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, ) -> Option { if d.expected.is_unknown() || d.actual.is_unknown() { @@ -64,7 +64,7 @@ pub(crate) fn type_mismatch( ) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch<'_>) -> Option> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option> { let mut fixes = Vec::new(); if let Some(expr_ptr) = d.expr_or_pat.value.cast::() { @@ -80,7 +80,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch<'_>) -> Option, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile>, acc: &mut Vec, @@ -102,7 +102,7 @@ fn add_reference( } fn add_missing_ok_or_some( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile>, acc: &mut Vec, @@ -197,7 +197,7 @@ fn add_missing_ok_or_some( } fn remove_unnecessary_wrapper( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile>, acc: &mut Vec, @@ -279,7 +279,7 @@ fn remove_unnecessary_wrapper( } fn remove_semicolon( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile>, acc: &mut Vec, @@ -310,7 +310,7 @@ fn remove_semicolon( } fn str_ref_to_owned( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile>, acc: &mut Vec, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs index 5363f4a5cec58..b4c676cf48eea 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs @@ -9,7 +9,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered when rust-analyzer cannot infer some type. pub(crate) fn type_must_be_known<'db>( - ctx: &DiagnosticsContext<'db>, + ctx: &DiagnosticsContext<'db, '_>, d: &hir::TypeMustBeKnown<'db>, ) -> Diagnostic { let mut at_point = d.at_point.map(|it| it.syntax_node_ptr()); 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 ddd1dd402ea4e..e000d6388ae69 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 @@ -20,7 +20,10 @@ use syntax::AstNode; // Diagnostic: typed-hole // // This diagnostic is triggered when an underscore expression is used in an invalid position. -pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole<'_>) -> Diagnostic { +pub(crate) fn typed_hole<'db>( + ctx: &DiagnosticsContext<'_, 'db>, + d: &hir::TypedHole<'db>, +) -> Diagnostic { let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into())); let (message, fixes) = if d.expected.is_unknown() { ("`_` expressions may only appear on the left-hand side of an assignment".to_owned(), None) @@ -41,7 +44,7 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole<'_>) - .with_fixes(fixes) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole<'_>) -> Option> { +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 (original_range, _) = diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs index f81d34377da49..1bab4f453f9a6 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs @@ -2,7 +2,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: undeclared-label pub(crate) fn undeclared_label( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UndeclaredLabel, ) -> Diagnostic { let name = &d.name; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs index 5627393f31818..b652456c09e95 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // // This diagnostic is shown for builtin macros which are not yet implemented by rust-analyzer pub(crate) fn unimplemented_builtin_macro( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnimplementedBuiltinMacro, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 570319c347d49..dc6ae6f08ba5e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -20,7 +20,7 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity, fi // This diagnostic is shown for files that are not included in any crate, or files that are part of // crates rust-analyzer failed to discover. The file will not have IDE features available. pub(crate) fn unlinked_file( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, acc: &mut Vec, file_id: FileId, ) { @@ -73,7 +73,7 @@ pub(crate) fn unlinked_file( } fn fixes( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, file_id: FileId, trigger_range: TextRange, ) -> Option> { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs index 0c9e0d6ce440c..52138b7cd5184 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs @@ -2,7 +2,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unreachable-label pub(crate) fn unreachable_label( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnreachableLabel, ) -> Diagnostic { let name = &d.name; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs index f181021bdc5cd..7797c665fd0af 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if the referenced associated item does not exist. pub(crate) fn unresolved_assoc_item( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedAssocItem, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs index 7c3eacf7e3ad4..2c1f1e72835d2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate. pub(crate) fn unresolved_extern_crate( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedExternCrate, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( 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 59ec259adf36e..78e13677cfb1d 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 @@ -22,7 +22,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran // // This diagnostic is triggered if a field does not exist on a given type. pub(crate) fn unresolved_field( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedField<'_>, ) -> Diagnostic { let method_suffix = if d.method_with_same_name_exists { @@ -52,7 +52,7 @@ pub(crate) fn unresolved_field( .with_fixes(fixes(ctx, d)) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField<'_>) -> Option> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedField<'_>) -> Option> { let mut fixes = Vec::new(); if d.method_with_same_name_exists { fixes.extend(method_fix(ctx, &d.expr)); @@ -62,7 +62,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField<'_>) -> Option, d: &hir::UnresolvedField<'_>) -> Option { +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 expr = d.expr.value.to_node(&root).left()?; @@ -101,7 +101,7 @@ fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField<'_>) -> Opti } fn add_variant_to_union( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, adt_union: Union, field_name: &str, suggested_type: Type, @@ -129,7 +129,7 @@ fn add_variant_to_union( } fn add_field_to_struct_fix( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, adt_struct: Struct, field_name: &str, suggested_type: Type, @@ -263,7 +263,7 @@ fn record_field_layout( // FIXME: We should fill out the call here, move the cursor and trigger signature help fn method_fix( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, expr_ptr: &InFile>>, ) -> Option { let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs index 801023dabd96f..6ecf0be825e09 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if an expr-position ident is invalid. pub(crate) fn unresolved_ident( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedIdent, ) -> Diagnostic { let mut range = diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs index 0da535d11b4fe..f9a125de13f4a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // This diagnostic is triggered if rust-analyzer is unable to resolve a path in // a `use` declaration. pub(crate) fn unresolved_import( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedImport, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs index 030c82ca0ba79..9be7ef6fe7181 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // This diagnostic is triggered if rust-analyzer is unable to resolve the path // to a macro in a macro invocation. pub(crate) fn unresolved_macro_call( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedMacroCall, ) -> Diagnostic { let display_range = ctx.sema.diagnostics_display_range_for_range(d.range); 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 bd5d134348e27..93caf281f035f 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 @@ -17,7 +17,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran // // This diagnostic is triggered if a method does not exist on a given type. pub(crate) fn unresolved_method( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedMethodCall<'_>, ) -> Diagnostic { let suffix = if d.field_with_same_name.is_some() { @@ -49,7 +49,10 @@ pub(crate) fn unresolved_method( .with_fixes(fixes(ctx, d)) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall<'_>) -> Option> { +fn fixes( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::UnresolvedMethodCall<'_>, +) -> Option> { let field_fix = if let Some(ty) = &d.field_with_same_name { field_fix(ctx, d, ty) } else { @@ -71,7 +74,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall<'_>) -> Opt } fn field_fix( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedMethodCall<'_>, ty: &hir::Type<'_>, ) -> Option { @@ -108,7 +111,7 @@ fn field_fix( } fn assoc_func_fix( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedMethodCall<'_>, ) -> Option { if let Some(f) = d.assoc_func_with_same_name { 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 1a409d7e76a2f..1e0e9105d88b5 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 @@ -9,7 +9,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // // This diagnostic is triggered if rust-analyzer is unable to discover referred module. pub(crate) fn unresolved_module( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedModule, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( @@ -32,7 +32,7 @@ pub(crate) fn unresolved_module( .with_fixes(fixes(ctx, d)) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedModule) -> Option> { let root = ctx.sema.db.parse_or_expand(d.decl.file_id); let unresolved_module = d.decl.value.to_node(&root); Some( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs index 52a2f44fd0f8c..afc74445f4297 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -14,7 +14,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered when a local variable is not used. pub(crate) fn unused_variables( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnusedVariable, ) -> Option { let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); 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 7c07f8fd6c0e1..335e4b047cc3c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -195,7 +195,7 @@ impl Diagnostic { } fn new_with_syntax_node_ptr( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, code: DiagnosticCode, message: impl Into, node: InFile, @@ -281,17 +281,17 @@ impl DiagnosticsConfig { } } -struct DiagnosticsContext<'a> { +struct DiagnosticsContext<'a, 'db> { config: &'a DiagnosticsConfig, - sema: Semantics<'a, RootDatabase>, + sema: Semantics<'db, RootDatabase>, resolve: &'a AssistResolveStrategy, edition: Edition, display_target: DisplayTarget, is_nightly: bool, } -impl<'a> DiagnosticsContext<'a> { - fn db(&self) -> &'a RootDatabase { +impl<'db> DiagnosticsContext<'_, 'db> { + fn db(&self) -> &'db RootDatabase { self.sema.db } } @@ -778,7 +778,7 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist { } fn adjusted_display_range( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, diag_ptr: InFile>, adj: &dyn Fn(N) -> Option, ) -> FileRange { 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 cc322d2b9ebcc..8415220c14238 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -72,7 +72,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< SyntaxKind::MACRO_ITEMS, position.file_id, expansion, - &expansion_span_map, + expansion_span_map, krate, ); if let Some(err) = err { @@ -163,7 +163,7 @@ fn expand_macro_recur( result_span_map.merge( TextRange::at(offset_in_original_node, macro_call.syntax().text_range().len()), expanded.text_range().len(), - &expansion_span_map, + expansion_span_map, ); Some(expand(sema, expanded, error, result_span_map, u32::from(offset_in_original_node) as i32)) } 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 e6ef7b894913e..12ce457ea6bac 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -695,14 +695,14 @@ fn merge_map(res: &mut HighlightMap, new: Option) { /// Preorder walk all the expression's child expressions. /// For macro calls, the callback will be called on the expanded expressions after /// visiting the macro call itself. -struct WalkExpandedExprCtx<'a> { - sema: &'a Semantics<'a, RootDatabase>, +struct WalkExpandedExprCtx<'a, 'db> { + sema: &'a Semantics<'db, RootDatabase>, depth: usize, check_ctx: &'static dyn Fn(&ast::Expr) -> bool, } -impl<'a> WalkExpandedExprCtx<'a> { - fn new(sema: &'a Semantics<'a, RootDatabase>) -> Self { +impl<'a, 'db> WalkExpandedExprCtx<'a, 'db> { + fn new(sema: &'a Semantics<'db, RootDatabase>) -> Self { Self { sema, depth: 0, check_ctx: &is_closure_or_blk_with_modif } } 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 4d712bf0f0e0c..4f60321e3250d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -544,7 +544,7 @@ pub(super) fn definition( 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); - body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into()); + body = prettify_macro_expansion(db, body, span_map, it.krate(db).into()); } if env::var_os("RA_DEV").is_some() { format!("{body}\n{}", render_const_eval_error(db, err, display_target)) @@ -576,7 +576,7 @@ pub(super) fn definition( 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); - body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into()); + body = prettify_macro_expansion(db, body, span_map, it.krate(db).into()); } if env::var_os("RA_DEV").is_some() { format!("{body}\n{}", render_const_eval_error(db, err, display_target)) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 0d2239c71fe9e..a15366fea9621 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -687,21 +687,21 @@ impl fmt::Debug for InlayHintLabelPart { } #[derive(Debug)] -struct InlayHintLabelBuilder<'a> { - sema: &'a Semantics<'a, RootDatabase>, +struct InlayHintLabelBuilder<'a, 'db> { + sema: &'a Semantics<'db, RootDatabase>, result: InlayHintLabel, last_part: String, resolve: bool, location: Option>, } -impl fmt::Write for InlayHintLabelBuilder<'_> { +impl fmt::Write for InlayHintLabelBuilder<'_, '_> { fn write_str(&mut self, s: &str) -> fmt::Result { self.last_part.write_str(s) } } -impl HirWrite for InlayHintLabelBuilder<'_> { +impl HirWrite for InlayHintLabelBuilder<'_, '_> { fn start_location_link(&mut self, def: ModuleDefId) { never!(self.location.is_some(), "location link is already started"); self.make_new_part(); @@ -737,7 +737,7 @@ impl HirWrite for InlayHintLabelBuilder<'_> { } } -impl InlayHintLabelBuilder<'_> { +impl InlayHintLabelBuilder<'_, '_> { fn make_new_part(&mut self) { let text = take(&mut self.last_part); if !text.is_empty() { @@ -755,18 +755,18 @@ impl InlayHintLabelBuilder<'_> { } } -fn label_of_ty( - famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, +fn label_of_ty<'db>( + famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, 'db>, config: &InlayHintsConfig<'_>, - ty: &hir::Type<'_>, + ty: &hir::Type<'db>, display_target: DisplayTarget, ) -> Option { - fn rec( - sema: &Semantics<'_, RootDatabase>, - famous_defs: &FamousDefs<'_, '_>, + fn rec<'db>( + sema: &Semantics<'db, RootDatabase>, + famous_defs: &FamousDefs<'_, 'db>, mut max_length: Option, - ty: &hir::Type<'_>, - label_builder: &mut InlayHintLabelBuilder<'_>, + ty: &hir::Type<'db>, + label_builder: &mut InlayHintLabelBuilder<'_, '_>, config: &InlayHintsConfig<'_>, display_target: DisplayTarget, ) -> Result<(), HirDisplayError> { @@ -790,7 +790,7 @@ fn label_of_ty( ) }); - let module_def_location = |label_builder: &mut InlayHintLabelBuilder<'_>, + let module_def_location = |label_builder: &mut InlayHintLabelBuilder<'_, '_>, def: ModuleDef, name| { let def = def.try_into(); diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index 936cf178f015a..76fdac097ff71 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -374,6 +374,13 @@ impl ValueResult { ValueResult { value: f(self.value), err: self.err } } + pub fn as_ref(&self) -> ValueResult<&T, E> + where + E: Clone, + { + ValueResult { value: &self.value, err: self.err.clone() } + } + pub fn map_err(self, f: impl FnOnce(E) -> E2) -> ValueResult { ValueResult { value: self.value, err: self.err.map(f) } } From c12a8f9759795dee55632fb4b11d1d91812e5ce7 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 1 May 2026 13:16:41 +0900 Subject: [PATCH 145/289] Remove impl of `Predicate::allow_normalization` --- .../hir-ty/src/next_solver/predicate.rs | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index 8658d03a9e3e8..2abd9bdd9401a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -705,34 +705,6 @@ impl<'db> rustc_type_ir::inherent::Predicate> for Predicate<'db> _ => None, } } - - /// Whether this projection can be soundly normalized. - /// - /// Wf predicates must not be normalized, as normalization - /// can remove required bounds which would cause us to - /// unsoundly accept some programs. See #91068. - fn allow_normalization(self) -> bool { - // TODO: this should probably live in rustc_type_ir - match self.inner().as_ref().skip_binder() { - PredicateKind::Clause(ClauseKind::WellFormed(_)) | PredicateKind::AliasRelate(..) => { - false - } - PredicateKind::Clause(ClauseKind::Trait(_)) - | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) - | PredicateKind::Clause(ClauseKind::TypeOutlives(_)) - | PredicateKind::Clause(ClauseKind::Projection(_)) - | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) - | PredicateKind::Clause(ClauseKind::HostEffect(..)) - | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) - | PredicateKind::DynCompatible(_) - | PredicateKind::Subtype(_) - | PredicateKind::Coerce(_) - | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_)) - | PredicateKind::ConstEquate(_, _) - | PredicateKind::NormalizesTo(..) - | PredicateKind::Ambiguous => true, - } - } } impl<'db> Predicate<'db> { From 99eb67375c2b5b72e8580f24d5aee96849e4c686 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Apr 2026 04:21:10 +0300 Subject: [PATCH 146/289] Remove more `Arc`s --- .../hir-def/src/nameres/attr_resolution.rs | 3 +- .../hir-def/src/nameres/tests/incremental.rs | 48 ++--- .../rust-analyzer/crates/hir-expand/src/db.rs | 101 +++++----- .../crates/hir-expand/src/declarative.rs | 9 +- .../crates/hir-expand/src/eager.rs | 9 +- .../crates/hir-expand/src/fixup.rs | 14 +- .../crates/hir-expand/src/lib.rs | 6 +- .../crates/hir-ty/src/consteval.rs | 3 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 43 ++-- .../crates/hir-ty/src/display.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 4 +- .../crates/hir-ty/src/layout/adt.rs | 2 +- .../crates/hir-ty/src/layout/target.rs | 6 +- .../rust-analyzer/crates/hir-ty/src/mir.rs | 3 - .../crates/hir-ty/src/mir/borrowck.rs | 82 +++++--- .../crates/hir-ty/src/mir/eval.rs | 183 ++++++++++-------- .../crates/hir-ty/src/mir/eval/shim.rs | 20 +- .../crates/hir-ty/src/mir/eval/shim/simd.rs | 4 +- .../crates/hir-ty/src/mir/lower.rs | 26 ++- .../crates/hir-ty/src/mir/monomorphization.rs | 25 ++- .../crates/hir-ty/src/mir/pretty.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 22 +-- .../crates/hir/src/term_search/tactics.rs | 5 +- .../crates/ide/src/hover/render.rs | 10 +- .../crates/ide/src/view_memory_layout.rs | 2 +- 25 files changed, 344 insertions(+), 290 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs index 062b55fcefdf2..5aabd7dbc6fcc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs @@ -9,7 +9,6 @@ use hir_expand::{ }; use span::SyntaxContext; use syntax::ast; -use triomphe::Arc; use crate::{ AstIdWithPath, MacroId, ModuleId, UnresolvedMacro, @@ -126,7 +125,7 @@ pub(super) fn attr_macro_as_call_id( krate, MacroCallKind::Attr { ast_id: item_attr.ast_id, - attr_args: arg.map(Arc::new), + attr_args: arg.map(Box::new), censored_attr_ids, }, macro_attr.ctxt, 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 60ec3035faed6..08f672aa0c0df 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 {} "ast_id_map", "parse", "real_span_map", - "decl_macro_expander_shim", + "DeclarativeMacroExpander::expander_", "file_item_tree_query", "ast_id_map", "parse", @@ -240,7 +240,7 @@ pub struct S {} "file_item_tree_query", "ast_id_map", "parse_macro_expansion", - "macro_arg_shim", + "macro_arg", ] "#]], expect![[r#" @@ -249,7 +249,7 @@ pub struct S {} "ast_id_map", "file_item_tree_query", "real_span_map", - "macro_arg_shim", + "macro_arg", "parse_macro_expansion", "ast_id_map", "file_item_tree_query", @@ -303,8 +303,8 @@ fn f() { foo } "file_item_tree_query", "ast_id_map", "parse_macro_expansion", - "expand_proc_macro_shim", - "macro_arg_shim", + "expand_proc_macro", + "macro_arg", "proc_macro_span_shim", ] "#]], @@ -314,8 +314,8 @@ fn f() { foo } "ast_id_map", "file_item_tree_query", "real_span_map", - "macro_arg_shim", - "expand_proc_macro_shim", + "macro_arg", + "expand_proc_macro", "parse_macro_expansion", "ast_id_map", "file_item_tree_query", @@ -415,7 +415,7 @@ pub struct S {} "ast_id_map", "parse", "real_span_map", - "decl_macro_expander_shim", + "DeclarativeMacroExpander::expander_", "file_item_tree_query", "ast_id_map", "parse", @@ -428,19 +428,19 @@ pub struct S {} "file_item_tree_query", "ast_id_map", "parse_macro_expansion", - "macro_arg_shim", - "decl_macro_expander_shim", + "macro_arg", + "DeclarativeMacroExpander::expander_", "macro_def_shim", "file_item_tree_query", "ast_id_map", "parse_macro_expansion", - "macro_arg_shim", + "macro_arg", "macro_def_shim", "file_item_tree_query", "ast_id_map", "parse_macro_expansion", - "expand_proc_macro_shim", - "macro_arg_shim", + "expand_proc_macro", + "macro_arg", "proc_macro_span_shim", ] "#]], @@ -450,10 +450,10 @@ pub struct S {} "ast_id_map", "file_item_tree_query", "real_span_map", - "macro_arg_shim", - "decl_macro_expander_shim", - "macro_arg_shim", - "macro_arg_shim", + "macro_arg", + "DeclarativeMacroExpander::expander_", + "macro_arg", + "macro_arg", ] "#]], ); @@ -526,7 +526,7 @@ m!(Z); "ast_id_map", "parse", "real_span_map", - "decl_macro_expander_shim", + "DeclarativeMacroExpander::expander_", "file_item_tree_query", "ast_id_map", "parse", @@ -539,15 +539,15 @@ m!(Z); "file_item_tree_query", "ast_id_map", "parse_macro_expansion", - "macro_arg_shim", + "macro_arg", "file_item_tree_query", "ast_id_map", "parse_macro_expansion", - "macro_arg_shim", + "macro_arg", "file_item_tree_query", "ast_id_map", "parse_macro_expansion", - "macro_arg_shim", + "macro_arg", ] "#]], ); @@ -575,9 +575,9 @@ m!(Z); "ast_id_map", "file_item_tree_query", "real_span_map", - "macro_arg_shim", - "macro_arg_shim", - "macro_arg_shim", + "macro_arg", + "macro_arg", + "macro_arg", ] "#]], ); 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 57c78748f8437..4b26c1f0e5d1a 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -3,6 +3,7 @@ use base_db::{Crate, SourceDatabase}; use mbe::MatchedArmIndex; use span::{AstIdMap, Edition, Span, SyntaxContext}; +use std::borrow::Cow; use syntax::{AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, ast}; use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree}; use triomphe::Arc; @@ -21,7 +22,7 @@ use crate::{ tt, }; /// This is just to ensure the types of smart_macro_arg and macro_arg are the same -type MacroArgResult = (Arc, SyntaxFixupUndoInfo, Span); +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 @@ -30,10 +31,10 @@ type MacroArgResult = (Arc, SyntaxFixupUndoInfo, Span); /// Actual max for `analysis-stats .` at some point: 30672. const TOKEN_LIMIT: usize = 2_097_152; -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum TokenExpander { +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum TokenExpander<'db> { /// Old-style `macro_rules` or the new macros 2.0 - DeclarativeMacro(Arc), + DeclarativeMacro(&'db DeclarativeMacroExpander), /// Stuff like `line!` and `file!`. BuiltIn(BuiltinFnLikeExpander), /// Built-in eagerly expanded fn-like macros (`include!`, `concat!`, etc.) @@ -99,34 +100,37 @@ pub trait ExpandDatabase: SourceDatabase { /// subtree. #[deprecated = "calling this is incorrect, call `macro_arg_considering_derives` instead"] #[salsa::invoke(macro_arg)] - fn macro_arg(&self, id: MacroCallId) -> MacroArgResult; + #[salsa::transparent] + fn macro_arg(&self, id: MacroCallId) -> &MacroArgResult; #[salsa::transparent] - fn macro_arg_considering_derives( - &self, + fn macro_arg_considering_derives<'db>( + &'db self, id: MacroCallId, kind: &MacroCallKind, - ) -> MacroArgResult; + ) -> &'db MacroArgResult; /// Fetches the expander for this macro. #[salsa::transparent] #[salsa::invoke(TokenExpander::macro_expander)] - fn macro_expander(&self, id: MacroDefId) -> TokenExpander; + fn macro_expander(&self, id: MacroDefId) -> TokenExpander<'_>; /// 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, - ) -> Arc; + ) -> &DeclarativeMacroExpander; /// Special case of the previous query 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::invoke(expand_proc_macro)] - fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult>; + #[salsa::transparent] + fn expand_proc_macro(&self, call: MacroCallId) -> &ExpandResult; /// 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 @@ -135,12 +139,12 @@ pub trait ExpandDatabase: SourceDatabase { #[salsa::invoke_interned(proc_macro_span)] fn proc_macro_span(&self, fun: AstId) -> Span; - /// Firewall query that returns the errors from the `parse_macro_expansion` query. #[salsa::invoke(parse_macro_expansion_error)] + #[salsa::transparent] fn parse_macro_expansion_error( &self, macro_call: MacroCallId, - ) -> Option>>>; + ) -> Option>>; #[salsa::transparent] fn syntax_context(&self, file: HirFileId, edition: Edition) -> SyntaxContext; @@ -179,7 +183,7 @@ pub fn expand_speculative( token_to_map: SyntaxToken, ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> { let loc = db.lookup_intern_macro_call(actual_macro_call); - let (_, _, span) = db.macro_arg_considering_derives(actual_macro_call, &loc.kind); + let (_, _, span) = *db.macro_arg_considering_derives(actual_macro_call, &loc.kind); let span_map = RealSpanMap::absolute(span.anchor.file_id); let span_map = SpanMap::RealSpanMap(&span_map); @@ -369,14 +373,7 @@ fn parse_macro_expansion( 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, - match &tt { - CowArc::Arc(it) => it, - CowArc::Owned(it) => it, - }, - expand_to, - ); + 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 } @@ -385,10 +382,10 @@ fn parse_macro_expansion( fn parse_macro_expansion_error( db: &dyn ExpandDatabase, macro_call_id: MacroCallId, -) -> Option>>> { +) -> 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(Arc::new(e)) } + if e.value.is_empty() && e.err.is_none() { None } else { Some(e) } } pub(crate) fn parse_with_map( @@ -411,11 +408,11 @@ pub(crate) fn parse_with_map( /// /// This is not connected to the database so it does not cached the result. However, the inner [macro_arg] query is #[allow(deprecated)] // we are macro_arg_considering_derives -fn macro_arg_considering_derives( - db: &dyn ExpandDatabase, +fn macro_arg_considering_derives<'db>( + db: &'db dyn ExpandDatabase, id: MacroCallId, kind: &MacroCallKind, -) -> MacroArgResult { +) -> &'db MacroArgResult { match kind { // Get the macro arg for the derive macro MacroCallKind::Derive { derive_macro_id, .. } => db.macro_arg(*derive_macro_id), @@ -424,6 +421,7 @@ fn macro_arg_considering_derives( } } +#[salsa_macros::tracked(returns(ref))] fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let loc = db.lookup_intern_macro_call(id); @@ -449,10 +447,10 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let dummy_tt = |kind| { ( - Arc::new(tt::TopSubtree::from_token_trees( + tt::TopSubtree::from_token_trees( tt::Delimiter { open: span, close: span, kind }, tt::TokenTreesView::empty(), - )), + ), SyntaxFixupUndoInfo::default(), span, ) @@ -501,7 +499,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { // proc macros expect their inputs without parentheses, MBEs expect it with them included tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); } - return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span); + return (tt, SyntaxFixupUndoInfo::NONE, span); } // MacroCallKind::Derive should not be here. As we are getting the argument for the derive macro MacroCallKind::Derive { .. } => { @@ -536,11 +534,11 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); } - (Arc::new(tt), undo_info, span) + (tt, undo_info, span) } -impl TokenExpander { - fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander { +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)) @@ -554,27 +552,26 @@ impl TokenExpander { } } -enum CowArc { - Arc(Arc), - Owned(T), -} - fn macro_expand( db: &dyn ExpandDatabase, macro_call_id: MacroCallId, loc: MacroCallLoc, -) -> ExpandResult<(CowArc, MatchedArmIndex)> { +) -> ExpandResult<(Cow<'_, 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 db.expand_proc_macro(macro_call_id).map(CowArc::Arc).zip_val(None); + return db + .expand_proc_macro(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 arg = macro_arg; let res = match loc.def.kind { MacroDefKind::Declarative(id, _) => db .decl_macro_expander(loc.def.krate, id) @@ -594,7 +591,7 @@ fn macro_expand( // As such we just return the input subtree here. let eager = match &loc.kind { MacroCallKind::FnLike { eager: None, .. } => { - return ExpandResult::ok(CowArc::Arc(macro_arg.clone())).zip_val(None); + return ExpandResult::ok(Cow::Borrowed(macro_arg)).zip_val(None); } MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager), _ => None, @@ -610,7 +607,7 @@ fn macro_expand( } MacroDefKind::BuiltInAttr(_, it) => { let mut res = it.expand(db, macro_call_id, arg, span); - fixup::reverse_fixups(&mut res.value, &undo_info); + fixup::reverse_fixups(&mut res.value, undo_info); res.zip_val(None) } MacroDefKind::ProcMacro(_, _, _) => unreachable!(), @@ -624,12 +621,12 @@ fn macro_expand( // Set a hard limit for the expanded tt if let Err(value) = check_tt_count(&tt) { return value - .map(|()| CowArc::Owned(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)))) + .map(|()| Cow::Owned(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)))) .zip_val(matched_arm); } } - ExpandResult { value: (CowArc::Owned(tt), matched_arm), err } + ExpandResult { value: (Cow::Owned(tt), matched_arm), err } } fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId) -> Span { @@ -643,10 +640,8 @@ fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId) -> Span { span_map.span_for_range(range) } -fn expand_proc_macro( - db: &dyn ExpandDatabase, - id: MacroCallId, -) -> ExpandResult> { +#[salsa_macros::tracked(returns(ref))] +fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult { let loc = db.lookup_intern_macro_call(id); let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(id, &loc.kind); @@ -666,7 +661,7 @@ fn expand_proc_macro( db, loc.def.krate, loc.krate, - ¯o_arg, + 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), @@ -676,12 +671,12 @@ fn expand_proc_macro( // Set a hard limit for the expanded tt if let Err(value) = check_tt_count(&tt) { - return value.map(|()| Arc::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)))); + return value.map(|()| tt::TopSubtree::empty(tt::DelimSpan::from_single(*span))); } - fixup::reverse_fixups(&mut tt, &undo_info); + fixup::reverse_fixups(&mut tt, undo_info); - ExpandResult { value: Arc::new(tt), err } + ExpandResult { value: tt, err } } pub(crate) fn token_tree_to_syntax_node( 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 aa745a2ccdc6a..ead10a51a83c1 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -10,7 +10,6 @@ use syntax::{ ast::{self, HasAttrs}, }; use syntax_bridge::DocCommentDesugarMode; -use triomphe::Arc; use crate::{ AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, Lookup, MacroCallId, @@ -78,12 +77,16 @@ impl DeclarativeMacroExpander { .map_err(Into::into), } } +} +#[salsa::tracked] +impl DeclarativeMacroExpander { + #[salsa::tracked(returns(ref))] pub(crate) fn expander( db: &dyn ExpandDatabase, def_crate: Crate, id: AstId, - ) -> Arc { + ) -> DeclarativeMacroExpander { let (root, map) = crate::db::parse_with_map(db, id.file_id); let root = root.syntax_node(); @@ -177,6 +180,6 @@ impl DeclarativeMacroExpander { HirFileId::MacroFile(macro_file) => macro_file.lookup(db).ctxt, HirFileId::FileId(file) => SyntaxContext::root(file.edition(db)), }); - Arc::new(DeclarativeMacroExpander { mac, transparency, edition }) + DeclarativeMacroExpander { mac, transparency, edition } } } 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 6e95ff01c3415..dddef17ce7686 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -22,7 +22,6 @@ use base_db::Crate; use span::SyntaxContext; use syntax::{AstPtr, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent, ted}; use syntax_bridge::DocCommentDesugarMode; -use triomphe::Arc; use crate::{ AstId, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, @@ -92,7 +91,7 @@ pub fn expand_eager_macro_input( let mut subtree = syntax_bridge::syntax_node_to_token_tree( &expanded_eager_input, arg_map, - span, + *span, DocCommentDesugarMode::Mbe, ); @@ -104,11 +103,11 @@ pub fn expand_eager_macro_input( kind: MacroCallKind::FnLike { ast_id, expand_to, - eager: Some(Arc::new(EagerCallInfo { - arg: Arc::new(subtree), + eager: Some(Box::new(EagerCallInfo { + arg: subtree, arg_id, error: err.clone(), - span, + span: *span, })), }, ctxt: call_site, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs index b51ec39ce7b4e..939104b709163 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -14,7 +14,7 @@ use syntax::{ match_ast, }; use syntax_bridge::DocCommentDesugarMode; -use triomphe::Arc; +use thin_vec::ThinVec; use tt::{Spacing, TransformTtAction, transform_tt}; use crate::{ @@ -35,8 +35,7 @@ pub(crate) struct SyntaxFixups { /// This is the information needed to reverse the fixups. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct SyntaxFixupUndoInfo { - // FIXME: ThinArc<[Subtree]> - original: Option>>, + original: Option>, } impl SyntaxFixupUndoInfo { @@ -59,7 +58,7 @@ pub(crate) fn fixup_syntax( let mut append = FxHashMap::::default(); let mut remove = FxHashSet::::default(); let mut preorder = node.preorder(); - let mut original = Vec::new(); + let mut original = ThinVec::new(); let dummy_range = FIXUP_DUMMY_RANGE; let fake_span = |range| { let span = span_map.span_for_range(range); @@ -317,13 +316,12 @@ pub(crate) fn fixup_syntax( } } } + original.shrink_to_fit(); let needs_fixups = !append.is_empty() || !original.is_empty(); SyntaxFixups { append, remove, - undo_info: SyntaxFixupUndoInfo { - original: needs_fixups.then(|| Arc::new(original.into_boxed_slice())), - }, + undo_info: SyntaxFixupUndoInfo { original: needs_fixups.then_some(original) }, } } @@ -340,7 +338,7 @@ fn has_error_to_handle(node: &SyntaxNode) -> bool { } pub(crate) fn reverse_fixups(tt: &mut TopSubtree, undo_info: &SyntaxFixupUndoInfo) { - let Some(undo_info) = undo_info.original.as_deref() else { return }; + let Some(undo_info) = &undo_info.original else { return }; let undo_info = &**undo_info; let top_subtree = tt.top_subtree(); let open_span = top_subtree.delimiter.open; 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 fc2a07435bf3e..403e544bf8f95 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -279,7 +279,7 @@ impl MacroDefKind { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct EagerCallInfo { /// The expanded argument of the eager macro. - arg: Arc, + arg: tt::TopSubtree, /// Call id of the eager macro's input file (this is the macro file for its fully expanded input). arg_id: MacroCallId, error: Option, @@ -296,7 +296,7 @@ pub enum MacroCallKind { /// for the eager input macro file. // FIXME: This is being interned, subtrees can vary quickly differing just slightly causing // leakage problems here - eager: Option>, + eager: Option>, }, Derive { ast_id: AstId, @@ -311,7 +311,7 @@ pub enum MacroCallKind { Attr { ast_id: AstId, // FIXME: This shouldn't be here, we can derive this from `invoc_attr_index`. - attr_args: Option>, + attr_args: Option>, /// This contains the list of all *active* attributes (derives and attr macros) preceding this /// attribute, including this attribute. You can retrieve the [`AttrId`] of the current attribute /// by calling [`invoc_attr()`] on this. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 80e7e05d76fed..87633ad4aa9e6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -16,7 +16,6 @@ use rustc_abi::Size; use rustc_apfloat::Float; use rustc_type_ir::inherent::IntoKind; use stdx::never; -use triomphe::Arc; use crate::{ LifetimeElisionKind, ParamEnvAndCrate, TyLoweringContext, @@ -300,7 +299,7 @@ pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'_, 'd if let Some(body_owner) = ctx.owner.as_def_with_body() && let Ok(mir_body) = lower_body_to_mir(ctx.db, body_owner, Body::of(ctx.db, body_owner), &infer, expr) - && let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None) + && let Ok((Ok(result), _)) = interpret_mir(ctx.db, &mir_body, true, None) { return Const::new_from_allocation( ctx.interner(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 2e130c1028ff0..5dba53a761e65 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -37,33 +37,44 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { // FXME: Collapse `mir_body_for_closure` into `mir_body` // and `monomorphized_mir_body_for_closure` into `monomorphized_mir_body` - #[salsa::invoke(crate::mir::mir_body_query)] - #[salsa::cycle(cycle_result = crate::mir::mir_body_cycle_result)] - fn mir_body(&self, def: DefWithBodyId) -> Result, MirLowerError>; + #[salsa::transparent] + fn mir_body(&self, def: DefWithBodyId) -> Result<&MirBody, MirLowerError> { + crate::mir::mir_body_query(self, def).as_ref().map_err(|err| err.clone()) + } - #[salsa::invoke(crate::mir::mir_body_for_closure_query)] - fn mir_body_for_closure(&self, def: InternedClosureId) -> Result, MirLowerError>; + #[salsa::transparent] + fn mir_body_for_closure(&self, def: InternedClosureId) -> Result<&MirBody, MirLowerError> { + crate::mir::mir_body_for_closure_query(self, def).as_ref().map_err(|err| err.clone()) + } - #[salsa::invoke(crate::mir::monomorphized_mir_body_query)] - #[salsa::cycle(cycle_result = crate::mir::monomorphized_mir_body_cycle_result)] + #[salsa::transparent] fn monomorphized_mir_body( &self, def: DefWithBodyId, subst: StoredGenericArgs, env: StoredParamEnvAndCrate, - ) -> Result, MirLowerError>; + ) -> Result<&MirBody, MirLowerError> { + crate::mir::monomorphized_mir_body_query(self, def, subst, env) + .as_ref() + .map_err(|err| err.clone()) + } - #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)] + #[salsa::transparent] fn monomorphized_mir_body_for_closure( &self, def: InternedClosureId, subst: StoredGenericArgs, env: StoredParamEnvAndCrate, - ) -> Result, MirLowerError>; + ) -> Result<&MirBody, MirLowerError> { + crate::mir::monomorphized_mir_body_for_closure_query(self, def, subst, env) + .as_ref() + .map_err(|err| err.clone()) + } - #[salsa::invoke(crate::mir::borrowck_query)] - #[salsa::lru(2024)] - fn borrowck(&self, def: DefWithBodyId) -> Result, MirLowerError>; + #[salsa::transparent] + fn borrowck(&self, def: DefWithBodyId) -> Result<&[BorrowckResult], MirLowerError> { + crate::mir::borrowck_query(self, def).as_ref().map(|it| &**it).map_err(|err| err.clone()) + } #[salsa::invoke(crate::consteval::const_eval)] #[salsa::transparent] @@ -110,8 +121,10 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { env: StoredParamEnvAndCrate, ) -> Result, LayoutError>; - #[salsa::invoke(crate::layout::target_data_layout_query)] - fn target_data_layout(&self, krate: Crate) -> Result, TargetLoadError>; + #[salsa::transparent] + fn target_data_layout(&self, krate: Crate) -> Result<&TargetDataLayout, TargetLoadError> { + crate::layout::target_data_layout_query(self, krate).as_ref().map_err(|err| err.clone()) + } #[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)] fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 2df190bb7a862..c300388c5ee3e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -974,7 +974,7 @@ fn render_const_scalar_inner<'db>( return f.write_str(""); }; let Some((var_id, var_layout)) = - detect_variant_from_bytes(&layout, f.db, &target_data_layout, b, e) + detect_variant_from_bytes(&layout, f.db, target_data_layout, b, e) else { return f.write_str(""); }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index d2759ddeeb197..da0be818f6b2f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -167,7 +167,7 @@ pub fn layout_of_ty_query( let Ok(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable); }; - let dl = &*target; + let dl = target; let cx = LayoutCx::new(dl); let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis); let cause = ObligationCause::dummy(); @@ -187,7 +187,7 @@ pub fn layout_of_ty_query( repr.packed(), &args, trait_env.as_ref(), - &target, + target, ); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 6090ddfd45ccb..e77fb139bed9d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -29,7 +29,7 @@ pub fn layout_of_adt_query( let Ok(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable); }; - let dl = &*target; + let dl = target; let cx = LayoutCx::new(dl); let handle_variant = |def: VariantId, var: &VariantFields| { var.fields() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs index 1752b56b0f6d9..26fa73e76bc3c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs @@ -3,17 +3,17 @@ use base_db::{Crate, target::TargetLoadError}; use hir_def::layout::TargetDataLayout; use rustc_abi::{AddressSpace, AlignFromBytesError, TargetDataLayoutError}; -use triomphe::Arc; use crate::db::HirDatabase; +#[salsa_macros::tracked(returns(ref))] pub fn target_data_layout_query( db: &dyn HirDatabase, krate: Crate, -) -> Result, TargetLoadError> { +) -> Result { match &krate.workspace_data(db).target { Ok(target) => match TargetDataLayout::parse_from_llvm_datalayout_string(&target.data_layout, AddressSpace::ZERO) { - Ok(it) => Ok(Arc::new(it)), + Ok(it) => Ok(it), Err(e) => { Err(match e { TargetDataLayoutError::InvalidAddressSpace { addr_space, cause, err } => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index a8e06f3a2b586..67cc67fd398e3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -47,9 +47,6 @@ pub use monomorphization::{ monomorphized_mir_body_for_closure_query, monomorphized_mir_body_query, }; -pub(crate) use lower::mir_body_cycle_result; -pub(crate) use monomorphization::monomorphized_mir_body_cycle_result; - use super::consteval::try_const_usize; pub type BasicBlockId = Idx; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index 17715d3fcd23b..dcd06ae25fb4e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -5,11 +5,11 @@ use std::iter; +use either::Either; use hir_def::{DefWithBodyId, ExpressionStoreOwnerId, HasModule}; use la_arena::ArenaMap; use rustc_hash::FxHashMap; use stdx::never; -use triomphe::Arc; use crate::{ closure_analysis::ProjectionKind as HirProjectionKind, @@ -57,68 +57,86 @@ pub struct BorrowRegion { #[derive(Debug, Clone, PartialEq, Eq)] pub struct BorrowckResult { - pub mir_body: Arc, + owner: Either, pub mutability_of_locals: ArenaMap, pub moved_out_of_ref: Vec, pub partially_moved: Vec, pub borrow_regions: Vec, } -fn all_mir_bodies( - db: &dyn HirDatabase, +impl BorrowckResult { + pub fn mir_body<'db>(&self, db: &'db dyn HirDatabase) -> &'db MirBody { + match self.owner { + Either::Left(it) => db.mir_body(it).unwrap(), + Either::Right(it) => db.mir_body_for_closure(it).unwrap(), + } + } +} + +fn all_mir_bodies<'db>( + db: &'db dyn HirDatabase, def: DefWithBodyId, - mut cb: impl FnMut(Arc) -> BorrowckResult, - mut merge_from_closures: impl FnMut(&mut BorrowckResult, &BorrowckResult), -) -> Result, MirLowerError> { - fn for_closure( - db: &dyn HirDatabase, + mut cb: impl FnMut(&'db MirBody, Either) -> BorrowckResult, + mut merge_from_closures: impl FnMut( + (&mut BorrowckResult, &'db MirBody), + (&BorrowckResult, &'db MirBody), + ), +) -> Result, MirLowerError> { + fn for_closure<'db>( + db: &'db dyn HirDatabase, c: InternedClosureId, - results: &mut Vec, - cb: &mut impl FnMut(Arc) -> BorrowckResult, - merge_from_closures: &mut impl FnMut(&mut BorrowckResult, &BorrowckResult), + results: &mut Vec<(BorrowckResult, &'db MirBody)>, + cb: &mut impl FnMut(&'db MirBody, Either) -> BorrowckResult, + merge_from_closures: &mut impl FnMut( + (&mut BorrowckResult, &'db MirBody), + (&BorrowckResult, &'db MirBody), + ), ) -> Result<(), MirLowerError> { match db.mir_body_for_closure(c) { Ok(body) => { let parent_index = results.len(); - results.push(cb(body.clone())); + results.push((cb(body, Either::Right(c)), body)); body.closures .iter() .try_for_each(|&it| for_closure(db, it, results, cb, merge_from_closures))?; merge(results, merge_from_closures, parent_index); Ok(()) } - Err(e) => Err(e), + Err(e) => Err(e.clone()), } } - fn merge( - results: &mut [BorrowckResult], - merge: &mut impl FnMut(&mut BorrowckResult, &BorrowckResult), + fn merge<'db>( + results: &mut [(BorrowckResult, &'db MirBody)], + merge: &mut impl FnMut((&mut BorrowckResult, &'db MirBody), (&BorrowckResult, &'db MirBody)), parent_index: usize, ) { let (parent_and_before, children) = results.split_at_mut(parent_index + 1); - let parent = &mut parent_and_before[parent_and_before.len() - 1]; - children.iter().for_each(|child| merge(parent, child)); + let (parent, parent_mir_body) = &mut parent_and_before[parent_and_before.len() - 1]; + children.iter().for_each(|(child, child_mir_body)| { + merge((parent, parent_mir_body), (child, child_mir_body)) + }); } let mut results = Vec::new(); match db.mir_body(def) { Ok(body) => { - results.push(cb(body.clone())); + results.push((cb(body, Either::Left(def)), body)); body.closures.iter().try_for_each(|&it| { for_closure(db, it, &mut results, &mut cb, &mut merge_from_closures) })?; merge(&mut results, &mut merge_from_closures, 0); - Ok(results.into()) + Ok(results.into_iter().map(|(it, _)| it).collect()) } - Err(e) => Err(e), + Err(e) => Err(e.clone()), } } +#[salsa_macros::tracked(returns(ref), lru = 2024)] pub fn borrowck_query( db: &dyn HirDatabase, def: DefWithBodyId, -) -> Result, MirLowerError> { +) -> Result, MirLowerError> { let _p = tracing::info_span!("borrowck_query").entered(); let module = def.module(db); let interner = DbInterner::new_with(db, module.krate(db)); @@ -128,20 +146,20 @@ pub fn borrowck_query( let res = all_mir_bodies( db, def, - |body| { + |body, owner| { // FIXME(next-solver): Opaques. let infcx = interner.infer_ctxt().build(typing_mode); BorrowckResult { - mutability_of_locals: mutability_of_locals(&infcx, env, &body), - moved_out_of_ref: moved_out_of_ref(&infcx, env, &body), - partially_moved: partially_moved(&infcx, env, &body), - borrow_regions: borrow_regions(db, &body), - mir_body: body, + owner, + mutability_of_locals: mutability_of_locals(&infcx, env, body), + moved_out_of_ref: moved_out_of_ref(&infcx, env, body), + partially_moved: partially_moved(&infcx, env, body), + borrow_regions: borrow_regions(db, body), } }, - |parent, child| { - for (upvar, child_locals) in &child.mir_body.upvar_locals { - let Some(&parent_local) = parent.mir_body.binding_locals.get(*upvar) else { + |(parent, parent_mir_body), (child, child_mir_body)| { + for (upvar, child_locals) in &child_mir_body.upvar_locals { + let Some(&parent_local) = parent_mir_body.binding_locals.get(*upvar) else { continue; }; for (child_local, capture_place) in child_locals { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 87d64e567e7fc..104b90eaf55c5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -156,26 +156,26 @@ impl TlsData { } } -struct StackFrame { - locals: Locals, +struct StackFrame<'a> { + locals: Locals<'a>, destination: Option, prev_stack_ptr: usize, span: (MirSpan, DefWithBodyId), } #[derive(Clone)] -enum MirOrDynIndex { - Mir(Arc), +enum MirOrDynIndex<'a> { + Mir(&'a MirBody), Dyn(usize), } -pub struct Evaluator<'db> { +pub struct Evaluator<'a, 'db> { db: &'db dyn HirDatabase, param_env: ParamEnvAndCrate<'db>, - target_data_layout: Arc, + target_data_layout: &'db TargetDataLayout, stack: Vec, heap: Vec, - code_stack: Vec, + code_stack: Vec>, /// Stores the global location of the statics. We const evaluate every static first time we need it /// and see it's missing, then we add it to this to reuse. static_locations: FxHashMap, @@ -190,11 +190,11 @@ pub struct Evaluator<'db> { layout_cache: RefCell, Arc>>, projected_ty_cache: RefCell, PlaceElem), Ty<'db>>>, not_special_fn_cache: RefCell>, - mir_or_dyn_index_cache: RefCell), MirOrDynIndex>>, + mir_or_dyn_index_cache: RefCell), MirOrDynIndex<'a>>>, /// Constantly dropping and creating `Locals` is very costly. We store /// old locals that we normally want to drop here, to reuse their allocations /// later. - unused_locals_store: RefCell>>, + unused_locals_store: RefCell>>>, cached_ptr_size: usize, cached_fn_trait_func: Option, cached_fn_mut_trait_func: Option, @@ -237,17 +237,21 @@ impl Interval { Self { addr, size } } - fn get<'a, 'db>(&self, memory: &'a Evaluator<'db>) -> Result<'db, &'a [u8]> { + fn get<'b, 'a, 'db: 'a>(&self, memory: &'b Evaluator<'a, 'db>) -> Result<'db, &'b [u8]> { memory.read_memory(self.addr, self.size) } - fn write_from_bytes<'db>(&self, memory: &mut Evaluator<'db>, bytes: &[u8]) -> Result<'db, ()> { + fn write_from_bytes<'a, 'db: 'a>( + &self, + memory: &mut Evaluator<'a, 'db>, + bytes: &[u8], + ) -> Result<'db, ()> { memory.write_memory(self.addr, bytes) } - fn write_from_interval<'db>( + fn write_from_interval<'a, 'db: 'a>( &self, - memory: &mut Evaluator<'db>, + memory: &mut Evaluator<'a, 'db>, interval: Interval, ) -> Result<'db, ()> { memory.copy_from_interval(self.addr, interval) @@ -259,16 +263,22 @@ impl Interval { } impl<'db> IntervalAndTy<'db> { - fn get<'a>(&self, memory: &'a Evaluator<'db>) -> Result<'db, &'a [u8]> { + fn get<'b, 'a>(&self, memory: &'b Evaluator<'a, 'db>) -> Result<'db, &'b [u8]> + where + 'db: 'a, + { memory.read_memory(self.interval.addr, self.interval.size) } - fn new( + fn new<'a>( addr: Address, ty: Ty<'db>, - evaluator: &Evaluator<'db>, - locals: &Locals, - ) -> Result<'db, IntervalAndTy<'db>> { + evaluator: &Evaluator<'a, 'db>, + locals: &Locals<'a>, + ) -> Result<'db, IntervalAndTy<'db>> + where + 'db: 'a, + { let size = evaluator.size_of_sized(ty, locals, "type of interval")?; Ok(IntervalAndTy { interval: Interval { addr, size }, ty }) } @@ -286,7 +296,7 @@ impl From for IntervalOrOwned { } impl IntervalOrOwned { - fn get<'a, 'db>(&'a self, memory: &'a Evaluator<'db>) -> Result<'db, &'a [u8]> { + fn get<'b, 'a, 'db: 'a>(&'b self, memory: &'b Evaluator<'a, 'db>) -> Result<'db, &'b [u8]> { Ok(match self { IntervalOrOwned::Owned(o) => o, IntervalOrOwned::Borrowed(b) => b.get(memory)?, @@ -576,9 +586,9 @@ impl DropFlags { } #[derive(Debug)] -struct Locals { +struct Locals<'a> { ptr: ArenaMap, - body: Arc, + body: &'a MirBody, drop_flags: DropFlags, } @@ -596,9 +606,9 @@ impl MirOutput { } } -pub fn interpret_mir<'db>( +pub fn interpret_mir<'a, 'db: 'a>( db: &'db dyn HirDatabase, - body: Arc, + body: &MirBody, // FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now // they share their body with their parent, so in MIR lowering we have locals of the parent body, which // might have placeholders. With this argument, we (wrongly) assume that every placeholder type has @@ -613,7 +623,7 @@ pub fn interpret_mir<'db>( if evaluator.ptr_size() != size_of::() { not_supported!("targets with different pointer size from host"); } - let interval = evaluator.interpret_mir(body.clone(), None.into_iter())?; + let interval = evaluator.interpret_mir(body, None.into_iter())?; let bytes = interval.get(&evaluator)?; let mut memory_map = evaluator.create_memory_map( bytes, @@ -638,13 +648,13 @@ const EXECUTION_LIMIT: usize = 100_000; #[cfg(not(test))] const EXECUTION_LIMIT: usize = 10_000_000; -impl<'db> Evaluator<'db> { +impl<'a, 'db: 'a> Evaluator<'a, 'db> { pub fn new( db: &'db dyn HirDatabase, owner: DefWithBodyId, assert_placeholder_ty_is_unused: bool, trait_env: Option>, - ) -> Result<'db, Evaluator<'db>> { + ) -> Result<'db, Evaluator<'a, 'db>> { let module = owner.module(db); let crate_id = module.krate(db); let target_data_layout = match db.target_data_layout(crate_id) { @@ -705,11 +715,11 @@ impl<'db> Evaluator<'db> { self.infcx.interner.lang_items() } - fn place_addr(&self, p: &Place, locals: &Locals) -> Result<'db, Address> { + fn place_addr(&self, p: &Place, locals: &Locals<'a>) -> Result<'db, Address> { Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0) } - fn place_interval(&self, p: &Place, locals: &Locals) -> Result<'db, Interval> { + fn place_interval(&self, p: &Place, locals: &Locals<'a>) -> Result<'db, Interval> { let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?; Ok(Interval { addr: place_addr_and_ty.0, @@ -736,10 +746,10 @@ impl<'db> Evaluator<'db> { r } - fn place_addr_and_ty_and_metadata<'a>( - &'a self, + fn place_addr_and_ty_and_metadata<'b>( + &'b self, p: &Place, - locals: &'a Locals, + locals: &'b Locals<'a>, ) -> Result<'db, (Address, Ty<'db>, Option)> { let mut addr = locals.ptr[p.local].addr; let mut ty: Ty<'db> = locals.body.locals[p.local].ty.as_ref(); @@ -873,11 +883,11 @@ impl<'db> Evaluator<'db> { self.layout(Ty::new_adt(self.interner(), adt, subst)) } - fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals) -> Result<'db, Ty<'db>> { + fn place_ty<'b>(&'b self, p: &Place, locals: &'b Locals<'a>) -> Result<'db, Ty<'db>> { Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1) } - fn operand_ty(&self, o: &Operand, locals: &Locals) -> Result<'db, Ty<'db>> { + fn operand_ty(&self, o: &Operand, locals: &Locals<'a>) -> Result<'db, Ty<'db>> { Ok(match &o.kind { OperandKind::Copy(p) | OperandKind::Move(p) => self.place_ty(p, locals)?, OperandKind::Constant { konst: _, ty } => ty.as_ref(), @@ -898,7 +908,7 @@ impl<'db> Evaluator<'db> { fn operand_ty_and_eval( &mut self, o: &Operand, - locals: &mut Locals, + locals: &mut Locals<'a>, ) -> Result<'db, IntervalAndTy<'db>> { Ok(IntervalAndTy { interval: self.eval_operand(o, locals)?, @@ -908,7 +918,7 @@ impl<'db> Evaluator<'db> { fn interpret_mir( &mut self, - body: Arc, + body: &'a MirBody, args: impl Iterator, ) -> Result<'db, Interval> { if let Some(it) = self.stack_depth_limit.checked_sub(1) { @@ -917,8 +927,8 @@ impl<'db> Evaluator<'db> { return Err(MirEvalError::StackOverflow); } let mut current_block_idx = body.start_block; - let (mut locals, prev_stack_ptr) = self.create_locals_for_body(&body, None)?; - self.fill_locals_for_body(&body, &mut locals, args)?; + let (mut locals, prev_stack_ptr) = self.create_locals_for_body(body, None)?; + self.fill_locals_for_body(body, &mut locals, args)?; let prev_code_stack = mem::take(&mut self.code_stack); let span = (MirSpan::Unknown, body.owner); self.code_stack.push(StackFrame { locals, destination: None, prev_stack_ptr, span }); @@ -1073,7 +1083,7 @@ impl<'db> Evaluator<'db> { fn fill_locals_for_body( &mut self, body: &MirBody, - locals: &mut Locals, + locals: &mut Locals<'a>, args: impl Iterator, ) -> Result<'db, ()> { let mut remain_args = body.param_locals.len(); @@ -1096,19 +1106,15 @@ impl<'db> Evaluator<'db> { fn create_locals_for_body( &mut self, - body: &Arc, + body: &'a MirBody, destination: Option, - ) -> Result<'db, (Locals, usize)> { + ) -> Result<'db, (Locals<'a>, usize)> { let mut locals = match self.unused_locals_store.borrow_mut().entry(body.owner).or_default().pop() { - None => Locals { - ptr: ArenaMap::new(), - body: body.clone(), - drop_flags: DropFlags::default(), - }, + None => Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() }, Some(mut l) => { l.drop_flags.clear(); - l.body = body.clone(); + l.body = body; l } }; @@ -1145,7 +1151,7 @@ impl<'db> Evaluator<'db> { Ok((locals, prev_stack_pointer)) } - fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals) -> Result<'db, IntervalOrOwned> { + fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals<'a>) -> Result<'db, IntervalOrOwned> { use IntervalOrOwned::*; Ok(match r { Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?), @@ -1662,7 +1668,7 @@ impl<'db> Evaluator<'db> { Ok(r) } Variants::Multiple { tag, tag_encoding, variants, .. } => { - let size = tag.size(&*self.target_data_layout).bytes_usize(); + let size = tag.size(self.target_data_layout).bytes_usize(); let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field let is_signed = tag.is_signed(); match tag_encoding { @@ -1804,7 +1810,7 @@ impl<'db> Evaluator<'db> { &mut self, it: VariantId, subst: GenericArgs<'db>, - locals: &Locals, + locals: &Locals<'a>, ) -> Result<'db, (usize, Arc, Option<(usize, usize, i128)>)> { let adt = it.adt_id(self.db); if let DefWithBodyId::VariantId(f) = locals.body.owner @@ -1851,7 +1857,7 @@ impl<'db> Evaluator<'db> { if have_tag { Some(( layout.fields.offset(0).bytes_usize(), - tag.size(&*self.target_data_layout).bytes_usize(), + tag.size(self.target_data_layout).bytes_usize(), discriminant, )) } else { @@ -1898,7 +1904,7 @@ impl<'db> Evaluator<'db> { Ok(result) } - fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result<'db, Interval> { + fn eval_operand(&mut self, it: &Operand, locals: &mut Locals<'a>) -> Result<'db, Interval> { Ok(match &it.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { locals.drop_flags.remove_place(p, &locals.body.projection_store); @@ -2051,7 +2057,7 @@ impl<'db> Evaluator<'db> { #[allow(clippy::double_parens)] fn allocate_const_in_heap( &mut self, - locals: &Locals, + locals: &Locals<'a>, konst: Const<'db>, ) -> Result<'db, Interval> { match konst.kind() { @@ -2089,7 +2095,7 @@ impl<'db> Evaluator<'db> { fn allocate_allocation_in_heap( &mut self, - locals: &Locals, + locals: &Locals<'a>, allocation: Allocation<'db>, ) -> Result<'db, Interval> { let AllocationData { ty, memory: ref v, ref memory_map } = *allocation; @@ -2128,7 +2134,7 @@ impl<'db> Evaluator<'db> { Ok(Interval::new(addr, size)) } - fn eval_place(&mut self, p: &Place, locals: &Locals) -> Result<'db, Interval> { + fn eval_place(&mut self, p: &Place, locals: &Locals<'a>) -> Result<'db, Interval> { let addr = self.place_addr(p, locals)?; Ok(Interval::new( addr, @@ -2228,7 +2234,11 @@ impl<'db> Evaluator<'db> { Ok(()) } - fn size_align_of(&self, ty: Ty<'db>, locals: &Locals) -> Result<'db, Option<(usize, usize)>> { + fn size_align_of( + &self, + ty: Ty<'db>, + locals: &Locals<'a>, + ) -> Result<'db, Option<(usize, usize)>> { if let Some(layout) = self.layout_cache.borrow().get(&ty) { return Ok(layout .is_sized() @@ -2257,7 +2267,7 @@ impl<'db> Evaluator<'db> { fn size_of_sized( &self, ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, what: &'static str, ) -> Result<'db, usize> { match self.size_align_of(ty, locals)? { @@ -2271,7 +2281,7 @@ impl<'db> Evaluator<'db> { fn size_align_of_sized( &self, ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, what: &'static str, ) -> Result<'db, (usize, usize)> { match self.size_align_of(ty, locals)? { @@ -2312,13 +2322,13 @@ impl<'db> Evaluator<'db> { &self, bytes: &[u8], ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, ) -> Result<'db, ComplexMemoryMap<'db>> { - fn rec<'db>( - this: &Evaluator<'db>, + fn rec<'a, 'db: 'a>( + this: &Evaluator<'a, 'db>, bytes: &[u8], ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, mm: &mut ComplexMemoryMap<'db>, stack_depth_limit: usize, ) -> Result<'db, ()> { @@ -2436,7 +2446,7 @@ impl<'db> Evaluator<'db> { if let Some((v, l)) = detect_variant_from_bytes( &layout, this.db, - &this.target_data_layout, + this.target_data_layout, bytes, e, ) { @@ -2487,7 +2497,7 @@ impl<'db> Evaluator<'db> { ty_of_bytes: impl Fn(&[u8]) -> Result<'db, Ty<'db>> + Copy, addr: Address, ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, ) -> Result<'db, ()> { // FIXME: support indirect references let layout = self.layout(ty)?; @@ -2546,7 +2556,7 @@ impl<'db> Evaluator<'db> { if let Some((ev, layout)) = detect_variant_from_bytes( &layout, self.db, - &self.target_data_layout, + self.target_data_layout, self.read_memory(addr, layout.size.bytes_usize())?, e, ) { @@ -2619,10 +2629,10 @@ impl<'db> Evaluator<'db> { bytes: Interval, destination: Interval, args: &[IntervalAndTy<'db>], - locals: &Locals, + locals: &Locals<'a>, target_bb: Option, span: MirSpan, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { let id = from_bytes!(usize, bytes.get(self)?); let next_ty = self.vtable_map.ty(id)?; use rustc_type_ir::TyKind; @@ -2650,9 +2660,9 @@ impl<'db> Evaluator<'db> { generic_args: GenericArgs<'db>, destination: Interval, args: &[IntervalAndTy<'db>], - locals: &Locals, + locals: &Locals<'a>, span: MirSpan, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { let mir_body = self .db .monomorphized_mir_body_for_closure( @@ -2688,10 +2698,10 @@ impl<'db> Evaluator<'db> { generic_args: GenericArgs<'db>, destination: Interval, args: &[IntervalAndTy<'db>], - locals: &Locals, + locals: &Locals<'a>, target_bb: Option, span: MirSpan, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { match def { CallableDefId::FunctionId(def) => { if self.detect_fn_trait(def).is_some() { @@ -2746,9 +2756,9 @@ impl<'db> Evaluator<'db> { &self, def: FunctionId, generic_args: GenericArgs<'db>, - locals: &Locals, + locals: &Locals<'a>, span: MirSpan, - ) -> Result<'db, MirOrDynIndex> { + ) -> Result<'db, MirOrDynIndex<'a>> { let pair = (def, generic_args); if let Some(r) = self.mir_or_dyn_index_cache.borrow().get(&pair) { return Ok(r.clone()); @@ -2788,11 +2798,11 @@ impl<'db> Evaluator<'db> { mut def: FunctionId, args: &[IntervalAndTy<'db>], generic_args: GenericArgs<'db>, - locals: &Locals, + locals: &Locals<'a>, destination: Interval, target_bb: Option, span: MirSpan, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { if self.detect_and_exec_special_function( def, args, @@ -2854,18 +2864,18 @@ impl<'db> Evaluator<'db> { fn exec_looked_up_function( &mut self, - mir_body: Arc, - locals: &Locals, + mir_body: &'a MirBody, + locals: &Locals<'a>, def: FunctionId, arg_bytes: impl Iterator, span: MirSpan, destination: Interval, target_bb: Option, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { Ok(if let Some(target_bb) = target_bb { let (mut locals, prev_stack_ptr) = - self.create_locals_for_body(&mir_body, Some(destination))?; - self.fill_locals_for_body(&mir_body, &mut locals, arg_bytes.into_iter())?; + self.create_locals_for_body(mir_body, Some(destination))?; + self.fill_locals_for_body(mir_body, &mut locals, arg_bytes.into_iter())?; let span = (span, locals.body.owner); Some(StackFrame { locals, destination: Some(target_bb), prev_stack_ptr, span }) } else { @@ -2885,11 +2895,11 @@ impl<'db> Evaluator<'db> { def: FunctionId, args: &[IntervalAndTy<'db>], generic_args: GenericArgs<'db>, - locals: &Locals, + locals: &Locals<'a>, destination: Interval, target_bb: Option, span: MirSpan, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { let func = args .first() .ok_or_else(|| MirEvalError::InternalError("fn trait with no arg".into()))?; @@ -2954,7 +2964,7 @@ impl<'db> Evaluator<'db> { } } - fn eval_static(&mut self, st: StaticId, locals: &Locals) -> Result<'db, Address> { + fn eval_static(&mut self, st: StaticId, locals: &Locals<'a>) -> Result<'db, Address> { if let Some(o) = self.static_locations.get(&st) { return Ok(*o); }; @@ -3001,7 +3011,12 @@ impl<'db> Evaluator<'db> { } } - fn drop_place(&mut self, place: &Place, locals: &mut Locals, span: MirSpan) -> Result<'db, ()> { + fn drop_place( + &mut self, + place: &Place, + locals: &mut Locals<'a>, + span: MirSpan, + ) -> Result<'db, ()> { let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?; if !locals.drop_flags.remove_place(place, &locals.body.projection_store) { return Ok(()); @@ -3016,7 +3031,7 @@ impl<'db> Evaluator<'db> { fn run_drop_glue_deep( &mut self, ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, addr: Address, _metadata: &[u8], span: MirSpan, 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 4154760f6e16f..a0978bd6e8df9 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 @@ -29,13 +29,13 @@ enum EvalLangItem { DropInPlace, } -impl<'db> Evaluator<'db> { +impl<'a, 'db: 'a> Evaluator<'a, 'db> { pub(super) fn detect_and_exec_special_function( &mut self, def: FunctionId, args: &[IntervalAndTy<'db>], generic_args: GenericArgs<'db>, - locals: &Locals, + locals: &Locals<'a>, destination: Interval, span: MirSpan, ) -> Result<'db, bool> { @@ -132,7 +132,7 @@ impl<'db> Evaluator<'db> { def: FunctionId, args: &[IntervalAndTy<'db>], self_ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, destination: Interval, span: MirSpan, ) -> Result<'db, ()> { @@ -190,7 +190,7 @@ impl<'db> Evaluator<'db> { layout: Arc, addr: Address, def: FunctionId, - locals: &Locals, + locals: &Locals<'a>, destination: Interval, span: MirSpan, ) -> Result<'db, ()> { @@ -296,7 +296,7 @@ impl<'db> Evaluator<'db> { it: EvalLangItem, generic_args: GenericArgs<'db>, args: &[IntervalAndTy<'db>], - locals: &Locals, + locals: &Locals<'a>, span: MirSpan, ) -> Result<'db, Vec> { use EvalLangItem::*; @@ -368,7 +368,7 @@ impl<'db> Evaluator<'db> { id: i64, args: &[IntervalAndTy<'db>], destination: Interval, - _locals: &Locals, + _locals: &Locals<'a>, _span: MirSpan, ) -> Result<'db, ()> { match id { @@ -399,7 +399,7 @@ impl<'db> Evaluator<'db> { args: &[IntervalAndTy<'db>], _generic_args: GenericArgs<'db>, destination: Interval, - locals: &Locals, + locals: &Locals<'a>, span: MirSpan, ) -> Result<'db, ()> { match as_str { @@ -563,7 +563,7 @@ impl<'db> Evaluator<'db> { args: &[IntervalAndTy<'db>], generic_args: GenericArgs<'db>, destination: Interval, - locals: &Locals, + locals: &Locals<'a>, span: MirSpan, needs_override: bool, ) -> Result<'db, bool> { @@ -1345,7 +1345,7 @@ impl<'db> Evaluator<'db> { &mut self, ty: Ty<'db>, metadata: Interval, - locals: &Locals, + locals: &Locals<'a>, ) -> Result<'db, (usize, usize)> { Ok(match ty.kind() { TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1), @@ -1404,7 +1404,7 @@ impl<'db> Evaluator<'db> { args: &[IntervalAndTy<'db>], generic_args: GenericArgs<'db>, destination: Interval, - locals: &Locals, + locals: &Locals<'a>, _span: MirSpan, ) -> Result<'db, ()> { // We are a single threaded runtime with no UB checking and no optimization, so diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs index 6e20562b4ed8b..2458597ff48a9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs @@ -6,7 +6,7 @@ use crate::consteval::try_const_usize; use super::*; -impl<'db> Evaluator<'db> { +impl<'a, 'db: 'a> Evaluator<'a, 'db> { fn detect_simd_ty(&self, ty: Ty<'db>) -> Result<'db, (usize, Ty<'db>)> { match ty.kind() { TyKind::Adt(adt_def, subst) => { @@ -53,7 +53,7 @@ impl<'db> Evaluator<'db> { args: &[IntervalAndTy<'db>], _generic_args: GenericArgs<'db>, destination: Interval, - _locals: &Locals, + _locals: &Locals<'a>, _span: MirSpan, ) -> Result<'db, ()> { match name { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 9c9cf0ac0b500..b2a7eaa6746e0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -25,7 +25,6 @@ use rustc_hash::FxHashMap; use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _}; use span::{Edition, FileId}; use syntax::TextRange; -use triomphe::Arc; use crate::{ Adjust, Adjustment, AutoBorrow, CallableDefId, ParamEnvAndCrate, @@ -2137,10 +2136,11 @@ fn cast_kind<'db>( }) } +#[salsa_macros::tracked(returns(ref), cycle_result = mir_body_for_closure_cycle_result)] pub fn mir_body_for_closure_query<'db>( db: &'db dyn HirDatabase, closure: InternedClosureId, -) -> Result<'db, Arc> { +) -> Result<'db, MirBody> { let InternedClosure { owner, expr, .. } = closure.loc(db); let body_owner = owner.as_def_with_body().expect("MIR lowering should only happen for body-owned closures"); @@ -2293,13 +2293,11 @@ pub fn mir_body_for_closure_query<'db>( return Err(MirLowerError::UnresolvedUpvar(err)); } ctx.result.shrink_to_fit(); - Ok(Arc::new(ctx.result)) + Ok(ctx.result) } -pub fn mir_body_query<'db>( - db: &'db dyn HirDatabase, - def: DefWithBodyId, -) -> Result<'db, Arc> { +#[salsa_macros::tracked(returns(ref), cycle_result = mir_body_cycle_result)] +pub fn mir_body_query<'db>(db: &'db dyn HirDatabase, def: DefWithBodyId) -> Result<'db, MirBody> { let krate = def.krate(db); let edition = krate.data(db).edition; let detail = match def { @@ -2328,14 +2326,22 @@ pub fn mir_body_query<'db>( let infer = InferenceResult::of(db, def); let mut result = lower_body_to_mir(db, def, body, infer, body.root_expr())?; result.shrink_to_fit(); - Ok(Arc::new(result)) + Ok(result) } -pub(crate) fn mir_body_cycle_result<'db>( +fn mir_body_cycle_result<'db>( _db: &'db dyn HirDatabase, _: salsa::Id, _def: DefWithBodyId, -) -> Result<'db, Arc> { +) -> Result<'db, MirBody> { + Err(MirLowerError::Loop) +} + +fn mir_body_for_closure_cycle_result<'db>( + _db: &'db dyn HirDatabase, + _: salsa::Id, + _def: InternedClosureId, +) -> Result<'db, MirBody> { Err(MirLowerError::Loop) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index dfce8ca2a5cfe..b3529437c0fac 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -12,7 +12,6 @@ use rustc_type_ir::inherent::IntoKind; use rustc_type_ir::{ FallibleTypeFolder, TypeFlags, TypeFoldable, TypeSuperFoldable, TypeVisitableExt, }; -use triomphe::Arc; use crate::{ ParamEnvAndCrate, @@ -240,38 +239,50 @@ impl<'db> Filler<'db> { } } +#[salsa_macros::tracked(returns(ref), cycle_result = monomorphized_mir_body_cycle_result)] pub fn monomorphized_mir_body_query( db: &dyn HirDatabase, owner: DefWithBodyId, subst: StoredGenericArgs, trait_env: StoredParamEnvAndCrate, -) -> Result, MirLowerError> { +) -> Result { let mut filler = Filler::new(db, trait_env.as_ref(), subst.as_ref()); let body = db.mir_body(owner)?; let mut body = (*body).clone(); filler.fill_body(&mut body)?; - Ok(Arc::new(body)) + Ok(body) } -pub(crate) fn monomorphized_mir_body_cycle_result( +fn monomorphized_mir_body_cycle_result( _db: &dyn HirDatabase, _: salsa::Id, _: DefWithBodyId, _: StoredGenericArgs, _: StoredParamEnvAndCrate, -) -> Result, MirLowerError> { +) -> Result { Err(MirLowerError::Loop) } +#[salsa_macros::tracked(returns(ref), cycle_result = monomorphized_mir_body_for_closure_cycle_result)] pub fn monomorphized_mir_body_for_closure_query( db: &dyn HirDatabase, closure: InternedClosureId, subst: StoredGenericArgs, trait_env: StoredParamEnvAndCrate, -) -> Result, MirLowerError> { +) -> Result { let mut filler = Filler::new(db, trait_env.as_ref(), subst.as_ref()); let body = db.mir_body_for_closure(closure)?; let mut body = (*body).clone(); filler.fill_body(&mut body)?; - Ok(Arc::new(body)) + Ok(body) +} + +fn monomorphized_mir_body_for_closure_cycle_result( + _db: &dyn HirDatabase, + _: salsa::Id, + _: InternedClosureId, + _: StoredGenericArgs, + _: StoredParamEnvAndCrate, +) -> Result { + Err(MirLowerError::Loop) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index de5ee223a1487..c224f2054fab2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -160,7 +160,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { let result = mem::take(&mut self.result); let indent = mem::take(&mut self.indent); let mut ctx = MirPrettyCtx { - body: &body, + body, local_to_binding: body.local_to_binding_map(), result, indent, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 6f5508691e5d4..b74f594ebe520 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1120,7 +1120,7 @@ fn macro_call_diagnostics<'db>( let Some(e) = db.parse_macro_expansion_error(macro_call_id) else { return; }; - let ValueResult { value: parse_errors, err } = &*e; + let ValueResult { value: parse_errors, err } = e; if let Some(err) = err { let loc = db.lookup_intern_macro_call(macro_call_id); let file_id = loc.kind.file_id(); @@ -1436,7 +1436,7 @@ impl Field { Type::new(db, var_id, ty) } - pub fn layout(&self, db: &dyn HirDatabase) -> Result { + pub fn layout<'db>(&self, db: &'db dyn HirDatabase) -> Result, LayoutError> { db.layout_of_ty( self.ty(db).ty.store(), param_env_from_has_crate( @@ -1696,7 +1696,7 @@ impl Enum { self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit)) } - pub fn layout(self, db: &dyn HirDatabase) -> Result { + pub fn layout<'db>(self, db: &'db dyn HirDatabase) -> Result, LayoutError> { Adt::from(self).layout(db) } @@ -1783,7 +1783,7 @@ impl EnumVariant { db.const_eval_discriminant(self.into()) } - pub fn layout(&self, db: &dyn HirDatabase) -> Result { + pub fn layout<'db>(&self, db: &'db dyn HirDatabase) -> Result, LayoutError> { let parent_enum = self.parent_enum(db); let parent_layout = parent_enum.layout(db)?; Ok(match &parent_layout.0.variants { @@ -1866,7 +1866,7 @@ impl Adt { has_non_default_type_params(db, self.into()) } - pub fn layout(self, db: &dyn HirDatabase) -> Result { + pub fn layout<'db>(self, db: &'db dyn HirDatabase) -> Result, LayoutError> { let interner = DbInterner::new_no_crate(db); let adt_id = AdtId::from(self); let args = GenericArgs::for_item_with_defaults(interner, adt_id.into(), |_, id, _| { @@ -2211,7 +2211,7 @@ impl DefWithBody { if let Ok(borrowck_results) = db.borrowck(id) { for borrowck_result in borrowck_results.iter() { - let mir_body = &borrowck_result.mir_body; + let mir_body = borrowck_result.mir_body(db); for moof in &borrowck_result.moved_out_of_ref { let span: InFile = match moof.span { mir::MirSpan::ExprId(e) => match source_map.expr_syntax(e) { @@ -6530,7 +6530,7 @@ impl<'db> Type<'db> { .collect() } - pub fn layout(&self, db: &'db dyn HirDatabase) -> Result { + pub fn layout(&self, db: &'db dyn HirDatabase) -> Result, LayoutError> { db.layout_of_ty(self.ty.store(), self.env.store()) .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap())) } @@ -6702,9 +6702,9 @@ impl<'db> Callable<'db> { } #[derive(Clone, Debug, Eq, PartialEq)] -pub struct Layout(Arc, Arc); +pub struct Layout<'db>(Arc, &'db TargetDataLayout); -impl Layout { +impl<'db> Layout<'db> { pub fn size(&self) -> u64 { self.0.size.bytes() } @@ -6714,7 +6714,7 @@ impl Layout { } pub fn niches(&self) -> Option { - Some(self.0.largest_niche?.available(&*self.1)) + Some(self.0.largest_niche?.available(self.1)) } pub fn field_offset(&self, field: Field) -> Option { @@ -6803,7 +6803,7 @@ impl Layout { let tag_size = if let layout::Variants::Multiple { tag, tag_encoding, .. } = &self.0.variants { match tag_encoding { - TagEncoding::Direct => tag.size(&*self.1).bytes_usize(), + TagEncoding::Direct => tag.size(self.1).bytes_usize(), TagEncoding::Niche { .. } => 0, } } else { diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index b11f7a172310f..99ad1e07391fa 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -56,11 +56,12 @@ pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>( let borrowck = db.borrowck(it.parent.as_def_with_body()?).ok()?; let invalid = borrowck.iter().any(|b| { + let mir_body = b.mir_body(ctx.sema.db); b.partially_moved.iter().any(|moved| { - Some(&moved.local) == b.mir_body.binding_locals.get(it.binding_id) + Some(&moved.local) == mir_body.binding_locals.get(it.binding_id) }) || b.borrow_regions.iter().any(|region| { // Shared borrows are fine - Some(®ion.local) == b.mir_body.binding_locals.get(it.binding_id) + Some(®ion.local) == mir_body.binding_locals.get(it.binding_id) && region.kind != BorrowKind::Shared }) }); 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 4f60321e3250d..e08bbc5c21b65 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -1136,12 +1136,12 @@ fn markup( } } -fn render_memory_layout( +fn render_memory_layout<'db>( config: Option, - layout: impl FnOnce() -> Result, - offset: impl FnOnce(&Layout) -> Option, - padding: impl FnOnce(&Layout) -> Option<(&str, u64)>, - tag: impl FnOnce(&Layout) -> Option, + layout: impl FnOnce() -> Result, LayoutError>, + offset: impl FnOnce(&Layout<'db>) -> Option, + padding: impl for<'a> FnOnce(&'a Layout<'db>) -> Option<(&'a str, u64)>, + tag: impl FnOnce(&Layout<'db>) -> Option, ) -> Option { let config = config?; let layout = layout().ok()?; diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 47ca616f3199b..1b9df9722b07e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -108,7 +108,7 @@ pub(crate) fn view_memory_layout( nodes: &mut Vec, db: &RootDatabase, ty: &Type<'_>, - layout: &Layout, + layout: &Layout<'_>, parent_idx: usize, display_target: DisplayTarget, ) { From d5ddd9dfb04dfd6033abbb6d7bfcea20583482ee Mon Sep 17 00:00:00 2001 From: Kao-Wei Yeh Date: Tue, 28 Apr 2026 17:22:41 +0800 Subject: [PATCH 147/289] feat: add diagnostic for E0784 --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 ++ .../crates/hir-ty/src/infer/expr.rs | 2 +- .../crates/hir/src/diagnostics.rs | 10 +++++ .../union_must_have_exactly_one_field.rs | 42 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 5 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_must_have_exactly_one_field.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 333219c5af870..5dbd5a779ecd7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -455,6 +455,10 @@ pub enum InferenceDiagnostic { at_point: Span, top_term: Option, }, + UnionMustHaveExactlyOneField { + #[type_visitable(ignore)] + expr: ExprId, + }, } /// A mismatch between an expected and an inferred type. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 782c6be7c39f1..4e721043cfa57 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1093,7 +1093,7 @@ impl<'db> InferenceContext<'_, 'db> { // Make sure the programmer specified correct number of fields. if matches!(adt_id, AdtId::UnionId(_)) && hir_fields.len() != 1 { - // FIXME: Emit an error: unions must specify exactly one field. + self.push_diagnostic(InferenceDiagnostic::UnionMustHaveExactlyOneField { expr }); } match base_expr { diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 095b527cb0845..d73050d71c46e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -112,6 +112,7 @@ diagnostics![AnyDiagnostic<'db> -> MissingLifetime, ElidedLifetimesInPath, TypeMustBeKnown<'db>, + UnionMustHaveExactlyOneField, ]; #[derive(Debug)] @@ -488,6 +489,11 @@ pub struct GenericDefaultRefersToSelf { pub segment: InFile>, } +#[derive(Debug)] +pub struct UnionMustHaveExactlyOneField { + pub expr: InFile, +} + #[derive(Debug)] pub struct InvalidLhsOfAssignment { pub lhs: InFile>>, @@ -853,6 +859,10 @@ impl<'db> AnyDiagnostic<'db> { }); TypeMustBeKnown { at_point, top_term }.into() } + &InferenceDiagnostic::UnionMustHaveExactlyOneField { expr } => { + let expr = expr_syntax(expr)?; + UnionMustHaveExactlyOneField { expr }.into() + } }) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_must_have_exactly_one_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_must_have_exactly_one_field.rs new file mode 100644 index 0000000000000..e9a3b4ba4dc20 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_must_have_exactly_one_field.rs @@ -0,0 +1,42 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: union-must-have-exactly-one-field +// +// A union expression does not have exactly one field. +pub(crate) fn union_must_have_exactly_one_field( + ctx: &DiagnosticsContext<'_>, + d: &hir::UnionMustHaveExactlyOneField, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0784"), + "union expressions should have exactly one field", + d.expr.map(|it| it.into()), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn union_must_have_exactly_one_field() { + check_diagnostics( + r#" +union Bird { + pigeon: u8, + turtledove: u16, +} + +fn main() { + let bird = Bird { pigeon: 0 }; + let bird = Bird {}; + // ^^^^^^^ error: union expressions should have exactly one field + let bird = Bird { pigeon: 0, turtledove: 1 }; + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: union expressions should have exactly one field +} +"#, + ); + } +} 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 335e4b047cc3c..aa1d2a43d58f1 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -72,6 +72,7 @@ mod handlers { pub(crate) mod typed_hole; pub(crate) mod undeclared_label; pub(crate) mod unimplemented_builtin_macro; + pub(crate) mod union_must_have_exactly_one_field; pub(crate) mod unreachable_label; pub(crate) mod unresolved_assoc_item; pub(crate) mod unresolved_extern_crate; @@ -489,6 +490,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d), AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d), AnyDiagnostic::PatternArgInExternFn(d) => handlers::pattern_arg_in_extern_fn::pattern_arg_in_extern_fn(&ctx, &d), + AnyDiagnostic::UnionMustHaveExactlyOneField(d) => handlers::union_must_have_exactly_one_field::union_must_have_exactly_one_field(&ctx, &d), }; res.push(d) } From d3b29dfa6ffca7e7ca3af97519475e423a60029e Mon Sep 17 00:00:00 2001 From: Kao-Wei Yeh Date: Fri, 1 May 2026 15:13:27 +0800 Subject: [PATCH 148/289] Rename `UnionMustHaveExactlyOneField` -> `UnionExprMustHaveExactlyOneField` --- src/tools/rust-analyzer/crates/hir-ty/src/infer.rs | 2 +- src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/diagnostics.rs | 8 ++++---- ...field.rs => union_expr_must_have_exactly_one_field.rs} | 8 ++++---- src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) rename src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/{union_must_have_exactly_one_field.rs => union_expr_must_have_exactly_one_field.rs} (81%) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 5dbd5a779ecd7..09c1325c41c52 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -455,7 +455,7 @@ pub enum InferenceDiagnostic { at_point: Span, top_term: Option, }, - UnionMustHaveExactlyOneField { + UnionExprMustHaveExactlyOneField { #[type_visitable(ignore)] expr: ExprId, }, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 4e721043cfa57..c59c919c518e7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1093,7 +1093,7 @@ impl<'db> InferenceContext<'_, 'db> { // Make sure the programmer specified correct number of fields. if matches!(adt_id, AdtId::UnionId(_)) && hir_fields.len() != 1 { - self.push_diagnostic(InferenceDiagnostic::UnionMustHaveExactlyOneField { expr }); + self.push_diagnostic(InferenceDiagnostic::UnionExprMustHaveExactlyOneField { expr }); } match base_expr { diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index d73050d71c46e..082c29c17415b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -112,7 +112,7 @@ diagnostics![AnyDiagnostic<'db> -> MissingLifetime, ElidedLifetimesInPath, TypeMustBeKnown<'db>, - UnionMustHaveExactlyOneField, + UnionExprMustHaveExactlyOneField, ]; #[derive(Debug)] @@ -490,7 +490,7 @@ pub struct GenericDefaultRefersToSelf { } #[derive(Debug)] -pub struct UnionMustHaveExactlyOneField { +pub struct UnionExprMustHaveExactlyOneField { pub expr: InFile, } @@ -859,9 +859,9 @@ impl<'db> AnyDiagnostic<'db> { }); TypeMustBeKnown { at_point, top_term }.into() } - &InferenceDiagnostic::UnionMustHaveExactlyOneField { expr } => { + &InferenceDiagnostic::UnionExprMustHaveExactlyOneField { expr } => { let expr = expr_syntax(expr)?; - UnionMustHaveExactlyOneField { expr }.into() + UnionExprMustHaveExactlyOneField { expr }.into() } }) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_must_have_exactly_one_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs similarity index 81% rename from src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_must_have_exactly_one_field.rs rename to src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs index e9a3b4ba4dc20..2569c52810a26 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_must_have_exactly_one_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs @@ -1,11 +1,11 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; -// Diagnostic: union-must-have-exactly-one-field +// Diagnostic: union-expr-must-have-exactly-one-field // // A union expression does not have exactly one field. -pub(crate) fn union_must_have_exactly_one_field( +pub(crate) fn union_expr_must_have_exactly_one_field( ctx: &DiagnosticsContext<'_>, - d: &hir::UnionMustHaveExactlyOneField, + d: &hir::UnionExprMustHaveExactlyOneField, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( ctx, @@ -21,7 +21,7 @@ mod tests { use crate::tests::check_diagnostics; #[test] - fn union_must_have_exactly_one_field() { + fn union_expr_must_have_exactly_one_field() { check_diagnostics( r#" union Bird { 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 aa1d2a43d58f1..618edc5c61861 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -72,7 +72,7 @@ mod handlers { pub(crate) mod typed_hole; pub(crate) mod undeclared_label; pub(crate) mod unimplemented_builtin_macro; - pub(crate) mod union_must_have_exactly_one_field; + pub(crate) mod union_expr_must_have_exactly_one_field; pub(crate) mod unreachable_label; pub(crate) mod unresolved_assoc_item; pub(crate) mod unresolved_extern_crate; @@ -490,7 +490,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d), AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d), AnyDiagnostic::PatternArgInExternFn(d) => handlers::pattern_arg_in_extern_fn::pattern_arg_in_extern_fn(&ctx, &d), - AnyDiagnostic::UnionMustHaveExactlyOneField(d) => handlers::union_must_have_exactly_one_field::union_must_have_exactly_one_field(&ctx, &d), + AnyDiagnostic::UnionExprMustHaveExactlyOneField(d) => handlers::union_expr_must_have_exactly_one_field::union_expr_must_have_exactly_one_field(&ctx, &d), }; res.push(d) } From 66430555681257c92c60cb840d9efa60ff1e38a7 Mon Sep 17 00:00:00 2001 From: Kao-Wei Yeh Date: Fri, 1 May 2026 16:01:30 +0800 Subject: [PATCH 149/289] Add missing lifetime argument to `union_expr_must_have_exactly_one_field` --- .../src/handlers/union_expr_must_have_exactly_one_field.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs index 2569c52810a26..7f1b2da4829a3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // A union expression does not have exactly one field. pub(crate) fn union_expr_must_have_exactly_one_field( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnionExprMustHaveExactlyOneField, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( From d3507eddf0a6bc9c7f3bde5bd6b480f6e5320eda Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 1 May 2026 22:41:36 +0800 Subject: [PATCH 150/289] fix: improve whitespaces for trait item complete Example --- ```rust trait SomeTrait {} trait Foo { fn function() where Self: SomeTrait; } impl Foo for Bar { fn f$0 } ``` **Before this PR** ```rust impl Foo for Bar { fn function() where Self: SomeTrait { $0 } } ``` **After this PR** ```rust impl Foo for Bar { fn function() where Self: SomeTrait { $0 } } ``` --- .../src/completions/item_list/trait_impl.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 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 99d37b699f3fc..3020fb39565f3 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 @@ -40,7 +40,9 @@ use ide_db::{ use syntax::ast::HasGenericParams; use syntax::{ AstNode, SmolStr, SyntaxElement, SyntaxKind, T, TextRange, ToSmolStr, - ast::{self, HasGenericArgs, HasTypeBounds, edit_in_place::AttrsOwnerEdit, make}, + ast::{ + self, HasGenericArgs, HasTypeBounds, edit::AstNodeEdit, edit_in_place::AttrsOwnerEdit, make, + }, format_smolstr, ted, }; @@ -233,13 +235,14 @@ fn add_function_impl_( get_transformed_fn(ctx, source.value, impl_def, async_sugaring) { let function_decl = function_declaration(ctx, &transformed_fn, source.file_id.macro_file()); + let ws = if function_decl.contains('\n') { "\n" } else { " " }; match ctx.config.snippet_cap { Some(cap) => { - let snippet = format!("{function_decl} {{\n $0\n}}"); + let snippet = format!("{function_decl}{ws}{{\n $0\n}}"); item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet)); } None => { - let header = format!("{function_decl} {{"); + let header = format!("{function_decl}{ws}{{"); item.text_edit(TextEdit::replace(replacement_range, header)); } }; @@ -296,7 +299,7 @@ fn get_transformed_fn( ctx.sema.source(impl_def)?.value, ); - let fn_ = fn_.clone_for_update(); + let fn_ = fn_.reset_indent(); // FIXME: Paths in nested macros are not handled well. See // `macro_generated_assoc_item2` test. let fn_ = ast::Fn::cast(transform.apply(fn_.syntax()))?; @@ -1256,7 +1259,7 @@ trait SomeTrait {} trait Foo { fn function() - where Self: SomeTrait; + where Self: SomeTrait; } struct Bar; @@ -1269,13 +1272,14 @@ trait SomeTrait {} trait Foo { fn function() - where Self: SomeTrait; + where Self: SomeTrait; } struct Bar; impl Foo for Bar { fn function() - where Self: SomeTrait { +where Self: SomeTrait +{ $0 } } From 08c9350a78284542b6cf76c3dc2895bd6f8f4383 Mon Sep 17 00:00:00 2001 From: Onyeka Obi Date: Fri, 1 May 2026 02:18:25 -0700 Subject: [PATCH 151/289] ide-diagnostics: emit error for mismatched array pattern length Resolves two FIXMEs in hir-ty/src/infer/pat.rs's check_array_pat_len. Adds an InferenceDiagnostic::MismatchedArrayPatLen variant that distinguishes exact-length mismatches (rustc E0527) from rest-pat under-length cases (E0528). Renders to "pattern requires N elements but array has M" or "pattern requires at least N elements but array has M" respectively. Tests cover too-few, too-many, rest-too-short, rest-ok, exact-ok, and singular-element pluralization. Part of rust-lang/rust-analyzer#22140. --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 10 ++ .../crates/hir-ty/src/infer/pat.rs | 14 ++- .../crates/hir/src/diagnostics.rs | 13 ++ .../src/handlers/mismatched_array_pat_len.rs | 114 ++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 5 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 09c1325c41c52..0e307feaa8795 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -319,6 +319,16 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] variant: VariantId, }, + MismatchedArrayPatLen { + #[type_visitable(ignore)] + pat: PatId, + #[type_visitable(ignore)] + expected: u128, + #[type_visitable(ignore)] + found: u128, + #[type_visitable(ignore)] + has_rest: bool, + }, PrivateField { #[type_visitable(ignore)] expr: ExprId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index a7b82dad4fc38..0b2c7dd745a22 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -1649,7 +1649,12 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; return (None, arr_ty); } - // FIXME: Emit an error: incorrect length. + self.push_diagnostic(InferenceDiagnostic::MismatchedArrayPatLen { + pat, + expected: len, + found: min_len, + has_rest: false, + }); } else if let Some(pat_len) = len.checked_sub(min_len) { // The variable-length pattern was there, // so it has an array type with the remaining elements left as its size... @@ -1657,7 +1662,12 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; } else { // ...however, in this case, there were no remaining elements. // That is, the slice pattern requires more than the array type offers. - // FIXME: Emit an error. + self.push_diagnostic(InferenceDiagnostic::MismatchedArrayPatLen { + pat, + expected: len, + found: min_len, + has_rest: true, + }); } } else if slice.is_none() { // We have a pattern with a fixed length, diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 082c29c17415b..5d3e117bd20ca 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -81,6 +81,7 @@ diagnostics![AnyDiagnostic<'db> -> NonExhaustiveLet, NonExhaustiveRecordExpr, NoSuchField, + MismatchedArrayPatLen, PatternArgInExternFn, PrivateAssocItem, PrivateField, @@ -231,6 +232,14 @@ pub struct MismatchedTupleStructPatArgCount { pub found: usize, } +#[derive(Debug)] +pub struct MismatchedArrayPatLen { + pub pat: InFile, + pub expected: u128, + pub found: u128, + pub has_rest: bool, +} + #[derive(Debug)] pub struct ExpectedFunction<'db> { pub call: InFile, @@ -672,6 +681,10 @@ impl<'db> AnyDiagnostic<'db> { let private = private.map(|id| Field { id, parent: variant.into() }); NoSuchField { field: expr_or_pat, private, variant }.into() } + &InferenceDiagnostic::MismatchedArrayPatLen { pat, expected, found, has_rest } => { + let pat = pat_syntax(pat)?.map(Into::into); + MismatchedArrayPatLen { pat, expected, found, has_rest }.into() + } &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { MismatchedArgCount { call_expr: expr_syntax(call_expr)?, expected, found }.into() } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs new file mode 100644 index 0000000000000..8cae405c92eef --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs @@ -0,0 +1,114 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: mismatched-array-pat-len +// +// This diagnostic is triggered when an array pattern's element count does not +// match the array's declared length. +pub(crate) fn mismatched_array_pat_len( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MismatchedArrayPatLen, +) -> Diagnostic { + let (code, message) = if d.has_rest { + ( + "E0528", + format!( + "pattern requires at least {} element{} but array has {}", + d.found, + if d.found == 1 { "" } else { "s" }, + d.expected, + ), + ) + } else { + ( + "E0527", + format!( + "pattern requires {} element{} but array has {}", + d.found, + if d.found == 1 { "" } else { "s" }, + d.expected, + ), + ) + }; + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError(code), + message, + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn array_pattern_too_few_elements() { + check_diagnostics( + r#" +fn f(arr: [i32; 3]) { + let [_a, _b] = arr; + //^^^^^^^^ error: pattern requires 2 elements but array has 3 +} +"#, + ); + } + + #[test] + fn array_pattern_too_many_elements() { + check_diagnostics( + r#" +fn f(arr: [i32; 2]) { + let [_a, _b, _c] = arr; + //^^^^^^^^^^^^ error: pattern requires 3 elements but array has 2 +} +"#, + ); + } + + #[test] + fn array_pattern_with_rest_too_short() { + check_diagnostics( + r#" +fn f(arr: [i32; 2]) { + let [_a, _b, _c, ..] = arr; + //^^^^^^^^^^^^^^^^ error: pattern requires at least 3 elements but array has 2 +} +"#, + ); + } + + #[test] + fn array_pattern_with_rest_ok() { + check_diagnostics( + r#" +fn f(arr: [i32; 5]) { + let [_a, _b, ..] = arr; +} +"#, + ); + } + + #[test] + fn array_pattern_exact_length_ok() { + check_diagnostics( + r#" +fn f(arr: [i32; 3]) { + let [_a, _b, _c] = arr; +} +"#, + ); + } + + #[test] + fn array_pattern_singular_element_uses_singular() { + check_diagnostics( + r#" +fn f(arr: [i32; 3]) { + let [_a] = arr; + //^^^^ error: pattern requires 1 element but array has 3 +} +"#, + ); + } +} 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 618edc5c61861..d029b8d33280c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -47,6 +47,7 @@ mod handlers { pub(crate) mod macro_error; pub(crate) mod malformed_derive; pub(crate) mod mismatched_arg_count; + pub(crate) mod mismatched_array_pat_len; pub(crate) mod missing_fields; pub(crate) mod missing_lifetime; pub(crate) mod missing_match_arms; @@ -425,6 +426,7 @@ pub fn semantic_diagnostics( }, AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d), AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d), + AnyDiagnostic::MismatchedArrayPatLen(d) => handlers::mismatched_array_pat_len::mismatched_array_pat_len(&ctx, &d), AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d), AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d), From 97a85f6ffd40f7d58aa168c4a9499b8531d4c24a Mon Sep 17 00:00:00 2001 From: Amit Singhmar Date: Sat, 25 Apr 2026 04:57:32 +0000 Subject: [PATCH 152/289] feat: allow renaming of elided lifetimes (Fixes rust-lang/rust-analyzer#19260) --- .../rust-analyzer/crates/ide/src/rename.rs | 94 +++++++++++++- .../crates/syntax/src/syntax_editor/edits.rs | 115 ++++++++++-------- 2 files changed, 157 insertions(+), 52 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index 2c6116f745761..6d1e141d2189d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -16,7 +16,7 @@ use std::fmt::Write; use stdx::{always, format_to, never}; use syntax::{ AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize, - ast::{self, HasArgList, prec::ExprPrecedence}, + ast::{self, HasArgList, make, prec::ExprPrecedence}, }; use ide_db::text_edit::TextEdit; @@ -79,7 +79,10 @@ pub(crate) fn prepare_rename( let sema = Semantics::new(db); let source_file = sema.parse_guess_edition(position.file_id); let syntax = source_file.syntax(); - + if let Some(lifetime_token) = syntax.token_at_offset(position.offset).find(|t| t.text() == "'_") + { + return Ok(RangeInfo::new(lifetime_token.text_range(), ())); + } let res = find_definitions(&sema, syntax, position, &Name::new_symbol_root(sym::underscore))? .filter(|(_, _, def, _, _)| def.range_for_rename(&sema).is_some()) .map(|(frange, kind, _, _, _)| { @@ -133,6 +136,13 @@ pub(crate) fn rename( let edition = file_id.edition(db); let (new_name, kind) = IdentifierKind::classify(edition, new_name)?; + if kind == IdentifierKind::Lifetime + && let Some(lifetime_token) = + syntax.token_at_offset(position.offset).find(|t| t.text() == "'_") + { + let new_name_str = new_name.display(db, edition).to_string(); + return rename_elided_lifetime(position, lifetime_token, &new_name_str); + } let defs = find_definitions(&sema, syntax, position, &new_name)?; let alias_fallback = @@ -797,6 +807,30 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: String) -> O Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) } +fn rename_elided_lifetime( + position: FilePosition, + lifetime_token: syntax::SyntaxToken, + new_name: &str, +) -> RenameResult { + let parent = lifetime_token.parent().unwrap(); + let root = parent.ancestors().last().unwrap(); + + let mut builder = SourceChangeBuilder::new(position.file_id); + + let editor = builder.make_editor(&root); + + editor.replace(lifetime_token, make::lifetime(new_name).syntax().clone()); + + if let Some(has_generic_params) = parent.ancestors().find_map(ast::AnyHasGenericParams::cast) { + let lifetime_param = make::lifetime_param(make::lifetime(new_name)); + editor.add_generic_param(&has_generic_params, lifetime_param.into()); + } + + builder.add_file_edits(position.file_id, editor); + + Ok(builder.finish()) +} + #[cfg(test)] mod tests { use expect_test::{Expect, expect}; @@ -3989,4 +4023,60 @@ mod foo { "#, ); } + + #[test] + fn test_rename_elided_lifetime_fn_no_generics() { + check( + "'a", + r#" +fn foo(x: &'_$0 str) {} +"#, + r#" +fn foo<'a>(x: &'a str) {} +"#, + ); + } + + #[test] + fn test_rename_elided_lifetime_fn_with_generics() { + check( + "'a", + r#" +fn foo(x: &'_$0 str, y: T) {} +"#, + r#" +fn foo<'a, T>(x: &'a str, y: T) {} +"#, + ); + } + + #[test] + fn test_rename_elided_lifetime_impl_no_generics() { + check( + "'a", + r#" +struct Foo<'a>(&'a str); +impl Foo<'_$0> {} +"#, + r#" +struct Foo<'a>(&'a str); +impl<'a> Foo<'a> {} +"#, + ); + } + + #[test] + fn test_rename_elided_lifetime_impl_with_generics() { + check( + "'a", + r#" +struct Foo<'a, T>(&'a str, T); +impl Foo<'_$0, T> {} +"#, + r#" +struct Foo<'a, T>(&'a str, T); +impl<'a, T> Foo<'a, T> {} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index 28e8ceed708f3..a684c0bfdbb42 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs @@ -3,7 +3,7 @@ use crate::{ AstToken, Direction, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T, algo::neighbor, - ast::{self, AstNode, Fn, GenericParam, HasGenericParams, HasName, edit::IndentLevel, make}, + ast::{self, AstNode, HasGenericParams, HasName, edit::IndentLevel, make}, syntax_editor::{Position, SyntaxEditor}, }; @@ -109,64 +109,79 @@ impl GetOrCreateWhereClause for ast::Enum { } impl SyntaxEditor { - /// Adds a new generic param to the function using `SyntaxEditor` - pub fn add_generic_param(&self, function: &Fn, new_param: GenericParam) { - match function.generic_param_list() { - Some(generic_param_list) => match generic_param_list.generic_params().last() { - Some(last_param) => { - // There exists a generic param list and it's not empty - let position = generic_param_list.r_angle_token().map_or_else( - || Position::last_child_of(function.syntax()), - Position::before, - ); - - if last_param - .syntax() - .next_sibling_or_token() - .is_some_and(|it| it.kind() == SyntaxKind::COMMA) - { - self.insert( - Position::after(last_param.syntax()), - new_param.syntax().clone(), - ); - self.insert( - Position::after(last_param.syntax()), - make::token(SyntaxKind::WHITESPACE), - ); - self.insert( - Position::after(last_param.syntax()), - make::token(SyntaxKind::COMMA), - ); + /// Adds a new generic param to the node using `SyntaxEditor` + pub fn add_generic_param( + &self, + node: &impl ast::HasGenericParams, + new_param: ast::GenericParam, + ) { + let make = self.make(); + match node.generic_param_list() { + Some(generic_param_list) => { + let is_lifetime = matches!(new_param, ast::GenericParam::LifetimeParam(_)); + + if let Some(first_param) = generic_param_list.generic_params().next() { + let last_lifetime = generic_param_list + .generic_params() + .filter(|p| matches!(p, ast::GenericParam::LifetimeParam(_))) + .last(); + + if is_lifetime { + if let Some(last_lt) = last_lifetime { + let elements = vec![ + make.token(SyntaxKind::COMMA).into(), + make.token(SyntaxKind::WHITESPACE).into(), + new_param.syntax().clone().into(), + ]; + self.insert_all(Position::after(last_lt.syntax()), elements); + } else { + // Insert before the first parameter + let elements = vec![ + new_param.syntax().clone().into(), + make.token(SyntaxKind::COMMA).into(), + make.token(SyntaxKind::WHITESPACE).into(), + ]; + self.insert_all(Position::before(first_param.syntax()), elements); + } } else { + let last_param = generic_param_list.generic_params().last().unwrap(); let elements = vec![ - make::token(SyntaxKind::COMMA).into(), - make::token(SyntaxKind::WHITESPACE).into(), + make.token(SyntaxKind::COMMA).into(), + make.token(SyntaxKind::WHITESPACE).into(), new_param.syntax().clone().into(), ]; - self.insert_all(position, elements); + self.insert_all(Position::after(last_param.syntax()), elements); + } + } else { + if let Some(l_angle) = generic_param_list.l_angle_token() { + self.insert(Position::after(l_angle), new_param.syntax().clone()); } } - None => { - // There exists a generic param list but it's empty - let position = Position::after(generic_param_list.l_angle_token().unwrap()); - self.insert(position, new_param.syntax()); - } - }, + } None => { - // There was no generic param list - let position = if let Some(name) = function.name() { - Position::after(name.syntax) - } else if let Some(fn_token) = function.fn_token() { - Position::after(fn_token) - } else if let Some(param_list) = function.param_list() { - Position::before(param_list.syntax) - } else { - Position::last_child_of(function.syntax()) - }; + let position = + if let Some(name) = node.syntax().children().find_map(ast::Name::cast) { + Position::after(name.syntax()) + } else if let Some(impl_node) = ast::Impl::cast(node.syntax().clone()) { + impl_node + .impl_token() + .map_or_else(|| Position::last_child_of(node.syntax()), Position::after) + } else if let Some(fn_node) = ast::Fn::cast(node.syntax().clone()) { + if let Some(fn_token) = fn_node.fn_token() { + Position::after(fn_token) + } else if let Some(param_list) = fn_node.param_list() { + Position::before(param_list.syntax()) + } else { + Position::last_child_of(node.syntax()) + } + } else { + Position::last_child_of(node.syntax()) + }; + let elements = vec![ - make::token(SyntaxKind::L_ANGLE).into(), + make.token(SyntaxKind::L_ANGLE).into(), new_param.syntax().clone().into(), - make::token(SyntaxKind::R_ANGLE).into(), + make.token(SyntaxKind::R_ANGLE).into(), ]; self.insert_all(position, elements); } From a4e178218790154759309d3f02df97d0d39f44a8 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 01:03:26 +0530 Subject: [PATCH 153/289] remove insert_use and insert_use_as_alias --- .../crates/ide-db/src/imports/insert_use.rs | 208 +----------------- 1 file changed, 5 insertions(+), 203 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index 7402ec8f5a7b7..16eddcbd35598 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -6,10 +6,11 @@ use std::cmp::Ordering; use hir::Semantics; use syntax::{ - Direction, NodeOrToken, SyntaxKind, SyntaxNode, algo, - ast::{self, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind, make}, + NodeOrToken, SyntaxKind, SyntaxNode, + ast::{ + self, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind, edit::IndentLevel, + }, syntax_editor::{Position, SyntaxEditor}, - ted, }; use crate::{ @@ -147,24 +148,6 @@ impl ImportScope { ImportScopeKind::Block(block) => block.syntax(), } } - - pub fn clone_for_update(&self) -> Self { - Self { - kind: match &self.kind { - ImportScopeKind::File(file) => ImportScopeKind::File(file.clone_for_update()), - ImportScopeKind::Module(item_list) => { - ImportScopeKind::Module(item_list.clone_for_update()) - } - ImportScopeKind::Block(block) => ImportScopeKind::Block(block.clone_for_update()), - }, - required_cfgs: self.required_cfgs.iter().map(|attr| attr.clone_for_update()).collect(), - } - } -} - -/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. -pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) { - insert_use_with_alias_option(scope, path, cfg, None); } /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. @@ -193,71 +176,7 @@ pub fn insert_use_as_alias( .expect("Failed to make ast node `Rename`"); let alias = node.rename(); - insert_use_with_alias_option(scope, path, cfg, alias); -} - -fn insert_use_with_alias_option( - scope: &ImportScope, - path: ast::Path, - cfg: &InsertUseConfig, - alias: Option, -) { - let _p = tracing::info_span!("insert_use_with_alias_option").entered(); - let mut mb = match cfg.granularity { - ImportGranularity::Crate => Some(MergeBehavior::Crate), - ImportGranularity::Module => Some(MergeBehavior::Module), - ImportGranularity::One => Some(MergeBehavior::One), - ImportGranularity::Item => None, - }; - if !cfg.enforce_granularity { - let file_granularity = guess_granularity_from_scope(scope); - mb = match file_granularity { - ImportGranularityGuess::Unknown => mb, - ImportGranularityGuess::Item => None, - ImportGranularityGuess::Module => Some(MergeBehavior::Module), - // We use the user's setting to infer if this is module or item. - ImportGranularityGuess::ModuleOrItem => match mb { - Some(MergeBehavior::Module) | None => mb, - // There isn't really a way to decide between module or item here, so we just pick one. - // FIXME: Maybe it is possible to infer based on semantic analysis? - Some(MergeBehavior::One | MergeBehavior::Crate) => Some(MergeBehavior::Module), - }, - ImportGranularityGuess::Crate => Some(MergeBehavior::Crate), - ImportGranularityGuess::CrateOrModule => match mb { - Some(MergeBehavior::Crate | MergeBehavior::Module) => mb, - Some(MergeBehavior::One) | None => Some(MergeBehavior::Crate), - }, - ImportGranularityGuess::One => Some(MergeBehavior::One), - }; - } - - let mut use_tree = make::use_tree(path, None, alias, false); - if mb == Some(MergeBehavior::One) && use_tree.path().is_some() { - use_tree = use_tree.clone_for_update(); - use_tree.wrap_in_tree_list(); - } - let use_item = make::use_(None, None, use_tree).clone_for_update(); - for attr in - scope.required_cfgs.iter().map(|attr| attr.syntax().clone_subtree().clone_for_update()) - { - ted::insert(ted::Position::first_child_of(use_item.syntax()), attr); - } - - // merge into existing imports if possible - if let Some(mb) = mb { - let filter = |it: &_| !(cfg.skip_glob_imports && ast::Use::is_simple_glob(it)); - for existing_use in - scope.as_syntax_node().children().filter_map(ast::Use::cast).filter(filter) - { - if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { - ted::replace(existing_use.syntax(), merged.syntax()); - return; - } - } - } - // either we weren't allowed to merge or there is no import that fits the merge conditions - // so look for the place we have to insert to - insert_use_(scope, use_item, cfg.group); + insert_use_with_alias_option_with_editor(scope, path, cfg, alias, editor); } fn insert_use_with_alias_option_with_editor( @@ -469,123 +388,6 @@ fn guess_granularity_from_scope(scope: &ImportScope) -> ImportGranularityGuess { } } -fn insert_use_(scope: &ImportScope, use_item: ast::Use, group_imports: bool) { - let scope_syntax = scope.as_syntax_node(); - let insert_use_tree = - use_item.use_tree().expect("`use_item` should have a use tree for `insert_path`"); - let group = ImportGroup::new(&insert_use_tree); - let path_node_iter = scope_syntax - .children() - .filter_map(|node| ast::Use::cast(node.clone()).zip(Some(node))) - .flat_map(|(use_, node)| { - let tree = use_.use_tree()?; - Some((tree, node)) - }); - - if group_imports { - // Iterator that discards anything that's not in the required grouping - // This implementation allows the user to rearrange their import groups as this only takes the first group that fits - let group_iter = path_node_iter - .clone() - .skip_while(|(use_tree, ..)| ImportGroup::new(use_tree) != group) - .take_while(|(use_tree, ..)| ImportGroup::new(use_tree) == group); - - // track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place - let mut last = None; - // find the element that would come directly after our new import - let post_insert: Option<(_, SyntaxNode)> = group_iter - .inspect(|(.., node)| last = Some(node.clone())) - .find(|(use_tree, _)| use_tree_cmp(&insert_use_tree, use_tree) != Ordering::Greater); - - if let Some((.., node)) = post_insert { - cov_mark::hit!(insert_group); - // insert our import before that element - return ted::insert(ted::Position::before(node), use_item.syntax()); - } - if let Some(node) = last { - cov_mark::hit!(insert_group_last); - // there is no element after our new import, so append it to the end of the group - return ted::insert(ted::Position::after(node), use_item.syntax()); - } - - // the group we were looking for actually doesn't exist, so insert - - let mut last = None; - // find the group that comes after where we want to insert - let post_group = path_node_iter - .inspect(|(.., node)| last = Some(node.clone())) - .find(|(use_tree, ..)| ImportGroup::new(use_tree) > group); - if let Some((.., node)) = post_group { - cov_mark::hit!(insert_group_new_group); - ted::insert(ted::Position::before(&node), use_item.syntax()); - if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) { - ted::insert(ted::Position::after(node), make::tokens::single_newline()); - } - return; - } - // there is no such group, so append after the last one - if let Some(node) = last { - cov_mark::hit!(insert_group_no_group); - ted::insert(ted::Position::after(&node), use_item.syntax()); - ted::insert(ted::Position::after(node), make::tokens::single_newline()); - return; - } - } else { - // There exists a group, so append to the end of it - if let Some((_, node)) = path_node_iter.last() { - cov_mark::hit!(insert_no_grouping_last); - ted::insert(ted::Position::after(node), use_item.syntax()); - return; - } - } - - let l_curly = match &scope.kind { - ImportScopeKind::File(_) => None, - // don't insert the imports before the item list/block expr's opening curly brace - ImportScopeKind::Module(item_list) => item_list.l_curly_token(), - // don't insert the imports before the item list's opening curly brace - ImportScopeKind::Block(block) => block.l_curly_token(), - }; - // there are no imports in this file at all - // so put the import after all inner module attributes and possible license header comments - if let Some(last_inner_element) = scope_syntax - .children_with_tokens() - // skip the curly brace - .skip(l_curly.is_some() as usize) - .take_while(|child| match child { - NodeOrToken::Node(node) => { - is_inner_attribute(node.clone()) && ast::Item::cast(node.clone()).is_none() - } - NodeOrToken::Token(token) => { - [SyntaxKind::WHITESPACE, SyntaxKind::COMMENT, SyntaxKind::SHEBANG] - .contains(&token.kind()) - } - }) - .filter(|child| child.as_token().is_none_or(|t| t.kind() != SyntaxKind::WHITESPACE)) - .last() - { - cov_mark::hit!(insert_empty_inner_attr); - ted::insert(ted::Position::after(&last_inner_element), use_item.syntax()); - ted::insert(ted::Position::after(last_inner_element), make::tokens::single_newline()); - } else { - match l_curly { - Some(b) => { - cov_mark::hit!(insert_empty_module); - ted::insert(ted::Position::after(&b), make::tokens::single_newline()); - ted::insert(ted::Position::after(&b), use_item.syntax()); - } - None => { - cov_mark::hit!(insert_empty_file); - ted::insert( - ted::Position::first_child_of(scope_syntax), - make::tokens::blank_line(), - ); - ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax()); - } - } - } -} - fn insert_use_with_editor_( scope: &ImportScope, use_item: ast::Use, From 1b1f9e3314b4d149a17a99c88165940cc5660ac3 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 01:04:40 +0530 Subject: [PATCH 154/289] correct the whitespace in insert_use_with_editor_ --- .../crates/ide-db/src/imports/insert_use.rs | 66 +++++++++++++------ 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index 16eddcbd35598..ebd557aaf6991 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -220,10 +220,7 @@ fn insert_use_with_alias_option_with_editor( if mb == Some(MergeBehavior::One) && use_tree.path().is_some() { use_tree.wrap_in_tree_list(); } - let use_item = make::use_(None, None, use_tree); - for attr in scope.required_cfgs.iter().map(|attr| attr.syntax().clone()) { - syntax_editor.insert(Position::first_child_of(use_item.syntax()), attr); - } + let use_item = make.use_(scope.required_cfgs.iter().cloned().rev(), None, use_tree); // merge into existing imports if possible if let Some(mb) = mb { @@ -425,12 +422,18 @@ fn insert_use_with_editor_( if let Some((.., node)) = post_insert { cov_mark::hit!(insert_group); // insert our import before that element - return syntax_editor.insert(Position::before(node), use_item.syntax()); + return syntax_editor.insert_all( + Position::before(node), + vec![use_item.syntax().clone().into(), make.whitespace("\n").into()], + ); } if let Some(node) = last { cov_mark::hit!(insert_group_last); // there is no element after our new import, so append it to the end of the group - return syntax_editor.insert(Position::after(node), use_item.syntax()); + return syntax_editor.insert_all( + Position::after(node), + vec![make.whitespace("\n").into(), use_item.syntax().clone().into()], + ); } // the group we were looking for actually doesn't exist, so insert @@ -442,24 +445,29 @@ fn insert_use_with_editor_( .find(|(use_tree, ..)| ImportGroup::new(use_tree) > group); if let Some((.., node)) = post_group { cov_mark::hit!(insert_group_new_group); - syntax_editor.insert(Position::before(&node), use_item.syntax()); - if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) { - syntax_editor.insert(Position::after(node), make.whitespace("\n")); - } + syntax_editor.insert_all( + Position::before(&node), + vec![use_item.syntax().clone().into(), make.whitespace("\n\n").into()], + ); return; } // there is no such group, so append after the last one if let Some(node) = last { cov_mark::hit!(insert_group_no_group); - syntax_editor.insert(Position::after(&node), use_item.syntax()); - syntax_editor.insert(Position::after(node), make.whitespace("\n")); + syntax_editor.insert_all( + Position::after(&node), + vec![make.whitespace("\n\n").into(), use_item.syntax().clone().into()], + ); return; } } else { // There exists a group, so append to the end of it if let Some((_, node)) = path_node_iter.last() { cov_mark::hit!(insert_no_grouping_last); - syntax_editor.insert(Position::after(node), use_item.syntax()); + syntax_editor.insert_all( + Position::after(node), + vec![make.whitespace("\n").into(), use_item.syntax().clone().into()], + ); return; } } @@ -490,20 +498,38 @@ fn insert_use_with_editor_( .last() { cov_mark::hit!(insert_empty_inner_attr); - syntax_editor.insert(Position::after(&last_inner_element), use_item.syntax()); - syntax_editor.insert(Position::after(last_inner_element), make.whitespace("\n")); + let indent = if l_curly.is_some() { + IndentLevel::from_node(scope_syntax) + 1 + } else { + IndentLevel::zero() + }; + syntax_editor.insert_all( + Position::after(&last_inner_element), + vec![ + make.whitespace(&format!("\n\n{indent}")).into(), + use_item.syntax().clone().into(), + ], + ); } else { match l_curly { Some(b) => { cov_mark::hit!(insert_empty_module); - syntax_editor.insert(Position::after(&b), make.whitespace("\n")); - syntax_editor.insert_with_whitespace(Position::after(&b), use_item.syntax()); + let indent = IndentLevel::from_node(scope_syntax) + 1; + syntax_editor.insert_all( + Position::after(&b), + vec![ + make.whitespace(&format!("\n{indent}")).into(), + use_item.syntax().clone().into(), + make.whitespace("\n").into(), + ], + ); } None => { cov_mark::hit!(insert_empty_file); - syntax_editor - .insert(Position::first_child_of(scope_syntax), make.whitespace("\n\n")); - syntax_editor.insert(Position::first_child_of(scope_syntax), use_item.syntax()); + syntax_editor.insert_all( + Position::first_child_of(scope_syntax), + vec![use_item.syntax().clone().into(), make.whitespace("\n\n").into()], + ); } } } From ce51b5985e675808a6f17caef856dfdca3958949 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 01:08:17 +0530 Subject: [PATCH 155/289] use editor variant of insert_use in auto_import --- .../ide-assists/src/handlers/auto_import.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index 9bfb47e69dca1..ac0bae7cd9b1a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -7,7 +7,7 @@ use ide_db::{ helpers::mod_path_to_ast, imports::{ import_assets::{ImportAssets, ImportCandidate, LocatedImport, TraitImportCandidate}, - insert_use::{ImportScope, insert_use, insert_use_as_alias}, + insert_use::{ImportScope, insert_use_as_alias_with_editor, insert_use_with_editor}, }, }; use syntax::{AstNode, Edition, SyntaxNode, ast, match_ast}; @@ -125,8 +125,14 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Opt (AssistId::quick_fix("auto_import"), import_path.display(ctx.db(), edition)); let add_normal_import = |acc: &mut Assists, label| { acc.add_group(&group_label, assist_id, label, range, |builder| { - let scope = builder.make_import_scope_mut(scope.clone()); - insert_use(&scope, mod_path_to_ast(&import_path, edition), &ctx.config.insert_use); + let editor = builder.make_editor(scope.as_syntax_node()); + insert_use_with_editor( + &scope, + mod_path_to_ast(&import_path, edition), + &ctx.config.insert_use, + &editor, + ); + builder.add_file_edits(ctx.vfs_file_id(), editor); }) }; let add_underscore_import = |acc: &mut Assists, name: &TraitImportCandidate<'_>, label| { @@ -139,13 +145,15 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Opt name.assoc_item_name.text() )); acc.add_group(&group_label, assist_id, label, range, |builder| { - let scope = builder.make_import_scope_mut(scope.clone()); - insert_use_as_alias( + let editor = builder.make_editor(scope.as_syntax_node()); + insert_use_as_alias_with_editor( &scope, mod_path_to_ast(&import_path, edition), &ctx.config.insert_use, edition, + &editor, ); + builder.add_file_edits(ctx.vfs_file_id(), editor); }); }; From 18ffe08b9d10409fcd0801c71c7b4076931473c3 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 01:10:04 +0530 Subject: [PATCH 156/289] use editor variant of insert_use and insert_use_as_alias in resolve_completion_edits --- .../crates/ide-completion/src/lib.rs | 17 ++++++++++------- .../src/ast/syntax_factory/constructors.rs | 6 +++++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 66ecb790a0aaa..4ca3257c5cbaf 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -23,7 +23,7 @@ use ide_db::{ syntax_helpers::tree_diff::diff, text_edit::TextEdit, }; -use syntax::ast::make; +use syntax::{AstNode, syntax_editor::SyntaxEditor}; use crate::{ completions::Completions, @@ -296,23 +296,26 @@ pub fn resolve_completion_edits( let current_module = sema.scope(position_for_import)?.module(); let current_crate = current_module.krate(db); let current_edition = current_crate.edition(db); - let new_ast = scope.clone_for_update(); let mut import_insert = TextEdit::builder(); + let (editor, _) = SyntaxEditor::new(original_file.syntax().clone()); + let make = editor.make(); imports.into_iter().for_each(|import| { - let full_path = make::path_from_text_with_edition(&import.path, current_edition); + let full_path = make.path_from_text_with_edition(&import.path, current_edition); if import.as_underscore { - insert_use::insert_use_as_alias( - &new_ast, + insert_use::insert_use_as_alias_with_editor( + &scope, full_path, &config.insert_use, current_edition, + &editor, ); } else { - insert_use::insert_use(&new_ast, full_path, &config.insert_use); + insert_use::insert_use_with_editor(&scope, full_path, &config.insert_use, &editor); } }); - diff(scope.as_syntax_node(), new_ast.as_syntax_node()).into_text_edit(&mut import_insert); + let edit = editor.finish(); + diff(edit.old_root(), edit.new_root()).into_text_edit(&mut import_insert); Some(vec![import_insert.finish()]) } 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 5a01580c56fc2..bebf595f00d9d 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 @@ -2,7 +2,7 @@ use either::Either; use crate::{ - AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, + AstNode, Edition, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, ast::{ self, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, HasTypeBounds, HasVisibility, Lifetime, Param, RangeItem, make, @@ -131,6 +131,10 @@ impl SyntaxFactory { make::path_from_text(text).clone_for_update() } + pub fn path_from_text_with_edition(&self, text: &str, edition: Edition) -> ast::Path { + make::path_from_text_with_edition(text, edition).clone_for_update() + } + pub fn path_concat(&self, first: ast::Path, second: ast::Path) -> ast::Path { make::path_concat(first, second).clone_for_update() } From 89e8a0040ccadb480864b4f4288ac4de6f1bb4e4 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 01:11:23 +0530 Subject: [PATCH 157/289] use editor variant of insert_use in check_with_config --- .../crates/ide-db/src/imports/insert_use/tests.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs index 6c7b97458d208..4fa05c4603461 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs @@ -1342,14 +1342,14 @@ fn check_with_config( }; let sema = &Semantics::new(&db); let source_file = sema.parse(file_id); + let (editor, _) = SyntaxEditor::new(source_file.syntax().clone()); let file = pos .and_then(|pos| source_file.syntax().token_at_offset(pos.expect_offset()).next()?.parent()) .and_then(|it| ImportScope::find_insert_use_container(&it, sema)) .unwrap_or_else(|| ImportScope { - kind: ImportScopeKind::File(source_file), + kind: ImportScopeKind::File(source_file.clone()), required_cfgs: vec![], - }) - .clone_for_update(); + }); let path = ast::SourceFile::parse(&format!("use {path};"), span::Edition::CURRENT) .tree() .syntax() @@ -1357,8 +1357,9 @@ fn check_with_config( .find_map(ast::Path::cast) .unwrap(); - insert_use(&file, path, config); - let result = file.as_syntax_node().ancestors().last().unwrap().to_string(); + insert_use_with_editor(&file, path, config, &editor); + let edit = editor.finish(); + let result = edit.new_root().to_string(); assert_eq_text!(&trim_indent(ra_fixture_after), &result); } From db9d8b22c4e78ef98e9e110dcbad7b0d3603ab3c Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 01:13:11 +0530 Subject: [PATCH 158/289] add insert_uses_with_editor to insert_use and accordingly migrate json_in_items to SyntaxEditor --- .../crates/ide-db/src/imports/insert_use.rs | 34 ++++++++++++++++++- .../src/handlers/json_is_not_rust.rs | 22 ++++++++---- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index ebd557aaf6991..4b0373271c038 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -160,11 +160,43 @@ pub fn insert_use_with_editor( insert_use_with_alias_option_with_editor(scope, path, cfg, None, syntax_editor); } -pub fn insert_use_as_alias( +pub fn insert_uses_with_editor( + scope: &ImportScope, + paths: impl IntoIterator, + cfg: &InsertUseConfig, + syntax_editor: &SyntaxEditor, +) { + let paths = paths.into_iter().collect::>(); + if paths.len() > 1 + && scope.as_syntax_node().parent().is_none() + && scope.required_cfgs.is_empty() + && !scope.as_syntax_node().children().any(|node| ast::Use::cast(node).is_some()) + { + let make = syntax_editor.make(); + let elements = paths + .into_iter() + .flat_map(|path| { + let use_tree = make.use_tree(path, None, None, false); + let use_item = make.use_(None, None, use_tree); + [use_item.syntax().clone().into(), make.whitespace("\n").into()] + }) + .chain([make.whitespace("\n").into()]) + .collect(); + syntax_editor.insert_all(Position::first_child_of(scope.as_syntax_node()), elements); + return; + } + + for path in paths { + insert_use_with_editor(scope, path, cfg, syntax_editor); + } +} + +pub fn insert_use_as_alias_with_editor( scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig, edition: span::Edition, + editor: &SyntaxEditor, ) { let text: &str = "use foo as _"; let parse = syntax::SourceFile::parse(text, edition); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index 20bfcc2deecee..24f1e3ad836a0 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -2,12 +2,11 @@ //! example. use hir::{FindPathConfig, PathResolution, Semantics}; +use ide_db::imports::insert_use::insert_uses_with_editor; use ide_db::text_edit::TextEdit; use ide_db::{ - EditionedFileId, FileRange, FxHashMap, RootDatabase, - helpers::mod_path_to_ast, - imports::insert_use::{ImportScope, insert_use}, - source_change::SourceChangeBuilder, + EditionedFileId, FileRange, FxHashMap, RootDatabase, helpers::mod_path_to_ast, + imports::insert_use::ImportScope, source_change::SourceChangeBuilder, }; use itertools::Itertools; use stdx::{format_to, never}; @@ -138,7 +137,7 @@ pub(crate) fn json_in_items( .stable() .with_fixes(Some(vec![{ let mut scb = SourceChangeBuilder::new(vfs_file_id); - let scope = scb.make_import_scope_mut(import_scope); + let editor = scb.make_editor(import_scope.as_syntax_node()); let current_module = semantics_scope.module(); let cfg = FindPathConfig { @@ -148,6 +147,7 @@ pub(crate) fn json_in_items( allow_unstable: true, }; + let mut imports_to_insert = Vec::new(); if !scope_has("Serialize") && let Some(PathResolution::Def(it)) = serialize_resolved && let Some(it) = current_module.find_use_path( @@ -157,7 +157,7 @@ pub(crate) fn json_in_items( cfg, ) { - insert_use(&scope, mod_path_to_ast(&it, edition), &config.insert_use); + imports_to_insert.push(mod_path_to_ast(&it, edition)); } if !scope_has("Deserialize") && let Some(PathResolution::Def(it)) = deserialize_resolved @@ -168,8 +168,16 @@ pub(crate) fn json_in_items( cfg, ) { - insert_use(&scope, mod_path_to_ast(&it, edition), &config.insert_use); + imports_to_insert.push(mod_path_to_ast(&it, edition)); } + + insert_uses_with_editor( + &import_scope, + imports_to_insert, + &config.insert_use, + &editor, + ); + scb.add_file_edits(vfs_file_id, editor); let mut sc = scb.finish(); sc.insert_source_edit(vfs_file_id, edit.finish()); fix("convert_json_to_struct", "Convert JSON to struct", sc, range) From cccb9b9fb1e68a57c3e1ee8e20c246dd7bfaa34b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 01:19:17 +0530 Subject: [PATCH 159/289] replace insert_use with its editor variant in convert_bool_to_enum --- .../src/handlers/convert_bool_to_enum.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs index 456a4d1fcf17b..4c3168219fc63 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs @@ -1,12 +1,13 @@ use either::Either; use hir::ModuleDef; +use ide_db::imports::insert_use::insert_use_with_editor; use ide_db::text_edit::TextRange; use ide_db::{ - FxHashSet, + FileId, FxHashSet, assists::AssistId, defs::Definition, helpers::mod_path_to_ast_with_factory, - imports::insert_use::{ImportScope, insert_use}, + imports::insert_use::ImportScope, search::{FileReference, UsageSearchResult}, source_change::SourceChangeBuilder, }; @@ -85,8 +86,10 @@ pub(crate) fn convert_bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_, '_ &mut delayed_mutations, &make, ); - for (scope, path) in delayed_mutations { - insert_use(&scope, path, &ctx.config.insert_use); + for (file_id, scope, path) in delayed_mutations { + let editor = edit.make_editor(scope.as_syntax_node()); + insert_use_with_editor(&scope, path, &ctx.config.insert_use, &editor); + edit.add_file_edits(file_id, editor); } }, ) @@ -212,11 +215,12 @@ fn replace_usages( usages: UsageSearchResult, target_definition: Definition, target_module: &hir::Module, - delayed_mutations: &mut Vec<(ImportScope, ast::Path)>, + delayed_mutations: &mut Vec<(FileId, ImportScope, ast::Path)>, make: &SyntaxFactory, ) { for (file_id, references) in usages { - edit.edit_file(file_id.file_id(ctx.db())); + let vfs_file_id = file_id.file_id(ctx.db()); + edit.edit_file(vfs_file_id); let refs_with_imports = augment_references_with_imports(ctx, references, target_module, make); @@ -323,8 +327,7 @@ fn replace_usages( // add imports across modules where needed if let Some((scope, path)) = import_data { - let scope = edit.make_import_scope_mut(scope); - delayed_mutations.push((scope, path)); + delayed_mutations.push((vfs_file_id, scope, path)); } }, ) From 4ee59f325129e9afc9935c1ac4793a0d07b2e9cb Mon Sep 17 00:00:00 2001 From: Onyeka Obi Date: Fri, 1 May 2026 05:26:58 -0700 Subject: [PATCH 160/289] hir-ty: merge block validation passes in `validate_block` Address review feedback on rust-lang/rust-analyzer#22239: combine the must-use loop and the non-exhaustive-let loop into a single pass over `statements`, and lift the existing handling into a dedicated `check_non_exhaustive_let` method. `check_unused_must_use` and `check_non_exhaustive_let` now return `Option`; the caller pushes the result. This keeps `&self.infcx` (held by `MatchCheckCtx`) compatible with the mutable borrow on `self.diagnostics` via field-level split borrows. --- .../rust-analyzer/crates/hir-def/src/attrs.rs | 4 + .../crates/hir-ty/src/diagnostics/expr.rs | 137 ++++++++++++------ .../crates/hir/src/diagnostics.rs | 11 ++ .../src/handlers/unused_must_use.rs | 110 ++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + .../crates/intern/src/symbol/symbols.rs | 1 + 6 files changed, 224 insertions(+), 41 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_must_use.rs 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 5dc410be27216..7757d537e6501 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -137,6 +137,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow< "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED), "ignore" => attr_flags.insert(AttrFlags::IS_IGNORE), "lang" => attr_flags.insert(AttrFlags::LANG_ITEM), + "must_use" => attr_flags.insert(AttrFlags::IS_MUST_USE), "path" => attr_flags.insert(AttrFlags::HAS_PATH), "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE), "export_name" => { @@ -227,6 +228,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow< "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE), "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED), "macro_export" => attr_flags.insert(AttrFlags::IS_MACRO_EXPORT), + "must_use" => attr_flags.insert(AttrFlags::IS_MUST_USE), "no_mangle" => attr_flags.insert(AttrFlags::NO_MANGLE), "pointee" => attr_flags.insert(AttrFlags::IS_POINTEE), "non_exhaustive" => attr_flags.insert(AttrFlags::NON_EXHAUSTIVE), @@ -335,6 +337,8 @@ bitflags::bitflags! { const MACRO_STYLE_PARENTHESES = 1 << 48; const PREFER_UNDERSCORE_IMPORT = 1 << 49; + + const IS_MUST_USE = 1 << 50; } } 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 93772ca452aac..be29926a380d9 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 @@ -7,7 +7,9 @@ use std::fmt; use base_db::Crate; use either::Either; use hir_def::{ - AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup, + AdtId, AssocItemId, AttrDefId, CallableDefId, DefWithBodyId, HasModule, ItemContainerId, + Lookup, + attrs::AttrFlags, lang_item::LangItems, resolver::{HasResolver, ValueNs}, }; @@ -33,7 +35,7 @@ use crate::{ }, display::{DisplayTarget, HirDisplay}, next_solver::{ - DbInterner, ParamEnv, Ty, TyKind, TypingMode, + CallableIdWrapper, DbInterner, ParamEnv, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, InferCtxt}, }, }; @@ -67,6 +69,9 @@ pub enum BodyValidationDiagnostic { RemoveUnnecessaryElse { if_expr: ExprId, }, + UnusedMustUse { + expr: ExprId, + }, } impl BodyValidationDiagnostic { @@ -328,52 +333,71 @@ 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 &Statement::Let { pat, initializer, else_branch: None, .. } = stmt else { - continue; + let diag = match *stmt { + Statement::Expr { expr: stmt_expr, has_semi: true } if self.validate_lints => { + self.check_unused_must_use(stmt_expr) + } + Statement::Let { pat, initializer, else_branch: None, .. } => { + self.check_non_exhaustive_let(&cx, &pattern_arena, pat, initializer) + } + _ => None, }; - if self.infer.type_mismatch_for_pat(pat).is_some() { - continue; - } - let Some(initializer) = initializer else { continue }; - let Some(ty) = self.infer.type_of_expr_with_adjust(initializer) else { continue }; - if ty.references_non_lt_error() { - continue; + if let Some(diag) = diag { + self.diagnostics.push(diag); } + } + } - let mut have_errors = false; - let deconstructed_pat = self.lower_pattern(&cx, pat, &mut have_errors); + fn check_non_exhaustive_let<'a>( + &self, + cx: &MatchCheckCtx<'a, 'db>, + pattern_arena: &'a Arena>, + pat: PatId, + initializer: Option, + ) -> Option { + if self.infer.type_mismatch_for_pat(pat).is_some() { + return None; + } + let initializer = initializer?; + let ty = self.infer.type_of_expr_with_adjust(initializer)?; + if ty.references_non_lt_error() { + return None; + } - // optimization, wildcard trivially hold - if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) { - continue; - } + let mut have_errors = false; + let deconstructed_pat = self.lower_pattern(cx, pat, &mut have_errors); - let match_arm = rustc_pattern_analysis::MatchArm { - pat: pattern_arena.alloc(deconstructed_pat), - has_guard: false, - arm_data: (), - }; - let report = match cx.compute_match_usefulness(&[match_arm], ty, None) { - Ok(v) => v, - Err(e) => { - debug!(?e, "match usefulness error"); - continue; - } - }; - let witnesses = report.non_exhaustiveness_witnesses; - if !witnesses.is_empty() { - self.diagnostics.push(BodyValidationDiagnostic::NonExhaustiveLet { - pat, - uncovered_patterns: missing_match_arms( - &cx, - ty, - witnesses, - false, - self.owner.krate(self.db()), - ), - }); + // optimization, wildcard trivially hold + if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) { + return None; + } + + let match_arm = rustc_pattern_analysis::MatchArm { + pat: pattern_arena.alloc(deconstructed_pat), + has_guard: false, + arm_data: (), + }; + let report = match cx.compute_match_usefulness(&[match_arm], ty, None) { + Ok(v) => v, + Err(e) => { + debug!(?e, "match usefulness error"); + return None; } + }; + let witnesses = report.non_exhaustiveness_witnesses; + if witnesses.is_empty() { + return None; } + Some(BodyValidationDiagnostic::NonExhaustiveLet { + pat, + uncovered_patterns: missing_match_arms( + cx, + ty, + witnesses, + false, + self.owner.krate(self.db()), + ), + }) } fn lower_pattern<'a>( @@ -391,6 +415,37 @@ impl<'db> ExprValidator<'db> { pattern } + fn check_unused_must_use(&self, expr: ExprId) -> Option { + let db = self.db(); + let must_use_fn = match &self.body[expr] { + Expr::Call { callee, .. } => { + let callee_ty = self.infer.expr_ty(*callee); + if let TyKind::FnDef(CallableIdWrapper(CallableDefId::FunctionId(func)), _) = + callee_ty.kind() + { + AttrFlags::query(db, AttrDefId::FunctionId(func)) + .contains(AttrFlags::IS_MUST_USE) + } else { + false + } + } + Expr::MethodCall { .. } => { + self.infer.method_resolution(expr).is_some_and(|(func, _)| { + AttrFlags::query(db, AttrDefId::FunctionId(func)) + .contains(AttrFlags::IS_MUST_USE) + }) + } + _ => return None, + }; + let must_use_ty = + self.infer.type_of_expr_with_adjust(expr).is_some_and(|ty| match ty.kind() { + TyKind::Adt(adt, _) => AttrFlags::query(db, AttrDefId::AdtId(adt.def_id())) + .contains(AttrFlags::IS_MUST_USE), + _ => false, + }); + (must_use_fn || must_use_ty).then_some(BodyValidationDiagnostic::UnusedMustUse { expr }) + } + fn check_for_trailing_return(&mut self, body_expr: ExprId, body: &Body) { if !self.validate_lints { return; diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 082c29c17415b..3259abb536134 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -86,6 +86,7 @@ diagnostics![AnyDiagnostic<'db> -> PrivateField, RemoveTrailingReturn, RemoveUnnecessaryElse, + UnusedMustUse, ReplaceFilterMapNextWithFindMap, TraitImplIncorrectSafety, TraitImplMissingAssocItems, @@ -397,6 +398,11 @@ pub struct RemoveUnnecessaryElse { pub if_expr: InFile>, } +#[derive(Debug)] +pub struct UnusedMustUse { + pub expr: InFile, +} + #[derive(Debug)] pub struct CastToUnsized<'db> { pub expr: InFile, @@ -628,6 +634,11 @@ impl<'db> AnyDiagnostic<'db> { ); } } + BodyValidationDiagnostic::UnusedMustUse { expr } => { + if let Ok(source_ptr) = source_map.expr_syntax(expr) { + return Some(UnusedMustUse { expr: source_ptr }.into()); + } + } } None } 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 new file mode 100644 index 0000000000000..4b9ecf216927f --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_must_use.rs @@ -0,0 +1,110 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: unused-must-use +// +// This diagnostic is triggered when a value with the `#[must_use]` attribute +// is dropped without being used. +pub(crate) fn unused_must_use( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::UnusedMustUse, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_must_use"), + "unused return value that must be used", + d.expr.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn unused_must_use_function_call() { + check_diagnostics( + r#" +#[must_use] +fn produces() -> i32 { 0 } +fn main() { + produces(); + //^^^^^^^^^^ warn: unused return value that must be used +} +"#, + ); + } + + #[test] + fn unused_must_use_method_call() { + check_diagnostics( + r#" +struct S; +impl S { + #[must_use] + fn produces(&self) -> i32 { 0 } +} +fn main() { + let s = S; + s.produces(); + //^^^^^^^^^^^^ warn: unused return value that must be used +} +"#, + ); + } + + #[test] + fn unused_must_use_type() { + check_diagnostics( + r#" +#[must_use] +struct Important; +fn produces() -> Important { Important } +fn main() { + produces(); + //^^^^^^^^^^ warn: unused return value that must be used +} +"#, + ); + } + + #[test] + fn no_warning_when_value_used() { + check_diagnostics( + r#" +#[must_use] +fn produces() -> i32 { 0 } +fn main() { + let _x = produces(); +} +"#, + ); + } + + #[test] + fn no_warning_when_no_must_use_attribute() { + check_diagnostics( + r#" +fn ordinary() -> i32 { 0 } +fn main() { + ordinary(); +} +"#, + ); + } + + #[test] + fn no_warning_when_value_assigned() { + check_diagnostics( + r#" +#[must_use] +fn produces() -> i32 { 0 } +fn main() { + let x; + x = produces(); + let _ = x; +} +"#, + ); + } +} 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 618edc5c61861..1d5811954cc70 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -82,6 +82,7 @@ mod handlers { pub(crate) mod unresolved_macro_call; pub(crate) mod unresolved_method; pub(crate) mod unresolved_module; + pub(crate) mod unused_must_use; pub(crate) mod unused_variables; // The handlers below are unusual, the implement the diagnostics as well. @@ -461,6 +462,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d), AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d), AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d), + AnyDiagnostic::UnusedMustUse(d) => handlers::unused_must_use::unused_must_use(&ctx, &d), AnyDiagnostic::UnusedMut(d) => match handlers::mutability_errors::unused_mut(&ctx, &d) { Some(it) => it, None => continue, diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index c0053a3f210ea..db3c3c52009fe 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -332,6 +332,7 @@ define_symbols! { module_path, mul_assign, mul, + must_use, naked_asm, ne, neg, From 197e2df6abb1b6c230cfb160348fa3a7693c188d Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 09:55:49 +0530 Subject: [PATCH 161/289] remove unused a method in edit_in_place --- .../crates/syntax/src/ast/edit_in_place.rs | 98 +------------------ 1 file changed, 1 insertion(+), 97 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 5396826394c7e..da5dcb5d80914 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -14,7 +14,7 @@ use crate::{ ted, }; -use super::{GenericParam, HasName}; +use super::HasName; pub trait AttrsOwnerEdit: ast::HasAttrs { fn remove_attrs_and_docs(&self) { @@ -43,24 +43,6 @@ pub trait AttrsOwnerEdit: ast::HasAttrs { impl AttrsOwnerEdit for T {} impl ast::GenericParamList { - pub fn add_generic_param(&self, generic_param: ast::GenericParam) { - match self.generic_params().last() { - Some(last_param) => { - let position = ted::Position::after(last_param.syntax()); - let elements = vec![ - make::token(T![,]).into(), - make::tokens::single_space().into(), - generic_param.syntax().clone().into(), - ]; - ted::insert_all(position, elements); - } - None => { - let after_l_angle = ted::Position::after(self.l_angle_token().unwrap()); - ted::insert(after_l_angle, generic_param.syntax()); - } - } - } - /// Removes the existing generic param pub fn remove_generic_param(&self, generic_param: ast::GenericParam) { if let Some(previous) = generic_param.syntax().prev_sibling() { @@ -76,35 +58,6 @@ impl ast::GenericParamList { } } - /// Find the params corresponded to generic arg - pub fn find_generic_arg(&self, generic_arg: &ast::GenericArg) -> Option { - self.generic_params().find_map(move |param| match (¶m, &generic_arg) { - (ast::GenericParam::LifetimeParam(a), ast::GenericArg::LifetimeArg(b)) => { - (a.lifetime()?.lifetime_ident_token()?.text() - == b.lifetime()?.lifetime_ident_token()?.text()) - .then_some(param) - } - (ast::GenericParam::TypeParam(a), ast::GenericArg::TypeArg(b)) => { - debug_assert_eq!(b.syntax().first_token(), b.syntax().last_token()); - (a.name()?.text() == b.syntax().first_token()?.text()).then_some(param) - } - (ast::GenericParam::ConstParam(a), ast::GenericArg::TypeArg(b)) => { - debug_assert_eq!(b.syntax().first_token(), b.syntax().last_token()); - (a.name()?.text() == b.syntax().first_token()?.text()).then_some(param) - } - _ => None, - }) - } - - /// Removes the corresponding generic arg - pub fn remove_generic_arg(&self, generic_arg: &ast::GenericArg) { - let param_to_remove = self.find_generic_arg(generic_arg); - - if let Some(param) = ¶m_to_remove { - self.remove_generic_param(param.clone()); - } - } - /// Constructs a matching [`ast::GenericArgList`] pub fn to_generic_args(&self) -> ast::GenericArgList { let args = self.generic_params().filter_map(|param| match param { @@ -133,35 +86,12 @@ impl ast::WhereClause { } ted::append_child(self.syntax(), predicate.syntax()); } - - pub fn remove_predicate(&self, predicate: ast::WherePred) { - if let Some(previous) = predicate.syntax().prev_sibling() { - if let Some(next_token) = previous.next_sibling_or_token() { - ted::remove_all(next_token..=predicate.syntax().clone().into()); - } - } else if let Some(next) = predicate.syntax().next_sibling() { - if let Some(next_token) = next.prev_sibling_or_token() { - ted::remove_all(predicate.syntax().clone().into()..=next_token); - } - } else { - ted::remove(predicate.syntax()); - } - } } pub trait Removable: AstNode { fn remove(&self); } -impl Removable for ast::TypeBoundList { - fn remove(&self) { - match self.syntax().siblings_with_tokens(Direction::Prev).find(|it| it.kind() == T![:]) { - Some(colon) => ted::remove_all(colon..=self.syntax().clone().into()), - None => ted::remove(self.syntax()), - } - } -} - impl Removable for ast::UseTree { fn remove(&self) { for dir in [Direction::Next, Direction::Prev] { @@ -427,32 +357,6 @@ impl ast::RecordExprFieldList { } impl ast::RecordExprField { - /// This will either replace the initializer, or in the case that this is a shorthand convert - /// the initializer into the name ref and insert the expr as the new initializer. - pub fn replace_expr(&self, expr: ast::Expr) { - if self.name_ref().is_some() { - match self.expr() { - Some(prev) => ted::replace(prev.syntax(), expr.syntax()), - None => ted::append_child(self.syntax(), expr.syntax()), - } - return; - } - // this is a shorthand - if let Some(ast::Expr::PathExpr(path_expr)) = self.expr() - && let Some(path) = path_expr.path() - && let Some(name_ref) = path.as_single_name_ref() - { - path_expr.syntax().detach(); - let children = vec![ - name_ref.syntax().clone().into(), - ast::make::token(T![:]).into(), - ast::make::tokens::single_space().into(), - expr.syntax().clone().into(), - ]; - ted::insert_all_raw(ted::Position::last_child_of(self.syntax()), children); - } - } - /// [`SyntaxEditor`]-based equivalent of [`replace_expr`](Self::replace_expr). pub fn replace_expr_with_editor(&self, editor: &SyntaxEditor, expr: ast::Expr) { if self.name_ref().is_some() { From c9aba45495bd5c4de4c2460d984efe0d2bc8c216 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 09:59:30 +0530 Subject: [PATCH 162/289] fix doc --- src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index da5dcb5d80914..defa2f10f73bc 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -357,7 +357,8 @@ impl ast::RecordExprFieldList { } impl ast::RecordExprField { - /// [`SyntaxEditor`]-based equivalent of [`replace_expr`](Self::replace_expr). + /// This will either replace the initializer, or in the case that this is a shorthand convert + /// the initializer into the name ref and insert the expr as the new initializer. pub fn replace_expr_with_editor(&self, editor: &SyntaxEditor, expr: ast::Expr) { if self.name_ref().is_some() { if let Some(prev) = self.expr() { From 8c40ad8d774a5b9dc1ca7df19b83fca38781bf76 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 10:00:50 +0530 Subject: [PATCH 163/289] rename replace_expr_with_editor to replace_expr --- .../crates/ide-assists/src/handlers/inline_call.rs | 2 +- src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 d95456fd3fb58..5299680980234 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 @@ -560,7 +560,7 @@ fn inline( let inline_direct = |editor: &SyntaxEditor, usage: &PathExpr, replacement: &ast::Expr| { if let Some(field) = path_expr_as_record_field(usage) { cov_mark::hit!(inline_call_inline_direct_field); - field.replace_expr_with_editor(editor, replacement.clone()); + field.replace_expr(editor, replacement.clone()); } else { editor.replace(usage.syntax(), replacement.syntax()); } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index defa2f10f73bc..fe215c466f652 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -359,7 +359,7 @@ impl ast::RecordExprFieldList { impl ast::RecordExprField { /// This will either replace the initializer, or in the case that this is a shorthand convert /// the initializer into the name ref and insert the expr as the new initializer. - pub fn replace_expr_with_editor(&self, editor: &SyntaxEditor, expr: ast::Expr) { + pub fn replace_expr(&self, editor: &SyntaxEditor, expr: ast::Expr) { if self.name_ref().is_some() { if let Some(prev) = self.expr() { editor.replace(prev.syntax(), expr.syntax()); From ea223ea4438a1ea51ea5046c6bf97a94624a41c7 Mon Sep 17 00:00:00 2001 From: Kao-Wei Yeh Date: Sat, 2 May 2026 17:08:18 +0800 Subject: [PATCH 164/289] Fix `expand_glob_import` assist to not include items from the current module when expanding a glob import, which can cause cyclic imports. --- .../src/handlers/expand_glob_import.rs | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs index 24ffc6787dd03..f5d2a6cfc5198 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -162,7 +162,8 @@ fn build_expanded_import( } }; - let refs_in_target = find_refs_in_mod(ctx, target_module, visible_from, must_be_pub); + let refs_in_target = + find_refs_in_mod(ctx, target_module, current_module, visible_from, must_be_pub); let imported_defs = find_imported_defs(ctx, use_item); let filtered_defs = @@ -303,6 +304,7 @@ impl Refs { fn find_refs_in_mod( ctx: &AssistContext<'_, '_>, expandable: Expandable, + current_module: Module, visible_from: Module, must_be_pub: bool, ) -> Refs { @@ -312,6 +314,10 @@ fn find_refs_in_mod( let refs = module_scope .into_iter() .filter_map(|(n, d)| Ref::from_scope_def(ctx, n, d)) + .filter(|r| match r.def { + Definition::Module(it) => it != current_module, + _ => r.def.module(ctx.db()).map_or(false, |it| it != current_module), + }) .filter(|r| !must_be_pub || r.is_pub) .collect(); Refs(refs) @@ -443,6 +449,31 @@ fn qux(bar: Bar, baz: Baz) { ) } + #[test] + fn expanding_glob_import_on_cycle_import() { + check_assist( + expand_glob_import, + r" +mod foo { + pub use crate::*$0; + pub struct Foo; + pub fn bar() -> Bar { _ = Foo; Bar } +} +pub use foo::*; +pub struct Bar; +", + r" +mod foo { + pub use crate::Bar; + pub struct Foo; + pub fn bar() -> Bar { _ = Foo; Bar } +} +pub use foo::*; +pub struct Bar; +", + ) + } + #[test] fn expanding_glob_import_unused() { check_assist( From ad596931c3acbfc0fe7677b9e1fa042f730bec46 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 15:08:35 +0530 Subject: [PATCH 165/289] Add add_item editor variant and normalize_with_editor variant --- .../crates/syntax/src/ast/edit_in_place.rs | 54 ++++++++++++++++--- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index fe215c466f652..b490f72591dd4 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -10,7 +10,7 @@ use crate::{ SyntaxNode, SyntaxToken, algo::{self, neighbor}, ast::{self, edit::IndentLevel, make}, - syntax_editor::SyntaxEditor, + syntax_editor::{self, SyntaxEditor}, ted, }; @@ -301,26 +301,35 @@ impl ast::AssocItemList { /// /// Attention! This function does align the first line of `item` with respect to `self`, /// but it does _not_ change indentation of other lines (if any). - pub fn add_item(&self, item: ast::AssocItem) { + pub fn add_item(&self, editor: &SyntaxEditor, item: ast::AssocItem) { + let make = editor.make(); let (indent, position, whitespace) = match self.assoc_items().last() { Some(last_item) => ( IndentLevel::from_node(last_item.syntax()), - ted::Position::after(last_item.syntax()), + syntax_editor::Position::after(last_item.syntax()), "\n\n", ), None => match self.l_curly_token() { Some(l_curly) => { - normalize_ws_between_braces(self.syntax()); - (IndentLevel::from_token(&l_curly) + 1, ted::Position::after(&l_curly), "\n") + normalize_ws_between_braces_with_editor(editor, self.syntax()); + ( + IndentLevel::from_token(&l_curly) + 1, + syntax_editor::Position::after(&l_curly), + "\n", + ) } - None => (IndentLevel::zero(), ted::Position::last_child_of(self.syntax()), "\n"), + None => ( + IndentLevel::zero(), + syntax_editor::Position::last_child_of(self.syntax()), + "\n", + ), }, }; let elements: Vec = vec![ - make::tokens::whitespace(&format!("{whitespace}{indent}")).into(), + make.whitespace(&format!("{whitespace}{indent}")).into(), item.syntax().clone().into(), ]; - ted::insert_all(position, elements); + editor.insert_all(position, elements); } } @@ -452,6 +461,35 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { Some(()) } +fn normalize_ws_between_braces_with_editor(editor: &SyntaxEditor, node: &SyntaxNode) -> Option<()> { + let make = editor.make(); + let l = node + .children_with_tokens() + .filter_map(|it| it.into_token()) + .find(|it| it.kind() == T!['{'])?; + let r = node + .children_with_tokens() + .filter_map(|it| it.into_token()) + .find(|it| it.kind() == T!['}'])?; + + let indent = IndentLevel::from_node(node); + + match l.next_sibling_or_token() { + Some(ws) + if ws.kind() == SyntaxKind::WHITESPACE + && ws.next_sibling_or_token()?.into_token()? == r => + { + editor.replace(ws, make.whitespace(&format!("\n{indent}"))); + } + Some(ws) if ws.kind() == T!['}'] => { + editor + .insert(syntax_editor::Position::after(l), make.whitespace(&format!("\n{indent}"))); + } + _ => (), + } + Some(()) +} + pub trait Indent: AstNode + Clone + Sized { fn indent_level(&self) -> IndentLevel { IndentLevel::from_node(self.syntax()) From b9c6ec08b6ff7719c097b4eb8347faed7bf8a4ae Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 15:11:14 +0530 Subject: [PATCH 166/289] create impl with generate_impl_with_item in extract_function --- .../crates/ide-assists/src/handlers/extract_function.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index a8aa0d6e5de4d..44123dc20dee0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -34,7 +34,7 @@ use syntax::{ use crate::{ AssistId, assist_context::{AssistContext, Assists}, - utils::generate_impl, + utils::generate_impl_with_item, }; // Assist: extract_function @@ -161,8 +161,8 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => { let fn_def = fn_def.indent_with_mapping(1.into(), make); - let impl_ = generate_impl(make, &adt).indent(new_indent); - impl_.get_or_create_assoc_item_list().add_item(fn_def.into()); + let body = make.assoc_item_list([fn_def.into()]); + let impl_ = generate_impl_with_item(make, &adt, Some(body)).indent(new_indent); impl_.syntax().clone() } From 7b42ea63c0a162fb9cda2ff8a06d43f02d009ab7 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 15:11:59 +0530 Subject: [PATCH 167/289] make sure to collect assoc items first and pass them into impl_trait --- .../handlers/generate_blanket_trait_impl.rs | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index e99dd81066645..3902ee170ec16 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -91,6 +91,23 @@ pub(crate) fn generate_blanket_trait_impl( let trait_gen_args = traitd.generic_param_list().map(|param_list| param_list.to_generic_args()); + let body = traitd.assoc_item_list().and_then(|trait_assoc_list| { + let items = trait_assoc_list + .assoc_items() + .filter_map(|item| { + let item = match item { + ast::AssocItem::Fn(method) if method.body().is_none() => { + todo_fn(make, &method, ctx.config).into() + } + ast::AssocItem::Const(_) | ast::AssocItem::TypeAlias(_) => item, + _ => return None, + }; + Some(item.reset_indent().indent(1.into())) + }) + .collect::>(); + (!items.is_empty()).then(|| make.assoc_item_list(items)) + }); + let impl_ = make.impl_trait( cfg_attrs(&traitd), is_unsafe, @@ -103,23 +120,9 @@ pub(crate) fn generate_blanket_trait_impl( thisty.into(), trait_where_clause, None, - None, + body, ); - if let Some(trait_assoc_list) = traitd.assoc_item_list() { - let assoc_item_list = impl_.get_or_create_assoc_item_list_with_editor(&editor); - for item in trait_assoc_list.assoc_items() { - let item = match item { - ast::AssocItem::Fn(method) if method.body().is_none() => { - todo_fn(make, &method, ctx.config).into() - } - ast::AssocItem::Const(_) | ast::AssocItem::TypeAlias(_) => item, - _ => continue, - }; - assoc_item_list.add_item(item.reset_indent().indent(1.into())); - } - } - let impl_ = impl_.indent(indent); editor.insert_all( From 96ee5318488b0834b1698d15e26c52ecaeec1b86 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 16:02:38 +0530 Subject: [PATCH 168/289] remove add_predicate from impl ast::whereClause --- .../crates/syntax/src/ast/edit_in_place.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index fe215c466f652..7b59e65ab67c2 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -77,17 +77,6 @@ impl ast::GenericParamList { } } -impl ast::WhereClause { - pub fn add_predicate(&self, predicate: ast::WherePred) { - if let Some(pred) = self.predicates().last() - && !pred.syntax().siblings_with_tokens(Direction::Next).any(|it| it.kind() == T![,]) - { - ted::append_child_raw(self.syntax(), make::token(T![,])); - } - ted::append_child(self.syntax(), predicate.syntax()); - } -} - pub trait Removable: AstNode { fn remove(&self); } From 4a2b9b4c434fd5e14253241e90808f209c92f5fd Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 16:10:00 +0530 Subject: [PATCH 169/289] we can remove the add_predicate considering we no longer to in place edits --- src/tools/rust-analyzer/crates/syntax/src/ast/make.rs | 7 +------ 1 file changed, 1 insertion(+), 6 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 e8f5c9537df7a..718e5e2dcabd4 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -294,12 +294,7 @@ fn merge_where_clause( (None, None) => None, (None, Some(bs)) => Some(bs), (Some(ps), None) => Some(ps), - (Some(ps), Some(bs)) => { - let preds = where_clause(std::iter::empty()).clone_for_update(); - ps.predicates().for_each(|p| preds.add_predicate(p)); - bs.predicates().for_each(|p| preds.add_predicate(p)); - Some(preds) - } + (Some(ps), Some(bs)) => Some(where_clause(ps.predicates().chain(bs.predicates()))), } } From 76689280c1e81dfa4f0ffe7ce1f487800a969019 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 2 May 2026 23:58:06 +0800 Subject: [PATCH 170/289] fix: Fix unwrap_branch in match_arm Example --- ```rust fn main() { match 1 { 1 => if true { foo(); } else {$0 bar(); } _ => (), } } ``` **Before this PR** ```rust fn main() { match 1 { 1 => if true { foo(); } bar(); _ => (), } } ``` **After this PR** ```rust fn main() { match 1 { 1 => { bar(); } _ => (), } } ``` --- .../ide-assists/src/handlers/unwrap_branch.rs | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs index a018451f61da5..a582af4e2ca65 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs @@ -70,6 +70,11 @@ pub(crate) fn unwrap_branch(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> O _ => return None, } }; + if ast::MatchArm::cast(container.parent()?).is_some() { + replacement = editor.make().tail_only_block_expr(replacement.into()); + prefer_container = Some(container.clone()); + break IndentLevel::from_node(&container); + } }; let is_branch = !block.is_standalone() || place.syntax().parent().and_then(ast::MatchArm::cast).is_some(); @@ -572,6 +577,88 @@ fn main() { ); } + #[test] + fn simple_if_in_match_arm() { + check_assist( + unwrap_branch, + r#" +fn main() { + match 1 { + 1 => if true {$0 + foo(); + } + _ => (), + } +} +"#, + r#" +fn main() { + match 1 { + 1 => { + foo(); + } + _ => (), + } +} +"#, + ); + + check_assist( + unwrap_branch, + r#" +fn main() { + match 1 { + 1 => if true { + foo(); + } else {$0 + bar(); + } + _ => (), + } +} +"#, + r#" +fn main() { + match 1 { + 1 => { + bar(); + } + _ => (), + } +} +"#, + ); + } + + #[test] + fn simple_match_in_match_arm() { + check_assist( + unwrap_branch, + r#" +fn main() { + match 1 { + 1 => match () { + _ => {$0 + foo(); + } + } + _ => (), + } +} +"#, + r#" +fn main() { + match 1 { + 1 => { + foo(); + } + _ => (), + } +} +"#, + ); + } + #[test] fn simple_loop() { check_assist( From 0822885287e34cc9ce47c72078082a4b9f30c04f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 24 Apr 2026 17:42:48 +0300 Subject: [PATCH 171/289] Make `ObligationCause` `Copy` In rustc it is not, but we're unlikely to add something non-`Copy` to it in the near future, and it simplifies the code. This was basically adding a derive then `cargo clippy --fix` and `cargo fmt`. --- .../crates/hir-ty/src/infer/coerce.rs | 4 +-- .../crates/hir-ty/src/infer/op.rs | 2 +- .../crates/hir-ty/src/infer/unify.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 2 +- .../crates/hir-ty/src/method_resolution.rs | 4 +-- .../hir-ty/src/method_resolution/probe.rs | 4 +-- .../crates/hir-ty/src/next_solver/infer/at.rs | 26 +++++++++---------- .../hir-ty/src/next_solver/infer/mod.rs | 6 ++--- .../src/next_solver/infer/relate/lattice.rs | 9 ++----- .../hir-ty/src/next_solver/infer/traits.rs | 8 +++--- .../hir-ty/src/next_solver/normalize.rs | 2 +- .../src/next_solver/structural_normalize.rs | 2 +- .../crates/hir-ty/src/next_solver/util.rs | 2 +- .../crates/hir-ty/src/specialization.rs | 2 +- 14 files changed, 35 insertions(+), 40 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index ddff23e840430..b2bed0ce2be1a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -324,7 +324,7 @@ where if source_ty != target_ty { obligations.push(Obligation::new( self.interner(), - self.cause.clone(), + self.cause, self.param_env(), Binder::dummy(PredicateKind::Coerce(CoercePredicate { a: source_ty, @@ -669,7 +669,7 @@ where )?; // Create an obligation for `Source: CoerceUnsized`. - let cause = self.cause.clone(); + let cause = self.cause; let pred = TraitRef::new( self.interner(), coerce_unsized_did.into(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs index 5900027a98769..753a6513f8e5f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs @@ -301,7 +301,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // to allow more code to compile. let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; let method = self.table.lookup_method_for_operator( - cause.clone(), + cause, opname, trait_did, lhs_ty, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 4342375621d1d..ab5a7c3fc71a8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -65,7 +65,7 @@ impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { if self.ctx.predicate_has_self_ty(goal.predicate, self.self_ty) { self.obligations_for_self_ty.push(Obligation::new( db, - self.root_cause.clone(), + *self.root_cause, goal.param_env, goal.predicate, )); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 133447dff3f18..c77c1b98d35af 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -553,7 +553,7 @@ pub fn callable_sig_from_fn_trait<'db>( let args = GenericArgs::new_from_slice(&[self_ty.into(), tupled_args.into()]); let trait_id = trait_.get_id(lang_items)?; let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); - let obligation = Obligation::new(interner, cause.clone(), param_env, trait_ref); + let obligation = Obligation::new(interner, cause, param_env, trait_ref); ocx.register_obligation(obligation); if !ocx.try_evaluate_obligations().is_empty() { return None; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 2c6c7ed9a5543..1f2ede332431f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -256,7 +256,7 @@ impl<'db> InferenceTable<'db> { let obligation = Obligation::new( self.interner(), - cause.clone(), + cause, self.param_env, TraitRef::new_from_args(self.interner(), trait_def_id.into(), args), ); @@ -330,7 +330,7 @@ impl<'db> InferenceTable<'db> { for ty in fn_sig.inputs_and_output { obligations.push(Obligation::new( interner, - obligation.cause.clone(), + obligation.cause, self.param_env, Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(ty.into()))), )); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index 6eeec6cb41c89..2e4ca139c086a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs @@ -1660,7 +1660,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { } let obligation = Obligation::new( self.interner(), - cause.clone(), + *cause, self.param_env(), Binder::dummy(trait_ref), ); @@ -1734,7 +1734,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { if let Some(xform_ret_ty) = xform_ret_ty { ocx.register_obligation(Obligation::new( self.interner(), - cause.clone(), + *cause, self.param_env(), ClauseKind::WellFormed(xform_ret_ty.into()), )); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs index 4784edf60f5a6..f63200a2e08cc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs @@ -199,7 +199,7 @@ impl<'a, 'db> At<'a, 'db> { .map(|goal| { Obligation::new( self.infcx.interner, - self.cause.clone(), + *self.cause, goal.param_env, goal.predicate, ) @@ -212,7 +212,7 @@ impl<'a, 'db> At<'a, 'db> { impl<'db> ToTrace<'db> for Ty<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), } } @@ -220,14 +220,14 @@ impl<'db> ToTrace<'db> for Ty<'db> { impl<'db> ToTrace<'db> for Region<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { - TypeTrace { cause: cause.clone(), values: ValuePairs::Regions(ExpectedFound::new(a, b)) } + TypeTrace { cause: *cause, values: ValuePairs::Regions(ExpectedFound::new(a, b)) } } } impl<'db> ToTrace<'db> for Const<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), } } @@ -236,7 +236,7 @@ impl<'db> ToTrace<'db> for Const<'db> { impl<'db> ToTrace<'db> for GenericArg<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: match (a.kind(), b.kind()) { (GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => { ValuePairs::Regions(ExpectedFound::new(a, b)) @@ -255,20 +255,20 @@ impl<'db> ToTrace<'db> for GenericArg<'db> { impl<'db> ToTrace<'db> for Term<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { - TypeTrace { cause: cause.clone(), values: ValuePairs::Terms(ExpectedFound::new(a, b)) } + TypeTrace { cause: *cause, values: ValuePairs::Terms(ExpectedFound::new(a, b)) } } } impl<'db> ToTrace<'db> for TraitRef<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { - TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) } + TypeTrace { cause: *cause, values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) } } } impl<'db> ToTrace<'db> for AliasTy<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::Aliases(ExpectedFound::new(a.into(), b.into())), } } @@ -276,14 +276,14 @@ impl<'db> ToTrace<'db> for AliasTy<'db> { impl<'db> ToTrace<'db> for AliasTerm<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { - TypeTrace { cause: cause.clone(), values: ValuePairs::Aliases(ExpectedFound::new(a, b)) } + TypeTrace { cause: *cause, values: ValuePairs::Aliases(ExpectedFound::new(a, b)) } } } impl<'db> ToTrace<'db> for FnSig> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::PolySigs(ExpectedFound::new(Binder::dummy(a), Binder::dummy(b))), } } @@ -291,14 +291,14 @@ impl<'db> ToTrace<'db> for FnSig> { impl<'db> ToTrace<'db> for PolyFnSig<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { - TypeTrace { cause: cause.clone(), values: ValuePairs::PolySigs(ExpectedFound::new(a, b)) } + TypeTrace { cause: *cause, values: ValuePairs::PolySigs(ExpectedFound::new(a, b)) } } } impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(a, b)), } } @@ -307,7 +307,7 @@ impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> { impl<'db> ToTrace<'db> for PolyExistentialProjection<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::ExistentialProjection(ExpectedFound::new(a, b)), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index f038c47a8bcfd..e1568d43bb8da 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -1352,7 +1352,7 @@ impl TyOrConstInferVar { impl<'db> TypeTrace<'db> { pub fn types(cause: &ObligationCause, a: Ty<'db>, b: Ty<'db>) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), } } @@ -1362,12 +1362,12 @@ impl<'db> TypeTrace<'db> { a: TraitRef<'db>, b: TraitRef<'db>, ) -> TypeTrace<'db> { - TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) } + TypeTrace { cause: *cause, values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) } } pub fn consts(cause: &ObligationCause, a: Const<'db>, b: Const<'db>) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs index 0443dbd8142bd..f3af697febcb2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs @@ -258,18 +258,13 @@ impl<'db> PredicateEmittingRelation> for LatticeOp<'_, 'db> { preds: impl IntoIterator, Predicate<'db>>>, ) { self.obligations.extend(preds.into_iter().map(|pred| { - Obligation::new(self.infcx.interner, self.trace.cause.clone(), self.param_env, pred) + Obligation::new(self.infcx.interner, self.trace.cause, self.param_env, pred) })) } fn register_goals(&mut self, goals: impl IntoIterator>>) { self.obligations.extend(goals.into_iter().map(|goal| { - Obligation::new( - self.infcx.interner, - self.trace.cause.clone(), - goal.param_env, - goal.predicate, - ) + Obligation::new(self.infcx.interner, self.trace.cause, goal.param_env, goal.predicate) })) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index 1edf256d012f0..523c94b36fc9f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -34,7 +34,7 @@ use super::InferCtxt; /// /// We do not want to intern this as there are a lot of obligation causes which /// only live for a short period of time. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ObligationCause { span: Span, } @@ -118,7 +118,7 @@ impl<'db> Elaboratable> for PredicateObligation<'db> { fn child(&self, clause: Clause<'db>) -> Self { Obligation { - cause: self.cause.clone(), + cause: self.cause, param_env: self.param_env, recursion_depth: 0, predicate: clause.as_predicate(), @@ -186,7 +186,7 @@ impl<'db> PredicateObligation<'db> { /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. pub fn flip_polarity(&self, _interner: DbInterner<'db>) -> Option> { Some(PredicateObligation { - cause: self.cause.clone(), + cause: self.cause, param_env: self.param_env, predicate: self.predicate.flip_polarity()?, recursion_depth: self.recursion_depth, @@ -228,7 +228,7 @@ impl<'db, O> Obligation<'db, O> { tcx: DbInterner<'db>, value: impl Upcast, P>, ) -> Obligation<'db, P> { - Obligation::with_depth(tcx, self.cause.clone(), self.recursion_depth, self.param_env, value) + Obligation::with_depth(tcx, self.cause, self.recursion_depth, self.param_env, value) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs index aa6f27c4c2d2a..c35434ed16947 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs @@ -102,7 +102,7 @@ impl<'db> NormalizationFolder<'_, 'db> { let infer_term = infcx.next_term_var_of_kind(alias_term, self.at.cause.span()); let obligation = Obligation::new( interner, - self.at.cause.clone(), + *self.at.cause, self.at.param_env, PredicateKind::AliasRelate(alias_term, infer_term, AliasRelationDirection::Equate), ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs index 7a70bae97cfea..769d2ae141f02 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs @@ -41,7 +41,7 @@ impl<'db> At<'_, 'db> { // (or a not-yet-defined opaque in scope). let obligation = Obligation::new( self.infcx.interner, - self.cause.clone(), + *self.cause, self.param_env, PredicateKind::AliasRelate(term, new_infer, AliasRelationDirection::Equate), ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index 3c5126cc4a3a3..43b4503204975 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -717,7 +717,7 @@ pub(crate) fn clauses_as_obligations<'db>( param_env: ParamEnv<'db>, ) -> impl Iterator> { clauses.into_iter().map(move |clause| Obligation { - cause: cause.clone(), + cause, param_env, predicate: clause.as_predicate(), recursion_depth: 0, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs index 7e4d3a83549ea..1ef8adb4d9850 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs @@ -110,7 +110,7 @@ fn specializes_query( ocx.register_obligations(clauses_as_obligations( GenericPredicates::query_all(db, parent_impl_def_id.into()) .iter_instantiated(interner, parent_args.as_slice()), - cause.clone(), + *cause, param_env, )); From 030950dbb7cebea5ed161006668a7c1ef9189f09 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 26 Apr 2026 15:10:14 +0300 Subject: [PATCH 172/289] Ensure we give spans for obligations everywhere --- .../crates/hir-ty/src/autoderef.rs | 24 +++-- .../crates/hir-ty/src/display.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 35 +++++--- .../crates/hir-ty/src/infer/autoderef.rs | 14 +-- .../crates/hir-ty/src/infer/callee.rs | 25 +++--- .../crates/hir-ty/src/infer/cast.rs | 31 ++++--- .../crates/hir-ty/src/infer/closure.rs | 71 +++++++++------ .../hir-ty/src/infer/closure/analysis.rs | 16 ++-- .../closure/analysis/expr_use_visitor.rs | 69 ++++----------- .../crates/hir-ty/src/infer/coerce.rs | 23 ++--- .../crates/hir-ty/src/infer/expr.rs | 88 +++++++++++-------- .../crates/hir-ty/src/infer/op.rs | 6 +- .../crates/hir-ty/src/infer/opaques.rs | 4 +- .../crates/hir-ty/src/infer/pat.rs | 46 ++++++---- .../crates/hir-ty/src/infer/path.rs | 7 +- .../crates/hir-ty/src/infer/place_op.rs | 9 +- .../crates/hir-ty/src/infer/unify.rs | 32 +++---- .../rust-analyzer/crates/hir-ty/src/lib.rs | 5 +- .../crates/hir-ty/src/method_resolution.rs | 2 +- .../hir-ty/src/method_resolution/confirm.rs | 35 ++++---- .../hir-ty/src/method_resolution/probe.rs | 30 ++++--- .../hir-ty/src/next_solver/infer/traits.rs | 34 ++----- .../crates/hir/src/diagnostics.rs | 4 + .../src/handlers/mismatched_arg_count.rs | 1 + 24 files changed, 324 insertions(+), 291 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 2c2400a14a7f6..a8ed4126abeae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -40,7 +40,7 @@ pub fn autoderef<'db>( let interner = DbInterner::new_with(db, env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); let (ty, _) = infcx.instantiate_canonical(Span::Dummy, &ty); - let autoderef = Autoderef::new(&infcx, env.param_env, ty); + let autoderef = Autoderef::new(&infcx, env.param_env, ty, Span::Dummy); let mut v = Vec::new(); for (ty, _steps) in autoderef { // `ty` may contain unresolved inference variables. Since there's no chance they would be @@ -155,6 +155,7 @@ pub(crate) struct GeneralAutoderef<'db, Ctx, Steps = Vec<(Ty<'db>, AutoderefKind // Configurations: include_raw_pointers: bool, use_receiver_trait: bool, + span: Span, } pub(crate) type Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> = @@ -200,7 +201,7 @@ where // autoderef expect this type to have been structurally normalized. if let TyKind::Alias(..) = ty.kind() { let (normalized_ty, obligations) = - structurally_normalize_ty(self.infcx(), self.param_env(), ty)?; + structurally_normalize_ty(self.infcx(), self.param_env(), ty, self.span)?; self.state.obligations.extend(obligations); (AutoderefKind::Builtin, normalized_ty) } else { @@ -232,8 +233,9 @@ impl<'a, 'db> Autoderef<'a, 'db> { infcx: &'a InferCtxt<'db>, param_env: ParamEnv<'db>, base_ty: Ty<'db>, + span: Span, ) -> Self { - Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty) + Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty, span) } } @@ -242,8 +244,9 @@ impl<'a, 'b, 'db> InferenceContextAutoderef<'a, 'b, 'db> { pub(crate) fn new_from_inference_context( ctx: &'a mut InferenceContext<'b, 'db>, base_ty: Ty<'db>, + span: Span, ) -> Self { - Self::new_impl(InferenceContextAutoderefCtx(ctx), base_ty) + Self::new_impl(InferenceContextAutoderefCtx(ctx), base_ty, span) } #[inline] @@ -258,8 +261,9 @@ impl<'a, 'db> Autoderef<'a, 'db, usize> { infcx: &'a InferCtxt<'db>, param_env: ParamEnv<'db>, base_ty: Ty<'db>, + span: Span, ) -> Self { - Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty) + Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty, span) } } @@ -269,7 +273,7 @@ where Steps: TrackAutoderefSteps<'db>, { #[inline] - fn new_impl(ctx: Ctx, base_ty: Ty<'db>) -> Self { + fn new_impl(ctx: Ctx, base_ty: Ty<'db>, span: Span) -> Self { GeneralAutoderef { state: AutoderefSnapshot { steps: Steps::default(), @@ -282,6 +286,7 @@ where traits: None, include_raw_pointers: false, use_receiver_trait: false, + span, } } @@ -338,7 +343,7 @@ where let trait_ref = TraitRef::new(interner, trait_.into(), [ty]); let obligation = - Obligation::new(interner, ObligationCause::new(), self.param_env(), trait_ref); + Obligation::new(interner, ObligationCause::new(self.span), self.param_env(), trait_ref); // We detect whether the self type implements `Deref` before trying to // structurally normalize. We use `predicate_may_hold_opaque_types_jank` // to support not-yet-defined opaque types. It will succeed for `impl Deref` @@ -352,6 +357,7 @@ where self.infcx(), self.param_env(), Ty::new_projection(interner, trait_target.into(), [ty]), + self.span, )?; debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); self.state.obligations.extend(obligations); @@ -403,9 +409,11 @@ fn structurally_normalize_ty<'db>( infcx: &InferCtxt<'db>, param_env: ParamEnv<'db>, ty: Ty<'db>, + span: Span, ) -> Option<(Ty<'db>, PredicateObligations<'db>)> { let mut ocx = ObligationCtxt::new(infcx); - let Ok(normalized_ty) = ocx.structurally_normalize_ty(&ObligationCause::misc(), param_env, ty) + let Ok(normalized_ty) = + ocx.structurally_normalize_ty(&ObligationCause::new(span), param_env, ty) else { // We shouldn't have errors here in the old solver, except for // evaluate/fulfill mismatches, but that's not a reason for an ICE. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index c300388c5ee3e..97693435fe0ca 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -771,7 +771,7 @@ fn render_const_scalar<'db>( ) -> Result { let param_env = ParamEnv::empty(); let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis); - let ty = infcx.at(&ObligationCause::new(), param_env).deeply_normalize(ty).unwrap_or(ty); + let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty); render_const_scalar_inner(f, b, memory_map, ty, param_env) } @@ -1056,7 +1056,7 @@ fn render_const_scalar_from_valtree<'db>( ) -> Result { let param_env = ParamEnv::empty(); let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis); - let ty = infcx.at(&ObligationCause::new(), param_env).deeply_normalize(ty).unwrap_or(ty); + let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty); render_const_scalar_from_valtree_inner(f, ty, valtree, param_env) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 09c1325c41c52..83659dde17bec 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1934,7 +1934,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } pub(crate) fn structurally_resolve_type(&mut self, node: ExprOrPatId, ty: Ty<'db>) -> Ty<'db> { - let result = self.table.try_structurally_resolve_type(ty); + let result = self.table.try_structurally_resolve_type(node.into(), ty); if result.is_ty_var() { self.type_must_be_known_at_this_point(node, ty) } else { result } } @@ -1944,14 +1944,18 @@ impl<'body, 'db> InferenceContext<'body, 'db> { expected: Ty<'db>, actual: Ty<'db>, ) -> Result<(), ()> { - let result = self.demand_eqtype_fixme_no_diag(expected, actual); + let result = self + .table + .at(&ObligationCause::new(id)) + .eq(expected, actual) + .map(|infer_ok| self.table.register_infer_ok(infer_ok)); if result.is_err() { self.result .type_mismatches .get_or_insert_default() .insert(id, TypeMismatch { expected: expected.store(), actual: actual.store() }); } - result + result.map_err(drop) } fn demand_eqtype_fixme_no_diag( @@ -1961,7 +1965,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { ) -> Result<(), ()> { let result = self .table - .at(&ObligationCause::new()) + .at(&ObligationCause::dummy()) .eq(expected, actual) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); result.map_err(drop) @@ -1975,7 +1979,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { ) -> Result<(), ()> { let result = self .table - .at(&ObligationCause::new()) + .at(&ObligationCause::new(id)) .sup(expected, actual) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); if result.is_err() { @@ -2016,11 +2020,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.types.types.error } - pub(crate) fn require_type_is_sized(&mut self, ty: Ty<'db>) { + pub(crate) fn require_type_is_sized(&mut self, ty: Ty<'db>, span: Span) { if !ty.references_non_lt_error() && let Some(sized_trait) = self.lang_items.Sized { - self.table.register_bound(ty, sized_trait, ObligationCause::new()); + self.table.register_bound(ty, sized_trait, ObligationCause::new(span)); } } @@ -2085,7 +2089,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { args, ), ); - ty = self.table.try_structurally_resolve_type(alias); + ty = self.table.try_structurally_resolve_type(node.into(), alias); segments = segments.skip(1); } @@ -2185,7 +2189,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { never!("resolver should always resolve lang item paths"); return (self.err_ty(), None); }; - return self.resolve_variant_on_alias(ty, None, mod_path); + return self.resolve_variant_on_alias(node, ty, None, mod_path); }; let mut remaining_segments = path.segments().skip(remaining_idx); @@ -2298,7 +2302,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { let ty = self.db.ty(it.into()).instantiate(interner, args); let ty = self.insert_type_vars(ty, Span::Dummy); - self.resolve_variant_on_alias(ty, unresolved, mod_path) + self.resolve_variant_on_alias(node, ty, unresolved, mod_path) } TypeNs::AdtSelfType(_) => { // FIXME this could happen in array size expressions, once we're checking them @@ -2330,12 +2334,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> { fn resolve_variant_on_alias( &mut self, + node: ExprOrPatId, ty: Ty<'db>, unresolved: Option, path: &ModPath, ) -> (Ty<'db>, Option) { let remaining = unresolved.map(|it| path.segments()[it..].len()).filter(|it| it > &0); - let ty = self.table.try_structurally_resolve_type(ty); + let ty = self.table.try_structurally_resolve_type(node.into(), ty); match remaining { None => { let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id { @@ -2548,10 +2553,14 @@ impl<'db> Expectation<'db> { /// an expected type. Otherwise, we might write parts of the type /// when checking the 'then' block which are incompatible with the /// 'else' branch. - fn adjust_for_branches(&self, table: &mut unify::InferenceTable<'db>) -> Expectation<'db> { + fn adjust_for_branches( + &self, + table: &mut unify::InferenceTable<'db>, + span: Span, + ) -> Expectation<'db> { match *self { Expectation::HasType(ety) => { - let ety = table.try_structurally_resolve_type(ety); + let ety = table.try_structurally_resolve_type(span, ety); if ety.is_ty_var() { Expectation::None } else { Expectation::HasType(ety) } } Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs index a6c7b2dbb9c38..0ba3b3dd056d1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs @@ -5,7 +5,7 @@ use std::iter; use rustc_ast_ir::Mutability; use crate::{ - Adjust, Adjustment, OverloadedDeref, + Adjust, Adjustment, OverloadedDeref, Span, autoderef::{Autoderef, AutoderefCtx, AutoderefKind, GeneralAutoderef}, infer::unify::InferenceTable, next_solver::{ @@ -15,12 +15,16 @@ use crate::{ }; impl<'db> InferenceTable<'db> { - pub(crate) fn autoderef(&self, base_ty: Ty<'db>) -> Autoderef<'_, 'db, usize> { - Autoderef::new(&self.infer_ctxt, self.param_env, base_ty) + pub(crate) fn autoderef(&self, base_ty: Ty<'db>, span: Span) -> Autoderef<'_, 'db, usize> { + Autoderef::new(&self.infer_ctxt, self.param_env, base_ty, span) } - pub(crate) fn autoderef_with_tracking(&self, base_ty: Ty<'db>) -> Autoderef<'_, 'db> { - Autoderef::new_with_tracking(&self.infer_ctxt, self.param_env, base_ty) + pub(crate) fn autoderef_with_tracking( + &self, + base_ty: Ty<'db>, + span: Span, + ) -> Autoderef<'_, 'db> { + Autoderef::new_with_tracking(&self.infer_ctxt, self.param_env, base_ty, span) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs index 9fe566b8fe068..9c134cb75f311 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs @@ -43,9 +43,11 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { let original_callee_ty = self.infer_expr_no_expect(callee_expr, ExprIsRead::Yes); - let expr_ty = self.table.try_structurally_resolve_type(original_callee_ty); + let expr_ty = + self.table.try_structurally_resolve_type(callee_expr.into(), original_callee_ty); - let mut autoderef = GeneralAutoderef::new_from_inference_context(self, expr_ty); + let mut autoderef = + GeneralAutoderef::new_from_inference_context(self, expr_ty, callee_expr.into()); let mut result = None; let mut error_reported = false; while result.is_none() && autoderef.next().is_some() { @@ -95,7 +97,7 @@ impl<'db> InferenceContext<'_, 'db> { }; // we must check that return type of called functions is WF: - self.table.register_wf_obligation(output.into(), ObligationCause::new()); + self.table.register_wf_obligation(output.into(), ObligationCause::new(call_expr)); output } @@ -108,7 +110,8 @@ impl<'db> InferenceContext<'_, 'db> { error_reported: &mut bool, ) -> Option> { let final_ty = autoderef.final_ty(); - let adjusted_ty = autoderef.ctx().table.try_structurally_resolve_type(final_ty); + let adjusted_ty = + autoderef.ctx().table.try_structurally_resolve_type(callee_expr.into(), final_ty); // If the callee is a function pointer or a closure, then we're all set. match adjusted_ty.kind() { @@ -242,8 +245,8 @@ impl<'db> InferenceContext<'_, 'db> { // is implemented, and use this information for diagnostic. autoderef .ctx() - .try_overloaded_call_traits(adjusted_ty, Some(arg_exprs)) - .or_else(|| autoderef.ctx().try_overloaded_call_traits(adjusted_ty, None)) + .try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs)) + .or_else(|| autoderef.ctx().try_overloaded_call_traits(call_expr, adjusted_ty, None)) .map(|(autoref, method)| { let adjustments = autoderef.adjust_steps_as_infer_ok(); let mut adjustments = autoderef.ctx().table.register_infer_ok(adjustments); @@ -255,6 +258,7 @@ impl<'db> InferenceContext<'_, 'db> { fn try_overloaded_call_traits( &mut self, + call_expr: ExprId, adjusted_ty: Ty<'db>, opt_arg_exprs: Option<&[ExprId]>, ) -> Option<(Option, MethodCallee<'db>)> { @@ -311,7 +315,7 @@ impl<'db> InferenceContext<'_, 'db> { // one which may apply. So if we treat opaques as inference variables // `Box: Fn` is considered ambiguous and chosen. if let Some(ok) = self.table.lookup_method_for_operator( - ObligationCause::new(), + ObligationCause::new(call_expr), method_name, trait_def_id, adjusted_ty, @@ -464,8 +468,9 @@ impl<'db> InferenceContext<'_, 'db> { && let Some(ty) = fn_sig.inputs().last().copied() && let Some(tuple_trait) = self.lang_items.Tuple { - self.table.register_bound(ty, tuple_trait, ObligationCause::new()); - self.require_type_is_sized(ty); + let span = arg_exprs.last().copied().unwrap_or(call_expr); + self.table.register_bound(ty, tuple_trait, ObligationCause::new(span)); + self.require_type_is_sized(ty, span.into()); } fn_sig.output() @@ -538,7 +543,7 @@ impl<'a, 'db> DeferredCallResolution<'db> { assert!(ctx.infcx().closure_kind(self.closure_ty).is_some()); // We may now know enough to figure out fn vs fnmut etc. - match ctx.try_overloaded_call_traits(self.closure_ty, None) { + match ctx.try_overloaded_call_traits(self.call_expr, self.closure_ty, None) { Some((autoref, method_callee)) => { // One problem is that when we get here, we are going // to have a newly instantiated function signature diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index daf954c21798a..1dade4dfd7911 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -125,8 +125,9 @@ impl<'db> CastCheck<'db> { &mut self, ctx: &mut InferenceContext<'_, 'db>, ) -> Result<(), InferenceDiagnostic> { - self.expr_ty = ctx.table.try_structurally_resolve_type(self.expr_ty); - self.cast_ty = ctx.table.try_structurally_resolve_type(self.cast_ty); + self.expr_ty = + ctx.table.try_structurally_resolve_type(self.source_expr.into(), self.expr_ty); + self.cast_ty = ctx.table.try_structurally_resolve_type(self.expr.into(), self.cast_ty); // This should always come first so that we apply the coercion, which impacts infer vars. if ctx @@ -199,7 +200,8 @@ impl<'db> CastCheck<'db> { }, // array-ptr-cast CastTy::Ptr(t, m) => { - let t = ctx.table.try_structurally_resolve_type(t); + let t = + ctx.table.try_structurally_resolve_type(self.expr.into(), t); if !ctx.table.type_is_sized_modulo_regions(t) { return Err(CastError::IllegalCast); } @@ -262,8 +264,8 @@ impl<'db> CastCheck<'db> { t_cast: Ty<'db>, m_cast: Mutability, ) -> Result<(), CastError> { - let t_expr = ctx.table.try_structurally_resolve_type(t_expr); - let t_cast = ctx.table.try_structurally_resolve_type(t_cast); + let t_expr = ctx.table.try_structurally_resolve_type(self.expr.into(), t_expr); + let t_cast = ctx.table.try_structurally_resolve_type(self.expr.into(), t_cast); if m_expr >= m_cast && let TyKind::Array(ety, _) = t_expr.kind() @@ -306,8 +308,8 @@ impl<'db> CastCheck<'db> { src: Ty<'db>, dst: Ty<'db>, ) -> Result<(), CastError> { - let src_kind = pointer_kind(src, ctx).map_err(|_| CastError::Unknown)?; - let dst_kind = pointer_kind(dst, ctx).map_err(|_| CastError::Unknown)?; + let src_kind = pointer_kind(self.expr, src, ctx).map_err(|_| CastError::Unknown)?; + let dst_kind = pointer_kind(self.expr, dst, ctx).map_err(|_| CastError::Unknown)?; match (src_kind, dst_kind) { (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()), @@ -372,7 +374,7 @@ impl<'db> CastCheck<'db> { // This is `fcx.demand_eqtype`, but inlined to give a better error. if ctx .table - .at(&ObligationCause::dummy()) + .at(&ObligationCause::new(self.expr)) .eq(src_obj, dst_obj) .map(|infer_ok| ctx.table.register_infer_ok(infer_ok)) .is_err() @@ -457,7 +459,7 @@ impl<'db> CastCheck<'db> { ctx: &mut InferenceContext<'_, 'db>, expr_ty: Ty<'db>, ) -> Result<(), CastError> { - match pointer_kind(expr_ty, ctx).map_err(|_| CastError::Unknown)? { + match pointer_kind(self.expr, expr_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownExprPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -471,7 +473,7 @@ impl<'db> CastCheck<'db> { ctx: &mut InferenceContext<'_, 'db>, cast_ty: Ty<'db>, ) -> Result<(), CastError> { - match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? { + match pointer_kind(self.expr, cast_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownCastPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -487,7 +489,7 @@ impl<'db> CastCheck<'db> { ctx: &mut InferenceContext<'_, 'db>, cast_ty: Ty<'db>, ) -> Result<(), CastError> { - match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? { + match pointer_kind(self.expr, cast_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownCastPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -516,10 +518,11 @@ enum PointerKind<'db> { } fn pointer_kind<'db>( + expr: ExprId, ty: Ty<'db>, ctx: &mut InferenceContext<'_, 'db>, ) -> Result>, ()> { - let ty = ctx.table.try_structurally_resolve_type(ty); + let ty = ctx.table.try_structurally_resolve_type(expr.into(), ty); if ctx.table.type_is_sized_modulo_regions(ty) { return Ok(Some(PointerKind::Thin)); @@ -540,14 +543,14 @@ fn pointer_kind<'db>( let last_field_ty = ctx.db.field_types(id.into())[last_field] .get() .instantiate(ctx.interner(), subst); - pointer_kind(last_field_ty, ctx) + pointer_kind(expr, last_field_ty, ctx) } else { Ok(Some(PointerKind::Thin)) } } TyKind::Tuple(subst) => match subst.iter().next_back() { None => Ok(Some(PointerKind::Thin)), - Some(ty) => pointer_kind(ty, ctx), + Some(ty) => pointer_kind(expr, ty, ctx), }, TyKind::Foreign(_) => Ok(Some(PointerKind::Thin)), TyKind::Alias(..) => Ok(Some(PointerKind::OfAlias)), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index f90f135919460..218d8e2f3e0bc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -18,7 +18,7 @@ use rustc_type_ir::{ use tracing::debug; use crate::{ - FnAbi, + FnAbi, Span, db::{InternedClosure, InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId}, infer::{BreakableKind, Diverges, coerce::CoerceMany, pat::PatOrigin}, next_solver::{ @@ -80,14 +80,14 @@ impl<'db> InferenceContext<'_, 'db> { // type, and see if can glean a closure kind from there. let (expected_sig, expected_kind) = match expected.to_option(&mut self.table) { Some(ty) => { - let ty = self.table.try_structurally_resolve_type(ty); + let ty = self.table.try_structurally_resolve_type(closure_expr.into(), ty); self.deduce_closure_signature(closure_expr, ty, closure_kind) } None => (None, None), }; let ClosureSignatures { bound_sig, mut liberated_sig } = - self.sig_of_closure(closure_expr, arg_types, ret_type, expected_sig); + self.sig_of_closure(closure_expr, args, arg_types, ret_type, expected_sig); debug!(?bound_sig, ?liberated_sig); @@ -143,7 +143,7 @@ impl<'db> InferenceContext<'_, 'db> { ClosureKind::OldCoroutine(_) | ClosureKind::Coroutine { kind: CoroutineKind::Gen, .. } => { let yield_ty = self.table.next_ty_var(closure_expr.into()); - self.require_type_is_sized(yield_ty); + self.require_type_is_sized(yield_ty, closure_expr.into()); yield_ty } ClosureKind::Coroutine { kind: CoroutineKind::Async, .. } => { @@ -151,7 +151,7 @@ impl<'db> InferenceContext<'_, 'db> { } ClosureKind::Coroutine { kind: CoroutineKind::AsyncGen, .. } => { let yield_ty = self.table.next_ty_var(closure_expr.into()); - self.require_type_is_sized(yield_ty); + self.require_type_is_sized(yield_ty, closure_expr.into()); self.poll_option_ty(yield_ty) } _ => unreachable!(), @@ -475,7 +475,7 @@ impl<'db> InferenceContext<'_, 'db> { _ = self .table .infer_ctxt - .at(&ObligationCause::new(), self.table.param_env) + .at(&ObligationCause::new(closure_expr), self.table.param_env) .eq(inferred_fnptr_sig, generalized_fnptr_sig) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); @@ -681,14 +681,21 @@ impl<'db> InferenceContext<'_, 'db> { fn sig_of_closure( &mut self, closure_expr: ExprId, - decl_inputs: &[Option], - decl_output: Option, + decl_inputs: &[PatId], + decl_input_tys: &[Option], + decl_output_ty: Option, expected_sig: Option>, ) -> ClosureSignatures<'db> { if let Some(e) = expected_sig { - self.sig_of_closure_with_expectation(closure_expr, decl_inputs, decl_output, e) + self.sig_of_closure_with_expectation( + closure_expr, + decl_inputs, + decl_input_tys, + decl_output_ty, + e, + ) } else { - self.sig_of_closure_no_expectation(closure_expr, decl_inputs, decl_output) + self.sig_of_closure_no_expectation(closure_expr, decl_input_tys, decl_output_ty) } } @@ -755,18 +762,25 @@ impl<'db> InferenceContext<'_, 'db> { fn sig_of_closure_with_expectation( &mut self, closure_expr: ExprId, - decl_inputs: &[Option], - decl_output: Option, + decl_inputs: &[PatId], + decl_input_tys: &[Option], + decl_output_ty: Option, expected_sig: PolyFnSig<'db>, ) -> ClosureSignatures<'db> { // Watch out for some surprises and just ignore the // expectation if things don't see to match up with what we // expect. if expected_sig.c_variadic() { - return self.sig_of_closure_no_expectation(closure_expr, decl_inputs, decl_output); - } else if expected_sig.skip_binder().inputs_and_output.len() != decl_inputs.len() + 1 { - return self - .sig_of_closure_with_mismatched_number_of_arguments(decl_inputs, decl_output); + return self.sig_of_closure_no_expectation( + closure_expr, + decl_input_tys, + decl_output_ty, + ); + } else if expected_sig.skip_binder().inputs_and_output.len() != decl_input_tys.len() + 1 { + return self.sig_of_closure_with_mismatched_number_of_arguments( + decl_input_tys, + decl_output_ty, + ); } // Create a `PolyFnSig`. Note the oddity that late bound @@ -798,11 +812,14 @@ impl<'db> InferenceContext<'_, 'db> { match self.merge_supplied_sig_with_expectation( closure_expr, decl_inputs, - decl_output, + decl_input_tys, + decl_output_ty, closure_sigs, ) { Ok(infer_ok) => self.table.register_infer_ok(infer_ok), - Err(_) => self.sig_of_closure_no_expectation(closure_expr, decl_inputs, decl_output), + Err(_) => { + self.sig_of_closure_no_expectation(closure_expr, decl_input_tys, decl_output_ty) + } } } @@ -822,15 +839,17 @@ impl<'db> InferenceContext<'_, 'db> { fn merge_supplied_sig_with_expectation( &mut self, closure_expr: ExprId, - decl_inputs: &[Option], - decl_output: Option, + decl_inputs: &[PatId], + decl_input_tys: &[Option], + decl_output_ty: Option, mut expected_sigs: ClosureSignatures<'db>, ) -> InferResult<'db, ClosureSignatures<'db>> { // Get the signature S that the user gave. // // (See comment on `sig_of_closure_with_expectation` for the // meaning of these letters.) - let supplied_sig = self.supplied_sig_of_closure(closure_expr, decl_inputs, decl_output); + let supplied_sig = + self.supplied_sig_of_closure(closure_expr, decl_input_tys, decl_output_ty); debug!(?supplied_sig); @@ -858,19 +877,21 @@ impl<'db> InferenceContext<'_, 'db> { // The liberated version of this signature should be a subtype // of the liberated form of the expectation. - for (supplied_ty, expected_ty) in iter::zip( - supplied_sig.inputs().iter().copied(), + for ((decl_input, supplied_ty), expected_ty) in iter::zip( + iter::zip(decl_inputs, supplied_sig.inputs().iter().copied()), expected_sigs.liberated_sig.inputs().iter().copied(), ) { // Check that E' = S'. - let cause = ObligationCause::new(); + let cause = ObligationCause::new(*decl_input); let InferOk { value: (), obligations } = table.infer_ctxt.at(&cause, table.param_env).eq(expected_ty, supplied_ty)?; all_obligations.extend(obligations); } let supplied_output_ty = supplied_sig.output(); - let cause = ObligationCause::new(); + let cause = ObligationCause::new( + decl_output_ty.map(Span::TypeRefId).unwrap_or(closure_expr.into()), + ); let InferOk { value: (), obligations } = table .infer_ctxt diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs index eb4eb8cce3a85..ce7931bb3c829 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -52,7 +52,7 @@ use span::Edition; use tracing::{debug, instrument}; use crate::{ - FnAbi, + FnAbi, Span, infer::{ CaptureInfo, CaptureSourceStack, CapturedPlace, InferenceContext, UpvarCapture, closure::analysis::expr_use_visitor::{ @@ -920,12 +920,12 @@ impl<'a, 'db> InferenceContext<'a, 'db> { self.result.closures_data.insert(closure_def_id, closure_data); } - fn normalize_capture_place(&self, place: Place) -> Place { + fn normalize_capture_place(&self, span: Span, place: Place) -> Place { let mut place = self.infcx().resolve_vars_if_possible(place); // In the new solver, types in HIR `Place`s can contain unnormalized aliases, // which can ICE later (e.g. when projecting fields for diagnostics). - let cause = ObligationCause::misc(); + let cause = ObligationCause::new(span); let at = self.table.at(&cause); match normalize::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( at, @@ -1011,7 +1011,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // Normalize eagerly when inserting into `capture_information`, so all downstream // capture analysis can assume a normalized `Place`. - self.normalize_capture_place(place) + self.normalize_capture_place(var_hir_id.into(), place) } /// A captured place is mutable if @@ -1215,7 +1215,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind { let mut dummy_capture_info = CaptureInfo { sources: SmallVec::new(), capture_kind: dummy_capture_kind }; - let place = ctx.normalize_capture_place(place_with_id.place.clone()); + let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone()); let place = restrict_capture_precision(place, &mut dummy_capture_info); @@ -1231,7 +1231,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind { }; assert_eq!(self.closure_def_id, upvar_closure); - let place = ctx.normalize_capture_place(place_with_id.place.clone()); + let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone()); self.capture_information.push(( place, @@ -1246,7 +1246,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind { }; assert_eq!(self.closure_def_id, upvar_closure); - let place = ctx.normalize_capture_place(place_with_id.place.clone()); + let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone()); self.capture_information.push(( place, @@ -1271,7 +1271,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind { let mut capture_info = CaptureInfo { sources: place_with_id.origins.iter().cloned().collect(), capture_kind }; - let place = ctx.normalize_capture_place(place_with_id.place.clone()); + let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone()); // We only want repr packed restriction to be applied to reading references into a packed // struct, and not when the data is being moved. Therefore we call this method here instead diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index b234e69197b47..44dc1a63d1a48 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -14,22 +14,19 @@ use hir_def::{ }, resolver::ValueNs, }; -use rustc_ast_ir::{try_visit, visit::VisitorResult}; -use rustc_type_ir::{ - FallibleTypeFolder, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, - inherent::{IntoKind, Ty as _}, -}; +use macros::{TypeFoldable, TypeVisitable}; +use rustc_type_ir::inherent::{IntoKind, Ty as _}; use smallvec::{SmallVec, smallvec}; use syntax::ast::{BinaryOp, UnaryOp}; use tracing::{debug, instrument, trace}; use crate::{ - Adjust, Adjustment, AutoBorrow, + Adjust, Adjustment, AutoBorrow, Span, infer::{ ByRef, CaptureSourceStack, InferenceContext, UpvarCapture, closure::analysis::BorrowKind, }, method_resolution::CandidateId, - next_solver::{DbInterner, ErrorGuaranteed, StoredTy, Ty, TyKind}, + next_solver::{ErrorGuaranteed, StoredTy, Ty, TyKind}, upvars::UpvarsRef, utils::EnumerateAndAdjustIterator, }; @@ -71,12 +68,13 @@ pub enum PlaceBase { Upvar { closure: ExprId, var_id: BindingId }, } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] pub struct Projection { /// Type after the projection is applied. pub ty: StoredTy, /// Defines the kind of access made by the projection. + #[type_visitable(ignore)] pub kind: ProjectionKind, } @@ -84,61 +82,17 @@ pub struct Projection { /// always correspond to a syntactic place expression. For example, when /// processing a pattern, a `Place` can be used to refer to the sub-value /// currently being inspected. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] pub struct Place { /// The type of the `PlaceBase` pub base_ty: StoredTy, /// The "outermost" place that holds this value. + #[type_visitable(ignore)] pub base: PlaceBase, /// How this place is derived from the base place. pub projections: Vec, } -impl<'db> TypeVisitable> for Place { - fn visit_with>>(&self, visitor: &mut V) -> V::Result { - let Self { base_ty, base: _, projections } = self; - try_visit!(base_ty.as_ref().visit_with(visitor)); - for proj in projections { - let Projection { ty, kind: _ } = proj; - try_visit!(ty.as_ref().visit_with(visitor)); - } - V::Result::output() - } -} - -impl<'db> TypeFoldable> for Place { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - let Self { base_ty, base, projections } = self; - let base_ty = base_ty.as_ref().try_fold_with(folder)?.store(); - let projections = projections - .into_iter() - .map(|proj| { - let Projection { ty, kind } = proj; - let ty = ty.as_ref().try_fold_with(folder)?.store(); - Ok(Projection { ty, kind }) - }) - .collect::>()?; - Ok(Self { base_ty, base, projections }) - } - - fn fold_with>>(self, folder: &mut F) -> Self { - let Self { base_ty, base, projections } = self; - let base_ty = base_ty.as_ref().fold_with(folder).store(); - let projections = projections - .into_iter() - .map(|proj| { - let Projection { ty, kind } = proj; - let ty = ty.as_ref().fold_with(folder).store(); - Projection { ty, kind } - }) - .collect(); - Self { base_ty, base, projections } - } -} - impl Place { /// Returns an iterator of the types that have to be dereferenced to access /// the `Place`. @@ -216,6 +170,13 @@ impl PlaceWithOrigin { origin_stack.push(origin); } } + + pub(crate) fn span(&self) -> Span { + match self.origins.first() { + Some(origin) => origin.final_source().into(), + None => Span::Dummy, + } + } } /// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index b2bed0ce2be1a..15265b9104102 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -377,7 +377,8 @@ where let mut first_error = None; let mut r_borrow_var = None; - let mut autoderef = Autoderef::new_with_tracking(self.infcx(), self.param_env(), a); + let mut autoderef = + Autoderef::new_with_tracking(self.infcx(), self.param_env(), a, self.cause.span()); let mut found = None; for (referent_ty, autoderefs) in autoderef.by_ref() { @@ -897,11 +898,11 @@ impl<'db> InferenceContext<'_, 'db> { allow_two_phase: AllowTwoPhase, expr_is_read: ExprIsRead, ) -> RelateResult<'db, Ty<'db>> { - let source = self.table.try_structurally_resolve_type(expr_ty); - target = self.table.try_structurally_resolve_type(target); + let source = self.table.try_structurally_resolve_type(expr.into(), expr_ty); + target = self.table.try_structurally_resolve_type(expr.into(), target); debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target); - let cause = ObligationCause::with_span(expr.into()); + let cause = ObligationCause::new(expr); let coerce_never = self.expr_guaranteed_to_constitute_read_for_never(expr, expr_is_read); let mut coerce = Coerce { delegate: InferenceCoercionDelegate(self), @@ -930,8 +931,8 @@ impl<'db> InferenceContext<'_, 'db> { new: ExprId, new_ty: Ty<'db>, ) -> RelateResult<'db, Ty<'db>> { - let prev_ty = self.table.try_structurally_resolve_type(prev_ty); - let new_ty = self.table.try_structurally_resolve_type(new_ty); + let prev_ty = self.table.try_structurally_resolve_type(new.into(), prev_ty); + let new_ty = self.table.try_structurally_resolve_type(new.into(), new_ty); debug!( "coercion::try_find_coercion_lub({:?}, {:?}, exprs={:?} exprs)", prev_ty, @@ -977,7 +978,7 @@ impl<'db> InferenceContext<'_, 'db> { // We need to eagerly handle nested obligations due to lazy norm. let mut ocx = ObligationCtxt::new(&table.infer_ctxt); let value = ocx.lub( - &ObligationCause::with_span(new.into()), + &ObligationCause::new(new), table.param_env, prev_ty, new_ty, @@ -1029,7 +1030,7 @@ impl<'db> InferenceContext<'_, 'db> { let sig = self .table .infer_ctxt - .at(&ObligationCause::with_span(new.into()), self.table.param_env) + .at(&ObligationCause::new(new), self.table.param_env) .lub(a_sig, b_sig) .map(|ok| self.table.register_infer_ok(ok))?; @@ -1075,7 +1076,7 @@ impl<'db> InferenceContext<'_, 'db> { // operate on values and not places, so a never coercion is valid. let mut coerce = Coerce { delegate: InferenceCoercionDelegate(self), - cause: ObligationCause::with_span(new.into()), + cause: ObligationCause::new(new), allow_two_phase: AllowTwoPhase::No, coerce_never: true, use_lub: true, @@ -1111,7 +1112,7 @@ impl<'db> InferenceContext<'_, 'db> { .commit_if_ok(|table| { table .infer_ctxt - .at(&ObligationCause::with_span(new.into()), table.param_env) + .at(&ObligationCause::new(new), table.param_env) .lub(prev_ty, new_ty) }) .unwrap_err()) @@ -1459,7 +1460,7 @@ fn coerce<'db>( let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); let ((ty1_with_vars, ty2_with_vars), vars) = infcx.instantiate_canonical(Span::Dummy, tys); - let cause = ObligationCause::new(); + let cause = ObligationCause::dummy(); // FIXME: Target features. let target_features = TargetFeatures::default(); let mut coerce = Coerce { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index c59c919c518e7..a03e891114854 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -327,7 +327,7 @@ impl<'db> InferenceContext<'_, 'db> { let ty = match expr { Expr::Missing => self.err_ty(), &Expr::If { condition, then_branch, else_branch } => { - let expected = &expected.adjust_for_branches(&mut self.table); + let expected = &expected.adjust_for_branches(&mut self.table, tgt_expr.into()); self.infer_expr_coerce_never( condition, &Expectation::HasType(self.types.types.bool), @@ -346,14 +346,20 @@ impl<'db> InferenceContext<'_, 'db> { expected.coercion_target_type(&mut self.table, then_branch.into()), &coercion_sites, ); - coerce.coerce(self, &ObligationCause::new(), then_branch, then_ty, ExprIsRead::Yes); + coerce.coerce( + self, + &ObligationCause::new(then_branch), + then_branch, + then_ty, + ExprIsRead::Yes, + ); match else_branch { Some(else_branch) => { let else_ty = self.infer_expr_inner(else_branch, expected, ExprIsRead::Yes); let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); coerce.coerce( self, - &ObligationCause::new(), + &ObligationCause::new(else_branch), else_branch, else_ty, ExprIsRead::Yes, @@ -364,7 +370,7 @@ impl<'db> InferenceContext<'_, 'db> { coerce.coerce_forced_unit( self, tgt_expr, - &ObligationCause::new(), + &ObligationCause::new(tgt_expr), true, ExprIsRead::Yes, ); @@ -461,7 +467,7 @@ impl<'db> InferenceContext<'_, 'db> { self.infer_top_pat(arm.pat, input_ty, PatOrigin::MatchArm); } - let expected = expected.adjust_for_branches(&mut self.table); + let expected = expected.adjust_for_branches(&mut self.table, tgt_expr.into()); let result_ty = match &expected { // We don't coerce to `()` so that if the match expression is a // statement it's branches can have any consistent type. @@ -485,7 +491,7 @@ impl<'db> InferenceContext<'_, 'db> { all_arms_diverge &= self.diverges; coerce.coerce( self, - &ObligationCause::new(), + &ObligationCause::new(arm.expr), arm.expr, arm_ty, ExprIsRead::Yes, @@ -536,10 +542,11 @@ impl<'db> InferenceContext<'_, 'db> { match find_breakable(&mut self.breakables, label) { Some(ctxt) => match ctxt.coerce.take() { Some(mut coerce) => { + let expr = expr.unwrap_or(tgt_expr); coerce.coerce( self, - &ObligationCause::new(), - expr.unwrap_or(tgt_expr), + &ObligationCause::new(expr), + expr, val_ty, ExprIsRead::Yes, ); @@ -598,7 +605,7 @@ impl<'db> InferenceContext<'_, 'db> { self.infer_record_expr(tgt_expr, expected, path, fields, *spread) } Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected), - Expr::Await { expr } => self.infer_await_expr(*expr), + Expr::Await { expr } => self.infer_await_expr(tgt_expr, *expr), Expr::Cast { expr, type_ref } => { let cast_ty = self.make_body_ty(*type_ref); let expr_ty = @@ -751,7 +758,7 @@ impl<'db> InferenceContext<'_, 'db> { Expr::Tuple { exprs, .. } => { let mut tys = match expected .only_has_type(&mut self.table) - .map(|t| self.table.try_structurally_resolve_type(t).kind()) + .map(|t| self.table.try_structurally_resolve_type(tgt_expr.into(), t).kind()) { Some(TyKind::Tuple(substs)) => substs .iter() @@ -964,14 +971,14 @@ impl<'db> InferenceContext<'_, 'db> { ty } - fn infer_await_expr(&mut self, awaitee: ExprId) -> Ty<'db> { + fn infer_await_expr(&mut self, expr: ExprId, awaitee: ExprId) -> Ty<'db> { let awaitee_ty = self.infer_expr_no_expect(awaitee, ExprIsRead::Yes); let (Some(into_future), Some(into_future_output)) = (self.lang_items.IntoFuture, self.lang_items.IntoFutureOutput) else { return self.types.types.error; }; - self.table.register_bound(awaitee_ty, into_future, ObligationCause::new()); + self.table.register_bound(awaitee_ty, into_future, ObligationCause::new(expr)); // Do not eagerly normalize. Ty::new_projection(self.interner(), into_future_output.into(), [awaitee_ty]) } @@ -1002,7 +1009,7 @@ impl<'db> InferenceContext<'_, 'db> { self.check_record_expr_fields(adt_ty, expected, expr, variant, fields, base_expr); - self.require_type_is_sized(adt_ty); + self.require_type_is_sized(adt_ty, expr.into()); adt_ty } @@ -1017,12 +1024,12 @@ impl<'db> InferenceContext<'_, 'db> { ) { let interner = self.interner(); - let adt_ty = self.table.try_structurally_resolve_type(adt_ty); + let adt_ty = self.table.try_structurally_resolve_type(expr.into(), adt_ty); let adt_ty_hint = expected.only_has_type(&mut self.table).and_then(|expected| { self.infcx() .fudge_inference_if_ok(|| { let mut ocx = ObligationCtxt::new(self.infcx()); - ocx.sup(&ObligationCause::new(), self.table.param_env, expected, adt_ty)?; + ocx.sup(&ObligationCause::new(expr), self.table.param_env, expected, adt_ty)?; if !ocx.try_evaluate_obligations().is_empty() { return Err(TypeError::Mismatch); } @@ -1084,7 +1091,7 @@ impl<'db> InferenceContext<'_, 'db> { // Check that the expected field type is WF. Otherwise, we emit no use-site error // in the case of coercions for non-WF fields, which leads to incorrect error // tainting. See issue #126272. - self.table.register_wf_obligation(field_type.into(), ObligationCause::new()); + self.table.register_wf_obligation(field_type.into(), ObligationCause::new(field.expr)); // Make sure to give a type to the field even if there's // an error, so we can continue type-checking. @@ -1131,7 +1138,7 @@ impl<'db> InferenceContext<'_, 'db> { if remaining_fields.remove(&field.name).is_some() { let target_ty = variant_field_tys[field_idx].get().instantiate(interner, args); - let cause = ObligationCause::new(); + let cause = ObligationCause::new(expr); match self.table.at(&cause).sup(target_ty, fru_ty) { Ok(InferOk { obligations, value: () }) => { self.table.register_predicates(obligations) @@ -1337,7 +1344,7 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { let elem_ty = match expected .to_option(&mut self.table) - .map(|t| self.table.try_structurally_resolve_type(t).kind()) + .map(|t| self.table.try_structurally_resolve_type(expr.into(), t).kind()) { Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st, _ => self.table.next_ty_var(expr.into()), @@ -1356,7 +1363,7 @@ impl<'db> InferenceContext<'_, 'db> { let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes); coerce.coerce( self, - &ObligationCause::new(), + &ObligationCause::new(expr), expr, cur_elem_ty, ExprIsRead::Yes, @@ -1402,7 +1409,13 @@ impl<'db> InferenceContext<'_, 'db> { let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes); let mut coerce_many = self.return_coercion.take().unwrap(); - coerce_many.coerce(self, &ObligationCause::new(), expr, return_expr_ty, ExprIsRead::Yes); + coerce_many.coerce( + self, + &ObligationCause::new(expr), + expr, + return_expr_ty, + ExprIsRead::Yes, + ); self.return_coercion = Some(coerce_many); } @@ -1416,7 +1429,7 @@ impl<'db> InferenceContext<'_, 'db> { coerce.coerce_forced_unit( self, ret, - &ObligationCause::new(), + &ObligationCause::new(ret), true, ExprIsRead::Yes, ); @@ -1600,11 +1613,12 @@ impl<'db> InferenceContext<'_, 'db> { fn lookup_field( &mut self, + field_expr: ExprId, receiver_ty: Ty<'db>, name: &Name, ) -> Option<(Ty<'db>, Either, Vec, bool)> { let interner = self.interner(); - let mut autoderef = self.table.autoderef_with_tracking(receiver_ty); + let mut autoderef = self.table.autoderef_with_tracking(receiver_ty, field_expr.into()); let mut private_field = None; let res = autoderef.by_ref().find_map(|(derefed_ty, _)| { let (field_id, parameters) = match derefed_ty.kind() { @@ -1692,7 +1706,7 @@ impl<'db> InferenceContext<'_, 'db> { return self.err_ty(); } - match self.lookup_field(receiver_ty, name) { + match self.lookup_field(tgt_expr, receiver_ty, name) { Some((ty, field_id, adjustments, is_public)) => { self.write_expr_adj(receiver, adjustments.into_boxed_slice()); self.result.field_resolutions.insert(tgt_expr, field_id); @@ -1761,7 +1775,7 @@ impl<'db> InferenceContext<'_, 'db> { CallableDefId::StructId(it) => it.into(), CallableDefId::EnumVariantId(it) => it.loc(self.db).parent.into(), }; - self.add_required_obligations_for_value_path(def_id, args); + self.add_required_obligations_for_value_path(tgt_expr.into(), def_id, args); } self.check_call_arguments( @@ -1787,7 +1801,7 @@ impl<'db> InferenceContext<'_, 'db> { expected: &Expectation<'db>, ) -> Ty<'db> { let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes); - let receiver_ty = self.table.try_structurally_resolve_type(receiver_ty); + let receiver_ty = self.table.try_structurally_resolve_type(receiver.into(), receiver_ty); let resolved = self.lookup_method_including_private( receiver_ty, @@ -1809,15 +1823,15 @@ impl<'db> InferenceContext<'_, 'db> { // Failed to resolve, report diagnostic and try to resolve as call to field access or // assoc function Err(_) => { - let field_with_same_name_exists = match self.lookup_field(receiver_ty, method_name) - { - Some((ty, field_id, adjustments, _public)) => { - self.write_expr_adj(receiver, adjustments.into_boxed_slice()); - self.result.field_resolutions.insert(tgt_expr, field_id); - Some(ty) - } - None => None, - }; + let field_with_same_name_exists = + match self.lookup_field(tgt_expr, receiver_ty, method_name) { + Some((ty, field_id, adjustments, _public)) => { + self.write_expr_adj(receiver, adjustments.into_boxed_slice()); + self.result.field_resolutions.insert(tgt_expr, field_id); + Some(ty) + } + None => None, + }; let assoc_func_with_same_name = self.with_method_resolution(tgt_expr.into(), receiver.into(), |ctx| { @@ -1961,13 +1975,13 @@ impl<'db> InferenceContext<'_, 'db> { // return type (likely containing type variables if the function // is polymorphic) and the expected return type. // No argument expectations are produced if unification fails. - let origin = ObligationCause::new(); + let origin = ObligationCause::new(call_expr); ocx.sup(&origin, self.table.param_env, expected_output, formal_output)?; for &ty in &formal_input_tys { ocx.register_obligation(Obligation::new( self.interner(), - ObligationCause::new(), + ObligationCause::new(call_expr), self.table.param_env, ClauseKind::WellFormed(ty.into()), )); @@ -2088,7 +2102,7 @@ impl<'db> InferenceContext<'_, 'db> { let formal_ty_error = this .table .infer_ctxt - .at(&ObligationCause::new(), this.table.param_env) + .at(&ObligationCause::new(provided_arg), this.table.param_env) .eq(formal_input_ty, coerced_ty); // If neither check failed, the types are compatible diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs index 753a6513f8e5f..0127fd6cdbcd1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs @@ -194,6 +194,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // particularly for things like `String + &String`. let rhs_ty_var = self.table.next_ty_var(rhs_expr.into()); let result = self.lookup_op_method( + expr, lhs_ty, Some((rhs_expr, rhs_ty_var)), self.lang_item_for_bin_op(op), @@ -265,7 +266,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { operand_ty: Ty<'db>, op: UnaryOp, ) -> Ty<'db> { - match self.lookup_op_method(operand_ty, None, self.lang_item_for_unop(op)) { + match self.lookup_op_method(ex, operand_ty, None, self.lang_item_for_unop(op)) { Ok(method) => { self.write_method_resolution(ex, method.def_id, method.args); method.sig.output() @@ -279,6 +280,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { fn lookup_op_method( &mut self, + expr: ExprId, lhs_ty: Ty<'db>, opt_rhs: Option<(ExprId, Ty<'db>)>, (opname, trait_did): (Symbol, Option), @@ -294,7 +296,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { ); let opt_rhs_ty = opt_rhs.map(|it| it.1); - let cause = ObligationCause::new(); + let cause = ObligationCause::new(expr); // We don't consider any other candidates if this lookup fails // so we can freely treat opaque types as inference variables here diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/opaques.rs index 02bce22d6d4cb..178b3fcbf5ae2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/opaques.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/opaques.rs @@ -4,6 +4,7 @@ use rustc_type_ir::{TypeVisitableExt, fold_regions}; use tracing::{debug, instrument}; use crate::{ + Span, infer::InferenceContext, next_solver::{ EarlyBinder, OpaqueTypeKey, SolverDefId, TypingMode, @@ -135,7 +136,8 @@ impl<'db> InferenceContext<'_, 'db> { return UsageKind::UnconstrainedHiddenType(hidden_type); } - let cause = ObligationCause::new(); + // FIXME: This should not use a dummy span. + let cause = ObligationCause::new(Span::Dummy); let at = self.table.infer_ctxt.at(&cause, self.table.param_env); let hidden_type = match at.deeply_normalize(hidden_type) { Ok(hidden_type) => hidden_type, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index a7b82dad4fc38..97165b9f07343 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -309,7 +309,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { Pat::TupleStruct { path, .. } => Some(self.resolve_tuple_struct_pat(pat_id, path)), _ => None, }; - let adjust_mode = self.calc_adjust_mode(pat, opt_path_res); + let adjust_mode = self.calc_adjust_mode(pat_id, pat, opt_path_res); let ty = self.infer_pat_inner(pat_id, opt_path_res, adjust_mode, expected, pat_info); let ty = self.insert_type_vars_shallow(ty); self.write_pat_ty(pat_id, ty); @@ -320,6 +320,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { && derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref) { let infer_ok = self.register_deref_mut_bounds_if_needed( + pat_id, pat_id, derefed_tys.iter().filter_map(|adjust| match adjust.kind { PatAdjust::OverloadedDeref => Some(adjust.source.as_ref()), @@ -393,7 +394,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { let expected = if let AdjustMode::Peel { .. } = adjust_mode && pat_info.pat_origin.default_binding_modes() { - self.table.try_structurally_resolve_type(expected) + self.table.try_structurally_resolve_type(pat.into(), expected) } else { expected }; @@ -432,7 +433,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // The scrutinee is a smart pointer; implicitly dereference it. This adds a // requirement that `expected: DerefPure`. - let inner_ty = self.deref_pat_target(expected); + let inner_ty = self.deref_pat_target(pat, expected); // Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any // `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`. @@ -604,6 +605,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { /// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`. fn calc_adjust_mode( &mut self, + pat_id: PatId, pat: &Pat, opt_path_res: Option, ()>>, ) -> AdjustMode { @@ -640,7 +642,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { let mut peeled_ty = lit_ty; let mut pat_ref_layers = 0; while let TyKind::Ref(_, inner_ty, mutbl) = - self.table.try_structurally_resolve_type(peeled_ty).kind() + self.table.try_structurally_resolve_type(pat_id.into(), peeled_ty).kind() { // We rely on references at the head of constants being immutable. debug_assert!(mutbl.is_not()); @@ -763,7 +765,10 @@ impl<'a, 'db> InferenceContext<'a, 'db> { match expected.kind() { // Allow `b"...": &[u8]` TyKind::Ref(_, inner_ty, _) - if self.table.try_structurally_resolve_type(inner_ty).is_slice() => + if self + .table + .try_structurally_resolve_type(expr.into(), inner_ty) + .is_slice() => { trace!(?expr, "polymorphic byte string lit"); pat_ty = self.types.types.static_u8_slice; @@ -788,7 +793,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // string literal patterns to have type `str`. This is accounted for when lowering to MIR. if self.features.deref_patterns && matches!(literal, Literal::String(_)) - && self.table.try_structurally_resolve_type(expected).is_str() + && self.table.try_structurally_resolve_type(expr.into(), expected).is_str() { pat_ty = self.types.types.str; } @@ -825,7 +830,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // be peeled to `str` while ty here is still `&str`, if we don't // err early here, a rather confusing unification error will be // emitted instead). - let ty = self.table.try_structurally_resolve_type(ty); + let ty = self.table.try_structurally_resolve_type(expr.into(), ty); let fail = !(ty.is_numeric() || ty.is_char() || ty.is_ty_var() || ty.references_error()); Some((fail, ty, expr)) @@ -1118,7 +1123,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; let pat_ty = Ty::new(interner, TyKind::Tuple(element_tys)); if self.demand_eqtype(pat.into(), expected, pat_ty).is_err() { let expected = if let TyKind::Tuple(tys) = - self.table.try_structurally_resolve_type(expected).kind() + self.table.try_structurally_resolve_type(Span::Dummy, expected).kind() { for (expected_var, found) in iter::zip(element_tys, tys) { // Constrain the infer var so that the type mismatch error message, which contains it, @@ -1265,15 +1270,21 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; box_ty } - fn _infer_deref_pat(&mut self, inner: PatId, expected: Ty<'db>, pat_info: PatInfo) -> Ty<'db> { - let target_ty = self.deref_pat_target(expected); + fn _infer_deref_pat( + &mut self, + pat: PatId, + inner: PatId, + expected: Ty<'db>, + pat_info: PatInfo, + ) -> Ty<'db> { + let target_ty = self.deref_pat_target(pat, expected); self.infer_pat(inner, target_ty, pat_info); - let infer_ok = self.register_deref_mut_bounds_if_needed(inner, [expected]); + let infer_ok = self.register_deref_mut_bounds_if_needed(pat, inner, [expected]); self.table.register_infer_ok(infer_ok); expected } - fn deref_pat_target(&mut self, source_ty: Ty<'db>) -> Ty<'db> { + fn deref_pat_target(&mut self, pat: PatId, source_ty: Ty<'db>) -> Ty<'db> { let (Some(deref_pure), Some(deref_target)) = (self.lang_items.DerefPure, self.lang_items.DerefTarget) else { @@ -1281,10 +1292,10 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; }; // Register a `DerefPure` bound, which is required by all `deref!()` pats. let interner = self.interner(); - self.table.register_bound(source_ty, deref_pure, ObligationCause::new()); + self.table.register_bound(source_ty, deref_pure, ObligationCause::new(pat)); // The expected type for the deref pat's inner pattern is `::Target`. let target_ty = Ty::new_projection(interner, deref_target.into(), [source_ty]); - self.table.try_structurally_resolve_type(target_ty) + self.table.try_structurally_resolve_type(pat.into(), target_ty) } /// Check if the interior of a deref pattern (either explicit or implicit) has any `ref mut` @@ -1293,6 +1304,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; /// account for `ref mut` binding modes inherited from implicitly dereferencing `&mut` refs. fn register_deref_mut_bounds_if_needed( &self, + pat: PatId, inner: PatId, derefed_tys: impl IntoIterator>, ) -> InferOk<'db, ()> { @@ -1303,7 +1315,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; for mutably_derefed_ty in derefed_tys { infer_ok.obligations.push(Obligation::new( interner, - ObligationCause::new(), + ObligationCause::new(pat), self.table.param_env, TraitRef::new(interner, deref_mut.into(), [mutably_derefed_ty]), )); @@ -1350,7 +1362,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not(); } - expected = self.table.try_structurally_resolve_type(expected); + expected = self.table.try_structurally_resolve_type(pat.into(), expected); // Determine whether we're consuming an inherited reference and resetting the default // binding mode, based on edition and enabled experimental features. if let ByRef::Yes(inh_mut) = pat_info.binding_mode { @@ -1574,7 +1586,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; expected: Ty<'db>, pat_info: PatInfo, ) -> Ty<'db> { - let expected = self.table.try_structurally_resolve_type(expected); + let expected = self.table.try_structurally_resolve_type(pat.into(), expected); // If the pattern is irrefutable and `expected` is an infer ty, we try to equate it // to an array if the given pattern allows it. See issue #76342 diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index c020c9812b82e..4df38e96ee009 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -40,7 +40,7 @@ impl<'db> InferenceContext<'_, 'db> { }; let args = self.insert_type_vars(substs, id.into()); - self.add_required_obligations_for_value_path(generic_def, args); + self.add_required_obligations_for_value_path(id, generic_def, args); let ty = self.db.value_ty(value_def)?.instantiate(self.interner(), args); let ty = self.process_remote_user_written_ty(ty); @@ -226,6 +226,7 @@ impl<'db> InferenceContext<'_, 'db> { pub(super) fn add_required_obligations_for_value_path( &mut self, + node: ExprOrPatId, def: GenericDefId, subst: GenericArgs<'db>, ) { @@ -234,7 +235,7 @@ impl<'db> InferenceContext<'_, 'db> { let param_env = self.table.param_env; self.table.register_predicates(clauses_as_obligations( predicates.iter_instantiated(interner, subst.as_slice()), - ObligationCause::new(), + ObligationCause::new(node), param_env, )); } @@ -348,7 +349,7 @@ impl<'db> InferenceContext<'_, 'db> { name: &Name, id: ExprOrPatId, ) -> Option<(ValueNs, GenericArgs<'db>)> { - let ty = self.table.try_structurally_resolve_type(ty); + let ty = self.table.try_structurally_resolve_type(id.into(), ty); let (enum_id, subst) = match ty.as_adt() { Some((AdtId::EnumId(e), subst)) => (e, subst), _ => return None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs index 63841af682ed9..968d793615edd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs @@ -90,7 +90,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // autoderef that normal method probing does. They could likely be // consolidated. - let mut autoderef = InferenceContextAutoderef::new_from_inference_context(self, base_ty); + let mut autoderef = + InferenceContextAutoderef::new_from_inference_context(self, base_ty, base_expr.into()); let mut result = None; while result.is_none() && autoderef.next().is_some() { result = Self::try_index_step(expr, base_expr, index_expr, &mut autoderef, idx_ty); @@ -126,7 +127,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { let ctx = autoderef.ctx(); ctx.table.register_predicate(Obligation::new( ctx.interner(), - ObligationCause::new(), + ObligationCause::new(base_expr), ctx.table.param_env, ClauseKind::ConstArgHasType(ct, ctx.types.types.usize), )); @@ -206,7 +207,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // opaque types as rigid here to support `impl Deref>`. let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; self.table.lookup_method_for_operator( - ObligationCause::with_span(expr.into()), + ObligationCause::new(expr), imm_op, imm_tr, base_ty, @@ -239,7 +240,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // of the opaque. let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; table.lookup_method_for_operator( - ObligationCause::with_span(expr.into()), + ObligationCause::new(expr), mut_op, mut_tr, base_ty, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index ab5a7c3fc71a8..c089335708698 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -320,11 +320,11 @@ impl<'db> InferenceTable<'db> { /// In case there is still ambiguity, the returned type may be an inference /// variable. This is different from `structurally_resolve_type` which errors /// in this case. - pub(crate) fn try_structurally_resolve_type(&mut self, ty: Ty<'db>) -> Ty<'db> { + pub(crate) fn try_structurally_resolve_type(&mut self, span: Span, ty: Ty<'db>) -> Ty<'db> { if let TyKind::Alias(..) = ty.kind() { let result = self .infer_ctxt - .at(&ObligationCause::misc(), self.param_env) + .at(&ObligationCause::new(span), self.param_env) .structurally_normalize_ty(ty, &mut self.fulfillment_cx); match result { Ok(normalized_ty) => normalized_ty, @@ -396,22 +396,18 @@ impl<'db> InferenceTable<'db> { /// checking later, during regionck, that `arg` is well-formed. pub(crate) fn register_wf_obligation(&mut self, term: Term<'db>, cause: ObligationCause) { - let _ = (term, cause); - // FIXME: We don't currently register an obligation here because we don't implement - // wf checking anyway and this function is currently often passed dummy spans, which could - // prevent reporting "type annotation needed" errors. - // self.register_predicate(Obligation::new( - // self.interner(), - // cause, - // self.param_env, - // ClauseKind::WellFormed(term), - // )); + self.register_predicate(Obligation::new( + self.interner(), + cause, + self.param_env, + ClauseKind::WellFormed(term), + )); } /// Registers obligations that all `args` are well-formed. - pub(crate) fn add_wf_bounds(&mut self, args: GenericArgs<'db>) { + pub(crate) fn add_wf_bounds(&mut self, span: Span, args: GenericArgs<'db>) { for term in args.iter().filter_map(|it| it.as_term()) { - self.register_wf_obligation(term, ObligationCause::new()); + self.register_wf_obligation(term, ObligationCause::new(span)); } } @@ -425,7 +421,7 @@ impl<'db> InferenceTable<'db> { /// Whenever you lower a user-written type, you should call this. pub(crate) fn process_user_written_ty(&mut self, span: Span, ty: Ty<'db>) -> Ty<'db> { let ty = self.insert_type_vars(ty, span); - self.try_structurally_resolve_type(ty) + self.try_structurally_resolve_type(span, ty) } /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation, @@ -435,8 +431,7 @@ impl<'db> InferenceTable<'db> { // See https://github.com/rust-lang/rust/blob/cdb45c87e2cd43495379f7e867e3cc15dcee9f93/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs#L487-L495: // Even though the new solver only lazily normalizes usually, here we eagerly normalize so that not everything needs // to normalize before inspecting the `TyKind`. - // FIXME(next-solver): We should not deeply normalize here, only shallowly. - self.try_structurally_resolve_type(ty) + self.try_structurally_resolve_type(Span::Dummy, ty) } } @@ -700,7 +695,8 @@ pub(super) mod resolve_completely { T: Into> + TypeSuperFoldable> + Copy, { let value = if self.should_normalize { - let cause = ObligationCause::new(); + // FIXME: This should not use a dummy span. + let cause = ObligationCause::new(Span::Dummy); let at = self.ctx.table.at(&cause); let universes = vec![None; outer_exclusive_binder(value).as_usize()]; match deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index c77c1b98d35af..c6075b03dabe7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -63,7 +63,7 @@ use std::{hash::Hash, ops::ControlFlow}; use hir_def::{ CallableDefId, ExpressionStoreOwnerId, GenericDefId, LifetimeParamId, TypeAliasId, TypeOrConstParamId, TypeParamId, - hir::{ExprId, ExprOrPatId, PatId}, + hir::{BindingId, ExprId, ExprOrPatId, PatId}, resolver::TypeNs, type_ref::{Rawness, TypeRefId}, }; @@ -678,11 +678,12 @@ pub fn known_const_to_ast<'db>( pub enum Span { ExprId(ExprId), PatId(PatId), + BindingId(BindingId), TypeRefId(TypeRefId), /// An unimportant location. Errors on this will be suppressed. Dummy, } -impl_from!(ExprId, PatId, TypeRefId for Span); +impl_from!(ExprId, PatId, BindingId, TypeRefId for Span); impl From for Span { fn from(value: ExprOrPatId) -> Self { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 1f2ede332431f..86477f2c0b6be 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -316,7 +316,7 @@ impl<'db> InferenceTable<'db> { let bounds = GenericPredicates::query_all(self.db, method_item.into()); let bounds = clauses_as_obligations( bounds.iter_instantiated(interner, args.as_slice()), - ObligationCause::new(), + cause, self.param_env, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs index a29e3db18d252..6601ff7a013e3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs @@ -39,7 +39,7 @@ use crate::{ struct ConfirmContext<'a, 'b, 'db> { ctx: &'a mut InferenceContext<'b, 'db>, candidate: FunctionId, - expr: ExprId, + call_expr: ExprId, } #[derive(Debug)] @@ -74,9 +74,9 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { fn new( ctx: &'a mut InferenceContext<'b, 'db>, candidate: FunctionId, - expr: ExprId, + call_expr: ExprId, ) -> ConfirmContext<'a, 'b, 'db> { - ConfirmContext { ctx, candidate, expr } + ConfirmContext { ctx, candidate, call_expr } } #[inline] @@ -181,7 +181,8 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { ) -> (Ty<'db>, Box<[Adjustment]>) { // Commit the autoderefs by calling `autoderef` again, but this // time writing the results into the various typeck results. - let mut autoderef = self.ctx.table.autoderef_with_tracking(unadjusted_self_ty); + let mut autoderef = + self.ctx.table.autoderef_with_tracking(unadjusted_self_ty, self.call_expr.into()); let Some((mut target, n)) = autoderef.nth(pick.autoderefs) else { return (Ty::new_error(self.interner(), ErrorGuaranteed), Box::new([])); }; @@ -191,7 +192,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { self.ctx.table.register_infer_ok(autoderef.adjust_steps_as_infer_ok()); match pick.autoref_or_ptr_adjustment { Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => { - let region = self.infcx().next_region_var(self.expr.into()); + let region = self.infcx().next_region_var(self.call_expr.into()); // Type we're wrapping in a reference, used later for unsizing let base_ty = target; @@ -255,7 +256,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { ) -> GenericArgs<'db> { match pick.kind { probe::InherentImplPick(impl_def_id) => { - self.infcx().fresh_args_for_item(self.expr.into(), impl_def_id.into()) + self.infcx().fresh_args_for_item(self.call_expr.into(), impl_def_id.into()) } probe::ObjectPick(trait_def_id) => { @@ -297,7 +298,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { // the process we will unify the transformed-self-type // of the method with the actual type in order to // unify some of these variables. - self.infcx().fresh_args_for_item(self.expr.into(), trait_def_id.into()) + self.infcx().fresh_args_for_item(self.call_expr.into(), trait_def_id.into()) } probe::WhereClausePick(poly_trait_ref) => { @@ -317,7 +318,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { // yield an object-type (e.g., `&Object` or `Box` // etc). - let mut autoderef = self.ctx.table.autoderef(self_ty); + let mut autoderef = self.ctx.table.autoderef(self_ty, self.call_expr.into()); // We don't need to gate this behind arbitrary self types // per se, but it does make things a bit more gated. @@ -466,7 +467,11 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { LifetimeElisionKind::Infer, false, None, - &mut LowererCtx { ctx: self.ctx, expr: self.expr, parent_args: parent_args.as_slice() }, + &mut LowererCtx { + ctx: self.ctx, + expr: self.call_expr, + parent_args: parent_args.as_slice(), + }, ) } @@ -480,7 +485,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { "unify_receivers: self_ty={:?} method_self_ty={:?} pick={:?}", self_ty, method_self_ty, pick ); - let cause = ObligationCause::new(); + let cause = ObligationCause::new(self.call_expr); match self.ctx.table.at(&cause).sup(method_self_ty, self_ty) { Ok(infer_ok) => { self.ctx.table.register_infer_ok(infer_ok); @@ -488,7 +493,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { Err(_) => { if self.ctx.features.arbitrary_self_types { self.ctx.result.type_mismatches.get_or_insert_default().insert( - self.expr.into(), + self.call_expr.into(), TypeMismatch { expected: method_self_ty.store(), actual: self_ty.store() }, ); } @@ -513,7 +518,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { let method_predicates = clauses_as_obligations( GenericPredicates::query_all(self.db(), def_id.into()) .iter_instantiated(self.interner(), all_args), - ObligationCause::new(), + ObligationCause::new(self.call_expr), self.ctx.table.param_env, ); @@ -539,13 +544,13 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { // this is a projection from a trait reference, so we have to // make sure that the trait reference inputs are well-formed. - self.ctx.table.add_wf_bounds(all_args); + self.ctx.table.add_wf_bounds(self.call_expr.into(), all_args); // the function type must also be well-formed (this is not // implied by the args being well-formed because of inherent // impls and late-bound regions - see issue #28609). for ty in sig.inputs_and_output { - self.ctx.table.register_wf_obligation(ty.into(), ObligationCause::new()); + self.ctx.table.register_wf_obligation(ty.into(), ObligationCause::new(self.call_expr)); } } @@ -614,7 +619,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { T: TypeFoldable> + Copy, { self.infcx().instantiate_binder_with_fresh_vars( - self.expr.into(), + self.call_expr.into(), BoundRegionConversionTime::FnCall, value, ) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index 2e4ca139c086a..34cd941e91bcd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs @@ -339,7 +339,7 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { let ty = self .infcx .instantiate_query_response_and_region_obligations( - &ObligationCause::new(), + &ObligationCause::new(self.receiver_span), self.param_env, &orig_values, ty, @@ -403,7 +403,8 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { // converted to, in order to find out which of those methods might actually // be callable. let mut autoderef_via_deref = - Autoderef::new(infcx, self.param_env, self_ty).include_raw_pointers(); + Autoderef::new(infcx, self.param_env, self_ty, self.receiver_span) + .include_raw_pointers(); let mut reached_raw_pointer = false; let arbitrary_self_types_enabled = @@ -412,9 +413,10 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { let reachable_via_deref = autoderef_via_deref.by_ref().map(|_| true).chain(std::iter::repeat(false)); - let mut autoderef_via_receiver = Autoderef::new(infcx, self.param_env, self_ty) - .include_raw_pointers() - .use_receiver_trait(); + let mut autoderef_via_receiver = + Autoderef::new(infcx, self.param_env, self_ty, self.receiver_span) + .include_raw_pointers() + .use_receiver_trait(); let steps = autoderef_via_receiver .by_ref() .zip(reachable_via_deref) @@ -1270,7 +1272,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { let InferOk { value: self_ty, obligations: instantiate_self_ty_obligations } = self .infcx() .instantiate_query_response_and_region_obligations( - &ObligationCause::new(), + &ObligationCause::new(self.ctx.receiver_span), self.param_env(), self.orig_steps_var_values, &step.self_ty, @@ -1497,8 +1499,12 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { &self, trait_ref: TraitRef<'db>, ) -> SelectionResult<'db, Selection<'db>> { - let obligation = - Obligation::new(self.interner(), ObligationCause::new(), self.param_env(), trait_ref); + let obligation = Obligation::new( + self.interner(), + ObligationCause::new(self.ctx.call_span), + self.param_env(), + trait_ref, + ); self.infcx().select(&obligation) } @@ -1525,7 +1531,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { // up with the `self` parameter of the method. let _ = self .infcx() - .at(&ObligationCause::dummy(), self.param_env()) + .at(&ObligationCause::new(self.ctx.call_span), self.param_env()) .sup(xform_self_ty, self_ty); match self.select_trait_candidate(trait_ref) { Ok(Some(ImplSource::UserDefined(ref impl_data))) => { @@ -1556,7 +1562,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { ) -> ProbeResult { self.infcx().probe(|_| { let mut result = ProbeResult::Match; - let cause = &ObligationCause::new(); + let cause = &ObligationCause::new(self.ctx.call_span); let mut ocx = ObligationCtxt::new(self.infcx()); // Subtle: we're not *really* instantiating the current self type while @@ -1600,7 +1606,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { let impl_bounds = GenericPredicates::query_all(self.db(), impl_def_id.into()); let impl_bounds = clauses_as_obligations( impl_bounds.iter_instantiated(self.interner(), impl_args.as_slice()), - ObligationCause::new(), + ObligationCause::new(self.ctx.call_span), self.param_env(), ); // Convert the bounds into obligations. @@ -1805,7 +1811,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { // reachable. In this case we don't care about opaque // types there. let Ok(ok) = self.infcx().instantiate_query_response_and_region_obligations( - &ObligationCause::new(), + &ObligationCause::new(self.ctx.receiver_span), self.param_env(), self.orig_steps_var_values, &step.self_ty, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index 523c94b36fc9f..12a6652bf7c30 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -27,13 +27,6 @@ use crate::{ use super::InferCtxt; /// The reason why we incurred this obligation; used for error reporting. -/// -/// Non-misc `ObligationCauseCode`s are stored on the heap. This gives the -/// best trade-off between keeping the type small (which makes copies cheaper) -/// while not doing too many heap allocations. -/// -/// We do not want to intern this as there are a lot of obligation causes which -/// only live for a short period of time. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ObligationCause { span: Span, @@ -41,23 +34,13 @@ pub struct ObligationCause { impl ObligationCause { #[inline] - pub fn new() -> ObligationCause { - ObligationCause { span: Span::Dummy } - } - - #[inline] - pub fn with_span(span: Span) -> ObligationCause { - ObligationCause { span } + pub fn new>(span: S) -> ObligationCause { + ObligationCause { span: span.into() } } #[inline] pub fn dummy() -> ObligationCause { - ObligationCause::new() - } - - #[inline] - pub fn misc() -> ObligationCause { - ObligationCause::new() + ObligationCause::new(Span::Dummy) } #[inline] @@ -66,13 +49,6 @@ impl ObligationCause { } } -impl Default for ObligationCause { - #[inline] - fn default() -> Self { - Self::new() - } -} - /// An `Obligation` represents some trait reference (e.g., `i32: Eq`) for /// which the "impl_source" must be found. The process of finding an "impl_source" is /// called "resolving" the `Obligation`. This process consists of @@ -128,11 +104,11 @@ impl<'db> Elaboratable> for PredicateObligation<'db> { fn child_with_derived_cause( &self, clause: Clause<'db>, - _span: Span, + span: Span, _parent_trait_pred: PolyTraitPredicate<'db>, _index: usize, ) -> Self { - let cause = ObligationCause::new(); + let cause = ObligationCause::new(span); Obligation { cause, param_env: self.param_env, diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 3259abb536134..6a0996fae6735 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -851,6 +851,10 @@ impl<'db> AnyDiagnostic<'db> { hir_ty::Span::ExprId(idx) => expr_syntax(idx)?.map(|it| it.wrap_right()), hir_ty::Span::PatId(idx) => pat_syntax(idx)?.map(|it| it.wrap_right()), hir_ty::Span::TypeRefId(idx) => type_syntax(idx)?.map(|it| it.wrap_left()), + hir_ty::Span::BindingId(idx) => { + pat_syntax(source_map.patterns_for_binding(idx)[0])? + .map(|it| it.wrap_right()) + } hir_ty::Span::Dummy => unreachable!( "should never create TypeMustBeKnown diagnostic for dummy spans" ), diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index 179f9dcec5f51..f6293e35d0c37 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -205,6 +205,7 @@ trait Foo { fn method(&self, _arg: usize) {} } fn f() { let x; + // ^ error: type annotations needed x.method(); } "#, From 1a63148ffd451756dcbdfee6671e9d0a4bb1dd78 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 26 Apr 2026 22:45:44 +0300 Subject: [PATCH 173/289] Diagnose trait errors I.e. type does not implement trait. There are a lot of other solver errors to be emitted, and the error is pretty simple currently, but this is a huge step! --- .../rust-analyzer/crates/hir-def/src/attrs.rs | 8 + .../src/expr_store/lower/format_args.rs | 48 +- .../crates/hir-def/src/hir/format_args.rs | 9 +- .../crates/hir-expand/src/files.rs | 10 + .../crates/hir-ty/src/diagnostics/expr.rs | 14 +- .../crates/hir-ty/src/display.rs | 21 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 86 ++- .../crates/hir-ty/src/infer/coerce.rs | 17 +- .../crates/hir-ty/src/infer/expr.rs | 20 +- .../crates/hir-ty/src/infer/pat.rs | 7 +- .../crates/hir-ty/src/infer/unify.rs | 40 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 1 + .../hir-ty/src/method_resolution/confirm.rs | 7 +- .../crates/hir-ty/src/mir/lower.rs | 11 +- .../crates/hir-ty/src/next_solver/binder.rs | 26 +- .../crates/hir-ty/src/next_solver/consts.rs | 39 +- .../crates/hir-ty/src/next_solver/fulfill.rs | 2 +- .../hir-ty/src/next_solver/infer/errors.rs | 704 ++++++++++++++++++ .../hir-ty/src/next_solver/infer/mod.rs | 1 + .../hir-ty/src/next_solver/infer/traits.rs | 3 +- .../crates/hir-ty/src/next_solver/inspect.rs | 6 + .../hir-ty/src/next_solver/normalize.rs | 1 - .../hir-ty/src/next_solver/predicate.rs | 1 + .../crates/hir-ty/src/solver_errors.rs | 90 +++ .../rust-analyzer/crates/hir-ty/src/tests.rs | 43 +- .../crates/hir-ty/src/tests/regression.rs | 1 - .../crates/hir/src/diagnostics.rs | 121 ++- .../rust-analyzer/crates/hir/src/display.rs | 10 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 64 +- .../src/handlers/invalid_cast.rs | 2 +- .../src/handlers/missing_unsafe.rs | 2 +- .../src/handlers/remove_trailing_return.rs | 2 +- .../src/handlers/type_mismatch.rs | 25 +- .../src/handlers/type_must_be_known.rs | 4 +- .../src/handlers/undeclared_label.rs | 8 +- .../src/handlers/unimplemented_trait.rs | 53 ++ .../crates/ide-diagnostics/src/lib.rs | 2 + .../crates/ide-diagnostics/src/tests.rs | 4 +- .../crates/ide/src/references.rs | 3 +- .../rust-analyzer/src/cli/analysis_stats.rs | 45 +- .../crates/test-utils/src/minicore.rs | 56 ++ 41 files changed, 1395 insertions(+), 222 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/errors.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/solver_errors.rs create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs 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 7757d537e6501..352c98a76adf5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -265,6 +265,12 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow< } _ => {} }, + "diagnostic" => match &*second_segment { + "do_not_recommend" => { + attr_flags.insert(AttrFlags::DIAGNOSTIC_DO_NOT_RECOMMEND) + } + _ => {} + }, _ => {} }, } @@ -339,6 +345,8 @@ bitflags::bitflags! { const PREFER_UNDERSCORE_IMPORT = 1 << 49; const IS_MUST_USE = 1 << 50; + + const DIAGNOSTIC_DO_NOT_RECOMMEND = 1 << 51; } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs index beb126717314f..b058ad6d9ab8b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs @@ -30,12 +30,14 @@ impl<'db> ExprCollector<'db> { ) -> ExprId { let mut args = FormatArgumentsCollector::default(); f.args().for_each(|arg| { + let expr = arg.expr(); args.add(FormatArgument { kind: match arg.arg_name() { Some(name) => FormatArgumentKind::Named(Name::new_root(name.name().text())), None => FormatArgumentKind::Normal, }, - expr: self.collect_expr_opt(arg.expr()), + syntax: expr.as_ref().map(AstPtr::new), + expr: self.collect_expr_opt(expr), }); }); let template = f.template(); @@ -53,8 +55,10 @@ impl<'db> ExprCollector<'db> { Some(((s, is_direct_literal), template)) => { let call_ctx = SyntaxContext::root(self.def_map.edition()); let hygiene = self.hygiene_id_for(s.syntax().text_range()); + let template_ptr = AstPtr::new(&template); let fmt = format_args::parse( &s, + template_ptr, fmt_snippet, args, is_direct_literal, @@ -65,10 +69,7 @@ impl<'db> ExprCollector<'db> { .template_map .get_or_insert_with(Default::default) .implicit_capture_to_source - .insert( - expr_id, - self.expander.in_file((AstPtr::new(&template), range)), - ); + .insert(expr_id, self.expander.in_file((template_ptr, range))); } if !hygiene.is_root() { self.store.ident_hygiene.insert(expr_id.into(), hygiene); @@ -340,7 +341,8 @@ impl<'db> ExprCollector<'db> { expr: args_ident_expr, name: Name::new_tuple_field(arg_index), }); - self.make_argument(arg, ty) + let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax); + self.make_argument(arg_ptr, arg, ty) }) .collect(); let args = @@ -557,7 +559,8 @@ impl<'db> ExprCollector<'db> { rawness: Rawness::Ref, mutability: Mutability::Shared, }); - self.make_argument(arg, ty) + let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax); + self.make_argument(arg_ptr, arg, ty) }) .collect(); let array = @@ -649,7 +652,8 @@ impl<'db> ExprCollector<'db> { rawness: Rawness::Ref, mutability: Mutability::Shared, }); - self.make_argument(ref_arg, ty) + let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax); + self.make_argument(arg_ptr, ref_arg, ty) }) .collect(); let args = @@ -716,7 +720,8 @@ impl<'db> ExprCollector<'db> { expr: args_ident_expr, name: Name::new_tuple_field(arg_index), }); - self.make_argument(arg, ty) + let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax); + self.make_argument(arg_ptr, arg, ty) }) .collect(); let array = @@ -976,11 +981,16 @@ impl<'db> ExprCollector<'db> { /// ```text /// ::new_…(arg) /// ``` - fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId { + fn make_argument( + &mut self, + arg_ptr: Option>, + arg: ExprId, + ty: ArgumentType, + ) -> ExprId { use ArgumentType::*; use FormatTrait::*; - let new_fn = self.ty_rel_lang_path_desugared_expr( + let new_fn = self.ty_rel_lang_path( self.lang_items().FormatArgument, match ty { Format(Display) => sym::new_display, @@ -995,6 +1005,22 @@ impl<'db> ExprCollector<'db> { Usize => sym::from_usize, }, ); + let new_fn = match new_fn { + Some(new_fn) => { + let new_fn = self.store.exprs.alloc(Expr::Path(new_fn)); + if let Some(arg_ptr) = arg_ptr { + // Trait errors (the argument does not implement the expected fmt trait) will show + // on this path, so to not end up with synthetic syntax we insert this mapping. We + // don't want to insert the other way's mapping in order to not override the source + // for the argument. + self.store + .expr_map_back + .insert(new_fn, self.expander.in_file(arg_ptr.wrap_left())); + } + new_fn + } + None => self.missing_expr(), + }; self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) }) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs index 271484da7b9a2..366857f233168 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs @@ -7,7 +7,7 @@ use rustc_parse_format as parse; use span::SyntaxContext; use stdx::TupleExt; use syntax::{ - TextRange, + AstPtr, TextRange, ast::{self, IsString}, }; @@ -146,6 +146,7 @@ pub enum FormatCount { pub struct FormatArgument { pub kind: FormatArgumentKind, pub expr: ExprId, + pub syntax: Option>, } #[derive(Clone, PartialEq, Eq, Debug)] @@ -171,6 +172,7 @@ use PositionUsedAs::*; #[allow(clippy::unnecessary_lazy_evaluations)] pub(crate) fn parse( s: &ast::String, + string_ptr: AstPtr, fmt_snippet: Option, mut args: FormatArgumentsCollector, is_direct_literal: bool, @@ -273,6 +275,11 @@ pub(crate) fn parse( // FIXME: This is problematic, we might want to synthesize a dummy // expression proper and/or desugar these. expr: synth(name, span), + // FIXME: This will lead us to show failed trait bounds (e.g. `T: Display`) + // on the whole template string for implicit arguments instead of just their name. + // Fixing this is hard since we don't have an `AstPtr` for the arg, and it's + // only part of an expression. + syntax: Some(string_ptr), })) } } 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 09a5c9326e058..d64090f9133d3 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs @@ -128,6 +128,16 @@ impl ErasedAstId { } } +impl InFileWrapper> { + #[inline] + pub fn upcast(self) -> InFileWrapper> + where + N: Into, + { + self.map(|it| it.upcast()) + } +} + impl InFileWrapper { pub fn new(file_id: FileKind, value: T) -> Self { Self { file_id, value } 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 be29926a380d9..768b185ae7bc9 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 @@ -182,7 +182,7 @@ impl<'db> ExprValidator<'db> { } // Check that the number of arguments matches the number of parameters. - if self.infer.expr_type_mismatches().next().is_some() { + if self.infer.exprs_have_type_mismatches() { // FIXME: Due to shortcomings in the current type system implementation, only emit // this diagnostic if there are no type mismatches in the containing function. } else if let Expr::MethodCall { receiver, .. } = expr { @@ -355,7 +355,7 @@ impl<'db> ExprValidator<'db> { pat: PatId, initializer: Option, ) -> Option { - if self.infer.type_mismatch_for_pat(pat).is_some() { + if self.infer.pat_has_type_mismatch(pat) { return None; } let initializer = initializer?; @@ -683,13 +683,13 @@ pub fn record_pattern_missing_fields( fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResult) -> bool { fn walk(pat: PatId, body: &Body, infer: &InferenceResult, has_type_mismatches: &mut bool) { - match infer.type_mismatch_for_pat(pat) { - Some(_) => *has_type_mismatches = true, - None if *has_type_mismatches => (), - None => { + match infer.pat_has_type_mismatch(pat) { + true => *has_type_mismatches = true, + false if *has_type_mismatches => (), + false => { let pat = &body[pat]; if let Pat::ConstBlock(expr) | Pat::Lit(expr) = *pat { - *has_type_mismatches |= infer.type_mismatch_for_expr(expr).is_some(); + *has_type_mismatches |= infer.expr_has_type_mismatch(expr); if *has_type_mismatches { return; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 97693435fe0ca..fda572a02ba7f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -59,8 +59,8 @@ use crate::{ next_solver::{ AliasTy, Allocation, Clause, ClauseKind, Const, ConstKind, DbInterner, ExistentialPredicate, FnSig, GenericArg, GenericArgKind, GenericArgs, ParamEnv, PolyFnSig, - Region, SolverDefId, StoredEarlyBinder, StoredTy, Term, TermKind, TraitRef, Ty, TyKind, - TypingMode, ValTree, + Region, SolverDefId, StoredEarlyBinder, StoredTy, Term, TermKind, TraitPredicate, TraitRef, + Ty, TyKind, TypingMode, ValTree, abi::Safety, infer::{DbInternerInferExt, traits::ObligationCause}, }, @@ -2275,6 +2275,23 @@ impl<'db> HirDisplay<'db> for TraitRef<'db> { } } +impl<'db> HirDisplay<'db> for TraitPredicate<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { + self.self_ty().hir_fmt(f)?; + f.write_str(": ")?; + match self.polarity { + rustc_type_ir::PredicatePolarity::Positive => {} + rustc_type_ir::PredicatePolarity::Negative => f.write_char('!')?, + } + let trait_ = self.def_id().0; + f.start_location_link(trait_.into()); + write!(f, "{}", TraitSignature::of(f.db, trait_).name.display(f.db, f.edition()))?; + f.end_location_link(); + let substs = &self.trait_ref.args[1..]; + hir_fmt_generic_args(f, substs, None, None) + } +} + impl<'db> HirDisplay<'db> for Region<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { match self.kind() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 83659dde17bec..4c860210a999a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -89,6 +89,7 @@ use crate::{ abi::Safety, infer::{InferCtxt, ObligationInspector, traits::ObligationCause}, }, + solver_errors::SolverDiagnostic, utils::TargetFeatureIsSafeInTarget, }; @@ -459,13 +460,13 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] expr: ExprId, }, -} - -/// A mismatch between an expected and an inferred type. -#[derive(Clone, PartialEq, Eq, Debug, Hash, TypeVisitable, TypeFoldable)] -pub struct TypeMismatch { - pub expected: StoredTy, - pub actual: StoredTy, + TypeMismatch { + #[type_visitable(ignore)] + node: ExprOrPatId, + expected: StoredTy, + found: StoredTy, + }, + SolverDiagnostic(SolverDiagnostic), } /// Represents coercing a value to a different type of value. @@ -685,7 +686,6 @@ pub struct InferenceResult { pub(crate) type_of_type_placeholder: FxHashMap, pub(crate) type_of_opaque: FxHashMap, - pub(crate) type_mismatches: Option>>, /// Whether there are any type-mismatching errors in the result. // FIXME: This isn't as useful as initially thought due to us falling back placeholders to // `TyKind::Error`. @@ -693,6 +693,8 @@ pub struct InferenceResult { pub(crate) has_errors: bool, /// During inference this field is empty and [`InferenceContext::diagnostics`] is filled instead. diagnostics: ThinVec, + // FIXME: Remove this, change it to be in `InferenceContext`: + nodes_with_type_mismatches: Option>>, /// Interned `Error` type to return references to. // FIXME: Remove this. @@ -986,12 +988,12 @@ impl InferenceResult { assoc_resolutions: Default::default(), tuple_field_access_types: Default::default(), diagnostics: Default::default(), + nodes_with_type_mismatches: Default::default(), type_of_expr: Default::default(), type_of_pat: Default::default(), type_of_binding: Default::default(), type_of_type_placeholder: Default::default(), type_of_opaque: Default::default(), - type_mismatches: Default::default(), skipped_ref_pats: Default::default(), has_errors: Default::default(), error_ty: error_ty.store(), @@ -1042,26 +1044,22 @@ impl InferenceResult { ExprOrPatId::PatId(id) => self.assoc_resolutions_for_pat(id), } } - pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> { - self.type_mismatches.as_deref()?.get(&expr.into()) + pub fn expr_or_pat_has_type_mismatch(&self, node: ExprOrPatId) -> bool { + self.nodes_with_type_mismatches.as_ref().is_some_and(|it| it.contains(&node)) } - pub fn type_mismatch_for_pat(&self, pat: PatId) -> Option<&TypeMismatch> { - self.type_mismatches.as_deref()?.get(&pat.into()) + pub fn expr_has_type_mismatch(&self, expr: ExprId) -> bool { + self.expr_or_pat_has_type_mismatch(expr.into()) } - pub fn type_mismatches(&self) -> impl Iterator { - self.type_mismatches - .as_deref() - .into_iter() - .flatten() - .map(|(expr_or_pat, mismatch)| (*expr_or_pat, mismatch)) - } - pub fn expr_type_mismatches(&self) -> impl Iterator { - self.type_mismatches.as_deref().into_iter().flatten().filter_map( - |(expr_or_pat, mismatch)| match *expr_or_pat { - ExprOrPatId::ExprId(expr) => Some((expr, mismatch)), - _ => None, - }, - ) + pub fn pat_has_type_mismatch(&self, pat: PatId) -> bool { + self.expr_or_pat_has_type_mismatch(pat.into()) + } + pub fn exprs_have_type_mismatches(&self) -> bool { + self.nodes_with_type_mismatches + .as_ref() + .is_some_and(|it| it.iter().any(|node| node.is_expr())) + } + pub fn has_type_mismatches(&self) -> bool { + self.nodes_with_type_mismatches.is_some() } pub fn placeholder_types<'db>(&self) -> impl Iterator)> { self.type_of_type_placeholder.iter().map(|(&type_ref, ty)| (type_ref, ty.as_ref())) @@ -1408,7 +1406,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { type_of_type_placeholder, type_of_opaque, skipped_ref_pats, - type_mismatches, closures_data, has_errors, error_ty: _, @@ -1418,6 +1415,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { tuple_field_access_types, coercion_casts: _, diagnostics: result_diagnostics, + nodes_with_type_mismatches, } = &mut result; let mut resolver = WriteBackCtxt::new(table, diagnostics, vars_emitted_type_must_be_known_for); @@ -1441,12 +1439,9 @@ impl<'body, 'db> InferenceContext<'body, 'db> { type_of_type_placeholder.shrink_to_fit(); type_of_opaque.shrink_to_fit(); - if let Some(type_mismatches) = type_mismatches { + if let Some(nodes_with_type_mismatches) = nodes_with_type_mismatches { *has_errors = true; - for mismatch in type_mismatches.values_mut() { - resolver.resolve_type_mismatch(mismatch); - } - type_mismatches.shrink_to_fit(); + nodes_with_type_mismatches.shrink_to_fit(); } for (_, subst) in method_resolutions.values_mut() { resolver.resolve_completely(subst); @@ -1938,6 +1933,21 @@ impl<'body, 'db> InferenceContext<'body, 'db> { if result.is_ty_var() { self.type_must_be_known_at_this_point(node, ty) } else { result } } + pub(crate) fn emit_type_mismatch( + &mut self, + node: ExprOrPatId, + expected: Ty<'db>, + found: Ty<'db>, + ) { + if self.result.nodes_with_type_mismatches.get_or_insert_default().insert(node) { + self.diagnostics.push(InferenceDiagnostic::TypeMismatch { + node, + expected: expected.store(), + found: found.store(), + }); + } + } + fn demand_eqtype( &mut self, id: ExprOrPatId, @@ -1950,10 +1960,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { .eq(expected, actual) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); if result.is_err() { - self.result - .type_mismatches - .get_or_insert_default() - .insert(id, TypeMismatch { expected: expected.store(), actual: actual.store() }); + self.emit_type_mismatch(id, expected, actual); } result.map_err(drop) } @@ -1983,10 +1990,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { .sup(expected, actual) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); if result.is_err() { - self.result - .type_mismatches - .get_or_insert_default() - .insert(id, TypeMismatch { expected: expected.store(), actual: actual.store() }); + self.emit_type_mismatch(id, expected, actual); } result.map_err(drop) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 15265b9104102..e5767c2d46a8b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -55,9 +55,7 @@ use crate::{ Adjust, Adjustment, AutoBorrow, ParamEnvAndCrate, PointerCast, Span, TargetFeatures, autoderef::Autoderef, db::{HirDatabase, InternedClosure, InternedClosureId}, - infer::{ - AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch, expr::ExprIsRead, - }, + infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext, expr::ExprIsRead}, next_solver::{ Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, CallableIdWrapper, Canonical, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, @@ -1393,14 +1391,11 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { self.final_ty = Some(icx.types.types.error); - icx.result.type_mismatches.get_or_insert_default().insert( - expression.into(), - if label_expression_as_expected { - TypeMismatch { expected: found.store(), actual: expected.store() } - } else { - TypeMismatch { expected: expected.store(), actual: found.store() } - }, - ); + if label_expression_as_expected { + icx.emit_type_mismatch(expression.into(), found, expected); + } else { + icx.emit_type_mismatch(expression.into(), expected, found); + } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index a03e891114854..680800244bf9e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -42,7 +42,7 @@ use crate::{ }; use super::{ - BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, + BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, cast::CastCheck, find_breakable, }; @@ -117,10 +117,7 @@ impl<'db> InferenceContext<'_, 'db> { match self.coerce(expr, ty, target, AllowTwoPhase::No, is_read) { Ok(res) => res, Err(_) => { - self.result.type_mismatches.get_or_insert_default().insert( - expr.into(), - TypeMismatch { expected: target.store(), actual: ty.store() }, - ); + self.emit_type_mismatch(expr.into(), target, ty); target } } @@ -1592,13 +1589,7 @@ impl<'db> InferenceContext<'_, 'db> { ) .is_err() { - this.result.type_mismatches.get_or_insert_default().insert( - expr.into(), - TypeMismatch { - expected: t.store(), - actual: this.types.types.unit.store(), - }, - ); + this.emit_type_mismatch(expr.into(), t, this.types.types.unit); } t } else { @@ -2161,10 +2152,7 @@ impl<'db> InferenceContext<'_, 'db> { && args_count_matches { // Don't report type mismatches if there is a mismatch in args count. - self.result.type_mismatches.get_or_insert_default().insert( - (*arg).into(), - TypeMismatch { expected: expected.store(), actual: found.store() }, - ); + self.emit_type_mismatch((*arg).into(), expected, found); } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 97165b9f07343..39fb0b8ab8e0e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -29,7 +29,7 @@ use crate::{ BindingMode, InferenceDiagnostic, Span, infer::{ AllowTwoPhase, ByRef, Expectation, InferenceContext, PatAdjust, PatAdjustment, - TypeMismatch, expr::ExprIsRead, + expr::ExprIsRead, }, next_solver::{ Const, TraitRef, Ty, TyKind, Tys, @@ -1695,10 +1695,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; match self.coerce(expr, expected, lhs_ty, AllowTwoPhase::No, expr_is_read) { Ok(ty) => ty, Err(_) => { - self.result.type_mismatches.get_or_insert_default().insert( - expr.into(), - TypeMismatch { expected: expected.store(), actual: lhs_ty.store() }, - ); + self.emit_type_mismatch(expr.into(), expected, lhs_ty); // `rhs_ty` is returned so no further type mismatches are // reported because of this mismatch. expected diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index c089335708698..6a34c5b8e5b6b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -11,9 +11,10 @@ use rustc_type_ir::{ solve::Certainty, }; use smallvec::SmallVec; +use thin_vec::ThinVec; use crate::{ - Span, + InferenceDiagnostic, Span, db::HirDatabase, next_solver::{ Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, @@ -29,6 +30,7 @@ use crate::{ inspect::{InspectConfig, InspectGoal, ProofTreeVisitor}, obligation_ctxt::ObligationCtxt, }, + solver_errors::SolverDiagnostic, traits::ParamEnvAndCrate, }; @@ -133,6 +135,7 @@ pub(crate) struct InferenceTable<'db> { pub(crate) infer_ctxt: InferCtxt<'db>, pub(super) fulfillment_cx: FulfillmentCtxt<'db>, pub(super) diverging_type_vars: FxHashSet>, + trait_errors: Vec>, } impl<'db> InferenceTable<'db> { @@ -153,6 +156,7 @@ impl<'db> InferenceTable<'db> { fulfillment_cx: FulfillmentCtxt::new(&infer_ctxt), infer_ctxt, diverging_type_vars: FxHashSet::default(), + trait_errors: Vec::new(), } } @@ -328,7 +332,10 @@ impl<'db> InferenceTable<'db> { .structurally_normalize_ty(ty, &mut self.fulfillment_cx); match result { Ok(normalized_ty) => normalized_ty, - Err(_errors) => Ty::new_error(self.interner(), ErrorGuaranteed), + Err(errors) => { + self.trait_errors.extend(errors); + Ty::new_error(self.interner(), ErrorGuaranteed) + } } } else { self.resolve_vars_with_obligations(ty) @@ -376,7 +383,8 @@ impl<'db> InferenceTable<'db> { } pub(crate) fn select_obligations_where_possible(&mut self) { - self.fulfillment_cx.try_evaluate_obligations(&self.infer_ctxt); + let errors = self.fulfillment_cx.try_evaluate_obligations(&self.infer_ctxt); + self.trait_errors.extend(errors); } pub(super) fn register_predicate(&mut self, obligation: PredicateObligation<'db>) { @@ -433,6 +441,16 @@ impl<'db> InferenceTable<'db> { // to normalize before inspecting the `TyKind`. self.try_structurally_resolve_type(Span::Dummy, ty) } + + fn emit_trait_errors(&mut self, diagnostics: &mut ThinVec) { + diagnostics.extend(std::mem::take(&mut self.trait_errors).into_iter().filter_map( + |error| { + let error = error.into_fulfillment_error(&self.infer_ctxt); + SolverDiagnostic::from_fulfillment_error(&error) + .map(InferenceDiagnostic::SolverDiagnostic) + }, + )); + } } impl fmt::Debug for InferenceTable<'_> { @@ -455,7 +473,7 @@ pub(super) mod resolve_completely { use crate::{ InferenceDiagnostic, Span, - infer::{TypeMismatch, unify::InferenceTable}, + infer::unify::InferenceTable, next_solver::{ Const, ConstKind, DbInterner, DefaultAny, GenericArg, Goal, Predicate, Region, Term, TermKind, Ty, TyKind, @@ -505,14 +523,6 @@ pub(super) mod resolve_completely { } } - pub(crate) fn resolve_type_mismatch(&mut self, value_ref: &mut TypeMismatch) { - // Ignore diagnostics from type mismatches, which are diagnostics themselves. - // FIXME: We should make type mismatches just regular diagnostics. - let prev_diagnostics_len = self.diagnostics.len(); - self.resolve_completely(value_ref); - self.diagnostics.truncate(prev_diagnostics_len); - } - pub(crate) fn resolve_completely(&mut self, value_ref: &mut T) where T: TypeFoldable>, @@ -538,6 +548,8 @@ pub(super) mod resolve_completely { pub(crate) fn resolve_diagnostics(mut self) -> (ThinVec, bool) { let has_errors = self.has_errors; + self.table.emit_trait_errors(&mut self.diagnostics); + // Ignore diagnostics made from resolving diagnostics. let mut diagnostics = std::mem::take(&mut self.diagnostics); diagnostics.retain_mut(|diagnostic| { @@ -706,8 +718,8 @@ pub(super) mod resolve_completely { self.nested_goals.extend(goals); value } - Err(_errors) => { - // FIXME: Report the error. + Err(errors) => { + self.ctx.table.trait_errors.extend(errors); value } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index c6075b03dabe7..5cb2a3d804d63 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -50,6 +50,7 @@ pub mod layout; pub mod method_resolution; pub mod mir; pub mod primitive; +pub mod solver_errors; pub mod traits; pub mod upvars; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs index 6601ff7a013e3..821d737cf976e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs @@ -18,7 +18,7 @@ use crate::{ Adjust, Adjustment, AutoBorrow, IncorrectGenericsLenKind, InferenceDiagnostic, LifetimeElisionKind, PointerCast, Span, db::HirDatabase, - infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch}, + infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext}, lower::{ GenericPredicates, path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, @@ -492,10 +492,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { } Err(_) => { if self.ctx.features.arbitrary_self_types { - self.ctx.result.type_mismatches.get_or_insert_default().insert( - self.call_expr.into(), - TypeMismatch { expected: method_self_ty.store(), actual: self_ty.store() }, - ); + self.ctx.emit_type_mismatch(self.call_expr.into(), method_self_ty, self_ty); } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index b2a7eaa6746e0..8f8f557716d2c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -33,7 +33,7 @@ use crate::{ display::{DisplayTarget, HirDisplay, hir_display_with_store}, generics::generics, infer::{ - CaptureSourceStack, CapturedPlace, TypeMismatch, UpvarCapture, + CaptureSourceStack, CapturedPlace, UpvarCapture, cast::CastTy, closure::analysis::expr_use_visitor::{ Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind, @@ -110,7 +110,6 @@ pub enum MirLowerError { UnresolvedField, UnsizedTemporary(StoredTy), MissingFunctionDefinition(DefWithBodyId, ExprId), - TypeMismatch(TypeMismatch), HasErrors, /// This should never happen. Type mismatch should catch everything. TypeError(&'static str), @@ -197,12 +196,6 @@ impl MirLowerError { )?; } MirLowerError::HasErrors => writeln!(f, "Type inference result contains errors")?, - MirLowerError::TypeMismatch(e) => writeln!( - f, - "Type mismatch: Expected {}, found {}", - e.expected.as_ref().display(db, display_target), - e.actual.as_ref().display(db, display_target), - )?, MirLowerError::GenericArgNotProvided(id, subst) => { let param_name = match *id { GenericParamId::TypeParamId(id) => { @@ -2402,7 +2395,7 @@ pub fn lower_to_mir_with_store<'db>( self_param: Option<(BindingId, Ty<'db>)>, is_root: bool, ) -> Result<'db, MirBody> { - if infer.type_mismatches().next().is_some() || infer.is_erroneous() { + if infer.has_type_mismatches() || infer.is_erroneous() { return Err(MirLowerError::HasErrors); } let mut ctx = MirLowerCtx::new(db, owner, store, infer); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs index 3645f8096cfd8..84cfbf27671a2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs @@ -1,8 +1,11 @@ +use hir_def::TraitId; +use macros::{TypeFoldable, TypeVisitable}; + use crate::{ FnAbi, next_solver::{ - Binder, Clauses, EarlyBinder, FnSig, PolyFnSig, StoredBoundVarKinds, StoredClauses, - StoredTy, StoredTys, Ty, abi::Safety, + Binder, Clauses, DbInterner, EarlyBinder, FnSig, PolyFnSig, StoredBoundVarKinds, + StoredClauses, StoredGenericArgs, StoredTy, StoredTys, TraitRef, Ty, abi::Safety, }, }; @@ -81,3 +84,22 @@ impl StoredPolyFnSig { ) } } + +#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] +pub struct StoredTraitRef { + #[type_visitable(ignore)] + def_id: TraitId, + args: StoredGenericArgs, +} + +impl StoredTraitRef { + #[inline] + pub fn new(trait_ref: TraitRef<'_>) -> Self { + Self { def_id: trait_ref.def_id.0, args: trait_ref.args.store() } + } + + #[inline] + pub fn get<'db>(&'db self, interner: DbInterner<'db>) -> TraitRef<'db> { + TraitRef::new_from_args(interner, self.def_id.into(), self.args.as_ref()) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index fa90e3d8a004b..2df9a5259ddfb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -11,13 +11,14 @@ use rustc_ast_ir::visit::VisitorResult; use rustc_type_ir::{ BoundVar, BoundVarIndexKind, ConstVid, DebruijnIndex, FlagComputation, Flags, GenericTypeVisitable, InferConst, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, - TypeVisitable, WithCachedTypeInfo, inherent::IntoKind, relate::Relate, + TypeVisitable, TypeVisitableExt, WithCachedTypeInfo, inherent::IntoKind, relate::Relate, }; use crate::{ ParamEnvAndCrate, next_solver::{ - AllocationData, impl_foldable_for_interned_slice, impl_stored_interned, interned_slice, + AllocationData, ClauseKind, ParamEnv, impl_foldable_for_interned_slice, + impl_stored_interned, interned_slice, }, }; @@ -146,6 +147,40 @@ impl std::fmt::Debug for ParamConst { } } +impl ParamConst { + pub fn find_const_ty_from_env<'db>(self, env: ParamEnv<'db>) -> Ty<'db> { + let mut candidates = env.clauses.iter().filter_map(|clause| { + // `ConstArgHasType` are never desugared to be higher ranked. + match clause.kind().skip_binder() { + ClauseKind::ConstArgHasType(param_ct, ty) => { + assert!(!(param_ct, ty).has_escaping_bound_vars()); + + match param_ct.kind() { + ConstKind::Param(param_ct) if param_ct.index == self.index => Some(ty), + _ => None, + } + } + _ => None, + } + }); + + // N.B. it may be tempting to fix ICEs by making this function return + // `Option>` instead of `Ty<'db>`; however, this is generally + // considered to be a bandaid solution, since it hides more important + // underlying issues with how we construct generics and predicates of + // items. It's advised to fix the underlying issue rather than trying + // to modify this function. + let ty = candidates.next().unwrap_or_else(|| { + panic!("cannot find `{self:?}` in param-env: {env:#?}"); + }); + assert!( + candidates.next().is_none(), + "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}" + ); + ty + } +} + #[derive( Copy, Clone, Debug, Hash, PartialEq, Eq, TypeVisitable, TypeFoldable, GenericTypeVisitable, )] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index ba9cd39d448f4..1fb4cb2b43e77 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -330,7 +330,7 @@ impl<'db> TypeVisitor> for StalledOnCoroutines<'_, 'db> { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum NextSolverError<'db> { TrueError(PredicateObligation<'db>), Ambiguity(PredicateObligation<'db>), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/errors.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/errors.rs new file mode 100644 index 0000000000000..d10f70274cd81 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/errors.rs @@ -0,0 +1,704 @@ +use std::{fmt, ops::ControlFlow}; + +use hir_def::{GeneralConstId, attrs::AttrFlags}; +use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt}; +use rustc_type_ir::{ + AliasRelationDirection, AliasTermKind, PredicatePolarity, + error::ExpectedFound, + inherent::{IntoKind as _, Ty as _}, + solve::{CandidateSource, Certainty, GoalSource, MaybeCause, NoSolution}, +}; +use tracing::{instrument, trace}; + +use crate::{ + Span, + next_solver::{ + AliasTerm, AnyImplId, Binder, ClauseKind, Const, ConstKind, DbInterner, EarlyBinder, + ErrorGuaranteed, HostEffectPredicate, PolyTraitPredicate, PredicateKind, SolverContext, + Term, TraitPredicate, Ty, TyKind, TypeError, + fulfill::NextSolverError, + infer::{ + InferCtxt, + select::SelectionError, + traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations}, + }, + inspect::{self, ProofTreeVisitor}, + normalize::deeply_normalize_for_diagnostics, + }, +}; + +#[derive(Debug)] +pub struct FulfillmentError<'db> { + pub obligation: PredicateObligation<'db>, + pub code: FulfillmentErrorCode<'db>, + /// Diagnostics only: the 'root' obligation which resulted in + /// the failure to process `obligation`. This is the obligation + /// that was initially passed to `register_predicate_obligation` + pub root_obligation: PredicateObligation<'db>, +} + +impl<'db> FulfillmentError<'db> { + pub fn new( + obligation: PredicateObligation<'db>, + code: FulfillmentErrorCode<'db>, + root_obligation: PredicateObligation<'db>, + ) -> FulfillmentError<'db> { + FulfillmentError { obligation, code, root_obligation } + } + + pub fn is_true_error(&self) -> bool { + match self.code { + FulfillmentErrorCode::Select(_) + | FulfillmentErrorCode::Project(_) + | FulfillmentErrorCode::Subtype(_, _) + | FulfillmentErrorCode::ConstEquate(_, _) => true, + FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => { + false + } + } + } +} + +#[derive(Clone)] +pub enum FulfillmentErrorCode<'db> { + /// Inherently impossible to fulfill; this trait is implemented if and only + /// if it is already implemented. + Cycle(PredicateObligations<'db>), + Select(SelectionError<'db>), + Project(MismatchedProjectionTypes<'db>), + Subtype(ExpectedFound>, TypeError<'db>), // always comes from a SubtypePredicate + ConstEquate(ExpectedFound>, TypeError<'db>), + Ambiguity { + /// Overflow is only `Some(suggest_recursion_limit)` when using the next generation + /// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by + /// emitting a fatal error instead. + overflow: Option, + }, +} + +impl<'db> fmt::Debug for FulfillmentErrorCode<'db> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + FulfillmentErrorCode::Select(ref e) => write!(f, "{e:?}"), + FulfillmentErrorCode::Project(ref e) => write!(f, "{e:?}"), + FulfillmentErrorCode::Subtype(ref a, ref b) => { + write!(f, "CodeSubtypeError({a:?}, {b:?})") + } + FulfillmentErrorCode::ConstEquate(ref a, ref b) => { + write!(f, "CodeConstEquateError({a:?}, {b:?})") + } + FulfillmentErrorCode::Ambiguity { overflow: None } => write!(f, "Ambiguity"), + FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => { + write!(f, "Overflow({suggest_increasing_limit})") + } + FulfillmentErrorCode::Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"), + } + } +} + +#[derive(Clone)] +pub struct MismatchedProjectionTypes<'db> { + pub err: TypeError<'db>, +} + +impl<'db> fmt::Debug for MismatchedProjectionTypes<'db> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "MismatchedProjectionTypes({:?})", self.err) + } +} + +impl<'db> NextSolverError<'db> { + pub fn into_fulfillment_error(self, infcx: &InferCtxt<'db>) -> FulfillmentError<'db> { + match self { + NextSolverError::TrueError(obligation) => { + fulfillment_error_for_no_solution(infcx, obligation) + } + NextSolverError::Ambiguity(obligation) => { + fulfillment_error_for_stalled(infcx, obligation) + } + NextSolverError::Overflow(obligation) => { + fulfillment_error_for_overflow(infcx, obligation) + } + } + } +} + +fn fulfillment_error_for_no_solution<'db>( + infcx: &InferCtxt<'db>, + root_obligation: PredicateObligation<'db>, +) -> FulfillmentError<'db> { + let interner = infcx.interner; + let db = interner.db; + let obligation = find_best_leaf_obligation(infcx, &root_obligation, false); + + let code = match obligation.predicate.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::Projection(_)) => { + FulfillmentErrorCode::Project( + // FIXME: This could be a `Sorts` if the term is a type + MismatchedProjectionTypes { err: TypeError::Mismatch }, + ) + } + PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, expected_ty)) => { + let ct_ty = match ct.kind() { + ConstKind::Unevaluated(uv) => { + let ct_ty = match uv.def.0 { + GeneralConstId::ConstId(konst) => db.value_ty(konst.into()).unwrap(), + GeneralConstId::StaticId(statik) => db.value_ty(statik.into()).unwrap(), + // FIXME: Return the type of the const here. + GeneralConstId::AnonConstId(_) => { + EarlyBinder::bind(Ty::new_error(interner, ErrorGuaranteed)) + } + }; + ct_ty.instantiate(interner, uv.args) + } + ConstKind::Param(param_ct) => param_ct.find_const_ty_from_env(obligation.param_env), + ConstKind::Value(cv) => cv.ty, + kind => panic!( + "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}" + ), + }; + FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType { + ct, + ct_ty, + expected_ty, + }) + } + PredicateKind::NormalizesTo(..) => { + FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) + } + PredicateKind::AliasRelate(_, _, _) => { + FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) + } + PredicateKind::Subtype(pred) => { + let (a, b) = infcx.enter_forall_and_leak_universe( + obligation.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(a, b); + FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) + } + PredicateKind::Coerce(pred) => { + let (a, b) = infcx.enter_forall_and_leak_universe( + obligation.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(b, a); + FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) + } + PredicateKind::Clause(_) | PredicateKind::DynCompatible(_) | PredicateKind::Ambiguous => { + FulfillmentErrorCode::Select(SelectionError::Unimplemented) + } + PredicateKind::ConstEquate(..) => { + panic!("unexpected goal: {obligation:?}") + } + }; + + FulfillmentError { obligation, code, root_obligation } +} + +fn fulfillment_error_for_stalled<'db>( + infcx: &InferCtxt<'db>, + root_obligation: PredicateObligation<'db>, +) -> FulfillmentError<'db> { + let (code, refine_obligation) = infcx.probe(|_| { + match <&SolverContext<'db>>::from(infcx).evaluate_root_goal( + root_obligation.as_goal(), + root_obligation.cause.span(), + None, + ) { + Ok(GoalEvaluation { + certainty: Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }, + .. + }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true), + Ok(GoalEvaluation { + certainty: + Certainty::Maybe { + cause: + MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ }, + .. + }, + .. + }) => ( + FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, + // Don't look into overflows because we treat overflows weirdly anyways. + // We discard the inference constraints from overflowing goals, so + // recomputing the goal again during `find_best_leaf_obligation` may apply + // inference guidance that makes other goals go from ambig -> pass, for example. + // + // FIXME: We should probably just look into overflows here. + false, + ), + Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => { + panic!( + "did not expect successful goal when collecting ambiguity errors for `{:?}`", + infcx.resolve_vars_if_possible(root_obligation.predicate), + ) + } + Err(_) => { + panic!( + "did not expect selection error when collecting ambiguity errors for `{:?}`", + infcx.resolve_vars_if_possible(root_obligation.predicate), + ) + } + } + }); + + FulfillmentError { + obligation: if refine_obligation { + find_best_leaf_obligation(infcx, &root_obligation, true) + } else { + root_obligation.clone() + }, + code, + root_obligation, + } +} + +fn fulfillment_error_for_overflow<'db>( + infcx: &InferCtxt<'db>, + root_obligation: PredicateObligation<'db>, +) -> FulfillmentError<'db> { + FulfillmentError { + obligation: find_best_leaf_obligation(infcx, &root_obligation, true), + code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) }, + root_obligation, + } +} + +#[instrument(level = "debug", skip(infcx), ret)] +fn find_best_leaf_obligation<'db>( + infcx: &InferCtxt<'db>, + obligation: &PredicateObligation<'db>, + consider_ambiguities: bool, +) -> PredicateObligation<'db> { + let obligation = infcx.resolve_vars_if_possible(obligation.clone()); + // FIXME: we use a probe here as the `BestObligation` visitor does not + // check whether it uses candidates which get shadowed by where-bounds. + // + // We should probably fix the visitor to not do so instead, as this also + // means the leaf obligation may be incorrect. + let obligation = infcx + .fudge_inference_if_ok(|| { + infcx + .visit_proof_tree( + obligation.as_goal(), + &mut BestObligation { obligation: obligation.clone(), consider_ambiguities }, + ) + .break_value() + .ok_or(()) + // walk around the fact that the cause in `Obligation` is ignored by folders so that + // we can properly fudge the infer vars in cause code. + .map(|o| (o.cause, o)) + }) + .map(|(cause, o)| PredicateObligation { cause, ..o }) + .unwrap_or(obligation); + deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation) +} + +struct BestObligation<'db> { + obligation: PredicateObligation<'db>, + consider_ambiguities: bool, +} + +impl<'db> BestObligation<'db> { + fn with_derived_obligation( + &mut self, + derived_obligation: PredicateObligation<'db>, + and_then: impl FnOnce(&mut Self) -> >::Result, + ) -> >::Result { + let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation); + let res = and_then(self); + self.obligation = old_obligation; + res + } + + /// Filter out the candidates that aren't interesting to visit for the + /// purposes of reporting errors. For ambiguities, we only consider + /// candidates that may hold. For errors, we only consider candidates that + /// *don't* hold and which have impl-where clauses that also don't hold. + fn non_trivial_candidates<'a>( + &self, + goal: &'a inspect::InspectGoal<'a, 'db>, + ) -> Vec> { + let mut candidates = goal.candidates(); + match self.consider_ambiguities { + true => { + // If we have an ambiguous obligation, we must consider *all* candidates + // that hold, or else we may guide inference causing other goals to go + // from ambig -> pass/fail. + candidates.retain(|candidate| candidate.result().is_ok()); + } + false => { + // We always handle rigid alias candidates separately as we may not add them for + // aliases whose trait bound doesn't hold. + candidates.retain(|c| !matches!(c.kind(), inspect::ProbeKind::RigidAlias { .. })); + // If we have >1 candidate, one may still be due to "boring" reasons, like + // an alias-relate that failed to hold when deeply evaluated. We really + // don't care about reasons like this. + if candidates.len() > 1 { + candidates.retain(|candidate| { + goal.infcx().probe(|_| { + candidate.instantiate_nested_goals(self.span()).iter().any( + |nested_goal| { + matches!( + nested_goal.source(), + GoalSource::ImplWhereBound + | GoalSource::AliasBoundConstCondition + | GoalSource::AliasWellFormed + ) && nested_goal.result().is_err() + }, + ) + }) + }); + } + } + } + + candidates + } + + /// HACK: We walk the nested obligations for a well-formed arg manually, + /// since there's nontrivial logic in `wf.rs` to set up an obligation cause. + /// Ideally we'd be able to track this better. + fn visit_well_formed_goal( + &mut self, + candidate: &inspect::InspectCandidate<'_, 'db>, + term: Term<'db>, + ) -> ControlFlow> { + let _ = (candidate, term); + // FIXME: rustc does this, but we don't process WF obligations yet: + // let infcx = candidate.goal().infcx(); + // let param_env = candidate.goal().goal().param_env; + // let body_id = self.obligation.cause.body_id; + + // for obligation in wf::unnormalized_obligations(infcx, param_env, term, self.span(), body_id) + // .into_iter() + // .flatten() + // { + // let nested_goal = candidate.instantiate_proof_tree_for_nested_goal( + // GoalSource::Misc, + // obligation.as_goal(), + // self.span(), + // ); + // // Skip nested goals that aren't the *reason* for our goal's failure. + // match (self.consider_ambiguities, nested_goal.result()) { + // (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) + // | (false, Err(_)) => {} + // _ => continue, + // } + + // self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; + // } + + ControlFlow::Break(self.obligation.clone()) + } + + /// If a normalization of an associated item or a trait goal fails without trying any + /// candidates it's likely that normalizing its self type failed. We manually detect + /// such cases here. + fn detect_error_in_self_ty_normalization( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + self_ty: Ty<'db>, + ) -> ControlFlow> { + assert!(!self.consider_ambiguities); + let interner = goal.infcx().interner; + if let TyKind::Alias(..) = self_ty.kind() { + let infer_term = goal.infcx().next_ty_var(self.obligation.cause.span()); + let pred = PredicateKind::AliasRelate( + self_ty.into(), + infer_term.into(), + AliasRelationDirection::Equate, + ); + let obligation = + Obligation::new(interner, self.obligation.cause, goal.goal().param_env, pred); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, pred), + goal.depth() + 1, + this, + ) + }) + } else { + ControlFlow::Continue(()) + } + } + + /// When a higher-ranked projection goal fails, check that the corresponding + /// higher-ranked trait goal holds or not. This is because the process of + /// instantiating and then re-canonicalizing the binder of the projection goal + /// forces us to be unable to see that the leak check failed in the nested + /// `NormalizesTo` goal, so we don't fall back to the rigid projection check + /// that should catch when a projection goal fails due to an unsatisfied trait + /// goal. + fn detect_trait_error_in_higher_ranked_projection( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + ) -> ControlFlow> { + let interner = goal.infcx().interner; + if let Some(projection_clause) = goal.goal().predicate.as_projection_clause() + && !projection_clause.bound_vars().is_empty() + { + let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(interner)); + let obligation = Obligation::new( + interner, + self.obligation.cause, + goal.goal().param_env, + deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred), + ); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, pred), + goal.depth() + 1, + this, + ) + }) + } else { + ControlFlow::Continue(()) + } + } + + /// It is likely that `NormalizesTo` failed without any applicable candidates + /// because the alias is not well-formed. + /// + /// As we only enter `RigidAlias` candidates if the trait bound of the associated type + /// holds, we discard these candidates in `non_trivial_candidates` and always manually + /// check this here. + fn detect_non_well_formed_assoc_item( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + alias: AliasTerm<'db>, + ) -> ControlFlow> { + let interner = goal.infcx().interner; + let obligation = Obligation::new( + interner, + self.obligation.cause, + goal.goal().param_env, + alias.trait_ref(interner), + ); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, alias.trait_ref(interner)), + goal.depth() + 1, + this, + ) + }) + } + + /// If we have no candidates, then it's likely that there is a + /// non-well-formed alias in the goal. + fn detect_error_from_empty_candidates( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + ) -> ControlFlow> { + let interner = goal.infcx().interner; + let pred_kind = goal.goal().predicate.kind(); + + match pred_kind.no_bound_vars() { + Some(PredicateKind::Clause(ClauseKind::Trait(pred))) => { + self.detect_error_in_self_ty_normalization(goal, pred.self_ty())?; + } + Some(PredicateKind::NormalizesTo(pred)) + if let AliasTermKind::ProjectionTy | AliasTermKind::ProjectionConst = + pred.alias.kind(interner) => + { + self.detect_error_in_self_ty_normalization(goal, pred.alias.self_ty())?; + self.detect_non_well_formed_assoc_item(goal, pred.alias)?; + } + Some(_) | None => {} + } + + ControlFlow::Break(self.obligation.clone()) + } +} + +impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> { + type Result = ControlFlow>; + + fn span(&self) -> Span { + self.obligation.cause.span() + } + + #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))] + fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'db>) -> Self::Result { + let interner = goal.infcx().interner; + // Skip goals that aren't the *reason* for our goal's failure. + match (self.consider_ambiguities, goal.result()) { + (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) | (false, Err(_)) => { + } + _ => return ControlFlow::Continue(()), + } + + let pred = goal.goal().predicate; + + let candidates = self.non_trivial_candidates(goal); + let candidate = match candidates.as_slice() { + [candidate] => candidate, + [] => return self.detect_error_from_empty_candidates(goal), + _ => return ControlFlow::Break(self.obligation.clone()), + }; + + // Don't walk into impls that have `do_not_recommend`. + if let inspect::ProbeKind::TraitCandidate { + source: CandidateSource::Impl(impl_def_id), + result: _, + } = candidate.kind() + && let AnyImplId::ImplId(impl_def_id) = impl_def_id + && AttrFlags::query(interner.db, impl_def_id.into()) + .contains(AttrFlags::DIAGNOSTIC_DO_NOT_RECOMMEND) + { + trace!("#[diagnostic::do_not_recommend] -> exit"); + return ControlFlow::Break(self.obligation.clone()); + } + + // FIXME: Also, what about considering >1 layer up the stack? May be necessary + // for normalizes-to. + let child_mode = match pred.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => { + ChildMode::Trait(pred.kind().rebind(trait_pred)) + } + PredicateKind::Clause(ClauseKind::HostEffect(host_pred)) => { + ChildMode::Host(pred.kind().rebind(host_pred)) + } + PredicateKind::NormalizesTo(normalizes_to) + if matches!( + normalizes_to.alias.kind(interner), + AliasTermKind::ProjectionTy | AliasTermKind::ProjectionConst + ) => + { + ChildMode::Trait(pred.kind().rebind(TraitPredicate { + trait_ref: normalizes_to.alias.trait_ref(interner), + polarity: PredicatePolarity::Positive, + })) + } + PredicateKind::Clause(ClauseKind::WellFormed(term)) => { + return self.visit_well_formed_goal(candidate, term); + } + _ => ChildMode::PassThrough, + }; + + let nested_goals = candidate.instantiate_nested_goals(self.span()); + + // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as + // an actual candidate, instead we should treat them as if the impl was never considered to + // have potentially applied. As if `impl Trait for for<..> fn(..A) -> R` was written + // instead of `impl Trait for T`. + // + // We do this as a separate loop so that we do not choose to tell the user about some nested + // goal before we encounter a `T: FnPtr` nested goal. + for nested_goal in &nested_goals { + if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() + && Some(poly_trait_pred.def_id().0) == interner.lang_items().FnPtrTrait + && let Err(NoSolution) = nested_goal.result() + { + return ControlFlow::Break(self.obligation.clone()); + } + } + + let mut impl_where_bound_count = 0; + for nested_goal in nested_goals { + trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result())); + + let nested_pred = nested_goal.goal().predicate; + + let make_obligation = |cause| Obligation { + cause, + param_env: nested_goal.goal().param_env, + predicate: nested_pred, + recursion_depth: self.obligation.recursion_depth + 1, + }; + + let obligation; + match (child_mode, nested_goal.source()) { + ( + ChildMode::Trait(_) | ChildMode::Host(_), + GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_), + ) => { + continue; + } + (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => { + obligation = make_obligation(derive_cause( + interner, + candidate.kind(), + self.obligation.cause, + impl_where_bound_count, + parent_trait_pred, + )); + impl_where_bound_count += 1; + } + ( + ChildMode::Host(parent_host_pred), + GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition, + ) => { + obligation = make_obligation(derive_host_cause( + interner, + candidate.kind(), + self.obligation.cause, + impl_where_bound_count, + parent_host_pred, + )); + impl_where_bound_count += 1; + } + (ChildMode::PassThrough, _) + | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => { + obligation = make_obligation(self.obligation.cause); + } + } + + self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; + } + + // alias-relate may fail because the lhs or rhs can't be normalized, + // and therefore is treated as rigid. + if let Some(PredicateKind::AliasRelate(lhs, rhs, _)) = pred.kind().no_bound_vars() { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, ClauseKind::WellFormed(lhs)), + goal.depth() + 1, + self, + )?; + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, ClauseKind::WellFormed(rhs)), + goal.depth() + 1, + self, + )?; + } + + self.detect_trait_error_in_higher_ranked_projection(goal)?; + + ControlFlow::Break(self.obligation.clone()) + } +} + +#[derive(Debug, Copy, Clone)] +enum ChildMode<'db> { + // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, + // and skip all `GoalSource::Misc`, which represent useless obligations + // such as alias-eq which may not hold. + Trait(PolyTraitPredicate<'db>), + // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, + // and skip all `GoalSource::Misc`, which represent useless obligations + // such as alias-eq which may not hold. + Host(Binder<'db, HostEffectPredicate<'db>>), + // Skip trying to derive an `ObligationCause` from this obligation, and + // report *all* sub-obligations as if they came directly from the parent + // obligation. + PassThrough, +} + +fn derive_cause<'db>( + _interner: DbInterner<'db>, + _candidate_kind: inspect::ProbeKind>, + cause: ObligationCause, + _idx: usize, + _parent_trait_pred: PolyTraitPredicate<'db>, +) -> ObligationCause { + cause +} + +fn derive_host_cause<'db>( + _interner: DbInterner<'db>, + _candidate_kind: inspect::ProbeKind>, + cause: ObligationCause, + _idx: usize, + _parent_host_pred: Binder<'db, HostEffectPredicate<'db>>, +) -> ObligationCause { + cause +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index e1568d43bb8da..0bb980c906096 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -53,6 +53,7 @@ use super::{ pub mod at; pub mod canonical; mod context; +pub mod errors; pub mod opaque_types; mod outlives; pub mod region_constraints; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index 12a6652bf7c30..4584b35796456 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -27,8 +27,9 @@ use crate::{ use super::InferCtxt; /// The reason why we incurred this obligation; used for error reporting. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeVisitable, TypeFoldable)] pub struct ObligationCause { + #[type_visitable(ignore)] span: Span, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs index 566f72fbd8e62..fdb1fa3d05157 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs @@ -24,6 +24,8 @@ use crate::{ }, }; +pub(crate) use rustc_next_trait_solver::solve::inspect::*; + pub(crate) struct InspectConfig { pub(crate) max_depth: usize, } @@ -319,6 +321,10 @@ impl<'a, 'db> InspectGoal<'a, 'db> { self.result } + pub(crate) fn source(&self) -> GoalSource { + self.source + } + pub(crate) fn depth(&self) -> usize { self.depth } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs index c35434ed16947..152b58baeb6de 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs @@ -229,7 +229,6 @@ impl<'db> FallibleTypeFolder> for NormalizationFolder<'_, 'db> { } // Deeply normalize a value and return it -#[expect(dead_code, reason = "rustc has this")] pub(crate) fn deeply_normalize_for_diagnostics<'db, T: TypeFoldable>>( infcx: &InferCtxt<'db>, param_env: ParamEnv<'db>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index 2abd9bdd9401a..e16428cd2e488 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -31,6 +31,7 @@ pub type ExistentialPredicate<'db> = ty::ExistentialPredicate>; pub type ExistentialTraitRef<'db> = ty::ExistentialTraitRef>; pub type ExistentialProjection<'db> = ty::ExistentialProjection>; pub type TraitPredicate<'db> = ty::TraitPredicate>; +pub type HostEffectPredicate<'db> = ty::HostEffectPredicate>; pub type ClauseKind<'db> = ty::ClauseKind>; pub type PredicateKind<'db> = ty::PredicateKind>; pub type NormalizesTo<'db> = ty::NormalizesTo>; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/solver_errors.rs b/src/tools/rust-analyzer/crates/hir-ty/src/solver_errors.rs new file mode 100644 index 0000000000000..e4e76fa67bacb --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/solver_errors.rs @@ -0,0 +1,90 @@ +//! Handling of trait solver errors and converting them to errors `hir` can pass to `ide-diagnostics`. +//! +//! Note that we also have [`crate::next_solver::infer::errors`], which takes the raw [`NextSolverError`], +//! and converts it into [`FulfillmentError`] that contains more details. +//! +//! [`NextSolverError`]: crate::next_solver::fulfill::NextSolverError + +use macros::{TypeFoldable, TypeVisitable}; +use rustc_type_ir::{PredicatePolarity, inherent::IntoKind}; + +use crate::{ + Span, + next_solver::{ + ClauseKind, DbInterner, PredicateKind, StoredTraitRef, TraitPredicate, + infer::{ + errors::{FulfillmentError, FulfillmentErrorCode}, + select::SelectionError, + }, + }, +}; + +#[derive(Debug, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)] +pub struct SolverDiagnostic { + pub span: Span, + pub kind: SolverDiagnosticKind, +} + +#[derive(Debug, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)] +pub enum SolverDiagnosticKind { + TraitUnimplemented { + trait_predicate: StoredTraitPredicate, + root_trait_predicate: Option, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)] +pub struct StoredTraitPredicate { + pub trait_ref: StoredTraitRef, + pub polarity: PredicatePolarity, +} + +impl StoredTraitPredicate { + #[inline] + pub fn get<'db>(&'db self, interner: DbInterner<'db>) -> TraitPredicate<'db> { + TraitPredicate { polarity: self.polarity, trait_ref: self.trait_ref.get(interner) } + } +} + +impl SolverDiagnostic { + pub fn from_fulfillment_error(error: &FulfillmentError<'_>) -> Option { + let span = error.obligation.cause.span(); + if span.is_dummy() { + return None; + } + + // FIXME: Handle more error kinds. + let kind = match &error.code { + FulfillmentErrorCode::Select(SelectionError::Unimplemented) => { + match error.obligation.predicate.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => { + handle_trait_unimplemented(error, trait_pred)? + } + _ => return None, + } + } + _ => return None, + }; + Some(SolverDiagnostic { span, kind }) + } +} + +fn handle_trait_unimplemented<'db>( + error: &FulfillmentError<'db>, + trait_pred: TraitPredicate<'db>, +) -> Option { + let trait_predicate = StoredTraitPredicate { + trait_ref: StoredTraitRef::new(trait_pred.trait_ref), + polarity: trait_pred.polarity, + }; + + let root_trait_predicate = match error.root_obligation.predicate.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => Some(StoredTraitPredicate { + trait_ref: StoredTraitRef::new(trait_pred.trait_ref), + polarity: trait_pred.polarity, + }), + _ => None, + }; + + Some(SolverDiagnosticKind::TraitUnimplemented { trait_predicate, root_trait_predicate }) +} 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 83767c42ea7c3..2fa70cd3a8c04 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -36,9 +36,9 @@ use syntax::{ use test_fixture::WithFixture; use crate::{ - InferenceResult, + InferenceDiagnostic, InferenceResult, display::{DisplayTarget, HirDisplay}, - infer::{Adjustment, TypeMismatch}, + infer::Adjustment, next_solver::Ty, setup_tracing, test_db::TestDB, @@ -195,7 +195,14 @@ fn check_impl( } } - for (expr_or_pat, mismatch) in inference_result.type_mismatches() { + let type_mismatches = + inference_result.diagnostics().iter().filter_map(|diag| match diag { + InferenceDiagnostic::TypeMismatch { node, expected, found } => { + Some((*node, expected.as_ref(), found.as_ref())) + } + _ => None, + }); + for (expr_or_pat, expected, actual) in type_mismatches { let Some(node) = (match expr_or_pat { hir_def::hir::ExprOrPatId::ExprId(expr) => { expr_node(body_source_map, expr, &db) @@ -207,8 +214,8 @@ fn check_impl( let range = node.as_ref().original_file_range_rooted(&db); let actual = format!( "expected {}, got {}", - mismatch.expected.as_ref().display_test(&db, display_target), - mismatch.actual.as_ref().display_test(&db, display_target) + expected.display_test(&db, display_target), + actual.display_test(&db, display_target) ); match mismatches.remove(&range) { Some(annotation) => assert_eq!(actual, annotation), @@ -326,7 +333,17 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { krate: Crate| { let display_target = DisplayTarget::from_crate(&db, krate); let mut types: Vec<(InFile, Ty<'_>)> = Vec::new(); - let mut mismatches: Vec<(InFile, &TypeMismatch)> = Vec::new(); + let type_mismatch_for_node = inference_result + .diagnostics() + .iter() + .filter_map(|diag| match diag { + InferenceDiagnostic::TypeMismatch { node, expected, found } => { + Some((*node, (expected.as_ref(), found.as_ref()))) + } + _ => None, + }) + .collect::>(); + let mut mismatches: Vec<(InFile, (Ty<'_>, Ty<'_>))> = Vec::new(); if let Some((binding_id, syntax_ptr)) = self_param { let ty = &inference_result.type_of_binding[binding_id]; @@ -349,8 +366,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { Err(SyntheticSyntax) => continue, }; types.push((node.clone(), ty.as_ref())); - if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat) { - mismatches.push((node, mismatch)); + if let Some(mismatch) = type_mismatch_for_node.get(&pat.into()) { + mismatches.push((node, *mismatch)); } } @@ -363,8 +380,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { Err(SyntheticSyntax) => continue, }; types.push((node.clone(), ty.as_ref())); - if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) { - mismatches.push((node, mismatch)); + if let Some(mismatch) = type_mismatch_for_node.get(&expr.into()) { + mismatches.push((node, *mismatch)); } } @@ -395,7 +412,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let range = node.value.text_range(); (range.start(), range.end()) }); - for (src_ptr, mismatch) in &mismatches { + for (src_ptr, (expected, actual)) in &mismatches { let range = src_ptr.value.text_range(); let macro_prefix = if src_ptr.file_id != file_id { "!" } else { "" }; format_to!( @@ -403,8 +420,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { "{}{:?}: expected {}, got {}\n", macro_prefix, range, - mismatch.expected.as_ref().display_test(&db, display_target), - mismatch.actual.as_ref().display_test(&db, display_target), + expected.display_test(&db, display_target), + actual.display_test(&db, display_target), ); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 1b63a4a2c0bb4..a5f349e59338f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2363,7 +2363,6 @@ fn test() { } "#, expect![[r#" - 46..49 'Foo': Foo 93..97 'self': Foo 108..125 '{ ... }': usize 118..119 'N': usize diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 6a0996fae6735..0f903045c9d99 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -15,12 +15,15 @@ use hir_def::{ }; use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name}; use hir_ty::{ - CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathGenericsSource, - PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind, + CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, ParamEnvAndCrate, + PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind, db::HirDatabase, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, display::{DisplayTarget, HirDisplay}, + next_solver::DbInterner, + solver_errors::SolverDiagnosticKind, }; +use stdx::{impl_from, never}; use syntax::{ AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange, ast::{self, HasGenericArgs}, @@ -36,6 +39,49 @@ pub use hir_ty::{ diagnostics::{CaseType, IncorrectCase}, }; +#[derive(Debug, Clone)] +pub enum SpanAst { + Expr(ast::Expr), + Pat(ast::Pat), + Type(ast::Type), +} +const _: () = { + use syntax::ast::*; + impl_from!(Expr, Pat, Type for SpanAst); +}; + +impl From> for SpanAst { + fn from(value: Either) -> Self { + match value { + Either::Left(it) => it.into(), + Either::Right(it) => it.into(), + } + } +} + +impl ast::AstNode for SpanAst { + fn can_cast(kind: syntax::SyntaxKind) -> bool { + ast::Expr::can_cast(kind) || ast::Pat::can_cast(kind) || ast::Type::can_cast(kind) + } + + fn cast(syntax: syntax::SyntaxNode) -> Option { + ast::Expr::cast(syntax.clone()) + .map(SpanAst::Expr) + .or_else(|| ast::Pat::cast(syntax.clone()).map(SpanAst::Pat)) + .or_else(|| ast::Type::cast(syntax).map(SpanAst::Type)) + } + + fn syntax(&self) -> &syntax::SyntaxNode { + match self { + SpanAst::Expr(it) => it.syntax(), + SpanAst::Pat(it) => it.syntax(), + SpanAst::Type(it) => it.syntax(), + } + } +} + +pub type SpanSyntax = InFile>; + macro_rules! diagnostics { ($AnyDiagnostic:ident <$db:lifetime> -> $($diag:ident $(<$lt:lifetime>)?,)*) => { #[derive(Debug)] @@ -114,6 +160,7 @@ diagnostics![AnyDiagnostic<'db> -> ElidedLifetimesInPath, TypeMustBeKnown<'db>, UnionExprMustHaveExactlyOneField, + UnimplementedTrait<'db>, ]; #[derive(Debug)] @@ -462,7 +509,7 @@ pub struct ElidedLifetimesInPath { #[derive(Debug)] pub struct TypeMustBeKnown<'db> { - pub at_point: InFile>>>, + pub at_point: SpanSyntax, pub top_term: Option, String>>, } @@ -510,6 +557,13 @@ pub struct PatternArgInExternFn { pub node: InFile>, } +#[derive(Debug)] +pub struct UnimplementedTrait<'db> { + pub span: SpanSyntax, + pub trait_predicate: crate::TraitPredicate<'db>, + pub root_trait_predicate: Option>, +} + impl<'db> AnyDiagnostic<'db> { pub(crate) fn body_validation_diagnostic( db: &'db dyn HirDatabase, @@ -646,9 +700,10 @@ impl<'db> AnyDiagnostic<'db> { pub(crate) fn inference_diagnostic( db: &'db dyn HirDatabase, def: DefWithBodyId, - d: &InferenceDiagnostic, + d: &'db InferenceDiagnostic, source_map: &hir_def::expr_store::BodySourceMap, sig_map: &hir_def::expr_store::ExpressionStoreSourceMap, + env: ParamEnvAndCrate<'db>, ) -> Option> { let expr_syntax = |expr| { source_map @@ -672,6 +727,18 @@ impl<'db> AnyDiagnostic<'db> { ExprOrPatId::ExprId(expr) => expr_syntax(expr), ExprOrPatId::PatId(pat) => pat_syntax(pat), }; + let span_syntax = |span| match span { + hir_ty::Span::ExprId(idx) => expr_syntax(idx).map(|it| it.upcast()), + hir_ty::Span::PatId(idx) => pat_syntax(idx).map(|it| it.upcast()), + hir_ty::Span::TypeRefId(idx) => type_syntax(idx).map(|it| it.upcast()), + hir_ty::Span::BindingId(idx) => { + pat_syntax(source_map.patterns_for_binding(idx)[0]).map(|it| it.upcast()) + } + hir_ty::Span::Dummy => { + never!("should never create a diagnostic for dummy spans"); + None + } + }; Some(match d { &InferenceDiagnostic::NoSuchField { field: expr, private, variant } => { let expr_or_pat = match expr { @@ -847,18 +914,7 @@ impl<'db> AnyDiagnostic<'db> { InvalidLhsOfAssignment { lhs }.into() } &InferenceDiagnostic::TypeMustBeKnown { at_point, ref top_term } => { - let at_point = match at_point { - hir_ty::Span::ExprId(idx) => expr_syntax(idx)?.map(|it| it.wrap_right()), - hir_ty::Span::PatId(idx) => pat_syntax(idx)?.map(|it| it.wrap_right()), - hir_ty::Span::TypeRefId(idx) => type_syntax(idx)?.map(|it| it.wrap_left()), - hir_ty::Span::BindingId(idx) => { - pat_syntax(source_map.patterns_for_binding(idx)[0])? - .map(|it| it.wrap_right()) - } - hir_ty::Span::Dummy => unreachable!( - "should never create TypeMustBeKnown diagnostic for dummy spans" - ), - }; + let at_point = span_syntax(at_point)?; let top_term = top_term.as_ref().map(|top_term| match top_term.as_ref().kind() { rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(Type { ty, @@ -878,6 +934,39 @@ impl<'db> AnyDiagnostic<'db> { let expr = expr_syntax(expr)?; UnionExprMustHaveExactlyOneField { expr }.into() } + InferenceDiagnostic::TypeMismatch { node, expected, found } => { + let expr_or_pat = expr_or_pat_syntax(*node)?; + TypeMismatch { + expr_or_pat, + expected: Type { env, ty: expected.as_ref() }, + actual: Type { env, ty: found.as_ref() }, + } + .into() + } + InferenceDiagnostic::SolverDiagnostic(d) => { + let span = span_syntax(d.span)?; + Self::solver_diagnostic(db, &d.kind, span, env)? + } + }) + } + + fn solver_diagnostic( + db: &'db dyn HirDatabase, + d: &'db SolverDiagnosticKind, + span: SpanSyntax, + env: ParamEnvAndCrate<'db>, + ) -> Option> { + let interner = DbInterner::new_no_crate(db); + Some(match d { + SolverDiagnosticKind::TraitUnimplemented { trait_predicate, root_trait_predicate } => { + let trait_predicate = + crate::TraitPredicate { inner: trait_predicate.get(interner), env }; + let root_trait_predicate = + root_trait_predicate.as_ref().map(|root_trait_predicate| { + crate::TraitPredicate { inner: root_trait_predicate.get(interner), env } + }); + UnimplementedTrait { span, trait_predicate, root_trait_predicate }.into() + } }) } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 139f078eef6b0..880c9d9ae68a2 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -30,8 +30,8 @@ use rustc_type_ir::inherent::IntoKind; use crate::{ Adt, AnyFunctionId, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum, EnumVariant, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, - LifetimeParam, Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitRef, - TupleField, Type, TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, + LifetimeParam, Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitPredicate, + TraitRef, TupleField, Type, TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, }; fn write_builtin_derive_impl_method<'db>( @@ -853,6 +853,12 @@ impl<'db> HirDisplay<'db> for TraitRef<'db> { } } +impl<'db> HirDisplay<'db> for TraitPredicate<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { + self.inner.hir_fmt(f) + } +} + impl<'db> HirDisplay<'db> for Trait { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { // FIXME(trait-alias) needs special handling to print the equal sign diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index b74f594ebe520..7ab9bca697b38 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2121,6 +2121,7 @@ impl DefWithBody { return; }; let krate = self.module(db).id.krate(db); + let env = body_param_env_from_has_crate(db, id); let (body, source_map) = Body::with_source_map(db, id); let sig_source_map = match self { @@ -2144,34 +2145,14 @@ impl DefWithBody { let infer = InferenceResult::of(db, id); for d in infer.diagnostics() { - acc.extend(AnyDiagnostic::inference_diagnostic(db, id, d, source_map, sig_source_map)); - } - - for (pat_or_expr, mismatch) in infer.type_mismatches() { - let expr_or_pat = match pat_or_expr { - ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left), - ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right), - }; - let expr_or_pat = match expr_or_pat { - Ok(Either::Left(expr)) => expr, - Ok(Either::Right(InFile { file_id, value: pat })) => { - // cast from Either -> Either<_, Pat> - let Some(ptr) = AstPtr::try_from_raw(pat.syntax_node_ptr()) else { - continue; - }; - InFile { file_id, value: ptr } - } - Err(SyntheticSyntax) => continue, - }; - - acc.push( - TypeMismatch { - expr_or_pat, - expected: Type::new(db, id, mismatch.expected.as_ref()), - actual: Type::new(db, id, mismatch.actual.as_ref()), - } - .into(), - ); + acc.extend(AnyDiagnostic::inference_diagnostic( + db, + id, + d, + source_map, + sig_source_map, + env, + )); } let missing_unsafe = hir_ty::diagnostics::missing_unsafe(db, id); @@ -6935,6 +6916,33 @@ pub trait HasVisibility { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum PredicatePolarity { + /// `T: Trait` + Positive, + /// `T: !Trait` + Negative, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TraitPredicate<'db> { + inner: hir_ty::next_solver::TraitPredicate<'db>, + env: ParamEnvAndCrate<'db>, +} + +impl<'db> TraitPredicate<'db> { + pub fn polarity(&self) -> PredicatePolarity { + match self.inner.polarity { + rustc_type_ir::PredicatePolarity::Positive => PredicatePolarity::Positive, + rustc_type_ir::PredicatePolarity::Negative => PredicatePolarity::Negative, + } + } + + pub fn trait_ref(&self) -> TraitRef<'db> { + TraitRef { env: self.env, trait_ref: self.inner.trait_ref } + } +} + /// Trait for obtaining the defining crate of an item. pub trait HasCrate { fn krate(&self, db: &dyn HirDatabase) -> Crate; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs index ba7556cd8b909..c004ee31aedaa 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -390,7 +390,7 @@ struct Bar; impl Foo for Bar {} -fn to_raw(_: *mut T) -> *mut () { +fn to_raw(_: *mut T) -> *mut () { loop {} } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 38cf548cc6ad8..aacbe72313dd2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -1062,7 +1062,7 @@ impl FooTrait for S2 { fn no_false_positive_on_format_args_since_1_89_0() { check_diagnostics( r#" -//- minicore: fmt +//- minicore: fmt, builtin_impls fn test() { let foo = 10; let bar = true; 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 fb1470b69f87e..b5a47e508e14d 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 @@ -333,7 +333,7 @@ fn foo(x: usize) -> u8 { } } "#, - std::iter::once("remove-unnecessary-else".to_owned()), + &["remove-unnecessary-else"], ); check_fix( r#" 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 d469405d61ad9..250c692d16f80 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 @@ -338,7 +338,8 @@ fn str_ref_to_owned( #[cfg(test)] mod tests { use crate::tests::{ - check_diagnostics, check_diagnostics_with_disabled, check_fix, check_has_fix, check_no_fix, + check_diagnostics, check_diagnostics_with_disabled, check_fix, check_fix_with_disabled, + check_has_fix, check_no_fix, }; #[test] @@ -755,7 +756,7 @@ fn foo() -> Result<(), ()> { #[test] fn wrapped_unit_as_return_expr() { - check_fix( + check_fix_with_disabled( r#" //- minicore: result fn foo(b: bool) -> Result<(), String> { @@ -773,6 +774,7 @@ fn foo(b: bool) -> Result<(), String> { Err("oh dear".to_owned()) }"#, + &["E0599"], ); } @@ -822,7 +824,7 @@ fn foo() -> SomeOtherEnum { 0$0 } #[test] fn unwrap_return_type() { - check_fix( + check_fix_with_disabled( r#" //- minicore: option, result fn div(x: i32, y: i32) -> i32 { @@ -840,6 +842,7 @@ fn div(x: i32, y: i32) -> i32 { x / y } "#, + &["E0282"], ); } @@ -897,7 +900,7 @@ fn div(x: i32, y: i32) -> i32 { #[test] fn unwrap_return_type_option_tail_unit() { - check_fix( + check_fix_with_disabled( r#" //- minicore: option, result fn div(x: i32, y: i32) { @@ -915,12 +918,13 @@ fn div(x: i32, y: i32) { } } "#, + &["E0282"], ); } #[test] fn unwrap_return_type_handles_generic_functions() { - check_fix( + check_fix_with_disabled( r#" //- minicore: option, result fn div(x: T) -> T { @@ -938,12 +942,13 @@ fn div(x: T) -> T { x } "#, + &["E0282"], ); } #[test] fn unwrap_return_type_handles_type_aliases() { - check_fix( + check_fix_with_disabled( r#" //- minicore: option, result type MyResult = T; @@ -965,12 +970,13 @@ fn div(x: i32, y: i32) -> MyResult { x / y } "#, + &["E0282"], ); } #[test] fn unwrap_tail_expr() { - check_fix( + check_fix_with_disabled( r#" //- minicore: result fn foo() -> () { @@ -983,12 +989,13 @@ fn foo() -> () { println!("Hello, world!"); } "#, + &["E0282"], ); } #[test] fn unwrap_to_empty_block() { - check_fix( + check_fix_with_disabled( r#" //- minicore: result fn foo() -> () { @@ -998,6 +1005,7 @@ fn foo() -> () { r#" fn foo() -> () {} "#, + &["E0282"], ); } @@ -1341,6 +1349,7 @@ pub fn foo(_: T) -> (T::Out,) { loop { } } fn main() { let _x = foo(2); // ^^ error: type annotations needed + // ^^^ error: the trait bound `i32: Foo` is not satisfied } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs index ad86df407af2c..30d8165e0a697 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs @@ -1,5 +1,5 @@ use either::Either; -use hir::HirDisplay; +use hir::{HirDisplay, SpanAst}; use stdx::format_to; use syntax::{AstNode, SyntaxNodePtr, ast}; @@ -17,7 +17,7 @@ pub(crate) fn type_must_be_known<'db>( // Do some adjustments to the node: FIXME: We should probably do that at the emitting site. let node = ctx.sema.to_node(d.at_point); - if let Either::Right(Either::Left(expr)) = &node + if let SpanAst::Expr(expr) = &node && let Some(Either::Left(top_ty)) = &d.top_term && let Some(expr_ty) = ctx.sema.type_of_expr(expr) && expr_ty.original == *top_ty diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs index 1bab4f453f9a6..7efc8a713619d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs @@ -86,16 +86,18 @@ fn foo() { check_diagnostics( r#" //- minicore: option, try -fn foo() { +fn foo() -> Option<()> { None?; + None } "#, ); check_diagnostics( r#" //- minicore: option, try, future -async fn foo() { +async fn foo() -> Option<()> { None?; + None } "#, ); @@ -103,7 +105,7 @@ async fn foo() { r#" //- minicore: option, try, future, fn async fn foo() { - || None?; + || { None?; Some(()) }; } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs new file mode 100644 index 0000000000000..4326aec4581d2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs @@ -0,0 +1,53 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: type-must-be-known +// +// This diagnostic is triggered when rust-analyzer cannot infer some type. +pub(crate) fn unimplemented_trait<'db>( + ctx: &DiagnosticsContext<'_, 'db>, + d: &hir::UnimplementedTrait<'db>, +) -> Diagnostic { + let message = match &d.root_trait_predicate { + Some(root_predicate) if *root_predicate != d.trait_predicate => format!( + "the trait bound `{}` is not satisfied\n\ + required by the bound `{}`\n", + d.trait_predicate.display(ctx.db(), ctx.display_target), + root_predicate.display(ctx.db(), ctx.display_target), + ), + _ => format!( + "the trait bound `{}` is not satisfied", + d.trait_predicate.display(ctx.db(), ctx.display_target), + ), + }; + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0277"), + message, + d.span.map(Into::into), + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn smoke_test() { + check_diagnostics( + r#" +trait Trait {} +impl Trait for [T; N] {} +fn foo(_v: impl Trait) {} +fn bar() { + foo(1); + // ^^^ error: the trait bound `i32: Trait` is not satisfied + foo([1]); + // ^^^ error: the trait bound `i32: Trait` is not satisfied + // | required by the bound `[i32; 1]: Trait` +} + "#, + ); + } +} 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 1d5811954cc70..451f79e828df2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -72,6 +72,7 @@ mod handlers { pub(crate) mod typed_hole; pub(crate) mod undeclared_label; pub(crate) mod unimplemented_builtin_macro; + pub(crate) mod unimplemented_trait; pub(crate) mod union_expr_must_have_exactly_one_field; pub(crate) mod unreachable_label; pub(crate) mod unresolved_assoc_item; @@ -493,6 +494,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d), AnyDiagnostic::PatternArgInExternFn(d) => handlers::pattern_arg_in_extern_fn::pattern_arg_in_extern_fn(&ctx, &d), AnyDiagnostic::UnionExprMustHaveExactlyOneField(d) => handlers::union_expr_must_have_exactly_one_field::union_expr_must_have_exactly_one_field(&ctx, &d), + AnyDiagnostic::UnimplementedTrait(d) => handlers::unimplemented_trait::unimplemented_trait(&ctx, &d), }; res.push(d) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index fc49542e3ccdd..4b9535ca061b9 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -57,11 +57,11 @@ fn check_nth_fix( pub(crate) fn check_fix_with_disabled( #[rust_analyzer::rust_fixture] ra_fixture_before: &str, #[rust_analyzer::rust_fixture] ra_fixture_after: &str, - disabled: impl Iterator, + disabled: &[&str], ) { let mut config = DiagnosticsConfig::test_sample(); config.expr_fill_default = ExprFillDefaultMode::Default; - config.disabled.extend(disabled); + config.disabled.extend(disabled.iter().map(|&disabled| disabled.to_owned())); check_nth_fix_with_config(config, 0, ra_fixture_before, ra_fixture_after) } diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index a2b317be5884a..3eb7867a3ab35 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -580,6 +580,7 @@ pub fn also_calls_foo() { "#, false, false, + // FIXME: The ranges here are volatile when minicore changes, that's not good. expect![[r#" foo Function FileId(1) 0..15 7..10 @@ -599,7 +600,7 @@ fn main() { false, false, expect![[r#" - Some Variant FileId(1) 6022..6054 6047..6051 + Some Variant FileId(1) 6737..6769 6762..6766 FileId(0) 46..50 "#]], 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 03146004054c8..d06690f203cfb 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 @@ -2,6 +2,7 @@ //! errors. use std::{ + cell::LazyCell, env, fmt, ops::AddAssign, panic::{AssertUnwindSafe, catch_unwind}, @@ -908,6 +909,18 @@ impl flags::AnalysisStats { // region:expressions let (previous_exprs, previous_unknown, previous_partially_unknown) = (num_exprs, num_exprs_unknown, num_exprs_partially_unknown); + let type_mismatch_for_node = LazyCell::new(|| { + inference_result + .diagnostics() + .iter() + .filter_map(|diag| match diag { + hir_ty::InferenceDiagnostic::TypeMismatch { node, expected, found } => { + Some((*node, (expected.as_ref(), found.as_ref()))) + } + _ => None, + }) + .collect::>() + }); for (expr_id, _) in body.exprs() { let ty = inference_result.expr_ty(expr_id); num_exprs += 1; @@ -964,9 +977,10 @@ impl flags::AnalysisStats { ty.display(db, display_target) ); } - if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) { + if inference_result.expr_has_type_mismatch(expr_id) { num_expr_type_mismatches += 1; if verbosity.is_verbose() { + let (expected, actual) = type_mismatch_for_node[&expr_id.into()]; if let Some((path, start, end)) = expr_syntax_range(db, vfs, sm(), expr_id) { bar.println(format!( @@ -976,24 +990,25 @@ impl flags::AnalysisStats { start.col, end.line + 1, end.col, - mismatch.expected.as_ref().display(db, display_target), - mismatch.actual.as_ref().display(db, display_target) + expected.display(db, display_target), + actual.display(db, display_target) )); } else { bar.println(format!( "{}: Expected {}, got {}", name.display(db, Edition::LATEST), - mismatch.expected.as_ref().display(db, display_target), - mismatch.actual.as_ref().display(db, display_target) + expected.display(db, display_target), + actual.display(db, display_target) )); } } if self.output == Some(OutputFormat::Csv) { + let (expected, actual) = type_mismatch_for_node[&expr_id.into()]; println!( r#"{},mismatch,"{}","{}""#, location_csv_expr(db, vfs, sm(), expr_id), - mismatch.expected.as_ref().display(db, display_target), - mismatch.actual.as_ref().display(db, display_target) + expected.display(db, display_target), + actual.display(db, display_target) ); } } @@ -1067,9 +1082,10 @@ impl flags::AnalysisStats { ty.display(db, display_target) ); } - if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) { + if inference_result.pat_has_type_mismatch(pat_id) { num_pat_type_mismatches += 1; if verbosity.is_verbose() { + let (expected, actual) = type_mismatch_for_node[&pat_id.into()]; if let Some((path, start, end)) = pat_syntax_range(db, vfs, sm(), pat_id) { bar.println(format!( "{} {}:{}-{}:{}: Expected {}, got {}", @@ -1078,24 +1094,25 @@ impl flags::AnalysisStats { start.col, end.line + 1, end.col, - mismatch.expected.as_ref().display(db, display_target), - mismatch.actual.as_ref().display(db, display_target) + expected.display(db, display_target), + actual.display(db, display_target) )); } else { bar.println(format!( "{}: Expected {}, got {}", name.display(db, Edition::LATEST), - mismatch.expected.as_ref().display(db, display_target), - mismatch.actual.as_ref().display(db, display_target) + expected.display(db, display_target), + actual.display(db, display_target) )); } } if self.output == Some(OutputFormat::Csv) { + let (expected, actual) = type_mismatch_for_node[&pat_id.into()]; println!( r#"{},mismatch,"{}","{}""#, location_csv_pat(db, vfs, sm(), pat_id), - mismatch.expected.as_ref().display(db, display_target), - mismatch.actual.as_ref().display(db, display_target) + expected.display(db, display_target), + actual.display(db, display_target) ); } } diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index e9ab066160338..29775590ea8cc 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -738,6 +738,30 @@ pub mod ops { pub struct RangeToInclusive { pub end: Idx, } + + // region:iterator + pub trait Step {} + macro_rules! impl_step { + ( $( $ty:ty ),* $(,)? ) => { + $( + impl Step for $ty {} + )* + }; + } + impl_step!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize); + + macro_rules! impl_iterator { + ( $( $range:ident ),* $(,)? ) => { + $( + impl Iterator for $range { + type Item = Idx; + fn next(&mut self) -> Option { loop {} } + } + )* + }; + } + impl_iterator!(Range, RangeFrom, RangeTo, RangeInclusive, RangeToInclusive); + // endregion:iterator } pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; pub use self::range::{RangeInclusive, RangeToInclusive}; @@ -1292,6 +1316,38 @@ pub mod fmt { fn fmt(&self, f: &mut Formatter<'_>) -> Result; } + impl Debug for &T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + T::fmt(&**self, f) + } + } + impl Display for &T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + T::fmt(&**self, f) + } + } + + macro_rules! impl_fmt_traits { + ( $($ty:ty),* $(,)? ) => { + $( + impl Debug for $ty { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { loop {} } + } + impl Display for $ty { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { loop {} } + } + )* + } + } + + impl_fmt_traits!(str); + + // region:builtin_impls + impl_fmt_traits!( + i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, bool, char, + ); + // endregion:builtin_impls + mod rt { use super::*; From 03bb8f7f912fbec2cc25f98ab0292e2cc23bccaf Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sat, 2 May 2026 23:50:23 +0300 Subject: [PATCH 174/289] Add FIXMEs for emitting an error --- src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index c81e4647d87dd..2a23e07a037ae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -483,6 +483,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { assoc_name.clone(), ) else { + // FIXME: Emit an error. return error_ty(); }; assoc_type @@ -503,6 +504,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { assoc_name.clone(), ) else { + // FIXME: Emit an error. return error_ty(); }; let (assoc_type, trait_args) = assoc_type From 816ac428899fa77d975d3406fcf304a669a51349 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 3 May 2026 00:31:18 +0300 Subject: [PATCH 175/289] Timeout tests after 5 minutes with nextest Since we have hanging tests, to not waste CI cycles before we find the cause. --- src/tools/rust-analyzer/.config/nextest.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/tools/rust-analyzer/.config/nextest.toml diff --git a/src/tools/rust-analyzer/.config/nextest.toml b/src/tools/rust-analyzer/.config/nextest.toml new file mode 100644 index 0000000000000..47e5bf53d0f0f --- /dev/null +++ b/src/tools/rust-analyzer/.config/nextest.toml @@ -0,0 +1,2 @@ +[profile.default] +slow-timeout = { period = "60s", terminate-after = 5 } From a1995eb888551e4daf2b29a545fd15f91b05dca2 Mon Sep 17 00:00:00 2001 From: "workflows-rust-analyzer[bot]" <223433972+workflows-rust-analyzer[bot]@users.noreply.github.com> Date: Sun, 3 May 2026 00:04:34 +0000 Subject: [PATCH 176/289] internal: update generated lints --- .../crates/ide-db/src/generated/lints.rs | 80 ++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index c60e4b2e1dc28..a2bd475a34633 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -736,7 +736,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[ Lint { label: "linker_messages", description: r##"warnings emitted at runtime by the target-specific linker program"##, - default_severity: Severity::Allow, + default_severity: Severity::Warning, warn_since: None, deny_since: None, }, @@ -4325,6 +4325,22 @@ pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize { sum } ``` +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "c_variadic_experimental_arch", + description: r##"# `c_variadic_experimental_arch` + +Allows defining c-variadic functions on targets where this feature has not yet undergone sufficient testing for stabilization. + +The tracking issue for this feature is: [#155973] + +[#155973]: https://github.com/rust-lang/rust/issues/155973 + +------------------------ "##, default_severity: Severity::Allow, warn_since: None, @@ -8844,6 +8860,22 @@ The tracking issue for this feature is: [#125119] This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "gpu_launch_sized_workgroup_mem", + description: r##"# `gpu_launch_sized_workgroup_mem` + + + +The tracking issue for this feature is: [#135513] + +[#135513]: https://github.com/rust-lang/rust/issues/135513 + ------------------------ "##, default_severity: Severity::Allow, @@ -12147,6 +12179,20 @@ The tracking issue for this feature is: [#130494] [#130494]: https://github.com/rust-lang/rust/issues/130494 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "pin_macro_internals", + description: r##"# `pin_macro_internals` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + ------------------------ "##, default_severity: Severity::Allow, @@ -16454,6 +16500,22 @@ The tracking issue for this feature is: [#146954] [#146954]: https://github.com/rust-lang/rust/issues/146954 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "view_types", + description: r##"# `view_types` + +Allows view types. + +The tracking issue for this feature is: [#155938] + +[#155938]: https://github.com/rust-lang/rust/issues/155938 + ------------------------ "##, default_severity: Severity::Allow, @@ -16634,6 +16696,22 @@ This feature is internal to the Rust compiler and is not intended for general us This feature is internal to the Rust compiler and is not intended for general use. +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "windows_permissions_ext", + description: r##"# `windows_permissions_ext` + + + +The tracking issue for this feature is: [#152956] + +[#152956]: https://github.com/rust-lang/rust/issues/152956 + ------------------------ "##, default_severity: Severity::Allow, From 1310ef67bafe9e408a18d3ac5c43d8228d014cf0 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 30 Apr 2026 07:41:53 +0300 Subject: [PATCH 177/289] Fix stack overflow on projection display As it turns out, displaying projections can always cause an infinite loop, even when not recursing into a type param's predicates. This does regress the previous test of infinite loops a bit, which is unfortunate. Maybe we should have a more fine-grained tracking? --- .../crates/hir-ty/src/display.rs | 192 +++++++++--------- .../ide-completion/src/tests/expression.rs | 56 +++++ .../crates/ide/src/inlay_hints/bind_pat.rs | 2 +- 3 files changed, 151 insertions(+), 99 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 6bc55bc0e4731..41cbb33651c41 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -10,8 +10,8 @@ use std::{ use base_db::{Crate, FxIndexMap}; use either::Either; use hir_def::{ - ExpressionStoreOwnerId, FindPathConfig, GenericDefId, GenericParamId, HasModule, LocalFieldId, - Lookup, ModuleDefId, ModuleId, TraitId, + ExpressionStoreOwnerId, FindPathConfig, GenericDefId, GenericParamId, HasModule, + ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, expr_store::{ExpressionStore, path::Path}, find_path::{self, PrefixKind}, hir::{ @@ -134,7 +134,15 @@ pub struct HirFormatter<'a, 'db> { display_lifetimes: DisplayLifetime, display_kind: DisplayKind, display_target: DisplayTarget, - bounds_formatting_ctx: BoundsFormattingCtx<'db>, + /// We can have recursive bounds like the following case: + /// ```ignore + /// where + /// T: Foo, + /// T::FooAssoc: Baz<::BarAssoc> + Bar + /// ``` + /// So, record the projection types met while formatting bounds and + /// prevent recursing into their bounds to avoid infinite loops. + currently_formatting_bounds: FxHashSet>, /// Whether formatting `impl Trait1 + Trait2` or `dyn Trait1 + Trait2` needs parentheses around it, /// for example when formatting `&(impl Trait1 + Trait2)`. trait_bounds_need_parens: bool, @@ -154,34 +162,6 @@ pub enum DisplayLifetime { Never, } -#[derive(Default)] -enum BoundsFormattingCtx<'db> { - Entered { - /// We can have recursive bounds like the following case: - /// ```ignore - /// where - /// T: Foo, - /// T::FooAssoc: Baz<::BarAssoc> + Bar - /// ``` - /// So, record the projection types met while formatting bounds and - //. prevent recursing into their bounds to avoid infinite loops. - projection_tys_met: FxHashSet>, - }, - #[default] - Exited, -} - -impl<'db> BoundsFormattingCtx<'db> { - fn contains(&self, proj: &AliasTy<'db>) -> bool { - match self { - BoundsFormattingCtx::Entered { projection_tys_met } => { - projection_tys_met.contains(proj) - } - BoundsFormattingCtx::Exited => false, - } - } -} - impl<'db> HirFormatter<'_, 'db> { pub fn start_location_link(&mut self, location: ModuleDefId) { self.fmt.start_location_link(location); @@ -195,26 +175,42 @@ impl<'db> HirFormatter<'_, 'db> { self.fmt.end_location_link(); } - fn format_bounds_with T>( + fn format_bounds_with Result>( &mut self, target: AliasTy<'db>, format_bounds: F, - ) -> T { - match self.bounds_formatting_ctx { - BoundsFormattingCtx::Entered { ref mut projection_tys_met } => { - projection_tys_met.insert(target); - format_bounds(self) - } - BoundsFormattingCtx::Exited => { - let mut projection_tys_met = FxHashSet::default(); - projection_tys_met.insert(target); - self.bounds_formatting_ctx = BoundsFormattingCtx::Entered { projection_tys_met }; - let res = format_bounds(self); - // Since we want to prevent only the infinite recursions in bounds formatting - // and do not want to skip formatting of other separate bounds, clear context - // when exiting the formatting of outermost bounds - self.bounds_formatting_ctx = BoundsFormattingCtx::Exited; - res + ) -> Result { + if self.currently_formatting_bounds.insert(target) { + let result = format_bounds(self); + self.currently_formatting_bounds.remove(&target); + result + } else { + if self.display_kind.is_source_code() { + Err(HirDisplayError::DisplaySourceCodeError(DisplaySourceCodeError::Cycle)) + } else { + match target.kind { + AliasTyKind::Projection { def_id } => { + let def_id = def_id.expect_type_alias(); + let ItemContainerId::TraitId(trait_) = def_id.loc(self.db).container else { + panic!("expected an assoc type"); + }; + let trait_name = &TraitSignature::of(self.db, trait_).name; + let assoc_type_name = &TypeAliasSignature::of(self.db, def_id).name; + write!( + self, + "<… as {}>::{}", + trait_name.display(self.db, self.edition()), + assoc_type_name.display(self.db, self.edition()), + )?; + if target.args.len() > 1 { + self.write_str("<…>")?; + } + Ok(()) + } + AliasTyKind::Inherent { .. } + | AliasTyKind::Opaque { .. } + | AliasTyKind::Free { .. } => self.write_str("…"), + } } } } @@ -368,7 +364,7 @@ pub trait HirDisplay<'db> { display_kind: DisplayKind::SourceCode { target_module_id: module_id, allow_opaque }, show_container_bounds: false, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, - bounds_formatting_ctx: Default::default(), + currently_formatting_bounds: Default::default(), trait_bounds_need_parens: false, }) { Ok(()) => {} @@ -544,6 +540,7 @@ pub enum DisplaySourceCodeError { PathNotFound, Coroutine, OpaqueType, + Cycle, } pub enum HirDisplayError { @@ -604,7 +601,7 @@ impl<'db, T: HirDisplay<'db>> HirDisplayWrapper<'_, 'db, T> { closure_style: self.closure_style, show_container_bounds: self.show_container_bounds, display_lifetimes: self.display_lifetimes, - bounds_formatting_ctx: Default::default(), + currently_formatting_bounds: Default::default(), trait_bounds_need_parens: false, }) } @@ -657,62 +654,61 @@ fn write_projection<'db>( alias: &AliasTy<'db>, needs_parens_if_multi: bool, ) -> Result { - if f.should_truncate() { - return write!(f, "{TYPE_HINT_TRUNCATION}"); - } - let trait_ref = alias.trait_ref(f.interner); - let self_ty = trait_ref.self_ty(); - - // if we are projection on a type parameter, check if the projection target has bounds - // itself, if so, we render them directly as `impl Bound` instead of the less useful - // `::Assoc` - if !f.display_kind.is_source_code() - && let TyKind::Param(param) = self_ty.kind() - && !f.bounds_formatting_ctx.contains(alias) - { - // FIXME: We shouldn't use `param.id`, it should be removed. We should know the - // `GenericDefId` from the formatted type (store it inside the `HirFormatter`). - let bounds = GenericPredicates::query_all(f.db, param.id.parent()) - .iter_identity() - .filter(|wc| { - let ty = match wc.kind().skip_binder() { - ClauseKind::Trait(tr) => tr.self_ty(), - ClauseKind::TypeOutlives(t) => t.0, - _ => return false, - }; - let TyKind::Alias(a) = ty.kind() else { - return false; - }; - a == *alias - }) - .collect::>(); - if !bounds.is_empty() { - return f.format_bounds_with(*alias, |f| { - write_bounds_like_dyn_trait_with_prefix( + f.format_bounds_with(*alias, |f| { + if f.should_truncate() { + return write!(f, "{TYPE_HINT_TRUNCATION}"); + } + let trait_ref = alias.trait_ref(f.interner); + let self_ty = trait_ref.self_ty(); + + // if we are projection on a type parameter, check if the projection target has bounds + // itself, if so, we render them directly as `impl Bound` instead of the less useful + // `::Assoc` + if !f.display_kind.is_source_code() + && let TyKind::Param(param) = self_ty.kind() + { + // FIXME: We shouldn't use `param.id`, it should be removed. We should know the + // `GenericDefId` from the formatted type (store it inside the `HirFormatter`). + let bounds = GenericPredicates::query_all(f.db, param.id.parent()) + .iter_identity() + .filter(|wc| { + let ty = match wc.kind().skip_binder() { + ClauseKind::Trait(tr) => tr.self_ty(), + ClauseKind::TypeOutlives(t) => t.0, + _ => return false, + }; + let TyKind::Alias(a) = ty.kind() else { + return false; + }; + a == *alias + }) + .collect::>(); + if !bounds.is_empty() { + return write_bounds_like_dyn_trait_with_prefix( f, "impl", Either::Left(Ty::new_alias(f.interner, *alias)), &bounds, SizedByDefault::NotSized, needs_parens_if_multi, - ) - }); + ); + } } - } - write!(f, "<")?; - self_ty.hir_fmt(f)?; - write!(f, " as ")?; - trait_ref.hir_fmt(f)?; - write!( - f, - ">::{}", - TypeAliasSignature::of(f.db, alias.kind.def_id().expect_type_alias()) - .name - .display(f.db, f.edition()) - )?; - let proj_params = &alias.args.as_slice()[trait_ref.args.len()..]; - hir_fmt_generics(f, proj_params, None, None) + write!(f, "<")?; + self_ty.hir_fmt(f)?; + write!(f, " as ")?; + trait_ref.hir_fmt(f)?; + write!( + f, + ">::{}", + TypeAliasSignature::of(f.db, alias.kind.def_id().expect_type_alias()) + .name + .display(f.db, f.edition()) + )?; + let proj_params = &alias.args.as_slice()[trait_ref.args.len()..]; + hir_fmt_generics(f, proj_params, None, None) + }) } impl<'db> HirDisplay<'db> for GenericArg<'db> { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index f6da07a6f2ed7..91852bb0f5b8c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -3829,3 +3829,59 @@ fn baz(v: impl Bar) { "#]], ); } + +#[test] +fn regression_21697() { + check( + r#" +trait SuperTrait { + type AssocTy; +} + +trait SubTrait::AssocTy>: SuperTrait {} + +fn tryme(param: impl SubTrait) { + param$0 +} + "#, + expect![[r#" + fn tryme(…) fn(impl SubTrait<::AssocTy> + ?Sized as SuperTrait>::AssocTy> + ?Sized) + lc param impl SubTrait<::AssocTy> + ?Sized as SuperTrait>::AssocTy> + ?Sized + tt SubTrait + tt SuperTrait + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index f194bb183e18d..57b723cbd8720 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -1336,7 +1336,7 @@ where { fn f(&self) { let x = self.field.foo(); - //^ impl Baz<<::Assoc as Bar>::Target> + Bar + //^ impl Baz<<<… as Foo>::Assoc as Bar>::Target> + Bar } } "#, From f250f2b30b115509fafac69d270f462ed14d540b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 14:08:13 +0530 Subject: [PATCH 178/289] Remove unused remove_generic_param --- .../crates/syntax/src/ast/edit_in_place.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index a893f65dc3953..7e2e26b4fd2d3 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -43,21 +43,6 @@ pub trait AttrsOwnerEdit: ast::HasAttrs { impl AttrsOwnerEdit for T {} impl ast::GenericParamList { - /// Removes the existing generic param - pub fn remove_generic_param(&self, generic_param: ast::GenericParam) { - if let Some(previous) = generic_param.syntax().prev_sibling() { - if let Some(next_token) = previous.next_sibling_or_token() { - ted::remove_all(next_token..=generic_param.syntax().clone().into()); - } - } else if let Some(next) = generic_param.syntax().next_sibling() { - if let Some(next_token) = next.prev_sibling_or_token() { - ted::remove_all(generic_param.syntax().clone().into()..=next_token); - } - } else { - ted::remove(generic_param.syntax()); - } - } - /// Constructs a matching [`ast::GenericArgList`] pub fn to_generic_args(&self) -> ast::GenericArgList { let args = self.generic_params().filter_map(|param| match param { From 2251e34578e8c6f6ffc4aac8bf50586c04f654d8 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 14:08:35 +0530 Subject: [PATCH 179/289] add make variant of to_generic_args --- .../crates/syntax/src/ast/edit_in_place.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 7e2e26b4fd2d3..7d7c875508316 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -9,7 +9,7 @@ use crate::{ SyntaxKind::{ATTR, COMMENT, WHITESPACE}, SyntaxNode, SyntaxToken, algo::{self, neighbor}, - ast::{self, edit::IndentLevel, make}, + ast::{self, edit::IndentLevel, make, syntax_factory::SyntaxFactory}, syntax_editor::{self, SyntaxEditor}, ted, }; @@ -44,17 +44,17 @@ impl AttrsOwnerEdit for T {} impl ast::GenericParamList { /// Constructs a matching [`ast::GenericArgList`] - pub fn to_generic_args(&self) -> ast::GenericArgList { + pub fn to_generic_args(&self, make: &SyntaxFactory) -> ast::GenericArgList { let args = self.generic_params().filter_map(|param| match param { ast::GenericParam::LifetimeParam(it) => { - Some(ast::GenericArg::LifetimeArg(make::lifetime_arg(it.lifetime()?))) + Some(ast::GenericArg::LifetimeArg(make.lifetime_arg(it.lifetime()?))) } ast::GenericParam::TypeParam(it) => { - Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?)))) + Some(ast::GenericArg::TypeArg(make.type_arg(make.ty_name(it.name()?)))) } ast::GenericParam::ConstParam(it) => { // Name-only const params get parsed as `TypeArg`s - Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?)))) + Some(ast::GenericArg::TypeArg(make.type_arg(make.ty_name(it.name()?)))) } }); From d623253c5c4398a56d41b35e2ac528d72925983c Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 14:09:21 +0530 Subject: [PATCH 180/289] add ty_name constructor method --- .../syntax/src/ast/syntax_factory/constructors.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 bebf595f00d9d..f5a88e40ea3ad 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 @@ -2229,6 +2229,21 @@ impl SyntaxFactory { make::ext::field_from_idents(parts) } + pub fn ty_name(&self, name: ast::Name) -> ast::Type { + let ast = make::ext::ty_name(name.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() + && let ast::Type::PathType(path_ty) = &ast + && let Some(name_ref) = path_ty.path().and_then(|path| path.segment()?.name_ref()) + { + let mut builder = SyntaxMappingBuilder::new(name_ref.syntax().parent().unwrap()); + builder.map_node(name.syntax().clone(), name_ref.syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + pub fn expr_await(&self, expr: ast::Expr) -> ast::AwaitExpr { let ast::Expr::AwaitExpr(ast) = make::expr_await(expr.clone()).clone_for_update() else { unreachable!() From 2bfbeae07ca6d6492840e9fe643189a1f3e42d98 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 2 May 2026 14:09:41 +0530 Subject: [PATCH 181/289] update other assist accordingly --- .../handlers/extract_struct_from_enum_variant.rs | 2 +- .../ide-assists/src/handlers/extract_type_alias.rs | 2 +- .../src/handlers/generate_blanket_trait_impl.rs | 2 +- .../src/handlers/generate_delegate_methods.rs | 2 +- .../src/handlers/generate_delegate_trait.rs | 13 ++++++++----- .../src/handlers/generate_getter_or_setter.rs | 2 +- .../handlers/generate_single_field_struct_from.rs | 2 +- .../src/handlers/generate_trait_from_impl.rs | 2 +- .../rust-analyzer/crates/ide-assists/src/utils.rs | 3 +-- 9 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 37867d656c364..50ce8d9b4d5ac 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -338,7 +338,7 @@ fn update_variant( let name = variant.name()?; let generic_args = generics .filter(|generics| generics.generic_params().count() > 0) - .map(|generics| generics.to_generic_args()); + .map(|generics| generics.to_generic_args(make)); // FIXME: replace with a `ast::make` constructor let ty = match generic_args { Some(generic_args) => make.ty(&format!("{name}{generic_args}")), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs index a654b681d18b7..ecb031e42d7e9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -75,7 +75,7 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_, '_>) generics.map(|it| make.generic_param_list(it.into_iter().cloned())); // Replace original type with the alias - let ty_args = generic_params.as_ref().map(|it| it.to_generic_args().generic_args()); + let ty_args = generic_params.as_ref().map(|it| it.to_generic_args(make).generic_args()); let new_ty = if let Some(ty_args) = ty_args { make.generic_ty_path_segment(make.name_ref(name), ty_args) } else { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index 3902ee170ec16..4454e4701312f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -89,7 +89,7 @@ pub(crate) fn generate_blanket_trait_impl( ))]); let trait_gen_args = - traitd.generic_param_list().map(|param_list| param_list.to_generic_args()); + traitd.generic_param_list().map(|param_list| param_list.to_generic_args(make)); let body = traitd.assoc_item_list().and_then(|trait_assoc_list| { let items = trait_assoc_list diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index a209e5fc29ecd..b5c6aec0bbd49 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -195,7 +195,7 @@ pub(crate) fn generate_delegate_methods( None => { let name = &strukt_name.to_string(); let ty_params = strukt.generic_param_list(); - let ty_args = ty_params.as_ref().map(|it| it.to_generic_args()); + let ty_args = ty_params.as_ref().map(|it| it.to_generic_args(make)); let where_clause = strukt.where_clause(); let assoc_item_list = make.assoc_item_list(vec![item]); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs index ed1599821904c..e21f1ab359840 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -274,9 +274,9 @@ fn generate_impl( None, delegee.is_unsafe(db), bound_params.clone(), - bound_params.map(|params| params.to_generic_args()), + bound_params.map(|params| params.to_generic_args(&make)), strukt_params.clone(), - strukt_params.map(|params| params.to_generic_args()), + strukt_params.map(|params| params.to_generic_args(&make)), delegee.is_auto(db), make.ty(&delegee.name(db).display_no_db(edition).to_smolstr()), strukt_ty, @@ -318,7 +318,7 @@ fn generate_impl( let strukt_params = resolve_name_conflicts(strukt_params, &old_impl_params); let (field_ty, ty_where_clause) = match &strukt_params { Some(strukt_params) => { - let args = strukt_params.to_generic_args(); + let args = strukt_params.to_generic_args(&make); let field_ty = rename_strukt_args(ctx, ast_strukt, field_ty, &args)?; let where_clause = ast_strukt .where_clause() @@ -349,6 +349,7 @@ fn generate_impl( // 2.2) Generate generic args applied on impl. let (transform_args, trait_gen_params) = generate_args_for_impl( + &make, old_impl_params, &old_impl.self_ty()?, &field_ty, @@ -375,7 +376,7 @@ fn generate_impl( } }); - let type_gen_args = strukt_params.clone().map(|params| params.to_generic_args()); + let type_gen_args = strukt_params.clone().map(|params| params.to_generic_args(&make)); let path_type = make.ty(&trait_.name(db).display_no_db(edition).to_smolstr()); let path_type = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type)?; // 3) Generate delegate trait impl @@ -593,13 +594,15 @@ fn finalize_delegate( // While the last two generic args `B` and `C` doesn't change, it remains // ``. So we apply `` as generic arguments to impl. fn generate_args_for_impl( + make: &SyntaxFactory, old_impl_gpl: Option, self_ty: &ast::Type, field_ty: &ast::Type, trait_params: Option, old_trait_args: &FxHashSet, ) -> (Option, Option) { - let Some(old_impl_args) = old_impl_gpl.map(|gpl| gpl.to_generic_args().generic_args()) else { + let Some(old_impl_args) = old_impl_gpl.map(|gpl| gpl.to_generic_args(make).generic_args()) + else { return (None, trait_params); }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index c8ab54474c750..7e5d5cec71bc5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs @@ -458,7 +458,7 @@ fn build_source_change( let make = editor.make(); let items = items(ctx, info_of_record_fields, &assist_info, make); let ty_params = assist_info.strukt.generic_param_list(); - let ty_args = ty_params.as_ref().map(|it| it.to_generic_args()); + let ty_args = ty_params.as_ref().map(|it| it.to_generic_args(make)); let impl_def = make.impl_( None, ty_params, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 23c7b2b7c8cbf..4348dfa212c70 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -87,7 +87,7 @@ pub(crate) fn generate_single_field_struct_from( let indent = strukt.indent_level(); let ty_where_clause = strukt.where_clause(); let type_gen_params = strukt.generic_param_list(); - let type_gen_args = type_gen_params.as_ref().map(|params| params.to_generic_args()); + let type_gen_args = type_gen_params.as_ref().map(|params| params.to_generic_args(make)); let trait_gen_args = Some(make.generic_arg_list( [ast::GenericArg::TypeArg(make.type_arg(main_field_ty.clone()))], false, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index d5f0eb234cf7b..2493ba663264d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -134,7 +134,7 @@ pub(crate) fn generate_trait_from_impl( ]; if let Some(params) = impl_ast.generic_param_list() { - let gen_args = ¶ms.to_generic_args(); + let gen_args = ¶ms.to_generic_args(make); elements.insert(1, gen_args.syntax().clone().into()); } 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 9d3af35ee5942..7251b0e1ffcdf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -630,8 +630,7 @@ fn generate_impl_inner( make.generic_param_list(itertools::chain(lifetime_params, ty_or_const_params)) }); - let generic_args = - generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update()); + let generic_args = generic_params.as_ref().map(|params| params.to_generic_args(make)); let adt_assoc_bounds = trait_ .as_ref() .zip(generic_params.as_ref()) From 73b678ab70295554f4884adaf8888857b6d475c2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 3 May 2026 06:01:50 +0300 Subject: [PATCH 182/289] Show the user's message for `#[must_use]` --- .../rust-analyzer/crates/hir-def/src/attrs.rs | 22 +++++++++ .../crates/hir-ty/src/diagnostics/expr.rs | 47 +++++++++---------- .../crates/hir/src/diagnostics.rs | 11 +++-- .../src/handlers/unused_must_use.rs | 30 ++++++++++-- 4 files changed, 76 insertions(+), 34 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 352c98a76adf5..43bbe99b13305 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -1159,6 +1159,28 @@ impl AttrFlags { }) } } + + /// Returns `None` if there is no `#[must_use]`, `Some(None)` if there is a `#[must_use]` without a message, + /// and `Some(Some(message))` if there is a `#[must_use]` with a message. + pub fn must_use_message(db: &dyn DefDatabase, owner: AttrDefId) -> Option> { + if !AttrFlags::query(db, owner).contains(AttrFlags::IS_MUST_USE) { + return None; + } + return Some(must_use_message(db, owner)); + + #[salsa::tracked(returns(as_deref))] + fn must_use_message(db: &dyn DefDatabase, owner: AttrDefId) -> Option> { + collect_attrs(db, owner, |attr| { + if let ast::Meta::KeyValueMeta(attr) = attr + && attr.path().is1("must_use") + && let Some(message) = attr.value_string() + { + return ControlFlow::Break(Box::from(&*message)); + } + ControlFlow::Continue(()) + }) + } + } } fn merge_repr(this: &mut ReprOptions, other: ReprOptions) { 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 768b185ae7bc9..760ebd27e0573 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 @@ -7,8 +7,7 @@ use std::fmt; use base_db::Crate; use either::Either; use hir_def::{ - AdtId, AssocItemId, AttrDefId, CallableDefId, DefWithBodyId, HasModule, ItemContainerId, - Lookup, + AdtId, AssocItemId, CallableDefId, DefWithBodyId, HasModule, ItemContainerId, Lookup, attrs::AttrFlags, lang_item::LangItems, resolver::{HasResolver, ValueNs}, @@ -46,7 +45,7 @@ pub(crate) use hir_def::{ hir::{Expr, ExprId, MatchArm, Pat, PatId, RecordSpread, Statement}, }; -pub enum BodyValidationDiagnostic { +pub enum BodyValidationDiagnostic<'db> { RecordMissingFields { record: Either, variant: VariantId, @@ -71,15 +70,16 @@ pub enum BodyValidationDiagnostic { }, UnusedMustUse { expr: ExprId, + message: Option<&'db str>, }, } -impl BodyValidationDiagnostic { +impl<'db> BodyValidationDiagnostic<'db> { pub fn collect( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, owner: DefWithBodyId, validate_lints: bool, - ) -> Vec { + ) -> Vec> { let _p = tracing::info_span!("BodyValidationDiagnostic::collect").entered(); let infer = InferenceResult::of(db, owner); let body = Body::of(db, owner); @@ -106,7 +106,7 @@ struct ExprValidator<'db> { body: &'db Body, infer: &'db InferenceResult, env: ParamEnv<'db>, - diagnostics: Vec, + diagnostics: Vec>, validate_lints: bool, infcx: InferCtxt<'db>, } @@ -354,7 +354,7 @@ impl<'db> ExprValidator<'db> { pattern_arena: &'a Arena>, pat: PatId, initializer: Option, - ) -> Option { + ) -> Option> { if self.infer.pat_has_type_mismatch(pat) { return None; } @@ -415,35 +415,32 @@ impl<'db> ExprValidator<'db> { pattern } - fn check_unused_must_use(&self, expr: ExprId) -> Option { - let db = self.db(); - let must_use_fn = match &self.body[expr] { + fn check_unused_must_use(&self, expr: ExprId) -> Option> { + let fn_def = match &self.body[expr] { Expr::Call { callee, .. } => { let callee_ty = self.infer.expr_ty(*callee); if let TyKind::FnDef(CallableIdWrapper(CallableDefId::FunctionId(func)), _) = callee_ty.kind() { - AttrFlags::query(db, AttrDefId::FunctionId(func)) - .contains(AttrFlags::IS_MUST_USE) + Some(func.into()) } else { - false + None } } Expr::MethodCall { .. } => { - self.infer.method_resolution(expr).is_some_and(|(func, _)| { - AttrFlags::query(db, AttrDefId::FunctionId(func)) - .contains(AttrFlags::IS_MUST_USE) - }) + self.infer.method_resolution(expr).map(|(func, _)| func.into()) } _ => return None, }; - let must_use_ty = - self.infer.type_of_expr_with_adjust(expr).is_some_and(|ty| match ty.kind() { - TyKind::Adt(adt, _) => AttrFlags::query(db, AttrDefId::AdtId(adt.def_id())) - .contains(AttrFlags::IS_MUST_USE), - _ => false, - }); - (must_use_fn || must_use_ty).then_some(BodyValidationDiagnostic::UnusedMustUse { expr }) + 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()), + _ => None, + }); + let must_use_diag = |owner| { + 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)) } fn check_for_trailing_return(&mut self, body_expr: ExprId, body: &Body) { diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index fd9ca8c4022b2..401bbd4a590cb 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -133,7 +133,7 @@ diagnostics![AnyDiagnostic<'db> -> PrivateField, RemoveTrailingReturn, RemoveUnnecessaryElse, - UnusedMustUse, + UnusedMustUse<'db>, ReplaceFilterMapNextWithFindMap, TraitImplIncorrectSafety, TraitImplMissingAssocItems, @@ -455,8 +455,9 @@ pub struct RemoveUnnecessaryElse { } #[derive(Debug)] -pub struct UnusedMustUse { +pub struct UnusedMustUse<'db> { pub expr: InFile, + pub message: Option<&'db str>, } #[derive(Debug)] @@ -576,7 +577,7 @@ pub struct UnimplementedTrait<'db> { impl<'db> AnyDiagnostic<'db> { pub(crate) fn body_validation_diagnostic( db: &'db dyn HirDatabase, - diagnostic: BodyValidationDiagnostic, + diagnostic: BodyValidationDiagnostic<'db>, source_map: &hir_def::expr_store::BodySourceMap, ) -> Option> { match diagnostic { @@ -697,9 +698,9 @@ impl<'db> AnyDiagnostic<'db> { ); } } - BodyValidationDiagnostic::UnusedMustUse { expr } => { + BodyValidationDiagnostic::UnusedMustUse { expr, message } => { if let Ok(source_ptr) = source_map.expr_syntax(expr) { - return Some(UnusedMustUse { expr: source_ptr }.into()); + return Some(UnusedMustUse { expr: source_ptr, message }.into()); } } } 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 4b9ecf216927f..e8d0717c91c2f 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 @@ -4,14 +4,18 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered when a value with the `#[must_use]` attribute // is dropped without being used. -pub(crate) fn unused_must_use( - ctx: &DiagnosticsContext<'_, '_>, - d: &hir::UnusedMustUse, +pub(crate) fn unused_must_use<'db>( + ctx: &DiagnosticsContext<'_, 'db>, + d: &hir::UnusedMustUse<'db>, ) -> Diagnostic { + let message = match d.message { + Some(message) => format!("unused return value that must be used: {message}"), + None => "unused return value that must be used".to_owned(), + }; Diagnostic::new_with_syntax_node_ptr( ctx, DiagnosticCode::RustcLint("unused_must_use"), - "unused return value that must be used", + message, d.expr.map(Into::into), ) .stable() @@ -53,6 +57,24 @@ fn main() { ); } + #[test] + fn with_message() { + check_diagnostics( + r#" +struct S; +impl S { + #[must_use = "custom message"] + fn produces(&self) -> i32 { 0 } +} +fn main() { + let s = S; + s.produces(); + //^^^^^^^^^^^^ warn: unused return value that must be used: custom message +} +"#, + ); + } + #[test] fn unused_must_use_type() { check_diagnostics( From 08732b17357d8bdb9f1e67a75751dee4d4ac5080 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 3 May 2026 08:04:10 +0300 Subject: [PATCH 183/289] Remove some unused code --- .../rust-analyzer/crates/hir-ty/src/lang_items.rs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs index ae53276f56a5b..c8e15e2f9c0c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs @@ -1,19 +1,8 @@ //! Functions to detect special lang items -use hir_def::{ - AdtId, TraitId, - lang_item::LangItems, - signatures::{StructFlags, StructSignature}, -}; +use hir_def::{TraitId, lang_item::LangItems}; use intern::{Symbol, sym}; -use crate::db::HirDatabase; - -pub fn is_box(db: &dyn HirDatabase, adt: AdtId) -> bool { - let AdtId::StructId(id) = adt else { return false }; - StructSignature::of(db, id).flags.contains(StructFlags::IS_BOX) -} - pub fn lang_items_for_bin_op( lang_items: &LangItems, op: syntax::ast::BinaryOp, From a0a91e94a5bd16284b05b363d7e1286e4d797a19 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 3 May 2026 10:32:13 +0530 Subject: [PATCH 184/289] add wrap_in_tree_list editor variant --- .../crates/syntax/src/ast/edit.rs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs index 0ffcbd212b3a5..20f1aaf22d0e1 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs @@ -2,7 +2,11 @@ //! immutable, all function here return a fresh copy of the tree, instead of //! doing an in-place modification. use parser::T; -use std::{fmt, iter, ops}; +use std::{ + fmt, + iter::{self, once}, + ops, +}; use crate::{ AstToken, NodeOrToken, SyntaxElement, @@ -252,6 +256,28 @@ impl ast::IdentPat { } } +impl ast::UseTree { + pub fn wrap_in_tree_list_with_editor(&self) -> Option { + if self.use_tree_list().is_some() + && self.path().is_none() + && self.star_token().is_none() + && self.rename().is_none() + { + return None; + } + + let (editor, use_tree) = SyntaxEditor::with_ast_node(self); + let make = editor.make(); + let first_child = use_tree.syntax().first_child_or_token()?; + let last_child = use_tree.syntax().last_child_or_token()?; + let use_tree_list = make.use_tree_list(once(self.clone())); + editor.replace_all(first_child..=last_child, vec![use_tree_list.syntax().clone().into()]); + + let edit = editor.finish(); + ast::UseTree::cast(edit.new_root().clone()) + } +} + pub fn indent(node: &SyntaxNode, level: IndentLevel) -> SyntaxNode { level.clone_increase_indent(node) } From 6af96dbe68e7b69d0392ee3c3598cd792369b176 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 3 May 2026 10:32:29 +0530 Subject: [PATCH 185/289] update insert_use to use editor variant --- .../crates/ide-db/src/imports/insert_use.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index 4b0373271c038..c3949f871314f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -248,9 +248,12 @@ fn insert_use_with_alias_option_with_editor( }; } - let use_tree = make.use_tree(path, None, alias, false); - if mb == Some(MergeBehavior::One) && use_tree.path().is_some() { - use_tree.wrap_in_tree_list(); + let mut use_tree = make.use_tree(path, None, alias, false); + if mb == Some(MergeBehavior::One) + && use_tree.path().is_some() + && let Some(wrapped) = use_tree.wrap_in_tree_list_with_editor() + { + use_tree = wrapped; } let use_item = make.use_(scope.required_cfgs.iter().cloned().rev(), None, use_tree); From f53ad8a288e2c613ccc51fd4b661040dcca871f0 Mon Sep 17 00:00:00 2001 From: Onyeka Obi Date: Fri, 1 May 2026 01:42:59 -0700 Subject: [PATCH 186/289] ide-diagnostics: emit error for duplicate field in record expression Resolves a FIXME at hir-ty/src/infer/expr.rs that allowed record expressions like `S { foo: 1, foo: 2 }` to type-check with no error. Adds an InferenceDiagnostic::DuplicateField variant, plumbs the cooked diagnostic through hir, and renders it as RustcHardError E0062 ("field specified more than once"). Tests: struct literal positive, enum variant literal positive, single-occurrence negative, no-conflict with the existing no-such-field path. Part of rust-lang/rust-analyzer#22140. --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 6 ++ .../crates/hir-ty/src/infer/expr.rs | 5 +- .../crates/hir/src/diagnostics.rs | 16 ++++ .../src/handlers/duplicate_field.rs | 84 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 5 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/duplicate_field.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index c61badf179fcc..d28ee4ab443b6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -330,6 +330,12 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] has_rest: bool, }, + DuplicateField { + #[type_visitable(ignore)] + field: ExprOrPatId, + #[type_visitable(ignore)] + variant: VariantId, + }, PrivateField { #[type_visitable(ignore)] expr: ExprId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 680800244bf9e..e07a1b2ba4885 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1073,7 +1073,10 @@ impl<'db> InferenceContext<'_, 'db> { variant_field_tys[i].get().instantiate(interner, args) } else { if let Some(field_idx) = seen_fields.get(&name) { - // FIXME: Emit an error: duplicate field. + self.push_diagnostic(InferenceDiagnostic::DuplicateField { + field: field.expr.into(), + variant, + }); variant_field_tys[*field_idx].get().instantiate(interner, args) } else { self.push_diagnostic(InferenceDiagnostic::NoSuchField { diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 401bbd4a590cb..cfc59fc383397 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -128,6 +128,7 @@ diagnostics![AnyDiagnostic<'db> -> NonExhaustiveRecordExpr, NoSuchField, MismatchedArrayPatLen, + DuplicateField, PatternArgInExternFn, PrivateAssocItem, PrivateField, @@ -267,6 +268,12 @@ pub struct NoSuchField { pub variant: VariantId, } +#[derive(Debug)] +pub struct DuplicateField { + pub field: InFile>>, + pub variant: VariantId, +} + #[derive(Debug)] pub struct PrivateAssocItem { pub expr_or_pat: InFile, @@ -764,6 +771,15 @@ impl<'db> AnyDiagnostic<'db> { let pat = pat_syntax(pat)?.map(Into::into); MismatchedArrayPatLen { pat, expected, found, has_rest }.into() } + &InferenceDiagnostic::DuplicateField { field: expr, variant } => { + let expr_or_pat = match expr { + ExprOrPatId::ExprId(expr) => { + source_map.field_syntax(expr).map(AstPtr::wrap_left) + } + ExprOrPatId::PatId(pat) => source_map.pat_field_syntax(pat), + }; + DuplicateField { field: expr_or_pat, variant }.into() + } &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { MismatchedArgCount { call_expr: expr_syntax(call_expr)?, expected, found }.into() } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/duplicate_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/duplicate_field.rs new file mode 100644 index 0000000000000..13d594066dfaf --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/duplicate_field.rs @@ -0,0 +1,84 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: duplicate-field +// +// This diagnostic is triggered when a record expression or pattern specifies +// the same field more than once. +pub(crate) fn duplicate_field( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::DuplicateField, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0062"), + "field specified more than once", + d.field.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn duplicate_field_in_struct_literal() { + check_diagnostics( + r#" +struct S { foo: i32, bar: i32 } +fn main() { + let _ = S { + foo: 1, + bar: 2, + foo: 3, + //^^^^^^ error: field specified more than once + }; +} +"#, + ); + } + + #[test] + fn duplicate_field_in_enum_variant_literal() { + check_diagnostics( + r#" +enum E { V { foo: i32 } } +fn main() { + let _ = E::V { + foo: 1, + foo: 2, + //^^^^^^ error: field specified more than once + }; +} +"#, + ); + } + + #[test] + fn no_duplicate_when_each_field_specified_once() { + check_diagnostics( + r#" +struct S { foo: i32, bar: i32 } +fn main() { + let _ = S { foo: 1, bar: 2 }; +} +"#, + ); + } + + #[test] + fn no_duplicate_for_unknown_field_falls_through_to_no_such_field() { + check_diagnostics( + r#" +struct S { foo: i32 } +fn main() { + let _ = S { + foo: 1, + bar: 2, + //^^^^^^ 💡 error: no such field + }; +} +"#, + ); + } +} 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 5882a8fdc263e..a490d48fed6a3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -32,6 +32,7 @@ mod handlers { pub(crate) mod await_outside_of_async; pub(crate) mod bad_rtn; pub(crate) mod break_outside_of_loop; + pub(crate) mod duplicate_field; pub(crate) mod elided_lifetimes_in_path; pub(crate) mod expected_function; pub(crate) mod generic_args_prohibited; @@ -442,6 +443,7 @@ pub fn semantic_diagnostics( handlers::non_exhaustive_record_expr::non_exhaustive_record_expr(&ctx, &d) } AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d), + AnyDiagnostic::DuplicateField(d) => handlers::duplicate_field::duplicate_field(&ctx, &d), AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), AnyDiagnostic::PrivateField(d) => handlers::private_field::private_field(&ctx, &d), AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d), From 8f29166225cdccc9ce1e05ef4f775e7749f0969b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 3 May 2026 14:43:09 +0300 Subject: [PATCH 187/289] Bump Salsa --- src/tools/rust-analyzer/Cargo.lock | 44 ++++++++++++++----- src/tools/rust-analyzer/Cargo.toml | 4 +- .../rust-analyzer/crates/span/src/hygiene.rs | 2 + 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 604bc345c41b9..f47bff58bd895 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -647,6 +647,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -734,7 +740,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -743,6 +749,17 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + [[package]] name = "hashlink" version = "0.10.0" @@ -1232,9 +1249,9 @@ dependencies = [ [[package]] name = "inventory" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" +checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" dependencies = [ "rustversion", ] @@ -2455,14 +2472,14 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "salsa" -version = "0.26.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f77debccd43ba198e9cee23efd7f10330ff445e46a98a2b107fed9094a1ee676" +checksum = "4612ff789805e65c87e9b38cb749a293212a615af065bed8a2001086801498c3" dependencies = [ "boxcar", "crossbeam-queue", "crossbeam-utils", - "hashbrown 0.15.5", + "hashbrown 0.17.0", "hashlink", "indexmap", "intrusive-collections", @@ -2476,19 +2493,20 @@ dependencies = [ "smallvec", "thin-vec", "tracing", + "typeid", ] [[package]] name = "salsa-macro-rules" -version = "0.26.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea07adbf42d91cc076b7daf3b38bc8168c19eb362c665964118a89bc55ef19a5" +checksum = "58e354cbac6939b9b09cd9c11fb419a53e64b4a0f755d929f56a09f4cc752e41" [[package]] name = "salsa-macros" -version = "0.26.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d4d8b66451b9c75ddf740b7fc8399bc7b8ba33e854a5d7526d18708f67b05" +checksum = "3067861075c2b80608f84ad49fb88f2c7610b94cdf8b4201e79ddee87f8980c8" dependencies = [ "proc-macro2", "quote", @@ -3110,6 +3128,12 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "unarray" version = "0.1.4" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index b8dedc6c50a01..4a93c26ff7f76 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -135,13 +135,13 @@ rayon = "1.10.0" rowan = "=0.15.18" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it -salsa = { version = "0.26", default-features = false, features = [ +salsa = { version = "0.26.2", default-features = false, features = [ "rayon", "salsa_unstable", "macros", "inventory", ] } -salsa-macros = "0.26" +salsa-macros = "0.26.2" semver = "1.0.26" serde = { version = "1.0.219" } serde_derive = { version = "1.0.219" } diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 0a81cef52ec5a..f475de93e0582 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -156,6 +156,8 @@ const _: () = { impl zalsa_::SalsaStructInDb for SyntaxContext { type MemoIngredientMap = salsa::plumbing::MemoIngredientSingletonIndex; + const LEAF_TYPE_IDS: &[salsa::plumbing::ConstTypeId] = + &[salsa::plumbing::ConstTypeId::of::()]; fn lookup_ingredient_index(aux: &zalsa_::Zalsa) -> salsa::plumbing::IngredientIndices { aux.lookup_jar_by_type::>().into() From 21a7bafe7e050d5d332640d3b687ac29bd76016f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 3 May 2026 15:13:36 +0300 Subject: [PATCH 188/289] Fix Clippy --- .../crates/hir-ty/src/builtin_derive.rs | 2 +- .../crates/hir-ty/src/consteval.rs | 8 ++--- .../rust-analyzer/crates/hir-ty/src/lower.rs | 36 +++++++++---------- .../crates/hir-ty/src/opaques.rs | 8 ++--- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs index f14e6e4e4d0f3..73c0313823d36 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs @@ -152,7 +152,7 @@ pub fn impl_trait<'db>( } #[salsa::tracked(returns(ref))] -pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates { +pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates { let loc = impl_.loc(db); let generic_params = GenericParams::of(db, loc.adt.into()); let interner = DbInterner::new_with(db, loc.module(db).krate(db)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 87633ad4aa9e6..0fdd3ff1d928b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -330,8 +330,8 @@ pub(crate) fn const_eval<'db>( }; #[salsa::tracked(returns(ref), cycle_result = const_eval_cycle_result)] - pub(crate) fn const_eval_query<'db>( - db: &'db dyn HirDatabase, + pub(crate) fn const_eval_query( + db: &dyn HirDatabase, def: ConstId, subst: StoredGenericArgs, trait_env: Option, @@ -371,8 +371,8 @@ pub(crate) fn const_eval_static<'db>( }; #[salsa::tracked(returns(ref), cycle_result = const_eval_static_cycle_result)] - pub(crate) fn const_eval_static_query<'db>( - db: &'db dyn HirDatabase, + pub(crate) fn const_eval_static_query( + db: &dyn HirDatabase, def: StaticId, ) -> Result { let interner = DbInterner::new_no_crate(db); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 7066d40813460..d3db2a90579ea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -1153,8 +1153,8 @@ pub(crate) fn impl_trait_with_diagnostics<'db>( }); #[salsa::tracked(returns(ref))] - pub(crate) fn impl_trait_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, + pub(crate) fn impl_trait_with_diagnostics_query( + db: &dyn HirDatabase, impl_id: ImplId, ) -> Option<(StoredEarlyBinder<(TraitId, StoredGenericArgs)>, Diagnostics)> { let impl_data = ImplSignature::of(db, impl_id); @@ -1441,8 +1441,8 @@ pub(crate) fn value_ty<'db>( return value_ty_query(db, def).as_ref().map(|it| it.get()); #[salsa::tracked(returns(ref))] - pub(crate) fn value_ty_query<'db>( - db: &'db dyn HirDatabase, + pub(crate) fn value_ty_query( + db: &dyn HirDatabase, def: ValueTyDefId, ) -> Option> { match def { @@ -1464,8 +1464,8 @@ pub(crate) fn type_for_type_alias_with_diagnostics<'db>( return (ty.get(), diags.clone()); #[salsa::tracked(returns(ref), cycle_result = type_for_type_alias_with_diagnostics_cycle_result)] - pub(crate) fn type_for_type_alias_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, + pub(crate) fn type_for_type_alias_with_diagnostics_query( + db: &dyn HirDatabase, t: TypeAliasId, ) -> (StoredEarlyBinder, Diagnostics) { let type_alias_data = TypeAliasSignature::of(db, t); @@ -1525,8 +1525,8 @@ pub(crate) fn impl_self_ty_with_diagnostics<'db>( return (ty.get(), diags.clone()); #[salsa::tracked(returns(ref), cycle_result = impl_self_ty_with_diagnostics_cycle_result)] - pub(crate) fn impl_self_ty_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, + pub(crate) fn impl_self_ty_with_diagnostics_query( + db: &dyn HirDatabase, impl_id: ImplId, ) -> (StoredEarlyBinder, Diagnostics) { let resolver = impl_id.resolver(db); @@ -1572,8 +1572,8 @@ pub(crate) fn const_param_ty_with_diagnostics<'db>( // FIXME: Make this query non-interned. #[salsa::tracked(returns(ref), cycle_result = const_param_ty_with_diagnostics_cycle_result)] - pub(crate) fn const_param_ty_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, + pub(crate) fn const_param_ty_with_diagnostics_query( + db: &dyn HirDatabase, _: (), def: ConstParamId, ) -> (StoredTy, Diagnostics) { @@ -1618,8 +1618,8 @@ pub(crate) fn field_types_query( /// Build the type of all specific fields of a struct or enum variant. #[salsa::tracked(returns(ref))] -pub(crate) fn field_types_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, +pub(crate) fn field_types_with_diagnostics_query( + db: &dyn HirDatabase, variant_id: VariantId, ) -> (ArenaMap>, Diagnostics) { let var_data = variant_id.fields(db); @@ -1940,8 +1940,8 @@ fn type_alias_bounds_with_diagnostics<'db>( ); #[salsa::tracked(returns(ref))] - pub fn type_alias_bounds_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, + pub fn type_alias_bounds_with_diagnostics_query( + db: &dyn HirDatabase, type_alias: TypeAliasId, ) -> (TypeAliasBounds>, Diagnostics) { let type_alias_data = TypeAliasSignature::of(db, type_alias); @@ -2157,8 +2157,8 @@ pub(crate) fn trait_environment<'db>( return ParamEnv { clauses: trait_environment_query(db, def).as_ref() }; #[salsa::tracked(returns(ref))] - pub(crate) fn trait_environment_query<'db>( - db: &'db dyn HirDatabase, + pub(crate) fn trait_environment_query( + db: &dyn HirDatabase, def: GenericDefId, ) -> StoredClauses { let module = def.module(db); @@ -2452,8 +2452,8 @@ pub(crate) fn callable_item_signature<'db>( return callable_item_signature_query(db, def).get_with(|sig| sig.get()); #[salsa::tracked(returns(ref))] - pub(crate) fn callable_item_signature_query<'db>( - db: &'db dyn HirDatabase, + pub(crate) fn callable_item_signature_query( + db: &dyn HirDatabase, def: CallableDefId, ) -> StoredEarlyBinder { match def { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs index c78e9d7c5dfa1..699b6307dc1ab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs @@ -91,8 +91,8 @@ pub(crate) fn opaque_types_defined_by( // These are firewall queries to prevent drawing dependencies between infers: #[salsa::tracked(returns(ref))] -pub(crate) fn rpit_hidden_types<'db>( - db: &'db dyn HirDatabase, +pub(crate) fn rpit_hidden_types( + db: &dyn HirDatabase, function: FunctionId, ) -> ArenaMap> { let infer = InferenceResult::of(db, DefWithBodyId::from(function)); @@ -105,8 +105,8 @@ pub(crate) fn rpit_hidden_types<'db>( } #[salsa::tracked(returns(ref))] -pub(crate) fn tait_hidden_types<'db>( - db: &'db dyn HirDatabase, +pub(crate) fn tait_hidden_types( + db: &dyn HirDatabase, type_alias: TypeAliasId, ) -> ArenaMap> { // Call this first, to not perform redundant work if there are no TAITs. From 79f80e1a82de64f887ede6067e47818922994df3 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 3 May 2026 20:31:54 +0530 Subject: [PATCH 189/289] Remove add_item from ast::AssocItemList we don't use it anywhere --- .../crates/syntax/src/ast/edit_in_place.rs | 77 +------------------ 1 file changed, 4 insertions(+), 73 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 7d7c875508316..93ca27c8e32a0 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -2,15 +2,15 @@ use std::iter::{empty, once, successors}; -use parser::{SyntaxKind, T}; +use parser::T; use crate::{ - AstNode, AstToken, Direction, SyntaxElement, + AstNode, AstToken, Direction, SyntaxKind::{ATTR, COMMENT, WHITESPACE}, - SyntaxNode, SyntaxToken, + SyntaxNode, algo::{self, neighbor}, ast::{self, edit::IndentLevel, make, syntax_factory::SyntaxFactory}, - syntax_editor::{self, SyntaxEditor}, + syntax_editor::SyntaxEditor, ted, }; @@ -270,75 +270,6 @@ impl ast::Impl { } } -impl ast::AssocItemList { - /// Adds a new associated item after all of the existing associated items. - /// - /// Attention! This function does align the first line of `item` with respect to `self`, - /// but it does _not_ change indentation of other lines (if any). - pub fn add_item(&self, editor: &SyntaxEditor, item: ast::AssocItem) { - let make = editor.make(); - let (indent, position, whitespace) = match self.assoc_items().last() { - Some(last_item) => ( - IndentLevel::from_node(last_item.syntax()), - syntax_editor::Position::after(last_item.syntax()), - "\n\n", - ), - None => match self.l_curly_token() { - Some(l_curly) => { - normalize_ws_between_braces_with_editor(editor, self.syntax()); - ( - IndentLevel::from_token(&l_curly) + 1, - syntax_editor::Position::after(&l_curly), - "\n", - ) - } - None => ( - IndentLevel::zero(), - syntax_editor::Position::last_child_of(self.syntax()), - "\n", - ), - }, - }; - let elements: Vec = vec![ - make.whitespace(&format!("{whitespace}{indent}")).into(), - item.syntax().clone().into(), - ]; - editor.insert_all(position, elements); - } -} - -impl ast::RecordExprFieldList { - pub fn add_field(&self, field: ast::RecordExprField) { - let is_multiline = self.syntax().text().contains_char('\n'); - let whitespace = if is_multiline { - let indent = IndentLevel::from_node(self.syntax()) + 1; - make::tokens::whitespace(&format!("\n{indent}")) - } else { - make::tokens::single_space() - }; - - if is_multiline { - normalize_ws_between_braces(self.syntax()); - } - - let position = match self.fields().last() { - Some(last_field) => { - let comma = get_or_insert_comma_after(last_field.syntax()); - ted::Position::after(comma) - } - None => match self.l_curly_token() { - Some(it) => ted::Position::after(it), - None => ted::Position::last_child_of(self.syntax()), - }, - }; - - ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]); - if is_multiline { - ted::insert(ted::Position::after(field.syntax()), ast::make::token(T![,])); - } - } -} - impl ast::RecordExprField { /// This will either replace the initializer, or in the case that this is a shorthand convert /// the initializer into the name ref and insert the expr as the new initializer. From 56b5938c6e2fe63dccc471058762e253c54e2819 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 3 May 2026 20:33:13 +0530 Subject: [PATCH 190/289] remove add_fields from ast::RecordPathFieldList and ast::RecordExprFieldList --- .../crates/syntax/src/ast/edit_in_place.rs | 104 ------------------ 1 file changed, 104 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 93ca27c8e32a0..2b947f2d0fa30 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -291,110 +291,6 @@ impl ast::RecordExprField { } } -impl ast::RecordPatFieldList { - pub fn add_field(&self, field: ast::RecordPatField) { - let is_multiline = self.syntax().text().contains_char('\n'); - let whitespace = if is_multiline { - let indent = IndentLevel::from_node(self.syntax()) + 1; - make::tokens::whitespace(&format!("\n{indent}")) - } else { - make::tokens::single_space() - }; - - if is_multiline { - normalize_ws_between_braces(self.syntax()); - } - - let position = match self.fields().last() { - Some(last_field) => { - let syntax = last_field.syntax(); - let comma = get_or_insert_comma_after(syntax); - ted::Position::after(comma) - } - None => match self.l_curly_token() { - Some(it) => ted::Position::after(it), - None => ted::Position::last_child_of(self.syntax()), - }, - }; - - ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]); - if is_multiline { - ted::insert(ted::Position::after(field.syntax()), ast::make::token(T![,])); - } - } -} - -fn get_or_insert_comma_after(syntax: &SyntaxNode) -> SyntaxToken { - match syntax - .siblings_with_tokens(Direction::Next) - .filter_map(|it| it.into_token()) - .find(|it| it.kind() == T![,]) - { - Some(it) => it, - None => { - let comma = ast::make::token(T![,]); - ted::insert(ted::Position::after(syntax), &comma); - comma - } - } -} - -fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { - let l = node - .children_with_tokens() - .filter_map(|it| it.into_token()) - .find(|it| it.kind() == T!['{'])?; - let r = node - .children_with_tokens() - .filter_map(|it| it.into_token()) - .find(|it| it.kind() == T!['}'])?; - - let indent = IndentLevel::from_node(node); - - match l.next_sibling_or_token() { - Some(ws) - if ws.kind() == SyntaxKind::WHITESPACE - && ws.next_sibling_or_token()?.into_token()? == r => - { - ted::replace(ws, make::tokens::whitespace(&format!("\n{indent}"))); - } - Some(ws) if ws.kind() == T!['}'] => { - ted::insert(ted::Position::after(l), make::tokens::whitespace(&format!("\n{indent}"))); - } - _ => (), - } - Some(()) -} - -fn normalize_ws_between_braces_with_editor(editor: &SyntaxEditor, node: &SyntaxNode) -> Option<()> { - let make = editor.make(); - let l = node - .children_with_tokens() - .filter_map(|it| it.into_token()) - .find(|it| it.kind() == T!['{'])?; - let r = node - .children_with_tokens() - .filter_map(|it| it.into_token()) - .find(|it| it.kind() == T!['}'])?; - - let indent = IndentLevel::from_node(node); - - match l.next_sibling_or_token() { - Some(ws) - if ws.kind() == SyntaxKind::WHITESPACE - && ws.next_sibling_or_token()?.into_token()? == r => - { - editor.replace(ws, make.whitespace(&format!("\n{indent}"))); - } - Some(ws) if ws.kind() == T!['}'] => { - editor - .insert(syntax_editor::Position::after(l), make.whitespace(&format!("\n{indent}"))); - } - _ => (), - } - Some(()) -} - pub trait Indent: AstNode + Clone + Sized { fn indent_level(&self) -> IndentLevel { IndentLevel::from_node(self.syntax()) From 7a9fd55bae1d080fbe995b46d6d6f95e097479c8 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 3 May 2026 20:40:30 +0530 Subject: [PATCH 191/289] Move add_field editor variant in RecordExprFieldList and RecordPatFieldList in edit --- .../crates/syntax/src/syntax_editor/edits.rs | 113 +++++++++++++++++- 1 file changed, 108 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index a684c0bfdbb42..0338d976b0d58 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs @@ -191,11 +191,7 @@ impl SyntaxEditor { fn get_or_insert_comma_after(editor: &SyntaxEditor, syntax: &SyntaxNode) -> SyntaxToken { let make = editor.make(); - match syntax - .siblings_with_tokens(Direction::Next) - .filter_map(|it| it.into_token()) - .find(|it| it.kind() == T![,]) - { + match comma_after(syntax) { Some(it) => it, None => { let comma = make.token(T![,]); @@ -241,6 +237,113 @@ impl ast::AssocItemList { } } +impl ast::RecordExprFieldList { + pub fn add_fields( + &self, + editor: &SyntaxEditor, + fields: impl IntoIterator, + ) { + add_record_fields( + editor, + self.syntax(), + self.fields().last().map(|it| it.syntax().clone()), + self.l_curly_token(), + fields.into_iter().map(|it| it.syntax().clone().into()), + ); + } +} + +impl ast::RecordPatFieldList { + pub fn add_fields( + &self, + editor: &SyntaxEditor, + fields: impl IntoIterator, + ) { + add_record_fields( + editor, + self.syntax(), + self.fields().last().map(|it| it.syntax().clone()), + self.l_curly_token(), + fields.into_iter().map(|it| it.syntax().clone().into()), + ); + } +} + +fn add_record_fields( + editor: &SyntaxEditor, + field_list: &SyntaxNode, + last_field: Option, + l_curly: Option, + fields: impl Iterator, +) { + let fields = fields.collect::>(); + if fields.is_empty() { + return; + } + + let make = editor.make(); + let is_multiline = field_list.text().contains_char('\n'); + let whitespace = || { + if is_multiline { + let indent = IndentLevel::from_node(field_list) + 1; + make.whitespace(&format!("\n{indent}")) + } else { + make.whitespace(" ") + } + }; + + if is_multiline { + normalize_ws_between_braces(editor, field_list); + } + + let mut elements = Vec::new(); + let next_after_insert; + let position = match last_field { + Some(last_field) => match comma_after(&last_field) { + Some(comma) => { + next_after_insert = comma.next_sibling_or_token(); + Position::after(comma) + } + None => { + next_after_insert = last_field.next_sibling_or_token(); + elements.push(make.token(T![,]).into()); + Position::after(last_field) + } + }, + None => match l_curly { + Some(it) => { + next_after_insert = it.next_sibling_or_token(); + Position::after(it) + } + None => { + next_after_insert = None; + Position::last_child_of(field_list) + } + }, + }; + + let fields_len = fields.len(); + for (idx, field) in fields.into_iter().enumerate() { + elements.push(whitespace().into()); + elements.push(field); + if is_multiline || idx + 1 != fields_len { + elements.push(make.token(T![,]).into()); + } + } + if !is_multiline && next_after_insert.is_some_and(|it| it.kind() != SyntaxKind::WHITESPACE) { + elements.push(make.whitespace(" ").into()); + } + + editor.insert_all(position, elements); +} + +fn comma_after(syntax: &SyntaxNode) -> Option { + syntax + .siblings_with_tokens(Direction::Next) + .filter_map(|it| it.into_token()) + .find(|it| it.kind() == T![,]) +} + impl ast::Impl { pub fn get_or_create_assoc_item_list_with_editor( &self, From 98cf5a41c34706ce58bc69112c878080d7000f9b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 3 May 2026 20:40:47 +0530 Subject: [PATCH 192/289] migrate missing_field to SyntaxEditor --- .../src/handlers/missing_fields.rs | 69 +++++++++++++++---- 1 file changed, 54 insertions(+), 15 deletions(-) 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 206d8caf90e9b..5896fb2cb14af 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 @@ -18,6 +18,7 @@ use stdx::format_to; use syntax::{ AstNode, Edition, SyntaxNode, SyntaxNodePtr, ToSmolStr, ast::{self, make}, + syntax_editor::SyntaxEditor, }; use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; @@ -115,16 +116,20 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingFields) -> Option| match ctx.config.expr_fill_default { - ExprFillDefaultMode::Todo => make::ext::expr_todo(), - ExprFillDefaultMode::Underscore => make::ext::expr_underscore(), + ExprFillDefaultMode::Todo => make.expr_todo(), + ExprFillDefaultMode::Underscore => make.expr_underscore().into(), ExprFillDefaultMode::Default => { - get_default_constructor(ctx, d, ty).unwrap_or_else(make::ext::expr_todo) + get_default_constructor(ctx, d, ty).unwrap_or_else(|| make.expr_todo()) } }; - let old_field_list = field_list_parent.record_expr_field_list()?; - let new_field_list = old_field_list.clone_for_update(); + let mut new_fields = Vec::new(); for (f, ty) in missing_fields.iter() { let field_expr = if let Some(local_candidate) = locals.get(&f.name(ctx.sema.db)) { cov_mark::hit!(field_shorthand); @@ -159,35 +164,69 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingFields) -> Option { let missing_fields = ctx.sema.record_pattern_missing_fields(field_list_parent); let old_field_list = field_list_parent.record_pat_field_list()?; - let new_field_list = old_field_list.clone_for_update(); + let root = old_field_list.syntax().ancestors().last()?; + let (editor, _) = SyntaxEditor::new(root); + let make = editor.make(); + + let mut new_fields = Vec::new(); for (f, _) in missing_fields.iter() { - let field = make::record_pat_field_shorthand( - make::ident_pat( + let field = make.record_pat_field_shorthand( + make.ident_pat( false, false, - make::name(&f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()), + make.name(&f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()), ) .into(), ); - new_field_list.add_field(field.clone_for_update()); + new_fields.push(field); } - build_text_edit(new_field_list.syntax(), old_field_list.syntax()) + let new_field_list = add_record_pat_fields(editor, &old_field_list, new_fields)?; + build_text_edit(&new_field_list, old_field_list.syntax()) } } } +fn add_record_expr_fields( + editor: SyntaxEditor, + field_list: &ast::RecordExprFieldList, + fields: Vec, +) -> Option { + let old_range = field_list.syntax().text_range(); + let kind = field_list.syntax().kind(); + field_list.add_fields(&editor, fields); + let edit = editor.finish(); + edit.new_root() + .descendants() + .find(|it| it.kind() == kind && it.text_range().start() == old_range.start()) +} + +fn add_record_pat_fields( + editor: SyntaxEditor, + field_list: &ast::RecordPatFieldList, + fields: Vec, +) -> Option { + let old_range = field_list.syntax().text_range(); + let kind = field_list.syntax().kind(); + field_list.add_fields(&editor, fields); + let edit = editor.finish(); + edit.new_root() + .descendants() + .find(|it| it.kind() == kind && it.text_range().start() == old_range.start()) +} + fn make_ty( ty: &hir::Type<'_>, db: &dyn HirDatabase, From 9c31fb53a2ef7e5e8deb11b8c9c1059ffc6f7b98 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 4 May 2026 03:52:43 +0800 Subject: [PATCH 193/289] fix: don't panic on `impl ?Sized` for introduce_named_type_parameter Example --- ```rust fn foo(bar: &$0impl ?Sized) {} ``` **Before this PR** ``` panic: Failed to make ast node `syntax::ast::generated::nodes::Name` from text mod ?; ``` **After this PR** ```rust fn foo<$0S: ?Sized>(bar: &S) {} ``` --- .../src/handlers/introduce_named_type_parameter.rs | 9 +++++++++ .../crates/ide-db/src/syntax_helpers/suggest_name.rs | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs index 06023476da334..427fbbeaa0e9f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs @@ -182,6 +182,15 @@ fn foo< ); } + #[test] + fn replace_impl_question_bounds() { + check_assist( + introduce_named_type_parameter, + r#"fn foo(bar: &$0impl ?Sized) {}"#, + r#"fn foo<$0S: ?Sized>(bar: &S) {}"#, + ); + } + #[test] fn replace_impl_with_mut() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 09e6115320664..76fea5c2623ca 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -193,7 +193,10 @@ impl NameGenerator { pub fn for_impl_trait_as_generic(&mut self, ty: &ast::ImplTraitType) -> SmolStr { let c = ty .type_bound_list() - .and_then(|bounds| bounds.syntax().text().char_at(0.into())) + .and_then(|bounds| { + let ty = bounds.bounds().next()?.ty()?; + ty.syntax().text().char_at(0.into()).filter(|ch| ch.is_alphabetic()) + }) .unwrap_or('T'); self.suggest_name(&c.to_string()) From 35c296813f2c3665147ed76132d72253fe50e91f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 4 May 2026 01:57:26 +0300 Subject: [PATCH 194/289] Fix handling of `self` in `lower_coroutine_body_with_moved_arguments()` I have hard time constructing a test for this, but this does fixes panics. --- .../crates/hir-def/src/expr_store/lower.rs | 25 +++++++++++++++++++ .../hir-def/src/expr_store/tests/body.rs | 1 + 2 files changed, 26 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index a82046149ae3e..93f8304230cf2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -165,6 +165,7 @@ pub(super) fn lower_body( }; let body_expr = collector.collect( + self_param, &mut params, body, if is_async_fn { @@ -963,6 +964,7 @@ impl<'db> ExprCollector<'db> { /// drop order are stable. fn lower_coroutine_body_with_moved_arguments( &mut self, + self_param: Option, params: &mut [PatId], body: ExprId, kind: CoroutineKind, @@ -995,6 +997,26 @@ impl<'db> ExprCollector<'db> { // `let = ;` statement as an optimization. let mut statements = Vec::new(); + + if let Some(self_param) = self_param { + let Binding { ref name, mode, hygiene, .. } = self.store.bindings[self_param]; + let name = name.clone(); + let child_binding_id = self.alloc_binding(name.clone(), mode, hygiene); + let child_pat_id = + self.alloc_pat_desugared(Pat::Bind { id: child_binding_id, subpat: None }); + self.add_definition_to_binding(child_binding_id, child_pat_id); + let expr = self.alloc_expr_desugared(Expr::Path(name.into())); + if !hygiene.is_root() { + self.store.ident_hygiene.insert(expr.into(), hygiene); + } + statements.push(Statement::Let { + pat: child_pat_id, + type_ref: None, + initializer: Some(expr), + else_branch: None, + }); + } + for param in params { let (name, hygiene, is_simple_parameter) = match self.store.pats[*param] { // Check if this is a binding pattern, if so, we can optimize and avoid adding a @@ -1094,6 +1116,7 @@ impl<'db> ExprCollector<'db> { fn collect( &mut self, + self_param: Option, params: &mut [PatId], expr: Option, awaitable: Awaitable, @@ -1111,6 +1134,7 @@ impl<'db> ExprCollector<'db> { (false, false) => unreachable!(), }; this.lower_coroutine_body_with_moved_arguments( + self_param, params, body, kind, @@ -1587,6 +1611,7 @@ impl<'db> ExprCollector<'db> { // It's important that this expr is allocated immediately before the closure. // We rely on it for `coroutine_for_closure()`. body = this.lower_coroutine_body_with_moved_arguments( + None, &mut args, body, kind, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs index 9727d87cf0372..e97718ca22c32 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs @@ -653,6 +653,7 @@ async fn main(&self, param1: i32, ref mut param2: i32, _: i32, param4 @ _: i32, "#, expect![[r#" fn main(self, param1, mut param2, mut 0, mut param4, mut 1) async { + let self = self; let mut param1 = param1; let mut param2 = param2; let ref mut param2 = param2; From 6414a74bfe530f00c2f99e9f3a3fcecbe0050c4e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 4 May 2026 03:19:00 +0300 Subject: [PATCH 195/289] Improve performance of integer-based symbols Currently only generated names and integers. Instead of always heap-allocating then interning them (generated names), or before that match against a small list of known symbols (integers), have a static array of `&str`s that allows us to get the common cases without interning or allocation, and even the reverse way - from a `Symbol` to a `usize` - can be made more efficient. Also avoid heap allocation even in the cold case, use an `ArrayString` instead as the maximum length is known and is small. This is somewhat important as expr store lowering commonly generates new names in desugarings (e.g. for loop, question mark operator, `format_args!`), and integer symbols are also common. --- src/tools/rust-analyzer/Cargo.lock | 1 + src/tools/rust-analyzer/Cargo.toml | 2 +- .../crates/hir-expand/src/builtin/quote.rs | 6 +- .../crates/hir-expand/src/name.rs | 56 +++++---- .../crates/ide-db/src/prime_caches.rs | 4 +- .../crates/ide/src/navigation_target.rs | 2 +- .../rust-analyzer/crates/intern/Cargo.toml | 1 + .../rust-analyzer/crates/intern/src/symbol.rs | 32 ++--- .../crates/intern/src/symbol/symbols.rs | 119 +++++++++++++++--- .../crates/mbe/src/expander/transcriber.rs | 6 +- 10 files changed, 149 insertions(+), 80 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index f47bff58bd895..be9a8c491572f 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1231,6 +1231,7 @@ dependencies = [ name = "intern" version = "0.0.0" dependencies = [ + "arrayvec", "dashmap", "hashbrown 0.14.5", "rayon", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 4a93c26ff7f76..45fc727b13186 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"] resolver = "2" [workspace.package] -rust-version = "1.91" +rust-version = "1.95" edition = "2024" license = "MIT OR Apache-2.0" authors = ["rust-analyzer team"] diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs index 51c4e225168fe..d84756377fc75 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs @@ -199,9 +199,9 @@ impl ToTokenTree for &T { } impl_to_to_tokentrees! { - span: u32 => self { crate::tt::Literal{text_and_suffix: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } }; - span: usize => self { crate::tt::Literal{text_and_suffix: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } }; - span: i32 => self { crate::tt::Literal{text_and_suffix: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } }; + span: u32 => self { crate::tt::Literal{text_and_suffix: sym::Integer::get(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } }; + span: usize => self { crate::tt::Literal{text_and_suffix: sym::Integer::get(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } }; + span: i32 => self { crate::tt::Literal{text_and_suffix: sym::Integer::get(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } }; span: bool => self { crate::tt::Ident{sym: if self { sym::true_ } else { sym::false_ }, span, is_raw: tt::IdentIsRaw::No } }; _span: crate::tt::Leaf => self { self }; _span: crate::tt::Literal => self { self }; 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 0408a6943d59f..3ddc305f9592c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -33,12 +33,14 @@ impl fmt::Debug for Name { } impl Ord for Name { + #[inline] fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.symbol.as_str().cmp(other.symbol.as_str()) } } impl PartialOrd for Name { + #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } @@ -46,74 +48,62 @@ impl PartialOrd for Name { // No need to strip `r#`, all comparisons are done against well-known symbols. impl PartialEq for Name { + #[inline] fn eq(&self, sym: &Symbol) -> bool { self.symbol == *sym } } impl PartialEq<&Symbol> for Name { + #[inline] fn eq(&self, &sym: &&Symbol) -> bool { self.symbol == *sym } } impl PartialEq for Symbol { + #[inline] fn eq(&self, name: &Name) -> bool { *self == name.symbol } } impl PartialEq for &Symbol { + #[inline] fn eq(&self, name: &Name) -> bool { **self == name.symbol } } impl Name { + #[inline] fn new_text(text: &str) -> Name { Name { symbol: Symbol::intern(text), ctx: () } } + #[inline] pub fn new(text: &str, mut ctx: SyntaxContext) -> Name { // For comparisons etc. we remove the edition, because sometimes we search for some `Name` // and we don't know which edition it came from. // Can't do that for all `SyntaxContextId`s because it breaks Salsa. ctx.remove_root_edition(); _ = ctx; - match text.strip_prefix("r#") { - Some(text) => Self::new_text(text), - None => Self::new_text(text), - } + let text = text.strip_prefix("r#").unwrap_or(text); + Self::new_text(text) } + #[inline] pub fn new_root(text: &str) -> Name { // The edition doesn't matter for hygiene. Self::new(text, SyntaxContext::root(Edition::Edition2015)) } + #[inline] pub fn new_tuple_field(idx: usize) -> Name { - let symbol = match idx { - 0 => sym::INTEGER_0, - 1 => sym::INTEGER_1, - 2 => sym::INTEGER_2, - 3 => sym::INTEGER_3, - 4 => sym::INTEGER_4, - 5 => sym::INTEGER_5, - 6 => sym::INTEGER_6, - 7 => sym::INTEGER_7, - 8 => sym::INTEGER_8, - 9 => sym::INTEGER_9, - 10 => sym::INTEGER_10, - 11 => sym::INTEGER_11, - 12 => sym::INTEGER_12, - 13 => sym::INTEGER_13, - 14 => sym::INTEGER_14, - 15 => sym::INTEGER_15, - _ => Symbol::intern(&idx.to_string()), - }; - Name { symbol, ctx: () } + Name::new_symbol_root(sym::Integer::get(idx)) } + #[inline] pub fn new_lifetime(lt: &str) -> Name { match lt.strip_prefix("'r#") { Some(lt) => Self::new_text(&format_smolstr!("'{lt}")), @@ -121,6 +111,7 @@ impl Name { } } + #[inline] pub fn new_symbol(symbol: Symbol, ctx: SyntaxContext) -> Self { debug_assert!(!symbol.as_str().starts_with("r#")); _ = ctx; @@ -128,6 +119,7 @@ impl Name { } // FIXME: This needs to go once we have hygiene + #[inline] pub fn new_symbol_root(sym: Symbol) -> Self { Self::new_symbol(sym, SyntaxContext::root(Edition::Edition2015)) } @@ -141,6 +133,7 @@ impl Name { /// Ideally, we want a `gensym` semantics for missing names -- each missing /// name is equal only to itself. It's not clear how to implement this in /// salsa though, so we punt on that bit for a moment. + #[inline] pub const fn missing() -> Name { Name { symbol: sym::MISSING_NAME, ctx: () } } @@ -150,23 +143,27 @@ impl Name { /// /// Use this method instead of comparing with `Self::missing()` as missing names /// (ideally should) have a `gensym` semantics. + #[inline] pub fn is_missing(&self) -> bool { - self == &Name::missing() + self.symbol == sym::MISSING_NAME } /// Generates a new name that attempts to be unique. Should only be used when body lowering and /// creating desugared locals and labels. The caller is responsible for picking an index /// that is stable across re-executions + #[inline] pub fn generate_new_name(idx: usize) -> Name { - Name::new_text(&format!("{idx}")) + Name::new_symbol_root(sym::RaGeneratedName::get(idx)) } /// Returns the tuple index this name represents if it is a tuple field. + #[inline] pub fn as_tuple_index(&self) -> Option { - self.symbol.as_str().parse().ok() + sym::Integer::as_uint(&self.symbol) } /// Whether this name needs to be escaped in the given edition via `r#`. + #[inline] pub fn needs_escape(&self, edition: Edition) -> bool { is_raw_identifier(self.symbol.as_str(), edition) } @@ -175,10 +172,12 @@ impl Name { /// /// Do not use this for user-facing text, use `display` instead to handle editions properly. // FIXME: This should take a database argument to hide the interning + #[inline] pub fn as_str(&self) -> &str { self.symbol.as_str() } + #[inline] pub fn display<'a>( &'a self, db: &dyn crate::db::ExpandDatabase, @@ -190,14 +189,17 @@ impl Name { // FIXME: Remove this in favor of `display`, see fixme on `as_str` #[doc(hidden)] + #[inline] pub fn display_no_db(&self, edition: Edition) -> impl fmt::Display + '_ { Display { name: self, edition } } + #[inline] pub fn symbol(&self) -> &Symbol { &self.symbol } + #[inline] pub fn is_generated(&self) -> bool { self.as_str().starts_with("") } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs index 12a48d65ac8c5..fb7edb1acd2f2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs @@ -5,7 +5,7 @@ use std::panic::AssertUnwindSafe; use base_db::all_crates; -use hir::{Symbol, import_map::ImportMap}; +use hir::{Symbol, import_map::ImportMap, sym}; use rustc_hash::FxHashMap; use salsa::{Cancelled, Database}; @@ -315,5 +315,5 @@ fn crate_name(db: &RootDatabase, krate: Crate) -> Symbol { .display_name .as_deref() .cloned() - .unwrap_or_else(|| Symbol::integer(salsa::plumbing::AsId::as_id(&krate).index() as usize)) + .unwrap_or_else(|| sym::Integer::get(salsa::plumbing::AsId::as_id(&krate).index() as usize)) } 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 99f8634bcb05a..03af25a9c0e1e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -580,7 +580,7 @@ impl TryToNav for hir::Field { |(FileRange { file_id, range: full_range }, focus_range)| { NavigationTarget::from_syntax( file_id, - Symbol::integer(self.index()), + sym::Integer::get(self.index()), focus_range, full_range, SymbolKind::Field, diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml index 39320ebd1cfef..1e6e8ff32f041 100644 --- a/src/tools/rust-analyzer/crates/intern/Cargo.toml +++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml @@ -19,6 +19,7 @@ hashbrown.workspace = true rustc-hash.workspace = true triomphe.workspace = true rayon.workspace = true +arrayvec.workspace = true [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs index 8b2d6e8717d23..72d32d1017747 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs @@ -73,10 +73,9 @@ impl TaggedArcPtr { /// /// You can only drop the `Arc` if the instance is dropped. #[inline] - pub(crate) unsafe fn try_as_arc_owned(self) -> Option>>> { + unsafe fn try_as_arc_owned(self) -> Option>>> { // Unpack the tag from the alignment niche - let tag = self.packed.as_ptr().addr() & Self::BOOL_BITS; - if tag != 0 { + if self.is_arc() { // Safety: We checked that the tag is non-zero -> true, so we are pointing to the data offset of an `Arc` Some(ManuallyDrop::new(unsafe { Arc::from_raw(self.pointer().as_ptr().cast::>()) @@ -86,6 +85,11 @@ impl TaggedArcPtr { } } + #[inline] + fn is_arc(&self) -> bool { + (self.packed.as_ptr().addr() & Self::BOOL_BITS) != 0 + } + #[inline] fn pack_arc(ptr: NonNull<*const str>) -> NonNull<*const str> { let packed_tag = true as usize; @@ -161,28 +165,6 @@ impl Symbol { unsafe { bucket.as_ref().0.clone() } } - pub fn integer(i: usize) -> Self { - match i { - 0 => symbols::INTEGER_0, - 1 => symbols::INTEGER_1, - 2 => symbols::INTEGER_2, - 3 => symbols::INTEGER_3, - 4 => symbols::INTEGER_4, - 5 => symbols::INTEGER_5, - 6 => symbols::INTEGER_6, - 7 => symbols::INTEGER_7, - 8 => symbols::INTEGER_8, - 9 => symbols::INTEGER_9, - 10 => symbols::INTEGER_10, - 11 => symbols::INTEGER_11, - 12 => symbols::INTEGER_12, - 13 => symbols::INTEGER_13, - 14 => symbols::INTEGER_14, - 15 => symbols::INTEGER_15, - i => Symbol::intern(&format!("{i}")), - } - } - pub fn empty() -> Self { symbols::__empty } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index db3c3c52009fe..ac6daaf00681f 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -1,15 +1,78 @@ //! Module defining all known symbols required by the rest of rust-analyzer. #![allow(non_upper_case_globals)] -use std::hash::{BuildHasher, BuildHasherDefault}; +use std::{ + hash::{BuildHasher, BuildHasherDefault}, + ptr::NonNull, +}; +use arrayvec::ArrayString; use dashmap::{DashMap, SharedValue}; use rustc_hash::FxHasher; use crate::{Symbol, symbol::TaggedArcPtr}; +macro_rules! last { + ( $($elems:literal,)+ ) => { + *[ $($elems,)* ].last().unwrap() + }; +} + +impl Integer { + #[inline] + pub fn as_uint(sym: &Symbol) -> Option { + if !sym.repr.is_arc() { + let elem_ref = sym.repr.pointer(); + // SAFETY: The types have the same layout. + let elem_ref = unsafe { std::mem::transmute::, &&str>(elem_ref) }; + Self::LIST.element_offset(elem_ref) + } else { + Self::as_uint_cold(sym) + } + } + + #[cold] + fn as_uint_cold(sym: &Symbol) -> Option { + sym.as_str().parse().ok() + } +} + macro_rules! define_symbols { - (@WITH_NAME: $($alias:ident = $value:literal,)* @PLAIN: $($name:ident,)*) => { + ( + @LISTS: $($list_type_name:ident = $list_prefix:literal + [ $($list_idx:literal,)+ ],)* + @WITH_NAME: $($alias:ident = $value:literal,)* + @PLAIN: $($name:ident,)* + ) => { + $( + pub enum $list_type_name {} + impl $list_type_name { + // Ensure we covered all numbers. + const LIST: &[&str; last!($($list_idx,)+) + 1] = { + static LIST: [&str; last!($($list_idx,)+) + 1] = [ $( concat!($list_prefix, $list_idx), )* ]; + &LIST + }; + + #[cold] + #[inline(never)] + fn create(idx: usize) -> Symbol { + use std::fmt::Write; + const MAX_LEN: usize = $list_prefix.len() + u64::MAX.ilog10() as usize + 1; + let mut s = ArrayString::::new(); + s.push_str($list_prefix); + _ = write!(s, "{idx}"); + Symbol::intern(&s) + } + + #[inline] + pub fn get(idx: usize) -> Symbol { + match Self::LIST.get(idx) { + Option::Some(s) => Symbol { repr: TaggedArcPtr::non_arc(s) }, + Option::None => Self::create(idx), + } + } + } + )* + // The strings should be in `static`s so that symbol equality holds. $( pub const $name: Symbol = { @@ -31,6 +94,14 @@ macro_rules! define_symbols { let hasher_ = dashmap_.hasher().clone(); let hash_one = |it_: &str| hasher_.hash_one(it_); { + $( + for s in $list_type_name::LIST { + let hash_ = hash_one(s); + let shard_idx_ = dashmap_.determine_shard(hash_ as usize); + let symbol = Symbol { repr: TaggedArcPtr::non_arc(s) }; + dashmap_.shards_mut()[shard_idx_].get_mut().insert(hash_, (symbol, SharedValue::new(())), |(x, _)| hash_one(x.as_str())); + } + )* $( let s = stringify!($name); let hash_ = hash_one(s); @@ -49,25 +120,37 @@ macro_rules! define_symbols { }; } define_symbols! { + @LISTS: + Integer = "" + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 255, + ], + RaGeneratedName = "" + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 255, + ], + @WITH_NAME: dotdotdot = "...", - INTEGER_0 = "0", - INTEGER_1 = "1", - INTEGER_2 = "2", - INTEGER_3 = "3", - INTEGER_4 = "4", - INTEGER_5 = "5", - INTEGER_6 = "6", - INTEGER_7 = "7", - INTEGER_8 = "8", - INTEGER_9 = "9", - INTEGER_10 = "10", - INTEGER_11 = "11", - INTEGER_12 = "12", - INTEGER_13 = "13", - INTEGER_14 = "14", - INTEGER_15 = "15", __empty = "", unsafe_ = "unsafe", in_ = "in", diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs index e8e7928c263f0..e135291d89dfe 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs @@ -222,7 +222,7 @@ fn expand_subtree( let index = ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |nest| nest.idx); builder.push(tt::Leaf::Literal(tt::Literal { - text_and_suffix: Symbol::integer(index), + text_and_suffix: sym::Integer::get(index), span: ctx.call_site, kind: tt::LitKind::Integer, suffix_len: 0, @@ -234,7 +234,7 @@ fn expand_subtree( 0 }); builder.push(tt::Leaf::Literal(tt::Literal { - text_and_suffix: Symbol::integer(length), + text_and_suffix: sym::Integer::get(length), span: ctx.call_site, kind: tt::LitKind::Integer, suffix_len: 0, @@ -278,7 +278,7 @@ fn expand_subtree( let res = count(binding, 0, depth.unwrap_or(0)); builder.push(tt::Leaf::Literal(tt::Literal { - text_and_suffix: Symbol::integer(res), + text_and_suffix: sym::Integer::get(res), span: ctx.call_site, suffix_len: 0, kind: tt::LitKind::Integer, From ec2adba389cc3afef8258513dae6740c2a85c9e6 Mon Sep 17 00:00:00 2001 From: Onyeka Obi Date: Sun, 3 May 2026 19:53:19 -0700 Subject: [PATCH 196/289] hir: use hir::Variant in DuplicateField, not VariantId Per review: do not expose hir-ty/hir-def types outside hir. Convert the lower-level VariantId to the public Variant wrapper at the inference-diagnostic boundary. --- src/tools/rust-analyzer/crates/hir/src/diagnostics.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index cfc59fc383397..b34c7b20c3239 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -31,7 +31,7 @@ use syntax::{ }; use triomphe::Arc; -use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type}; +use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, Variant}; pub use hir_def::VariantId; pub use hir_ty::{ @@ -271,7 +271,7 @@ pub struct NoSuchField { #[derive(Debug)] pub struct DuplicateField { pub field: InFile>>, - pub variant: VariantId, + pub variant: Variant, } #[derive(Debug)] @@ -778,7 +778,7 @@ impl<'db> AnyDiagnostic<'db> { } ExprOrPatId::PatId(pat) => source_map.pat_field_syntax(pat), }; - DuplicateField { field: expr_or_pat, variant }.into() + DuplicateField { field: expr_or_pat, variant: variant.into() }.into() } &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { MismatchedArgCount { call_expr: expr_syntax(call_expr)?, expected, found }.into() From a70e21632f9eb00ba87f24d43dbf9f43e73880bc Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 4 May 2026 08:45:42 +0530 Subject: [PATCH 197/289] syntaxeditor drains wrong side of changed ancestor, making nested edits broken --- .../crates/syntax/src/syntax_editor/edit_algo.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs index 27ea03ec09e7d..38c786b0f5689 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -114,6 +114,8 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { .rev() .position(|ancestor| ancestor.affected_range().contains_range(change.target_range())) { + let index = changed_ancestors.len() - 1 - index; + // Pop off any ancestors that aren't applicable changed_ancestors.drain((index + 1)..); @@ -284,7 +286,7 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { } } - for DependentChange { parent, child } in dependent_changes.into_iter() { + for DependentChange { parent, child } in dependent_changes.into_iter().rev() { let (input_ancestor, output_ancestor) = match &changes[parent as usize] { // No change will depend on an insert since changes can only depend on nodes in the root tree Change::Insert(_, _) | Change::InsertAll(_, _) => unreachable!(), From e20a47475a114e505b0ea6dd323ab2cd45c439f0 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 4 May 2026 08:46:00 +0530 Subject: [PATCH 198/289] add test in SyntaxEditor --- .../crates/syntax/src/syntax_editor.rs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index edd063ffd4617..704240e9ad997 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -701,6 +701,62 @@ mod tests { expect.assert_eq(&edit.new_root.to_string()); } + #[test] + fn test_dependent_change_prefers_nearest_changed_ancestor() { + let root = make::block_expr( + [], + Some( + make::block_expr( + [make::let_stmt( + make::ext::simple_ident_pat(make::name("second")).into(), + None, + Some(make::expr_literal("2").into()), + ) + .into()], + None, + ) + .into(), + ), + ); + + let (editor, root) = SyntaxEditor::with_ast_node(&root); + let make = editor.make(); + + let inner_block = + root.syntax().descendants().flat_map(ast::BlockExpr::cast).nth(1).unwrap(); + + let outer_replacement = make.block_expr([], Some(ast::Expr::BlockExpr(root.clone()))); + let inner_replacement = + make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); + + let first_let = make.let_stmt( + make::ext::simple_ident_pat(make::name("first")).into(), + None, + Some(make::expr_literal("1").into()), + ); + + editor.insert( + Position::first_child_of(inner_block.stmt_list().unwrap().syntax()), + first_let.syntax(), + ); + editor.replace(inner_block.syntax(), inner_replacement.syntax()); + editor.replace(root.syntax(), outer_replacement.syntax()); + + let edit = editor.finish(); + + let expect = expect![[r#" + { + { + { + let first = 1;{ + let second = 2; + } + } + } + }"#]]; + expect.assert_eq(&edit.new_root.to_string()); + } + #[test] fn test_replace_root_with_dependent() { let root = make::block_expr( From 2c17d2a1eab4d3a7dc81203aacb822a6d1939808 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 4 May 2026 10:45:37 +0530 Subject: [PATCH 199/289] add find_element to syntax_edit --- .../rust-analyzer/crates/syntax/src/syntax_editor.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index edd063ffd4617..e60933163f788 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -216,6 +216,17 @@ impl SyntaxEdit { pub fn find_annotation(&self, annotation: SyntaxAnnotation) -> &[SyntaxElement] { self.annotations.get(&annotation).as_ref().map_or(&[], |it| it.as_slice()) } + + pub fn find_element(&self, old_node: &SyntaxNode) -> Option { + let old_root_start = self.old_root.text_range().start(); + let old_start = old_node.text_range().start() - old_root_start; + let new_root_start = self.new_root.text_range().start(); + let kind = old_node.kind(); + + self.new_root + .descendants() + .find(|it| it.kind() == kind && it.text_range().start() - new_root_start == old_start) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] From 53e4688bdde5fe47f46cc9ecb6d568f83c729811 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 4 May 2026 10:45:50 +0530 Subject: [PATCH 200/289] update missing_fields to use find_element --- .../src/handlers/missing_fields.rs | 34 +++---------------- 1 file changed, 4 insertions(+), 30 deletions(-) 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 5896fb2cb14af..607f0cbd233db 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 @@ -170,7 +170,8 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingFields) -> Option { @@ -193,40 +194,13 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingFields) -> Option, -) -> Option { - let old_range = field_list.syntax().text_range(); - let kind = field_list.syntax().kind(); - field_list.add_fields(&editor, fields); - let edit = editor.finish(); - edit.new_root() - .descendants() - .find(|it| it.kind() == kind && it.text_range().start() == old_range.start()) -} - -fn add_record_pat_fields( - editor: SyntaxEditor, - field_list: &ast::RecordPatFieldList, - fields: Vec, -) -> Option { - let old_range = field_list.syntax().text_range(); - let kind = field_list.syntax().kind(); - field_list.add_fields(&editor, fields); - let edit = editor.finish(); - edit.new_root() - .descendants() - .find(|it| it.kind() == kind && it.text_range().start() == old_range.start()) -} - fn make_ty( ty: &hir::Type<'_>, db: &dyn HirDatabase, From 2d2fa27e371e513a670bf26d917d0ec1d6b026bf Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 4 May 2026 12:23:28 +0300 Subject: [PATCH 201/289] Port array and ref exprs inference from rustc --- .../crates/hir-ty/src/infer/expr.rs | 217 +++++++++++------- .../crates/hir-ty/src/infer/unify.rs | 32 ++- .../crates/hir-ty/src/tests/coercion.rs | 18 ++ .../src/handlers/invalid_cast.rs | 2 +- .../crates/ide/src/hover/tests.rs | 1 + 5 files changed, 185 insertions(+), 85 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 680800244bf9e..fb9b61d04f01c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -610,34 +610,13 @@ impl<'db> InferenceContext<'_, 'db> { self.deferred_cast_checks.push(CastCheck::new(tgt_expr, *expr, expr_ty, cast_ty)); cast_ty } - Expr::Ref { expr, rawness, mutability } => { - let mutability = lower_mutability(*mutability); - let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = expected - .only_has_type(&mut self.table) - .as_ref() - .and_then(|t| t.as_reference_or_ptr()) - { - if exp_mutability == Mutability::Mut && mutability == Mutability::Not { - // FIXME: record type error - expected mut reference but found shared ref, - // which cannot be coerced - } - if exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr { - // FIXME: record type error - expected reference but found ptr, - // which cannot be coerced - } - Expectation::rvalue_hint(self, exp_inner) - } else { - Expectation::none() - }; - let inner_ty = self.infer_expr_inner(*expr, &expectation, ExprIsRead::Yes); - match rawness { - Rawness::RawPtr => Ty::new_ptr(self.interner(), inner_ty, mutability), - Rawness::Ref => { - let lt = self.table.next_region_var(tgt_expr.into()); - Ty::new_ref(self.interner(), lt, inner_ty, mutability) - } - } - } + Expr::Ref { expr, rawness, mutability } => self.infer_ref_expr( + *rawness, + lower_mutability(*mutability), + *expr, + expected, + tgt_expr, + ), &Expr::Box { expr } => self.infer_expr_box(expr, expected), Expr::UnaryOp { expr, op } => self.infer_unop_expr(*op, *expr, expected, tgt_expr), Expr::BinaryOp { lhs, rhs, op } => match op { @@ -772,7 +751,12 @@ impl<'db> InferenceContext<'_, 'db> { Ty::new_tup(self.interner(), &tys) } - Expr::Array(array) => self.infer_expr_array(tgt_expr, array, expected), + Expr::Array(Array::ElementList { elements }) => { + self.infer_array_elements_expr(elements, expected, tgt_expr) + } + Expr::Array(Array::Repeat { initializer, repeat }) => { + self.infer_array_repeat_expr(*initializer, *repeat, expected, tgt_expr) + } Expr::Literal(lit) => match lit { Literal::Bool(..) => self.types.types.bool, Literal::String(..) => self.types.types.static_str_ref, @@ -968,6 +952,54 @@ impl<'db> InferenceContext<'_, 'db> { ty } + fn infer_ref_expr( + &mut self, + rawness: Rawness, + mutbl: Mutability, + oprnd: ExprId, + expected: &Expectation<'db>, + expr: ExprId, + ) -> Ty<'db> { + let hint = expected.only_has_type(&mut self.table).map_or(Expectation::None, |ty| { + match self.table.resolve_vars_with_obligations(ty).kind() { + TyKind::Ref(_, ty, _) | TyKind::RawPtr(ty, _) => { + if self.is_syntactic_place_expr(oprnd) { + // Places may legitimately have unsized types. + // For example, dereferences of a wide pointer and + // the last field of a struct can be unsized. + Expectation::has_type(ty) + } else { + Expectation::rvalue_hint(self, ty) + } + } + _ => Expectation::None, + } + }); + let ty = self.infer_expr_inner(oprnd, &hint, ExprIsRead::No); + + match rawness { + Rawness::RawPtr => Ty::new_ptr(self.interner(), ty, mutbl), + Rawness::Ref => { + // Note: at this point, we cannot say what the best lifetime + // is to use for resulting pointer. We want to use the + // shortest lifetime possible so as to avoid spurious borrowck + // errors. Moreover, the longest lifetime will depend on the + // precise details of the value whose address is being taken + // (and how long it is valid), which we don't know yet until + // type inference is complete. + // + // Therefore, here we simply generate a region variable. The + // region inferencer will then select a suitable value. + // Finally, borrowck will infer the value of the region again, + // this time with enough precision to check that the value + // whose address was taken can actually be made to live as long + // as it needs to live. + let region = self.table.next_region_var(expr.into()); + Ty::new_ref(self.interner(), region, ty, mutbl) + } + } + } + fn infer_await_expr(&mut self, expr: ExprId, awaitee: ExprId) -> Ty<'db> { let awaitee_ty = self.infer_expr_no_expect(awaitee, ExprIsRead::Yes); let (Some(into_future), Some(into_future_output)) = @@ -1333,68 +1365,93 @@ impl<'db> InferenceContext<'_, 'db> { oprnd_t } - fn infer_expr_array( + fn infer_array_repeat_expr( &mut self, - expr: ExprId, - array: &Array, + element: ExprId, + count: ExprId, expected: &Expectation<'db>, + expr: ExprId, ) -> Ty<'db> { - let elem_ty = match expected - .to_option(&mut self.table) - .map(|t| self.table.try_structurally_resolve_type(expr.into(), t).kind()) - { - Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st, - _ => self.table.next_ty_var(expr.into()), + let interner = self.interner(); + let usize = self.types.types.usize; + let count_ct = match self.store[count] { + Expr::Underscore => { + self.write_expr_ty(count, usize); + self.table.next_const_var(count.into()) + } + _ => { + self.infer_expr(count, &Expectation::HasType(usize), ExprIsRead::Yes); + consteval::eval_to_const(count, self) + } }; + let count = self.table.try_structurally_resolve_const(count.into(), count_ct); + let count = self.insert_const_vars_shallow(count); - let krate = self.resolver.krate(); + let uty = match expected { + Expectation::HasType(uty) => uty.builtin_index(), + _ => None, + }; - let expected = Expectation::has_type(elem_ty); - let (elem_ty, len) = match array { - Array::ElementList { elements, .. } if elements.is_empty() => { - (elem_ty, consteval::usize_const(self.db, Some(0), krate)) + let t = match uty { + Some(uty) => { + self.infer_expr_coerce(element, &Expectation::has_type(uty), ExprIsRead::Yes); + uty } - Array::ElementList { elements, .. } => { - let mut coerce = CoerceMany::with_coercion_sites(elem_ty, elements); - for &expr in elements.iter() { - let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes); - coerce.coerce( - self, - &ObligationCause::new(expr), - expr, - cur_elem_ty, - ExprIsRead::Yes, - ); - } - ( - coerce.complete(self), - consteval::usize_const(self.db, Some(elements.len() as u128), krate), - ) + None => { + let ty = self.table.next_ty_var(element.into()); + self.infer_expr(element, &Expectation::has_type(ty), ExprIsRead::Yes); + ty } - &Array::Repeat { initializer, repeat } => { - self.infer_expr_coerce( - initializer, - &Expectation::has_type(elem_ty), - ExprIsRead::Yes, - ); - let usize = self.types.types.usize; - let len = match self.store[repeat] { - Expr::Underscore => { - self.write_expr_ty(repeat, usize); - self.table.next_const_var(repeat.into()) - } - _ => { - self.infer_expr(repeat, &Expectation::HasType(usize), ExprIsRead::Yes); - consteval::eval_to_const(repeat, self) - } - }; + }; + + // We defer checking whether the element type is `Copy` as it is possible to have + // an inference variable as a repeat count and it seems unlikely that `Copy` would + // have inference side effects required for type checking to succeed. + // FIXME: Do it here like rustc. + // self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count)); + + let ty = Ty::new_array_with_const_len(interner, t, count); + self.table.register_wf_obligation(ty.into(), ObligationCause::new(expr)); + ty + } - (elem_ty, len) + fn infer_array_elements_expr( + &mut self, + args: &[ExprId], + expected: &Expectation<'db>, + expr: ExprId, + ) -> Ty<'db> { + let element_ty = if !args.is_empty() { + let coerce_to = expected + .to_option(&mut self.table) + .and_then(|uty| { + self.table + .resolve_vars_with_obligations(uty) + .builtin_index() + // Avoid using the original type variable as the coerce_to type, as it may resolve + // during the first coercion instead of being the LUB type. + .filter(|t| !self.table.resolve_vars_with_obligations(*t).is_ty_var()) + }) + .unwrap_or_else(|| self.table.next_ty_var(expr.into())); + let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); + + for &e in args { + // FIXME: the element expectation should use + // `try_structurally_resolve_and_adjust_for_branches` just like in `if` and `match`. + // While that fixes nested coercion, it will break [some + // code like this](https://github.com/rust-lang/rust/pull/140283#issuecomment-2958776528). + // If we find a way to support recursive tuple coercion, this break can be avoided. + let e_ty = + self.infer_expr_inner(e, &Expectation::has_type(coerce_to), ExprIsRead::Yes); + let cause = ObligationCause::new(e); + coerce.coerce(self, &cause, e, e_ty, ExprIsRead::Yes); } + coerce.complete(self) + } else { + self.table.next_ty_var(expr.into()) }; - // Try to evaluate unevaluated constant, and insert variable if is not possible. - let len = self.insert_const_vars_shallow(len); - Ty::new_array_with_const_len(self.interner(), elem_ty, len) + let array_len = args.len() as u128; + Ty::new_array(self.interner(), element_ty, array_len) } pub(super) fn infer_return(&mut self, expr: ExprId) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 6a34c5b8e5b6b..bb6e383e7d882 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -7,7 +7,7 @@ use hir_def::{ExpressionStoreOwnerId, GenericParamId, TraitId}; use rustc_hash::FxHashSet; use rustc_type_ir::{ TyVid, TypeFoldable, TypeVisitableExt, - inherent::{GenericArg as _, IntoKind, Ty as _}, + inherent::{Const as _, GenericArg as _, IntoKind, Ty as _}, solve::Certainty, }; use smallvec::SmallVec; @@ -17,9 +17,9 @@ use crate::{ InferenceDiagnostic, Span, db::HirDatabase, next_solver::{ - Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, - ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, TyKind, - TypingMode, + Canonical, ClauseKind, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, + GenericArgs, ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, + TyKind, TypingMode, fulfill::{FulfillmentCtxt, NextSolverError}, infer::{ DbInternerInferExt, InferCtxt, InferOk, @@ -342,6 +342,30 @@ impl<'db> InferenceTable<'db> { } } + pub(crate) fn try_structurally_resolve_const( + &mut self, + sp: Span, + ct: Const<'db>, + ) -> Const<'db> { + let ct = self.resolve_vars_with_obligations(ct); + + if let ConstKind::Unevaluated(..) = ct.kind() { + let result = self + .infer_ctxt + .at(&ObligationCause::new(sp), self.param_env) + .structurally_normalize_const(ct, &mut self.fulfillment_cx); + match result { + Ok(normalized_ct) => normalized_ct, + Err(errors) => { + self.trait_errors.extend(errors); + Const::new_error(self.interner(), ErrorGuaranteed) + } + } + } else { + ct + } + } + pub(crate) fn snapshot(&mut self) -> CombinedSnapshot { self.infer_ctxt.start_snapshot() } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index 05dbb1a8ac23f..a7f65d4fe89aa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -1036,3 +1036,21 @@ fn f() { "#, ); } + +#[test] +fn regression_22270() { + check_no_mismatches( + r#" +fn a() {} +fn b() {} + +fn foo(x: [T; N]) -> Vec { + loop {} +} + +fn bar() { + foo([a, b]); +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs index c004ee31aedaa..bd8fa69e28707 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -990,7 +990,7 @@ fn main() { fn rustc_issue_106883() { check_diagnostics_with_disabled( r#" -//- minicore: sized, deref +//- minicore: sized, deref, coerce_unsized, unsize use core::ops::Deref; struct Foo; diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 89d467cef8713..6d9c6d4c21dfb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -7152,6 +7152,7 @@ fn f() { let expr = [1, 2, $03$0, 4] } fn hover_range_functions() { check_hover_range( r#" +//- minicore: unsize, coerce_unsized fn f(a: &[T]) { } fn b() { $0f$0(&[1, 2, 3, 4, 5]); } "#, From 2ea3a0826e6f9e9f9b718dc40e0dcb73526825e5 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 4 May 2026 12:38:41 +0200 Subject: [PATCH 202/289] fix: Don't fetch diagnostics until proc-macros are loaded --- .../project-model/src/build_dependencies.rs | 2 +- .../crates/rust-analyzer/src/main_loop.rs | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 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 aff5391697414..e44af96f36435 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 @@ -382,7 +382,6 @@ impl WorkspaceBuildScripts { } Message::CompilerArtifact(message) => { with_output_for(&message.package_id, &mut |name, data| { - progress(format!("proc-macro {name} built")); if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt { data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro; } @@ -392,6 +391,7 @@ impl WorkspaceBuildScripts { .kind .contains(&cargo_metadata::TargetKind::ProcMacro) { + progress(format!("proc-macro {name} built")); data.proc_macro_dylib_path = match message.filenames.iter().find(|file| is_dylib(file)) { Some(filename) => { 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 f5b3658ea90ca..a9ce6f728b6a5 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 @@ -660,8 +660,20 @@ impl GlobalState { let subscriptions = subscriptions.clone(); // Do not fetch semantic diagnostics (and populate query results) if we haven't even // loaded the initial workspace yet. - let fetch_semantic = - self.vfs_done && self.fetch_workspaces_queue.last_op_result().is_some(); + // + // Only fetch semantic diagnostics when + // - we have fully populated the VFS + // - have a workspace + // - have finished fetching the build data once + // - and have finished loading the proc-macros once + let fetch_semantic = self.vfs_done + && self.fetch_workspaces_queue.last_op_result().is_some() + && (!self.config.run_build_scripts(None) + || (self.fetch_build_data_queue.last_op_result().is_none() + && !self.fetch_build_data_queue.op_in_progress())) + && (!self.config.expand_proc_macros() + || (self.fetch_proc_macros_queue.last_op_result().is_none() + && !self.fetch_proc_macros_queue.op_in_progress())); move |sender| { // We aren't observing the semantics token cache here let snapshot = AssertUnwindSafe(&snapshot); From 9e00c0fddc09f6b08ae6ba297d3d6d4e43b145a0 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 4 May 2026 14:57:26 +0300 Subject: [PATCH 203/289] Add missing solver lang items --- .../crates/hir-ty/src/next_solver/interner.rs | 22 +++++++++---------- .../src/handlers/unimplemented_trait.rs | 19 ++++++++++++++++ 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index bdbcaee4f6192..fb70734872d35 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1461,10 +1461,6 @@ impl<'db> Interner for DbInterner<'db> { SolverTraitLangItem, self, def_id.0, lang_item; ignore = { - AsyncFnKindHelper, - BikeshedGuaranteedNoDrop, - FusedIterator, - Field, AsyncFnOnceOutput, // This is incorrectly marked as `SolverTraitLangItem`, and is not used by the solver. } @@ -1493,6 +1489,10 @@ impl<'db> Interner for DbInterner<'db> { AsyncFnMut, AsyncFnOnce, TrivialClone, + AsyncFnKindHelper, + BikeshedGuaranteedNoDrop, + FusedIterator, + Field, ) } @@ -1509,10 +1509,7 @@ impl<'db> Interner for DbInterner<'db> { SolverLangItem, self, id; ignore = { - AsyncFnKindUpvars, DynMetadata, - FieldBase, - FieldType, } Metadata, @@ -1522,6 +1519,9 @@ impl<'db> Interner for DbInterner<'db> { CallRefFuture, CallOnceFuture, AsyncFnOnceOutput, + AsyncFnKindUpvars, + FieldBase, + FieldType, ) } SolverDefId::AdtId(AdtId::StructId(id)) => { @@ -1553,10 +1553,6 @@ impl<'db> Interner for DbInterner<'db> { SolverTraitLangItem, self, def_id.0; ignore = { - AsyncFnKindHelper, - BikeshedGuaranteedNoDrop, - FusedIterator, - Field, AsyncFnOnceOutput, // This is incorrectly marked as `SolverTraitLangItem`, and is not used by the solver. } @@ -1585,6 +1581,10 @@ impl<'db> Interner for DbInterner<'db> { AsyncFnMut, AsyncFnOnce, TrivialClone, + AsyncFnKindHelper, + BikeshedGuaranteedNoDrop, + FusedIterator, + Field, ) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs index 4326aec4581d2..bacf273f76855 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs @@ -50,4 +50,23 @@ fn bar() { "#, ); } + + #[test] + fn async_closure_does_not_trigger() { + check_diagnostics( + r#" +//- minicore: async_fn +fn spawn_in(_f: AsyncFn) +where + AsyncFn: AsyncFnOnce(), +{ +} + +fn foo() { + spawn_in(async move || {}); +} + + "#, + ); + } } From babb6359a342538cc88d7bfc7cb9f82a6dc11057 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 4 May 2026 16:19:59 +0300 Subject: [PATCH 204/289] Infer the expected type as the return type for async blocks defined by async fns Ported from rustc. --- .../crates/hir-ty/src/infer/closure.rs | 191 ++++++++++++++++-- .../crates/hir-ty/src/tests/coercion.rs | 15 ++ .../crates/hir-ty/src/tests/regression.rs | 10 +- .../crates/test-utils/src/minicore.rs | 31 +++ 4 files changed, 230 insertions(+), 17 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 218d8e2f3e0bc..2679efca7dc88 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -10,12 +10,12 @@ use hir_def::{ type_ref::TypeRefId, }; use rustc_type_ir::{ - ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, CoroutineClosureArgs, - CoroutineClosureArgsParts, Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, - TypeVisitor, + AliasTyKind, ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, + CoroutineClosureArgs, CoroutineClosureArgsParts, InferTy, Interner, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, inherent::{BoundExistentialPredicates, GenericArgs as _, IntoKind, Ty as _}, }; -use tracing::debug; +use tracing::{debug, instrument}; use crate::{ FnAbi, Span, @@ -86,8 +86,14 @@ impl<'db> InferenceContext<'_, 'db> { None => (None, None), }; - let ClosureSignatures { bound_sig, mut liberated_sig } = - self.sig_of_closure(closure_expr, args, arg_types, ret_type, expected_sig); + let ClosureSignatures { bound_sig, mut liberated_sig } = self.sig_of_closure( + closure_expr, + args, + arg_types, + ret_type, + expected_sig, + closure_kind, + ); debug!(?bound_sig, ?liberated_sig); @@ -685,6 +691,7 @@ impl<'db> InferenceContext<'_, 'db> { decl_input_tys: &[Option], decl_output_ty: Option, expected_sig: Option>, + closure_kind: ClosureKind, ) -> ClosureSignatures<'db> { if let Some(e) = expected_sig { self.sig_of_closure_with_expectation( @@ -693,9 +700,15 @@ impl<'db> InferenceContext<'_, 'db> { decl_input_tys, decl_output_ty, e, + closure_kind, ) } else { - self.sig_of_closure_no_expectation(closure_expr, decl_input_tys, decl_output_ty) + self.sig_of_closure_no_expectation( + closure_expr, + decl_input_tys, + decl_output_ty, + closure_kind, + ) } } @@ -706,8 +719,10 @@ impl<'db> InferenceContext<'_, 'db> { closure_expr: ExprId, decl_inputs: &[Option], decl_output: Option, + closure_kind: ClosureKind, ) -> ClosureSignatures<'db> { - let bound_sig = self.supplied_sig_of_closure(closure_expr, decl_inputs, decl_output); + let bound_sig = + self.supplied_sig_of_closure(closure_expr, decl_inputs, decl_output, closure_kind); self.closure_sigs(bound_sig) } @@ -766,6 +781,7 @@ impl<'db> InferenceContext<'_, 'db> { decl_input_tys: &[Option], decl_output_ty: Option, expected_sig: PolyFnSig<'db>, + closure_kind: ClosureKind, ) -> ClosureSignatures<'db> { // Watch out for some surprises and just ignore the // expectation if things don't see to match up with what we @@ -775,6 +791,7 @@ impl<'db> InferenceContext<'_, 'db> { closure_expr, decl_input_tys, decl_output_ty, + closure_kind, ); } else if expected_sig.skip_binder().inputs_and_output.len() != decl_input_tys.len() + 1 { return self.sig_of_closure_with_mismatched_number_of_arguments( @@ -815,11 +832,15 @@ impl<'db> InferenceContext<'_, 'db> { decl_input_tys, decl_output_ty, closure_sigs, + closure_kind, ) { Ok(infer_ok) => self.table.register_infer_ok(infer_ok), - Err(_) => { - self.sig_of_closure_no_expectation(closure_expr, decl_input_tys, decl_output_ty) - } + Err(_) => self.sig_of_closure_no_expectation( + closure_expr, + decl_input_tys, + decl_output_ty, + closure_kind, + ), } } @@ -843,13 +864,18 @@ impl<'db> InferenceContext<'_, 'db> { decl_input_tys: &[Option], decl_output_ty: Option, mut expected_sigs: ClosureSignatures<'db>, + closure_kind: ClosureKind, ) -> InferResult<'db, ClosureSignatures<'db>> { // Get the signature S that the user gave. // // (See comment on `sig_of_closure_with_expectation` for the // meaning of these letters.) - let supplied_sig = - self.supplied_sig_of_closure(closure_expr, decl_input_tys, decl_output_ty); + let supplied_sig = self.supplied_sig_of_closure( + closure_expr, + decl_input_tys, + decl_output_ty, + closure_kind, + ); debug!(?supplied_sig); @@ -923,12 +949,46 @@ impl<'db> InferenceContext<'_, 'db> { closure_expr: ExprId, decl_inputs: &[Option], decl_output: Option, + closure_kind: ClosureKind, ) -> PolyFnSig<'db> { let interner = self.interner(); let supplied_return = match decl_output { Some(output) => self.make_body_ty(output), - None => self.table.next_ty_var(closure_expr.into()), + None => match closure_kind { + // In the case of the async block that we create for a function body, + // we expect the return type of the block to match that of the enclosing + // function. + ClosureKind::Coroutine { + kind: CoroutineKind::Async, + source: CoroutineSource::Fn, + } => { + debug!("closure is async fn body"); + self.deduce_future_output_from_obligations(closure_expr).unwrap_or_else(|| { + // AFAIK, deducing the future output + // always succeeds *except* in error cases + // like #65159. I'd like to return Error + // here, but I can't because I can't + // easily (and locally) prove that we + // *have* reported an + // error. --nikomatsakis + self.table.next_ty_var(closure_expr.into()) + }) + } + // All `gen {}` and `async gen {}` must return unit. + ClosureKind::Coroutine { + kind: CoroutineKind::Gen | CoroutineKind::AsyncGen, + .. + } => self.types.types.unit, + + // For async blocks, we just fall back to `_` here. + // For closures/coroutines, we know nothing about the return + // type unless it was supplied. + ClosureKind::Coroutine { kind: CoroutineKind::Async, .. } + | ClosureKind::OldCoroutine(_) + | ClosureKind::Closure + | ClosureKind::CoroutineClosure(_) => self.table.next_ty_var(closure_expr.into()), + }, }; // First, convert the types that the user supplied (if any). let supplied_arguments = decl_inputs.iter().map(|&input| match input { @@ -945,6 +1005,109 @@ impl<'db> InferenceContext<'_, 'db> { )) } + /// Invoked when we are translating the coroutine that results + /// from desugaring an `async fn`. Returns the "sugared" return + /// type of the `async fn` -- that is, the return type that the + /// user specified. The "desugared" return type is an `impl + /// Future`, so we do this by searching through the + /// obligations to extract the `T`. + #[instrument(skip(self), level = "debug", ret)] + fn deduce_future_output_from_obligations(&mut self, body_def_id: ExprId) -> Option> { + let ret_coercion = self + .return_coercion + .as_ref() + .unwrap_or_else(|| panic!("async fn coroutine outside of a fn")); + + let ret_ty = ret_coercion.expected_ty(); + let ret_ty = self.table.resolve_vars_with_obligations(ret_ty); + + let get_future_output = |predicate: Predicate<'db>| { + // Search for a pending obligation like + // + // `::Output = T` + // + // where R is the return type we are expecting. This type `T` + // will be our output. + let bound_predicate = predicate.kind(); + if let PredicateKind::Clause(ClauseKind::Projection(proj_predicate)) = + bound_predicate.skip_binder() + { + self.deduce_future_output_from_projection(bound_predicate.rebind(proj_predicate)) + } else { + None + } + }; + + let output_ty = match ret_ty.kind() { + TyKind::Infer(InferTy::TyVar(ret_vid)) => self + .table + .obligations_for_self_ty(ret_vid) + .into_iter() + .find_map(|obligation| get_future_output(obligation.predicate))?, + TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { .. }, .. }) => { + return Some(self.types.types.error); + } + TyKind::Alias(AliasTy { kind: AliasTyKind::Opaque { def_id }, args, .. }) => def_id + .expect_opaque_ty() + .predicates(self.db) + .iter_instantiated_copied(self.interner(), &args) + .find_map(|p| get_future_output(p.as_predicate()))?, + TyKind::Error(_) => return Some(ret_ty), + _ => { + panic!("invalid async fn coroutine return type: {ret_ty:?}") + } + }; + + Some(output_ty) + } + + /// Given a projection like + /// + /// `::Output = T` + /// + /// where `X` is some type that has no late-bound regions, returns + /// `Some(T)`. If the projection is for some other trait, returns + /// `None`. + fn deduce_future_output_from_projection( + &self, + predicate: PolyProjectionPredicate<'db>, + ) -> Option> { + debug!("deduce_future_output_from_projection(predicate={:?})", predicate); + + // We do not expect any bound regions in our predicate, so + // skip past the bound vars. + let Some(predicate) = predicate.no_bound_vars() else { + debug!("deduce_future_output_from_projection: has late-bound regions"); + return None; + }; + + // Check that this is a projection from the `Future` trait. + let trait_def_id = predicate.projection_term.trait_def_id(self.interner()).0; + if Some(trait_def_id) != self.lang_items.Future { + debug!("deduce_future_output_from_projection: not a future"); + return None; + } + + // The `Future` trait has only one associated item, `Output`, + // so check that this is what we see. + let output_assoc_item = self.lang_items.FutureOutput; + if output_assoc_item != Some(predicate.projection_term.def_id.expect_type_alias()) { + panic!( + "projecting associated item `{:?}` from future, which is not Output `{:?}`", + predicate.projection_term.kind(self.interner()), + output_assoc_item, + ); + } + + // Extract the type from the projection. Note that there can + // be no bound variables in this type because the "self type" + // does not have any regions in it. + let output_ty = self.resolve_vars_if_possible(predicate.term); + debug!("deduce_future_output_from_projection: output_ty={:?}", output_ty); + // This is a projection on a Fn trait so will always be a type. + Some(output_ty.expect_type()) + } + /// Converts the types that the user supplied, in case that doing /// so should yield an error, but returns back a signature where /// all parameters are of type `ty::Error`. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index a7f65d4fe89aa..db06d55278b2b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -1054,3 +1054,18 @@ fn bar() { "#, ); } + +#[test] +fn async_fn_ret() { + check_no_mismatches( + r#" +//- minicore: coerce_unsized, unsize, future, index, slice, range +async fn foo(a: &[i32]) -> &[i32] { + if true { + return &[]; + } + &a[..0] +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index a5f349e59338f..87e6521e63cf0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2,6 +2,8 @@ mod new_solver; use expect_test::expect; +use crate::tests::check; + use super::{check_infer, check_no_mismatches, check_types}; #[test] @@ -2013,18 +2015,20 @@ where #[test] fn tait_async_stack_overflow_17199() { - check_types( + // The error here is because we don't support TAITs. + check( r#" //- minicore: fmt, future type Foo = impl core::fmt::Debug; async fn foo() -> Foo { () + // ^^ expected impl Debug, got () } async fn test() { let t = foo().await; - // ^ impl Debug + // ^ type: impl Debug } "#, ); @@ -2234,7 +2238,7 @@ type Bar = impl Foo; async fn f() -> Bar {} "#, expect![[r#" - 64..66 '{}': () + 64..66 '{}': impl Foo + ?Sized "#]], ); } diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 29775590ea8cc..802e6ab8ce17c 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -701,6 +701,37 @@ pub mod ops { unsafe impl SliceIndex<[T]> for usize { type Output = T; } + + macro_rules! impl_index_range { + ( $($range:ty,)* ) => { + $( + unsafe impl SliceIndex<[T]> for $range { + type Output = [T]; + } + )* + } + } + + // region:range + impl_index_range!( + crate::ops::RangeFull, + crate::ops::Range, + crate::ops::RangeFrom, + crate::ops::RangeTo, + crate::ops::RangeInclusive, + crate::ops::RangeToInclusive, + ); + // endregion:range + + // region:new_range + impl_index_range!( + crate::range::Range, + crate::range::RangeFrom, + crate::range::RangeInclusive, + crate::range::RangeToInclusive, + ); + // endregion:new_range + // endregion:slice } pub use self::index::{Index, IndexMut}; From 8aff8df3899c27c043051c6582cb748f5916134b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 4 May 2026 16:39:35 +0300 Subject: [PATCH 205/289] Prepare for merging from rust-lang/rust This updates the rust-version file to 1d72d7e8136faaebad3a85eeed432e6ea1b2ffab. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index e9fc6c4cd023e..fb590a192fca0 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -e22c616e4e87914135c1db261a03e0437255335e +1d72d7e8136faaebad3a85eeed432e6ea1b2ffab From 71346acde2180f7a66ae0c946489178a2e96ec1f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 4 May 2026 16:45:17 +0300 Subject: [PATCH 206/289] Remove usage of `references_error()` in upvar inference Generally right now r-a cannot use this method, as lifetime errors are too common. We could replace it by `references_no_lt_error()`, but I decided to just remove it since not inferring the things in that `if` will cause writeback to error (with a cryptic error) on the coroutine closure as having unresolved infer vars. We could change that, but removing the condition seems to be unable to cause harm as nothing in this `if` relies on error types to not be present. --- .../hir-ty/src/infer/closure/analysis.rs | 6 ++---- .../src/handlers/type_must_be_known.rs | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs index ce7931bb3c829..979551f316e75 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -44,7 +44,7 @@ use macros::{TypeFoldable, TypeVisitable}; use rustc_ast_ir::Mutability; use rustc_hash::{FxBuildHasher, FxHashMap}; use rustc_type_ir::{ - BoundVar, ClosureKind, TypeVisitableExt as _, + BoundVar, ClosureKind, inherent::{AdtDef as _, GenericArgs as _, IntoKind as _, Ty as _}, }; use smallvec::{SmallVec, smallvec}; @@ -403,9 +403,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // For coroutine-closures, we additionally must compute the // `coroutine_captures_by_ref_ty` type, which is used to generate the by-ref // version of the coroutine-closure's output coroutine. - if let UpvarArgs::CoroutineClosure(args) = args - && !args.references_error() - { + if let UpvarArgs::CoroutineClosure(args) = args { let closure_env_region: Region<'_> = Region::new_bound( self.interner(), rustc_type_ir::INNERMOST, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs index 30d8165e0a697..1bae982079202 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs @@ -112,6 +112,27 @@ fn bar(_inner: fn() -> *const T) {} fn foo() { bar(const { || 0 as *const i32 }) +} + "#, + ); + } + + #[test] + fn async_closure_does_not_trigger() { + check_diagnostics( + r#" +//- minicore: async_fn +struct Task(R); +fn spawn_in(_f: AsyncFn) -> Task +where + R: 'static, + AsyncFn: AsyncFnOnce(&()) -> R + 'static, +{ + loop {} +} + +fn foo() { + spawn_in(async move |cx| {}); } "#, ); From 3f11d447169f971e61cce28759cc23fc6972bebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 4 May 2026 16:49:32 +0300 Subject: [PATCH 207/289] Fix unimplemented-trait diagnostic name --- .../crates/ide-diagnostics/src/handlers/unimplemented_trait.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs index bacf273f76855..d94ceef642f77 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs @@ -2,7 +2,7 @@ use hir::HirDisplay; use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; -// Diagnostic: type-must-be-known +// Diagnostic: unimplemented-trait // // This diagnostic is triggered when rust-analyzer cannot infer some type. pub(crate) fn unimplemented_trait<'db>( From e9c05ff6310e232b1aa2ae4fd016cc923c18f99e Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 4 May 2026 15:07:21 +0000 Subject: [PATCH 208/289] Update ICU4X to 2.2 --- Cargo.lock | 72 ++++++++++--------- compiler/rustc_baked_icu_data/Cargo.toml | 6 +- .../src/data/list_and_v1.rs.data | 10 +-- compiler/rustc_baked_icu_data/src/data/mod.rs | 2 +- compiler/rustc_error_messages/Cargo.toml | 4 +- src/tools/tidy/src/deps.rs | 1 + 6 files changed, 49 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4648a12da487b..68cc7d0faddaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -756,7 +756,7 @@ checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" dependencies = [ "serde", "termcolor", - "unicode-width 0.1.14", + "unicode-width 0.2.2", ] [[package]] @@ -1758,12 +1758,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -1771,9 +1772,9 @@ dependencies = [ [[package]] name = "icu_list" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a0b7b126e2fc42777d3c348611553d540bd3683caa39b387c5dd1036bb21a8" +checksum = "aeeaf517689324395bed4767f7c65504f5455942ed4c14ee54c2087ca00b816e" dependencies = [ "icu_provider", "regex-automata", @@ -1784,9 +1785,9 @@ dependencies = [ [[package]] name = "icu_locale" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532b11722e350ab6bf916ba6eb0efe3ee54b932666afec989465f9243fe6dd60" +checksum = "d5a396343c7208121dc86e35623d3dfe19814a7613cfd14964994cdc9c9a2e26" dependencies = [ "icu_collections", "icu_locale_core", @@ -1799,9 +1800,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -1813,15 +1814,15 @@ dependencies = [ [[package]] name = "icu_locale_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03e2fcaefecdf05619f3d6f91740e79ab969b4dd54f77cbf546b1d0d28e3147" +checksum = "d5fdcc9ac77c6d74ff5cf6e65ef3181d6af32003b16fce3a77fb451d2f695993" [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1833,15 +1834,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -1853,15 +1854,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -3112,9 +3113,9 @@ checksum = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" [[package]] name = "quote" -version = "1.0.42" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -5551,9 +5552,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "serde_core", @@ -6710,9 +6711,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -6721,9 +6722,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", @@ -6774,20 +6775,21 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", "zerofrom", + "zerovec", ] [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "serde", "yoke", @@ -6797,9 +6799,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", diff --git a/compiler/rustc_baked_icu_data/Cargo.toml b/compiler/rustc_baked_icu_data/Cargo.toml index 2f1ab7df3790e..c3887e4580d71 100644 --- a/compiler/rustc_baked_icu_data/Cargo.toml +++ b/compiler/rustc_baked_icu_data/Cargo.toml @@ -5,8 +5,8 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -icu_list = { version = "2.0", default-features = false } -icu_locale = { version = "2.0", default-features = false, features = ["compiled_data"] } -icu_provider = { version = "2.0", features = ["baked", "sync"] } +icu_list = { version = "2.2", default-features = false } +icu_locale = { version = "2.2", default-features = false, features = ["compiled_data"] } +icu_provider = { version = "2.2", features = ["baked", "sync"] } zerovec = "0.11.0" # tidy-alphabetical-end diff --git a/compiler/rustc_baked_icu_data/src/data/list_and_v1.rs.data b/compiler/rustc_baked_icu_data/src/data/list_and_v1.rs.data index 1d60e0085fcbb..e89e10d20de7b 100644 --- a/compiler/rustc_baked_icu_data/src/data/list_and_v1.rs.data +++ b/compiler/rustc_baked_icu_data/src/data/list_and_v1.rs.data @@ -19,17 +19,17 @@ #[macro_export] macro_rules! __impl_list_and_v1 { ($ provider : ty) => { - #[clippy::msrv = "1.82"] + #[clippy::msrv = "1.86"] const _: () = <$provider>::MUST_USE_MAKE_PROVIDER_MACRO; - #[clippy::msrv = "1.82"] + #[clippy::msrv = "1.86"] impl $provider { const DATA_LIST_AND_V1: icu_provider::baked::zerotrie::Data = { const TRIE: icu_provider::baked::zerotrie::ZeroTrieSimpleAscii<&'static [u8]> = icu_provider::baked::zerotrie::ZeroTrieSimpleAscii { store: b"\xC8efijprtz\x18#.9DOZ\xC2ns\n\x1E\xC3NSW\x01\x02\x80\x85\x8A\x1E\xC3NSW\x01\x02\x81\x81\x81r\x1E\xC3NSW\x01\x02\x80\x86\x86t\x1E\xC3NSW\x01\x02\x82\x82\x82a\x1E\xC3NSW\x01\x02\x83\x83\x83t\x1E\xC3NSW\x01\x02\x80\x82\x82u\x1E\xC3NSW\x01\x02\x80\x87\x87r\x1E\xC3NSW\x01\x02\x80\x88\x88h\xC2\x1E-\t\xC3NSW\x01\x02\x83\x89\x89Han\xC2st\n\x1E\xC3NSW\x01\x02\x83\x89\x89\x1E\xC3NSW\x01\x02\x84\x89\x89" }; - const VALUES: &'static [::DataStruct] = &[icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" y ") }, 3u8), special_case: Some(icu_list::provider::SpecialCasePattern { condition: unsafe { icu_list::provider::SerdeDFA::from_dfa_bytes_unchecked(if cfg!(target_endian = "little") { b"rust-regex-automata-dfa-sparse\0\0\xFF\xFE\0\0\x02\0\0\0\0\0\0\0\x02\0\0\0\x0E\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x02\x02\x02\x03\x04\x04\x05\x06\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x08\t\t\t\n\x0B\x0B\x0C\r\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12\x12\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x14\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x16\x17\x17\x18\x19\x19\x19\x1A\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B(\x01\0\0\x01\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\x01\x80\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\x05\0\x05\x05\x06\x06\x0C\x0C\r\r\0\0S\0\0\0D\0\0\0S\0\0\0D\0\0\0\0\0\0\0\0\x02\0\0\x1B\0\0\x12\0\0\0\x12\0\0\0\0\x03\0\x06\x06\r\r\0\0h\0\0\0h\0\0\0\0\0\0\0\0\x0E\0\0\0\x02\x02\x04\x07\t\t\x0B\x0E\x13\x13\x14\x14\x15\x15\x16\x16\x17\x17\x18\x18\x19\x19\x1A\x1A\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0\xBF\0\0\0\xCE\0\0\0\xDD\0\0\0\xEC\0\0\0\xDD\0\0\0\xFB\0\0\0\n\x01\0\0\x19\x01\0\0\x12\0\0\0\0\x02\0\x0F\x11\0\0D\0\0\0\0\0\0\0\0\x02\0\x11\x11\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x0F\x11\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x0F\x10\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x10\x11\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\x0F\x11\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\x0F\x0F\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\x01\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\0\0\0\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF`\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0\x12\0\0\0\t\0\0\0\x12\0\0\0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" } else { b"rust-regex-automata-dfa-sparse\0\0\0\0\xFE\xFF\0\0\0\x02\0\0\0\0\0\0\0\x02\0\0\0\x0E\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x02\x02\x02\x03\x04\x04\x05\x06\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x08\t\t\t\n\x0B\x0B\x0C\r\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12\x12\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x14\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x16\x17\x17\x18\x19\x19\x19\x1A\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\0\0\x01(\0\x01\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\x80\x01\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\x05\x05\x05\x06\x06\x0C\x0C\r\r\0\0\0\0\0S\0\0\0D\0\0\0S\0\0\0D\0\0\0\0\0\0\x02\0\x1B\0\0\0\0\0\x12\0\0\0\x12\0\0\x03\x06\x06\r\r\0\0\0\0\0h\0\0\0h\0\0\0\0\0\0\x0E\0\0\x02\x02\x04\x07\t\t\x0B\x0E\x13\x13\x14\x14\x15\x15\x16\x16\x17\x17\x18\x18\x19\x19\x1A\x1A\0\0\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0\xBF\0\0\0\xCE\0\0\0\xDD\0\0\0\xEC\0\0\0\xDD\0\0\0\xFB\0\0\x01\n\0\0\x01\x19\0\0\0\x12\0\0\x02\x0F\x11\0\0\0\0\0D\0\0\0\0\0\0\x02\x11\x11\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x0F\x11\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x0F\x10\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x10\x11\0\0\0\0\0\xDD\0\0\0\0\0\0\x02\x0F\x11\0\0\0\0\0\xDD\0\0\0\0\0\0\x02\x0F\x0F\0\0\0\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\x03\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\x01\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0`\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0\x12\0\0\0\t\0\0\0\x12\0\0\0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }) }, pattern: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" e ") }, 3u8) }) }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" e ") }, 3u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE5\x92\x8C") }, 3u8), special_case: None }, pair: Some(icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), special_case: None }) }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", & ") }, 4u8), special_case: None }, pair: Some(icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" & ") }, 3u8), special_case: None }) }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" et ") }, 4u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" \xD0\xB8 ") }, 4u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" ve ") }, 4u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE5\x92\x8C") }, 3u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", and ") }, 6u8), special_case: None }, pair: Some(icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" and ") }, 5u8), special_case: None }) }]; + const VALUES: &'static [::DataStruct] = &[icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" y ") }, 3u8), special_case: Some(icu_list::provider::SpecialCasePattern { condition: unsafe { icu_list::provider::SerdeDFA::from_dfa_bytes_unchecked(if cfg!(target_endian = "little") { b"rust-regex-automata-dfa-sparse\0\0\xFF\xFE\0\0\x02\0\0\0\0\0\0\0\x02\0\0\0\x0E\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x02\x02\x02\x03\x04\x04\x05\x06\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x08\t\t\t\n\x0B\x0B\x0C\r\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12\x12\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x14\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x16\x17\x17\x18\x19\x19\x19\x1A\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B(\x01\0\0\x01\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\x01\x80\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\x05\0\x05\x05\x06\x06\x0C\x0C\r\r\0\0S\0\0\0D\0\0\0S\0\0\0D\0\0\0\0\0\0\0\0\x02\0\0\x1B\0\0\x12\0\0\0\x12\0\0\0\0\x03\0\x06\x06\r\r\0\0h\0\0\0h\0\0\0\0\0\0\0\0\x0E\0\0\0\x02\x02\x04\x07\t\t\x0B\x0E\x13\x13\x14\x14\x15\x15\x16\x16\x17\x17\x18\x18\x19\x19\x1A\x1A\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0\xBF\0\0\0\xCE\0\0\0\xDD\0\0\0\xEC\0\0\0\xDD\0\0\0\xFB\0\0\0\n\x01\0\0\x19\x01\0\0\x12\0\0\0\0\x02\0\x0F\x11\0\0D\0\0\0\0\0\0\0\0\x02\0\x11\x11\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x0F\x11\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x0F\x10\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x10\x11\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\x0F\x11\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\x0F\x0F\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\x01\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\0\0\0\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF#\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0\x12\0\0\0\t\0\0\0\x12\0\0\0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" } else { b"rust-regex-automata-dfa-sparse\0\0\0\0\xFE\xFF\0\0\0\x02\0\0\0\0\0\0\0\x02\0\0\0\x0E\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x02\x02\x02\x03\x04\x04\x05\x06\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x08\t\t\t\n\x0B\x0B\x0C\r\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12\x12\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x14\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x16\x17\x17\x18\x19\x19\x19\x1A\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\0\0\x01(\0\x01\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\x80\x01\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\x05\x05\x05\x06\x06\x0C\x0C\r\r\0\0\0\0\0S\0\0\0D\0\0\0S\0\0\0D\0\0\0\0\0\0\x02\0\x1B\0\0\0\0\0\x12\0\0\0\x12\0\0\x03\x06\x06\r\r\0\0\0\0\0h\0\0\0h\0\0\0\0\0\0\x0E\0\0\x02\x02\x04\x07\t\t\x0B\x0E\x13\x13\x14\x14\x15\x15\x16\x16\x17\x17\x18\x18\x19\x19\x1A\x1A\0\0\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0\xBF\0\0\0\xCE\0\0\0\xDD\0\0\0\xEC\0\0\0\xDD\0\0\0\xFB\0\0\x01\n\0\0\x01\x19\0\0\0\x12\0\0\x02\x0F\x11\0\0\0\0\0D\0\0\0\0\0\0\x02\x11\x11\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x0F\x11\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x0F\x10\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x10\x11\0\0\0\0\0\xDD\0\0\0\0\0\0\x02\x0F\x11\0\0\0\0\0\xDD\0\0\0\0\0\0\x02\x0F\x0F\0\0\0\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\x03\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\x01\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0#\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0\x12\0\0\0\t\0\0\0\x12\0\0\0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }) }, pattern: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" e ") }, 3u8) }) }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" e ") }, 3u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE5\x92\x8C") }, 3u8), special_case: None }, pair: Some(icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), special_case: None }) }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", & ") }, 4u8), special_case: None }, pair: Some(icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" & ") }, 3u8), special_case: None }) }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" et ") }, 4u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" \xD0\xB8 ") }, 4u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" ve ") }, 4u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE5\x92\x8C") }, 3u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", and ") }, 6u8), special_case: None }, pair: Some(icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" and ") }, 5u8), special_case: None }) }]; unsafe { icu_provider::baked::zerotrie::Data::from_trie_and_values_unchecked(TRIE, VALUES) } }; } - #[clippy::msrv = "1.82"] + #[clippy::msrv = "1.86"] impl icu_provider::DataProvider for $provider { fn load(&self, req: icu_provider::DataRequest) -> Result, icu_provider::DataError> { let mut metadata = icu_provider::DataResponseMetadata::default(); @@ -55,7 +55,7 @@ macro_rules! __impl_list_and_v1 { }; ($ provider : ty , ITER) => { __impl_list_and_v1!($provider); - #[clippy::msrv = "1.82"] + #[clippy::msrv = "1.86"] impl icu_provider::IterableDataProvider for $provider { fn iter_ids(&self) -> Result>, icu_provider::DataError> { Ok(icu_provider::baked::DataStore::iter(&Self::DATA_LIST_AND_V1).collect()) diff --git a/compiler/rustc_baked_icu_data/src/data/mod.rs b/compiler/rustc_baked_icu_data/src/data/mod.rs index 3146188a8e7af..fd43456b9e619 100644 --- a/compiler/rustc_baked_icu_data/src/data/mod.rs +++ b/compiler/rustc_baked_icu_data/src/data/mod.rs @@ -15,7 +15,7 @@ include!("list_and_v1.rs.data"); #[macro_export] macro_rules! __make_provider { ($ name : ty) => { - #[clippy::msrv = "1.82"] + #[clippy::msrv = "1.86"] impl $name { #[allow(dead_code)] pub(crate) const MUST_USE_MAKE_PROVIDER_MACRO: () = (); diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml index 687aff5e9229a..50f0b265527fe 100644 --- a/compiler/rustc_error_messages/Cargo.toml +++ b/compiler/rustc_error_messages/Cargo.toml @@ -6,8 +6,8 @@ edition = "2024" [dependencies] # tidy-alphabetical-start fluent-bundle = "0.16" -icu_list = { version = "2.0", default-features = false, features = ["alloc"] } -icu_locale = { version = "2.0", default-features = false } +icu_list = { version = "2.2", default-features = false, features = ["alloc"] } +icu_locale = { version = "2.2", default-features = false } intl-memoizer = "0.5.1" rustc_baked_icu_data = { path = "../rustc_baked_icu_data" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 1d12456d2bb23..7090faceef0a5 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -466,6 +466,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "unicode-script", "unicode-security", "unicode-width", + "utf8_iter", "utf8parse", "valuable", "version_check", From edb8b6ae953b6bc5afbca6b6428b8ff2d72b1685 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 4 May 2026 20:42:56 +0300 Subject: [PATCH 209/289] Catch `#[rustc_reservation_impl = "reason"]` And not just `#[rustc_reservation_impl]`. This has caused a bug that took me way too long to debug. Basically, with the reservation impl `From for T` overlapping with `From for T`, the solver couldn't deduce from the bound `Ty: From` that `var = Ty`. Up until now this only caused some unknown types, but with the "type annotations needed" it started becoming visible, and annoying. This is not the first time that we forget some attribute can be applied in more than one reason; perhaps we should unify handling for all `ast::Meta` kinds that have a path. --- .../rust-analyzer/crates/hir-def/src/attrs.rs | 1 + .../src/handlers/type_must_be_known.rs | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) 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 43bbe99b13305..90fae16ccd0c2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -140,6 +140,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow< "must_use" => attr_flags.insert(AttrFlags::IS_MUST_USE), "path" => attr_flags.insert(AttrFlags::HAS_PATH), "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE), + "rustc_reservation_impl" => attr_flags.insert(AttrFlags::RUSTC_RESERVATION_IMPL), "export_name" => { if let Some(value) = attr.value_string() && *value == *"main" diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs index 1bae982079202..a03352fe31d07 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs @@ -133,6 +133,28 @@ where fn foo() { spawn_in(async move |cx| {}); +} + "#, + ); + } + + #[test] + fn regression_22263() { + check_diagnostics( + r#" +trait From {} +impl From for T {} +#[rustc_reservation_impl = "blah blah"] +impl From for T {} + +fn any() -> T { + loop {} +} +fn foo>(_: T) -> U { + loop {} +} +fn bar() { + let _: () = foo(any()); } "#, ); From 6053e19f0b9dbbef44e89ce66d4f0b370c1b36c7 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 4 May 2026 19:00:01 +0300 Subject: [PATCH 210/289] Cache more things that are related to lang items (paren traits, children/sibling assoc types/functions) but are not lang items themselves And make use of them. --- .../crates/hir-def/src/lang_item.rs | 168 ++++++++++++++++-- .../crates/hir-ty/src/display.rs | 24 +-- .../crates/hir-ty/src/infer/callee.rs | 32 ++-- .../crates/hir-ty/src/infer/op.rs | 27 ++- .../crates/hir-ty/src/infer/place_op.rs | 17 +- .../crates/hir-ty/src/lang_items.rs | 57 +++--- .../rust-analyzer/crates/hir-ty/src/lib.rs | 5 +- .../crates/hir-ty/src/method_resolution.rs | 10 +- .../hir-ty/src/method_resolution/confirm.rs | 6 +- .../crates/hir-ty/src/mir/eval.rs | 33 +--- .../crates/hir-ty/src/mir/eval/shim.rs | 7 +- .../crates/hir-ty/src/mir/lower/as_place.rs | 24 +-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 29 +-- .../crates/hir/src/source_analyzer.rs | 52 ++---- 14 files changed, 269 insertions(+), 222 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index e5a9b5d46cc63..c2cbb9eda7268 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -194,16 +194,115 @@ fn resolve_core_macro( impl LangItems { fn resolve_manually(&mut self, db: &dyn DefDatabase) { - (|| { - let into_future_into_future = self.IntoFutureIntoFuture?; - let ItemContainerId::TraitId(into_future) = into_future_into_future.loc(db).container - else { - return None; + let parent_trait = + |lang_item: &mut Option, def: Option| match def?.loc(db).container + { + ItemContainerId::TraitId(trait_) => { + *lang_item = Some(trait_); + Some(trait_) + } + _ => None, + }; + let assoc_types = + |trait_: TraitId, assoc_types: &mut [(&mut Option, Symbol)]| { + let trait_items = trait_.trait_items(db); + for (assoc_type, name) in assoc_types { + **assoc_type = + trait_items.associated_type_by_name(&Name::new_symbol_root(name.clone())); + } }; - self.IntoFuture = Some(into_future); - self.IntoFutureOutput = into_future - .trait_items(db) - .associated_type_by_name(&Name::new_symbol_root(sym::Output)); + let methods = |trait_: TraitId, assoc_types: &mut [(&mut Option, Symbol)]| { + let trait_items = trait_.trait_items(db); + for (assoc_type, name) in assoc_types { + **assoc_type = trait_items.method_by_name(&Name::new_symbol_root(name.clone())); + } + }; + (|| { + let into_future = parent_trait(&mut self.IntoFuture, self.IntoFutureIntoFuture)?; + assoc_types(into_future, &mut [(&mut self.IntoFutureOutput, sym::Output)]); + Some(()) + })(); + + (|| { + let into_iterator = parent_trait(&mut self.IntoIterator, self.IntoIterIntoIter)?; + assoc_types( + into_iterator, + &mut [ + (&mut self.IntoIteratorItem, sym::Item), + (&mut self.IntoIterIntoIterType, sym::IntoIter), + ], + ); + Some(()) + })(); + + (|| { + assoc_types(self.Iterator?, &mut [(&mut self.IteratorItem, sym::Item)]); + Some(()) + })(); + + (|| { + assoc_types(self.AsyncIterator?, &mut [(&mut self.AsyncIteratorItem, sym::Item)]); + Some(()) + })(); + + for (op_trait, op_method, op_method_name) in [ + (self.Fn, &mut self.Fn_call, sym::call), + (self.FnMut, &mut self.FnMut_call_mut, sym::call_mut), + (self.FnOnce, &mut self.FnOnce_call_once, sym::call_once), + (self.AsyncFn, &mut self.AsyncFn_async_call, sym::async_call), + (self.AsyncFnMut, &mut self.AsyncFnMut_async_call_mut, sym::async_call_mut), + (self.AsyncFnOnce, &mut self.AsyncFnOnce_async_call_once, sym::async_call_once), + (self.Not, &mut self.Not_not, sym::not), + (self.Neg, &mut self.Neg_neg, sym::neg), + (self.Add, &mut self.Add_add, sym::add), + (self.Mul, &mut self.Mul_mul, sym::mul), + (self.Sub, &mut self.Sub_sub, sym::sub), + (self.Div, &mut self.Div_div, sym::div), + (self.Rem, &mut self.Rem_rem, sym::rem), + (self.Shl, &mut self.Shl_shl, sym::shl), + (self.Shr, &mut self.Shr_shr, sym::shr), + (self.BitXor, &mut self.BitXor_bitxor, sym::bitxor), + (self.BitOr, &mut self.BitOr_bitor, sym::bitor), + (self.BitAnd, &mut self.BitAnd_bitand, sym::bitand), + (self.AddAssign, &mut self.AddAssign_add_assign, sym::add_assign), + (self.MulAssign, &mut self.MulAssign_mul_assign, sym::mul_assign), + (self.SubAssign, &mut self.SubAssign_sub_assign, sym::sub_assign), + (self.DivAssign, &mut self.DivAssign_div_assign, sym::div_assign), + (self.RemAssign, &mut self.RemAssign_rem_assign, sym::rem_assign), + (self.ShlAssign, &mut self.ShlAssign_shl_assign, sym::shl_assign), + (self.ShrAssign, &mut self.ShrAssign_shr_assign, sym::shr_assign), + (self.BitXorAssign, &mut self.BitXorAssign_bitxor_assign, sym::bitxor_assign), + (self.BitOrAssign, &mut self.BitOrAssign_bitor_assign, sym::bitor_assign), + (self.BitAndAssign, &mut self.BitAndAssign_bitand_assign, sym::bitand_assign), + (self.Drop, &mut self.Drop_drop, sym::drop), + (self.Debug, &mut self.Debug_fmt, sym::fmt), + (self.Deref, &mut self.Deref_deref, sym::deref), + (self.DerefMut, &mut self.DerefMut_deref_mut, sym::deref_mut), + (self.Index, &mut self.Index_index, sym::index), + (self.IndexMut, &mut self.IndexMut_index_mut, sym::index_mut), + ] { + (|| { + methods(op_trait?, &mut [(op_method, op_method_name)]); + Some(()) + })(); + } + (|| { + methods( + self.PartialEq?, + &mut [(&mut self.PartialEq_eq, sym::eq), (&mut self.PartialEq_ne, sym::ne)], + ); + Some(()) + })(); + (|| { + methods( + self.PartialOrd?, + &mut [ + (&mut self.PartialOrd_le, sym::le), + (&mut self.PartialOrd_lt, sym::lt), + (&mut self.PartialOrd_ge, sym::ge), + (&mut self.PartialOrd_gt, sym::gt), + ], + ); Some(()) })(); } @@ -567,6 +666,53 @@ language_item_table! { LangItems => core::clone, Clone, CloneDerive; @resolve_manually: - IntoFuture, TraitId; - IntoFutureOutput, TypeAliasId; + + IntoFuture, TraitId; + IntoFutureOutput, TypeAliasId; + IntoIterator, TraitId; + IntoIteratorItem, TypeAliasId; + IntoIterIntoIterType, TypeAliasId; + IteratorItem, TypeAliasId; + AsyncIteratorItem, TypeAliasId; + + Fn_call, FunctionId; + FnMut_call_mut, FunctionId; + FnOnce_call_once, FunctionId; + AsyncFn_async_call, FunctionId; + AsyncFnMut_async_call_mut, FunctionId; + AsyncFnOnce_async_call_once, FunctionId; + Not_not, FunctionId; + Neg_neg, FunctionId; + Add_add, FunctionId; + Mul_mul, FunctionId; + Sub_sub, FunctionId; + Div_div, FunctionId; + Rem_rem, FunctionId; + Shl_shl, FunctionId; + Shr_shr, FunctionId; + BitXor_bitxor, FunctionId; + BitOr_bitor, FunctionId; + BitAnd_bitand, FunctionId; + AddAssign_add_assign, FunctionId; + MulAssign_mul_assign, FunctionId; + SubAssign_sub_assign, FunctionId; + DivAssign_div_assign, FunctionId; + RemAssign_rem_assign, FunctionId; + ShlAssign_shl_assign, FunctionId; + ShrAssign_shr_assign, FunctionId; + BitXorAssign_bitxor_assign, FunctionId; + BitOrAssign_bitor_assign, FunctionId; + BitAndAssign_bitand_assign, FunctionId; + PartialEq_eq, FunctionId; + PartialEq_ne, FunctionId; + PartialOrd_le, FunctionId; + PartialOrd_lt, FunctionId; + PartialOrd_ge, FunctionId; + PartialOrd_gt, FunctionId; + Drop_drop, FunctionId; + Debug_fmt, FunctionId; + Deref_deref, FunctionId; + DerefMut_deref_mut, FunctionId; + Index_index, FunctionId; + IndexMut_index_mut, FunctionId; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index f7d333f94c389..7c80066b49cb5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1726,11 +1726,9 @@ impl<'db> HirDisplay<'db> for Ty<'db> { subst.split_coroutine_args(); match kind { HirClosureKind::Coroutine { kind: CoroutineKind::Async, .. } => { - let future_trait = f.lang_items().Future; - let output = future_trait.and_then(|t| { - t.trait_items(db) - .associated_type_by_name(&Name::new_symbol_root(sym::Output)) - }); + let lang_items = f.lang_items(); + let future_trait = lang_items.Future; + let output = lang_items.FutureOutput; write!(f, "impl ")?; if let Some(t) = future_trait { f.start_location_link(t.into()); @@ -1752,11 +1750,9 @@ impl<'db> HirDisplay<'db> for Ty<'db> { write!(f, ">")?; } HirClosureKind::Coroutine { kind: CoroutineKind::Gen, .. } => { - let iterator_trait = f.lang_items().Iterator; - let item = iterator_trait.and_then(|t| { - t.trait_items(db) - .associated_type_by_name(&Name::new_symbol_root(sym::Item)) - }); + let lang_items = f.lang_items(); + let iterator_trait = lang_items.Iterator; + let item = lang_items.IteratorItem; write!(f, "impl ")?; if let Some(t) = iterator_trait { f.start_location_link(t.into()); @@ -1778,11 +1774,9 @@ impl<'db> HirDisplay<'db> for Ty<'db> { write!(f, ">")?; } HirClosureKind::Coroutine { kind: CoroutineKind::AsyncGen, .. } => { - let async_iterator_trait = f.lang_items().AsyncIterator; - let item = async_iterator_trait.and_then(|t| { - t.trait_items(db) - .associated_type_by_name(&Name::new_symbol_root(sym::Item)) - }); + let lang_items = f.lang_items(); + let async_iterator_trait = lang_items.AsyncIterator; + let item = lang_items.AsyncIteratorItem; write!(f, "impl ")?; if let Some(t) = async_iterator_trait { f.start_location_link(t.into()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs index 9c134cb75f311..2d1584aa4f522 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs @@ -2,7 +2,6 @@ use std::iter; -use intern::sym; use tracing::debug; use hir_def::{CallableDefId, ConstParamId, hir::ExprId, signatures::FunctionSignature}; @@ -277,27 +276,28 @@ impl<'db> InferenceContext<'_, 'db> { let call_trait_choices = if self.shallow_resolve(adjusted_ty).is_coroutine_closure() { [ - (self.lang_items.AsyncFn, sym::async_call, true), - (self.lang_items.AsyncFnMut, sym::async_call_mut, true), - (self.lang_items.AsyncFnOnce, sym::async_call_once, false), - (self.lang_items.Fn, sym::call, true), - (self.lang_items.FnMut, sym::call_mut, true), - (self.lang_items.FnOnce, sym::call_once, false), + (self.lang_items.AsyncFn, self.lang_items.AsyncFn_async_call, true), + (self.lang_items.AsyncFnMut, self.lang_items.AsyncFnMut_async_call_mut, true), + (self.lang_items.AsyncFnOnce, self.lang_items.AsyncFnOnce_async_call_once, false), + (self.lang_items.Fn, self.lang_items.Fn_call, true), + (self.lang_items.FnMut, self.lang_items.FnMut_call_mut, true), + (self.lang_items.FnOnce, self.lang_items.FnOnce_call_once, false), ] } else { [ - (self.lang_items.Fn, sym::call, true), - (self.lang_items.FnMut, sym::call_mut, true), - (self.lang_items.FnOnce, sym::call_once, false), - (self.lang_items.AsyncFn, sym::async_call, true), - (self.lang_items.AsyncFnMut, sym::async_call_mut, true), - (self.lang_items.AsyncFnOnce, sym::async_call_once, false), + (self.lang_items.Fn, self.lang_items.Fn_call, true), + (self.lang_items.FnMut, self.lang_items.FnMut_call_mut, true), + (self.lang_items.FnOnce, self.lang_items.FnOnce_call_once, false), + (self.lang_items.AsyncFn, self.lang_items.AsyncFn_async_call, true), + (self.lang_items.AsyncFnMut, self.lang_items.AsyncFnMut_async_call_mut, true), + (self.lang_items.AsyncFnOnce, self.lang_items.AsyncFnOnce_async_call_once, false), ] }; // Try the options that are least restrictive on the caller first. - for (opt_trait_def_id, method_name, borrow) in call_trait_choices { - let Some(trait_def_id) = opt_trait_def_id else { + for (opt_trait_def_id, opt_method_def_id, borrow) in call_trait_choices { + let (Some(trait_def_id), Some(method_def_id)) = (opt_trait_def_id, opt_method_def_id) + else { continue; }; @@ -316,8 +316,8 @@ impl<'db> InferenceContext<'_, 'db> { // `Box: Fn` is considered ambiguous and chosen. if let Some(ok) = self.table.lookup_method_for_operator( ObligationCause::new(call_expr), - method_name, trait_def_id, + method_def_id, adjusted_ty, opt_input_type, TreatNotYetDefinedOpaques::AsRigid, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs index 0127fd6cdbcd1..9119af9628eb9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs @@ -2,8 +2,7 @@ use std::collections::hash_map; -use hir_def::{GenericParamId, TraitId, hir::ExprId}; -use intern::{Symbol, sym}; +use hir_def::{FunctionId, GenericParamId, TraitId, hir::ExprId}; use rustc_ast_ir::Mutability; use rustc_type_ir::inherent::{IntoKind, Ty as _}; use syntax::ast::{ArithOp, BinaryOp, UnaryOp}; @@ -283,16 +282,16 @@ impl<'a, 'db> InferenceContext<'a, 'db> { expr: ExprId, lhs_ty: Ty<'db>, opt_rhs: Option<(ExprId, Ty<'db>)>, - (opname, trait_did): (Symbol, Option), + (op_method, trait_did): (Option, Option), ) -> Result, Vec>> { - let Some(trait_did) = trait_did else { + let (Some(trait_did), Some(op_method)) = (trait_did, op_method) else { // Bail if the operator trait is not defined. return Err(vec![]); }; debug!( "lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})", - lhs_ty, opname, trait_did + lhs_ty, op_method, trait_did ); let opt_rhs_ty = opt_rhs.map(|it| it.1); @@ -304,8 +303,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; let method = self.table.lookup_method_for_operator( cause, - opname, trait_did, + op_method, lhs_ty, opt_rhs_ty, treat_opaques, @@ -360,20 +359,20 @@ impl<'a, 'db> InferenceContext<'a, 'db> { } } - fn lang_item_for_bin_op(&self, op: BinaryOp) -> (Symbol, Option) { - let (method_name, trait_lang_item) = + fn lang_item_for_bin_op(&self, op: BinaryOp) -> (Option, Option) { + let (method, trait_lang_item) = crate::lang_items::lang_items_for_bin_op(self.lang_items, op) .expect("invalid operator provided"); - (method_name, trait_lang_item) + (method, trait_lang_item) } - fn lang_item_for_unop(&self, op: UnaryOp) -> (Symbol, Option) { - let (method_name, trait_lang_item) = match op { - UnaryOp::Not => (sym::not, self.lang_items.Not), - UnaryOp::Neg => (sym::neg, self.lang_items.Neg), + fn lang_item_for_unop(&self, op: UnaryOp) -> (Option, Option) { + let (method, trait_lang_item) = match op { + UnaryOp::Not => (self.lang_items.Not_not, self.lang_items.Not), + UnaryOp::Neg => (self.lang_items.Neg_neg, self.lang_items.Neg), UnaryOp::Deref => panic!("Deref is not overloadable"), }; - (method_name, trait_lang_item) + (method, trait_lang_item) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs index 968d793615edd..bbf047b8bacfb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs @@ -1,7 +1,6 @@ //! Inference of *place operators*: deref and indexing (operators that create places, as opposed to values). use hir_def::hir::ExprId; -use intern::sym; use rustc_ast_ir::Mutability; use rustc_type_ir::inherent::{IntoKind, Ty as _}; use tracing::debug; @@ -195,9 +194,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> { ) -> Option>> { debug!("try_overloaded_place_op({:?},{:?})", base_ty, op); - let (Some(imm_tr), imm_op) = (match op { - PlaceOp::Deref => (self.lang_items.Deref, sym::deref), - PlaceOp::Index => (self.lang_items.Index, sym::index), + let (Some(imm_tr), Some(imm_op)) = (match op { + PlaceOp::Deref => (self.lang_items.Deref, self.lang_items.Deref_deref), + PlaceOp::Index => (self.lang_items.Index, self.lang_items.Index_index), }) else { // Bail if `Deref` or `Index` isn't defined. return None; @@ -208,8 +207,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; self.table.lookup_method_for_operator( ObligationCause::new(expr), - imm_op, imm_tr, + imm_op, base_ty, opt_rhs_ty, treat_opaques, @@ -226,9 +225,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> { debug!("try_mutable_overloaded_place_op({:?},{:?})", base_ty, op); let lang_items = table.interner().lang_items(); - let (Some(mut_tr), mut_op) = (match op { - PlaceOp::Deref => (lang_items.DerefMut, sym::deref_mut), - PlaceOp::Index => (lang_items.IndexMut, sym::index_mut), + let (Some(mut_tr), Some(mut_op)) = (match op { + PlaceOp::Deref => (lang_items.DerefMut, lang_items.DerefMut_deref_mut), + PlaceOp::Index => (lang_items.IndexMut, lang_items.IndexMut_index_mut), }) else { // Bail if `DerefMut` or `IndexMut` isn't defined. return None; @@ -241,8 +240,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; table.lookup_method_for_operator( ObligationCause::new(expr), - mut_op, mut_tr, + mut_op, base_ty, opt_rhs_ty, treat_opaques, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs index c8e15e2f9c0c1..19d2d29c9e333 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs @@ -1,53 +1,52 @@ //! Functions to detect special lang items -use hir_def::{TraitId, lang_item::LangItems}; -use intern::{Symbol, sym}; +use hir_def::{FunctionId, TraitId, lang_item::LangItems}; pub fn lang_items_for_bin_op( lang_items: &LangItems, op: syntax::ast::BinaryOp, -) -> Option<(Symbol, Option)> { +) -> Option<(Option, Option)> { use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering}; Some(match op { BinaryOp::LogicOp(_) => return None, BinaryOp::ArithOp(aop) => match aop { - ArithOp::Add => (sym::add, lang_items.Add), - ArithOp::Mul => (sym::mul, lang_items.Mul), - ArithOp::Sub => (sym::sub, lang_items.Sub), - ArithOp::Div => (sym::div, lang_items.Div), - ArithOp::Rem => (sym::rem, lang_items.Rem), - ArithOp::Shl => (sym::shl, lang_items.Shl), - ArithOp::Shr => (sym::shr, lang_items.Shr), - ArithOp::BitXor => (sym::bitxor, lang_items.BitXor), - ArithOp::BitOr => (sym::bitor, lang_items.BitOr), - ArithOp::BitAnd => (sym::bitand, lang_items.BitAnd), + ArithOp::Add => (lang_items.Add_add, lang_items.Add), + ArithOp::Mul => (lang_items.Mul_mul, lang_items.Mul), + ArithOp::Sub => (lang_items.Sub_sub, lang_items.Sub), + ArithOp::Div => (lang_items.Div_div, lang_items.Div), + ArithOp::Rem => (lang_items.Rem_rem, lang_items.Rem), + ArithOp::Shl => (lang_items.Shl_shl, lang_items.Shl), + ArithOp::Shr => (lang_items.Shr_shr, lang_items.Shr), + ArithOp::BitXor => (lang_items.BitXor_bitxor, lang_items.BitXor), + ArithOp::BitOr => (lang_items.BitOr_bitor, lang_items.BitOr), + ArithOp::BitAnd => (lang_items.BitAnd_bitand, lang_items.BitAnd), }, BinaryOp::Assignment { op: Some(aop) } => match aop { - ArithOp::Add => (sym::add_assign, lang_items.AddAssign), - ArithOp::Mul => (sym::mul_assign, lang_items.MulAssign), - ArithOp::Sub => (sym::sub_assign, lang_items.SubAssign), - ArithOp::Div => (sym::div_assign, lang_items.DivAssign), - ArithOp::Rem => (sym::rem_assign, lang_items.RemAssign), - ArithOp::Shl => (sym::shl_assign, lang_items.ShlAssign), - ArithOp::Shr => (sym::shr_assign, lang_items.ShrAssign), - ArithOp::BitXor => (sym::bitxor_assign, lang_items.BitXorAssign), - ArithOp::BitOr => (sym::bitor_assign, lang_items.BitOrAssign), - ArithOp::BitAnd => (sym::bitand_assign, lang_items.BitAndAssign), + ArithOp::Add => (lang_items.AddAssign_add_assign, lang_items.AddAssign), + ArithOp::Mul => (lang_items.MulAssign_mul_assign, lang_items.MulAssign), + ArithOp::Sub => (lang_items.SubAssign_sub_assign, lang_items.SubAssign), + ArithOp::Div => (lang_items.DivAssign_div_assign, lang_items.DivAssign), + ArithOp::Rem => (lang_items.RemAssign_rem_assign, lang_items.RemAssign), + ArithOp::Shl => (lang_items.ShlAssign_shl_assign, lang_items.ShlAssign), + ArithOp::Shr => (lang_items.ShrAssign_shr_assign, lang_items.ShrAssign), + ArithOp::BitXor => (lang_items.BitXorAssign_bitxor_assign, lang_items.BitXorAssign), + ArithOp::BitOr => (lang_items.BitOrAssign_bitor_assign, lang_items.BitOrAssign), + ArithOp::BitAnd => (lang_items.BitAndAssign_bitand_assign, lang_items.BitAndAssign), }, BinaryOp::CmpOp(cop) => match cop { - CmpOp::Eq { negated: false } => (sym::eq, lang_items.PartialEq), - CmpOp::Eq { negated: true } => (sym::ne, lang_items.PartialEq), + CmpOp::Eq { negated: false } => (lang_items.PartialEq_eq, lang_items.PartialEq), + CmpOp::Eq { negated: true } => (lang_items.PartialEq_ne, lang_items.PartialEq), CmpOp::Ord { ordering: Ordering::Less, strict: false } => { - (sym::le, lang_items.PartialOrd) + (lang_items.PartialOrd_le, lang_items.PartialOrd) } CmpOp::Ord { ordering: Ordering::Less, strict: true } => { - (sym::lt, lang_items.PartialOrd) + (lang_items.PartialOrd_lt, lang_items.PartialOrd) } CmpOp::Ord { ordering: Ordering::Greater, strict: false } => { - (sym::ge, lang_items.PartialOrd) + (lang_items.PartialOrd_ge, lang_items.PartialOrd) } CmpOp::Ord { ordering: Ordering::Greater, strict: true } => { - (sym::gt, lang_items.PartialOrd) + (lang_items.PartialOrd_gt, lang_items.PartialOrd) } }, BinaryOp::Assignment { op: None } => return None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 5cb2a3d804d63..0c107460fa155 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -573,10 +573,7 @@ pub fn callable_sig_from_fn_trait<'db>( return None; }; - let fn_once_trait = lang_items.FnOnce?; - let output_assoc_type = fn_once_trait - .trait_items(db) - .associated_type_by_name(&Name::new_symbol_root(sym::Output))?; + let output_assoc_type = lang_items.FnOnceOutput?; let output_projection = Ty::new_alias( interner, AliasTy::new( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 86477f2c0b6be..f8aac3e63a157 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -25,7 +25,6 @@ use hir_def::{ signatures::{ConstSignature, FunctionSignature}, unstable_features::UnstableFeatures, }; -use intern::Symbol; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ TypeVisitableExt, @@ -224,8 +223,8 @@ impl<'db> InferenceTable<'db> { pub(super) fn lookup_method_for_operator( &self, cause: ObligationCause, - method_name: Symbol, trait_def_id: TraitId, + method_item: FunctionId, self_ty: Ty<'db>, opt_rhs_ty: Option>, treat_opaques: TreatNotYetDefinedOpaques, @@ -278,13 +277,6 @@ impl<'db> InferenceTable<'db> { // Trait must have a method named `m_name` and it should not have // type parameters or early-bound regions. let interner = self.interner(); - // We use `Ident::with_dummy_span` since no built-in operator methods have - // any macro-specific hygiene, so the span's context doesn't really matter. - let Some(method_item) = - trait_def_id.trait_items(self.db).method_by_name(&Name::new_symbol_root(method_name)) - else { - panic!("expected associated item for operator trait") - }; let def_id = method_item; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs index 821d737cf976e..ffd65a58d84e0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs @@ -2,7 +2,7 @@ //! is valid and registering all obligations. use hir_def::{ - FunctionId, GenericDefId, GenericParamId, ItemContainerId, TraitId, + FunctionId, GenericDefId, GenericParamId, TraitId, expr_store::path::{GenericArg as HirGenericArg, GenericArgs as HirGenericArgs}, hir::{ExprId, generics::GenericParamDataRef}, type_ref::TypeRefId, @@ -575,9 +575,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { fn check_for_illegal_method_calls(&self) { // Disallow calls to the method `drop` defined in the `Drop` trait. - if let ItemContainerId::TraitId(trait_def_id) = self.candidate.loc(self.db()).container - && self.ctx.lang_items.Drop.is_some_and(|drop_trait| drop_trait == trait_def_id) - { + if self.ctx.lang_items.Drop_drop.is_some_and(|drop_fn| drop_fn == self.candidate) { // FIXME: Report an error. } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 104b90eaf55c5..8701ad5be60bb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -11,14 +11,13 @@ use hir_def::{ item_tree::FieldsShape, lang_item::LangItems, layout::{TagEncoding, Variants}, - resolver::{HasResolver, TypeNs, ValueNs}, + resolver::{HasResolver, ValueNs}, signatures::{ EnumSignature, FunctionSignature, StaticFlags, StaticSignature, StructFlags, StructSignature, TraitSignature, }, }; -use hir_expand::{InFile, mod_path::path, name::Name}; -use intern::sym; +use hir_expand::{InFile, mod_path::path}; use la_arena::ArenaMap; use macros::GenericTypeVisitable; use rustc_abi::{Size, TargetDataLayout}; @@ -692,15 +691,9 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { mir_or_dyn_index_cache: RefCell::new(Default::default()), unused_locals_store: RefCell::new(Default::default()), cached_ptr_size, - cached_fn_trait_func: lang_items - .Fn - .and_then(|x| x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call))), - cached_fn_mut_trait_func: lang_items.FnMut.and_then(|x| { - x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call_mut)) - }), - cached_fn_once_trait_func: lang_items.FnOnce.and_then(|x| { - x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call_once)) - }), + cached_fn_trait_func: lang_items.Fn_call, + cached_fn_mut_trait_func: lang_items.FnMut_call_mut, + cached_fn_once_trait_func: lang_items.FnOnce_call_once, infcx, }) } @@ -3036,10 +3029,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { _metadata: &[u8], span: MirSpan, ) -> Result<'db, ()> { - let Some(drop_fn) = (|| { - let drop_trait = self.lang_items().Drop?; - drop_trait.trait_items(self.db).method_by_name(&Name::new_symbol_root(sym::drop)) - })() else { + let Some(drop_fn) = self.lang_items().Drop_drop else { // in some tests we don't have drop trait in minicore, and // we can ignore drop in them. return Ok(()); @@ -3150,16 +3140,9 @@ pub fn render_const_using_debug_impl<'db>( drop_flags: DropFlags::default(), }; let data = evaluator.allocate_allocation_in_heap(locals, c)?; + let lang_items = evaluator.interner().lang_items(); let resolver = owner.resolver(db); - let Some(TypeNs::TraitId(debug_trait)) = resolver.resolve_path_in_type_ns_fully( - db, - &hir_def::expr_store::path::Path::from_known_path_with_no_generic(path![core::fmt::Debug]), - ) else { - not_supported!("core::fmt::Debug not found"); - }; - let Some(debug_fmt_fn) = - debug_trait.trait_items(db).method_by_name(&Name::new_symbol_root(sym::fmt)) - else { + let Some(debug_fmt_fn) = lang_items.Debug_fmt else { not_supported!("core::fmt::Debug::fmt not found"); }; // a1 = &[""] 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 a0978bd6e8df9..284148873de07 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 @@ -4,7 +4,6 @@ use std::cmp::{self, Ordering}; use hir_def::{attrs::AttrFlags, signatures::FunctionSignature}; -use hir_expand::name::Name; use intern::sym; use rustc_type_ir::inherent::{GenericArgs as _, IntoKind, SliceLike, Ty as _}; use stdx::never; @@ -1202,11 +1201,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let addr = tuple.interval.addr.offset(offset); args.push(IntervalAndTy::new(addr, field, self, locals)?); } - if let Some(target) = self.lang_items().FnOnce - && let Some(def) = target - .trait_items(self.db) - .method_by_name(&Name::new_symbol_root(sym::call_once)) - { + if let Some(def) = self.lang_items().FnOnce_call_once { self.exec_fn_trait( def, &args, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs index fb4a9add818f3..0f7fc9a18e864 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs @@ -1,7 +1,6 @@ //! MIR lowering for places use hir_def::FunctionId; -use intern::sym; use rustc_type_ir::inherent::{Region as _, Ty as _}; use super::*; @@ -183,10 +182,7 @@ impl<'db> MirLowerCtx<'_, 'db> { expr_id.into(), 'b: { if let Some((f, _)) = self.infer.method_resolution(expr_id) - && let Some(deref_trait) = self.lang_items().DerefMut - && let Some(deref_fn) = deref_trait - .trait_items(self.db) - .method_by_name(&Name::new_symbol_root(sym::deref_mut)) + && let Some(deref_fn) = self.lang_items().DerefMut_deref_mut { break 'b deref_fn == f; } @@ -315,18 +311,12 @@ impl<'db> MirLowerCtx<'_, 'db> { mutability: bool, ) -> Result<'db, Option<(Place, BasicBlockId)>> { let lang_items = self.lang_items(); - let (mutability, trait_lang_item, trait_method_name, borrow_kind) = if !mutability { - ( - Mutability::Not, - lang_items.Deref, - Name::new_symbol_root(sym::deref), - BorrowKind::Shared, - ) + let (mutability, deref_fn, borrow_kind) = if !mutability { + (Mutability::Not, lang_items.Deref_deref, BorrowKind::Shared) } else { ( Mutability::Mut, - lang_items.DerefMut, - Name::new_symbol_root(sym::deref_mut), + lang_items.DerefMut_deref_mut, BorrowKind::Mut { kind: MutBorrowKind::Default }, ) }; @@ -335,11 +325,7 @@ impl<'db> MirLowerCtx<'_, 'db> { let target_ty_ref = Ty::new_ref(self.interner(), error_region, target_ty, mutability); let ref_place: Place = self.temp(ty_ref, current, span)?.into(); self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place), span); - let deref_trait = trait_lang_item.ok_or(MirLowerError::LangItemNotFound)?; - let deref_fn = deref_trait - .trait_items(self.db) - .method_by_name(&trait_method_name) - .ok_or(MirLowerError::LangItemNotFound)?; + let deref_fn = deref_fn.ok_or(MirLowerError::LangItemNotFound)?; let deref_fn_op = Operand::const_zst(Ty::new_fn_def( self.interner(), CallableDefId::FunctionId(deref_fn).into(), diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 7ab9bca697b38..753ff246c2f31 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -5699,21 +5699,15 @@ impl<'db> Type<'db> { /// This function is used in `.await` syntax completion. pub fn into_future_output(&self, db: &'db dyn HirDatabase) -> Option> { let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); - let trait_ = lang_items - .IntoFutureIntoFuture - .and_then(|into_future_fn| { - let assoc_item = as_assoc_item(db, AssocItem::Function, into_future_fn)?; - let into_future_trait = assoc_item.container_or_implemented_trait(db)?; - Some(into_future_trait.id) - }) - .or(lang_items.Future)?; + let (trait_, output_assoc_type) = lang_items + .IntoFuture + .zip(lang_items.IntoFutureOutput) + .or(lang_items.Future.zip(lang_items.FutureOutput))?; if !traits::implements_trait_unique(self.ty, db, self.env, trait_) { return None; } - let output_assoc_type = - trait_.trait_items(db).associated_type_by_name(&Name::new_symbol_root(sym::Output))?; self.normalize_trait_assoc_type(db, &[], output_assoc_type.into()) } @@ -5727,10 +5721,7 @@ impl<'db> Type<'db> { /// This does **not** resolve `IntoIterator`, only `Iterator`. pub fn iterator_item(self, db: &'db dyn HirDatabase) -> Option> { let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); - let iterator_trait = lang_items.Iterator?; - let iterator_item = iterator_trait - .trait_items(db) - .associated_type_by_name(&Name::new_symbol_root(sym::Item))?; + let iterator_item = lang_items.IteratorItem?; self.normalize_trait_assoc_type(db, &[], iterator_item.into()) } @@ -5745,19 +5736,13 @@ impl<'db> Type<'db> { /// Resolves the projection `::IntoIter` and returns the resulting type pub fn into_iterator_iter(self, db: &'db dyn HirDatabase) -> Option> { let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); - let trait_ = lang_items.IntoIterIntoIter.and_then(|into_iter_fn| { - let assoc_item = as_assoc_item(db, AssocItem::Function, into_iter_fn)?; - let into_iter_trait = assoc_item.container_or_implemented_trait(db)?; - Some(into_iter_trait.id) - })?; + let trait_ = lang_items.IntoIterator?; if !traits::implements_trait_unique(self.ty, db, self.env, trait_) { return None; } - let into_iter_assoc_type = trait_ - .trait_items(db) - .associated_type_by_name(&Name::new_symbol_root(sym::IntoIter))?; + let into_iter_assoc_type = lang_items.IntoIterIntoIterType?; self.normalize_trait_assoc_type(db, &[], into_iter_assoc_type.into()) } 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 783faa9ac86fe..f8f8152219d0d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -10,7 +10,7 @@ use std::iter::{self, once}; use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, DefWithBodyId, ExpressionStoreOwnerId, FieldId, - FunctionId, GenericDefId, LocalFieldId, ModuleDefId, StructId, TraitId, VariantId, + FunctionId, GenericDefId, LocalFieldId, ModuleDefId, StructId, VariantId, expr_store::{ Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, HygieneId, lower::ExprCollector, @@ -706,35 +706,25 @@ impl<'db> SourceAnalyzer<'db> { db: &'db dyn HirDatabase, prefix_expr: &ast::PrefixExpr, ) -> Option { + let lang_items = self.lang_items(db); let (_op_trait, op_fn) = match prefix_expr.op_kind()? { ast::UnaryOp::Deref => { // This can be either `Deref::deref` or `DerefMut::deref_mut`. // Since deref kind is inferenced and stored in `InferenceResult.method_resolution`, // use that result to find out which one it is. - let (deref_trait, deref) = self.lang_trait_fn( - db, - self.lang_items(db).Deref, - &Name::new_symbol_root(sym::deref), - )?; + let (deref_trait, deref) = (lang_items.Deref?, lang_items.Deref_deref?); self.infer() .and_then(|infer| { let expr = self.expr_id(prefix_expr.clone().into())?.as_expr()?; let (func, _) = infer.method_resolution(expr)?; - let (deref_mut_trait, deref_mut) = self.lang_trait_fn( - db, - self.lang_items(db).DerefMut, - &Name::new_symbol_root(sym::deref_mut), - )?; + let (deref_mut_trait, deref_mut) = + (lang_items.DerefMut?, lang_items.DerefMut_deref_mut?); if func == deref_mut { Some((deref_mut_trait, deref_mut)) } else { None } }) .unwrap_or((deref_trait, deref)) } - ast::UnaryOp::Not => { - self.lang_trait_fn(db, self.lang_items(db).Not, &Name::new_symbol_root(sym::not))? - } - ast::UnaryOp::Neg => { - self.lang_trait_fn(db, self.lang_items(db).Neg, &Name::new_symbol_root(sym::neg))? - } + ast::UnaryOp::Not => (lang_items.Not?, lang_items.Not_not?), + ast::UnaryOp::Neg => (lang_items.Neg?, lang_items.Neg_neg?), }; let ty = self.ty_of_expr(prefix_expr.expr()?)?; @@ -753,19 +743,16 @@ impl<'db> SourceAnalyzer<'db> { ) -> Option { let base_ty = self.ty_of_expr(index_expr.base()?)?; let index_ty = self.ty_of_expr(index_expr.index()?)?; + let lang_items = self.lang_items(db); - let (_index_trait, index_fn) = - self.lang_trait_fn(db, self.lang_items(db).Index, &Name::new_symbol_root(sym::index))?; + let (_index_trait, index_fn) = (lang_items.Index?, lang_items.Index_index?); let op_fn = self .infer() .and_then(|infer| { let expr = self.expr_id(index_expr.clone().into())?.as_expr()?; let (func, _) = infer.method_resolution(expr)?; - let (_index_mut_trait, index_mut_fn) = self.lang_trait_fn( - db, - self.lang_items(db).IndexMut, - &Name::new_symbol_root(sym::index_mut), - )?; + let (_index_mut_trait, index_mut_fn) = + (lang_items.IndexMut_index_mut?, lang_items.IndexMut_index_mut?); if func == index_mut_fn { Some(index_mut_fn) } else { None } }) .unwrap_or(index_fn); @@ -784,10 +771,8 @@ impl<'db> SourceAnalyzer<'db> { let lhs = self.ty_of_expr(binop_expr.lhs()?)?; let rhs = self.ty_of_expr(binop_expr.rhs()?)?; - let (_op_trait, op_fn) = - lang_items_for_bin_op(self.lang_items(db), op).and_then(|(name, lang_item)| { - self.lang_trait_fn(db, lang_item, &Name::new_symbol_root(name)) - })?; + let (op_fn, _op_trait) = lang_items_for_bin_op(self.lang_items(db), op) + .and_then(|(method, trait_)| method.zip(trait_))?; // HACK: subst for `index()` coincides with that for `Index` because `index()` itself // doesn't have any generic parameters, so we skip building another subst for `index()`. let substs = GenericArgs::new_from_slice(&[lhs.into(), rhs.into()]); @@ -1583,17 +1568,6 @@ impl<'db> SourceAnalyzer<'db> { hir_def::lang_item::lang_items(db, self.resolver.krate()) } - fn lang_trait_fn( - &self, - db: &'db dyn HirDatabase, - lang_trait: Option, - method_name: &Name, - ) -> Option<(TraitId, FunctionId)> { - let trait_id = lang_trait?; - let fn_id = trait_id.trait_items(db).method_by_name(method_name)?; - Some((trait_id, fn_id)) - } - fn ty_of_expr(&self, expr: ast::Expr) -> Option> { self.infer()?.type_of_expr_or_pat(self.expr_id(expr)?) } From 3b7a6902052e88034773fcf427280903e11c5d51 Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Mon, 4 May 2026 21:25:28 +0200 Subject: [PATCH 211/289] Backwards compatible CompletionImport#as_underscore --- src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs | 1 + 1 file changed, 1 insertion(+) 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 5d0d9209de2f8..91e065251d3f8 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 @@ -858,6 +858,7 @@ pub struct InlayHintResolveData { #[derive(Debug, Serialize, Deserialize)] pub struct CompletionImport { pub full_import_path: String, + #[serde(default)] pub as_underscore: bool, } From ed4412d03f7e5a2e6cb99f817a98d137c082f23a Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Mon, 4 May 2026 21:44:35 +0200 Subject: [PATCH 212/289] Update lsp-extensions.md --- .../rust-analyzer/docs/book/src/contributing/lsp-extensions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8ba6f6ab531e6..9bb10aac3f060 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 @@ +```rust,ignore (requires-Z-next-solver) +#![feature(generic_const_items)] +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![expect(incomplete_features)] + +type const ADD1: usize = const { N + 1 }; + +type const INC: usize = ADD1::; + +const ARR: [(); ADD1::<0>] = [(); INC::<0>]; +``` "##, default_severity: Severity::Allow, warn_since: None, @@ -8728,15 +8778,79 @@ The tracking issue for this feature is: [#151972] }, Lint { label: "generic_const_exprs", - description: r##"# `generic_const_exprs` + description: r##"# generic_const_exprs -Allows non-trivial generic constants which have to have wfness manually propagated to callers +Allows non-trivial generic constants which have to be shown to successfully evaluate +to a value by being part of an item signature. The tracking issue for this feature is: [#76560] + [#76560]: https://github.com/rust-lang/rust/issues/76560 ------------------------ + +Warning: This feature is incomplete; its design and syntax may change. + +See also: [min_generic_const_args], [generic_const_args] + +[min_generic_const_args]: min-generic-const-args.md +[generic_const_args]: generic-const-args.md + +## Examples + +```rust +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] + +// Use parameters that depend on a generic argument. +struct Foo +where + [(); N + 1]:, +{ + array: [usize; N + 1], +} + +// Use generic parameters in const operations. +trait Bar { + const X: usize; + const Y: usize; +} + +// Note `B::X * B::Y`. +const fn baz(x: [usize; B::X], y: [usize; B::Y]) -> [usize; B::X * B::Y] { + let mut out = [0; B::X * B::Y]; + let mut i = 0; + while i < B::Y { + let mut j = 0; + while j < B::X { + out[i * B::X + j] = y[i].saturating_mul(x[j]); + j += 1; + } + i += 1; + } + out +} + + +// Create a new type based on a generic argument. +pub struct Grow { + arr: [usize; N], +} + +impl Grow { + pub const fn grow(self, val: usize) -> Grow<{ N + 1 }> { + let mut new_arr = [0; { N + 1 }]; + let mut idx = 0; + while idx < N { + new_arr[idx] = self.arr[idx]; + idx += 1; + } + new_arr[N] = val; + Grow { arr: new_arr } + } +} +``` "##, default_severity: Severity::Allow, warn_since: None, @@ -8744,7 +8858,7 @@ The tracking issue for this feature is: [#76560] }, Lint { label: "generic_const_items", - description: r##"# `generic_const_items` + description: r##"# generic_const_items Allows generic parameters and where-clauses on free & associated const items. @@ -8753,6 +8867,50 @@ The tracking issue for this feature is: [#113521] [#113521]: https://github.com/rust-lang/rust/issues/113521 ------------------------ + +Warning: This feature is an [experiment] and lacks an RFC. +There are no guarantees that it will ever be stabilized. + +See also: [generic_const_exprs], [min_generic_const_args]. + +[experiment]: https://lang-team.rust-lang.org/how_to/experiment.html +[generic_const_exprs]: generic-const-exprs.md +[min_generic_const_args]: min-generic-const-args.md + +## Examples + +### Generic constant values + +```rust +#![allow(incomplete_features)] +#![feature(generic_const_items)] + +const GENERIC_VAL: usize = ARG + 1; + +#[test] +fn generic_const_arg() { + assert_eq!(GENERIC_VAL::<1>, 2); + assert_eq!(GENERIC_VAL::<2>, 3); +} +``` + +### Conditional constants + +```rust +#![allow(incomplete_features)] +#![feature(generic_const_items)] + +// `GENERIC_VAL::<0>` will fail to compile +const GENERIC_VAL: usize = if ARG > 0 { ARG + 1 } else { panic!("0 value") }; + +// Will fail to compile if the `Copy` derive is removed. +const COPY_MARKER: () = (); + +#[derive(Clone, Copy)] +struct Foo; + +const FOO_IS_COPY: () = COPY_MARKER::; +``` "##, default_severity: Severity::Allow, warn_since: None, @@ -9262,8 +9420,8 @@ The tracking issue for this feature is: [#154650] deny_since: None, }, Lint { - label: "integer_extend_truncate", - description: r##"# `integer_extend_truncate` + label: "integer_widen_truncate", + description: r##"# `integer_widen_truncate` @@ -10775,15 +10933,116 @@ The tracking issue for this feature is: [#154042] }, Lint { label: "min_generic_const_args", - description: r##"# `min_generic_const_args` + description: r##"# min_generic_const_args -Enables the generic const args MVP (only bare paths, not arbitrary computation). +Enables the generic const args MVP (paths to type const items and constructors for ADTs and primitives). The tracking issue for this feature is: [#132980] [#132980]: https://github.com/rust-lang/rust/issues/132980 ------------------------ + +Warning: This feature is incomplete; its design and syntax may change. + +This feature acts as a minimal alternative to [generic_const_exprs] that allows a smaller subset of functionality, +and uses a different approach for implementation. It is intentionally more restrictive, which helps with avoiding edge +cases that make the `generic_const_exprs` hard to implement properly. See [Feature background][feature_background] +for more details. + +Related features: [generic_const_args], [generic_const_items]. + +[feature_background]: https://github.com/rust-lang/project-const-generics/blob/main/documents/min_const_generics_plan.md +[generic_const_exprs]: generic-const-exprs.md +[generic_const_args]: generic-const-args.md +[generic_const_items]: generic-const-items.md + +## `type const` syntax + +This feature introduces new syntax: `type const`. +Constants marked as `type const` are allowed to be used in type contexts, e.g.: + +```compile_fail +#![allow(incomplete_features)] +#![feature(min_generic_const_args)] + +type const X: usize = 1; +const Y: usize = 1; + +struct Foo { + good_arr: [(); X], // Allowed + bad_arr: [(); Y], // Will not compile, `Y` must be `type const`. +} +``` + +## Examples + +```rust +#![allow(incomplete_features)] +#![feature(min_generic_const_args)] + +trait Bar { + type const VAL: usize; + type const VAL2: usize; +} + +struct Baz; + +impl Bar for Baz { + type const VAL: usize = 2; + type const VAL2: usize = const { Self::VAL * 2 }; +} + +struct Foo { + arr1: [usize; B::VAL], + arr2: [usize; B::VAL2], +} +``` + +Note that with [generic_const_exprs] the same example would look as follows: + +```rust +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] + +trait Bar { + const VAL: usize; + const VAL2: usize; +} + +struct Baz; + +impl Bar for Baz { + const VAL: usize = 2; + const VAL2: usize = const { Self::VAL * 2 }; +} + +struct Foo +where + [(); B::VAL]:, + [(); B::VAL2]:, +{ + arr1: [usize; B::VAL], + arr2: [usize; B::VAL2], +} +``` + +Use of const functions is allowed: + +```rust +#![allow(incomplete_features)] +#![feature(min_generic_const_args)] + +const VAL: usize = 1; + +const fn inc(val: usize) -> usize { + val + 1 +} + +type const INC: usize = const { inc(VAL) }; + +const ARR: [usize; INC] = [0; INC]; +``` "##, default_severity: Severity::Allow, warn_since: None, @@ -12023,6 +12282,22 @@ The tracking issue for this feature is: [#142503] [#142503]: https://github.com/rust-lang/rust/issues/142503 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "pathbuf_into_string", + description: r##"# `pathbuf_into_string` + + + +The tracking issue for this feature is: [#156203] + +[#156203]: https://github.com/rust-lang/rust/issues/156203 + ------------------------ "##, default_severity: Severity::Allow, @@ -14677,6 +14952,22 @@ The tracking issue for this feature is: [#119639] [#119639]: https://github.com/rust-lang/rust/issues/119639 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "tcp_keepalive", + description: r##"# `tcp_keepalive` + + + +The tracking issue for this feature is: [#155889] + +[#155889]: https://github.com/rust-lang/rust/issues/155889 + ------------------------ "##, default_severity: Severity::Allow, From d7b8e236fc36a793c6f032bb9d16caf24e245f39 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 10 May 2026 03:08:47 +0300 Subject: [PATCH 278/289] Remove support for interning in query_group --- .../crates/query-group-macro/src/lib.rs | 68 +----------------- .../crates/query-group-macro/src/queries.rs | 72 +------------------ .../query-group-macro/tests/interned.rs | 50 ------------- 3 files changed, 4 insertions(+), 186 deletions(-) delete mode 100644 src/tools/rust-analyzer/crates/query-group-macro/tests/interned.rs diff --git a/src/tools/rust-analyzer/crates/query-group-macro/src/lib.rs b/src/tools/rust-analyzer/crates/query-group-macro/src/lib.rs index 277cc0b269d71..6e89e0875e749 100644 --- a/src/tools/rust-analyzer/crates/query-group-macro/src/lib.rs +++ b/src/tools/rust-analyzer/crates/query-group-macro/src/lib.rs @@ -6,18 +6,15 @@ use std::vec; use proc_macro::TokenStream; use proc_macro2::Span; use queries::{ - GeneratedInputStruct, InputQuery, InputSetter, InputSetterWithDurability, Intern, Lookup, - Queries, SetterKind, TrackedQuery, Transparent, + GeneratedInputStruct, InputQuery, InputSetter, InputSetterWithDurability, Queries, SetterKind, + TrackedQuery, Transparent, }; use quote::{ToTokens, format_ident, quote}; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::visit_mut::VisitMut; -use syn::{ - Attribute, FnArg, ItemTrait, Path, Token, TraitItem, TraitItemFn, parse_quote, - parse_quote_spanned, -}; +use syn::{Attribute, FnArg, ItemTrait, Path, Token, TraitItem, parse_quote, parse_quote_spanned}; mod queries; @@ -106,7 +103,6 @@ enum QueryKind { Tracked, TrackedWithSalsaStruct, Transparent, - Interned, } #[derive(Default, Debug, Clone)] @@ -190,7 +186,6 @@ pub(crate) fn query_group_impl( let mut trait_methods = vec![]; let mut setter_trait_methods = vec![]; let mut lookup_signatures = vec![]; - let mut lookup_methods = vec![]; for item in &mut item_trait.items { if let syn::TraitItem::Fn(method) = item { @@ -202,7 +197,6 @@ pub(crate) fn query_group_impl( let mut query_kind = QueryKind::TrackedWithSalsaStruct; let mut invoke = None; let mut cycle = None; - let mut interned_struct_path = None; let mut lru = None; let params: Vec = signature.inputs.clone().into_iter().collect(); @@ -230,22 +224,6 @@ pub(crate) fn query_group_impl( } query_kind = QueryKind::Input; } - "interned" => { - let syn::ReturnType::Type(_, ty) = &signature.output else { - return Err(syn::Error::new( - span, - "interned queries must have return type", - )); - }; - let syn::Type::Path(path) = &**ty else { - return Err(syn::Error::new( - span, - "interned queries must have return type", - )); - }; - interned_struct_path = Some(path.path.clone()); - query_kind = QueryKind::Interned; - } "invoke_interned" => { let path = syn::parse::>(tts)?; invoke = Some(path.0.clone()); @@ -317,28 +295,6 @@ pub(crate) fn query_group_impl( }; setter_trait_methods.push(SetterKind::WithDurability(setter)); } - (QueryKind::Interned, None) => { - let interned_struct_path = interned_struct_path.unwrap(); - let method = Intern { - signature: signature.clone(), - pat_and_tys: pat_and_tys.clone(), - interned_struct_path: interned_struct_path.clone(), - }; - - trait_methods.push(Queries::Intern(method)); - - let mut method = Lookup { - signature: signature.clone(), - pat_and_tys: pat_and_tys.clone(), - return_ty: *return_ty, - interned_struct_path, - }; - method.prepare_signature(); - - lookup_signatures - .push(TraitItem::Fn(make_trait_method(method.signature.clone()))); - lookup_methods.push(method); - } // tracked function. it might have an invoke, or might not. (QueryKind::Tracked, invoke) => { let method = TrackedQuery { @@ -380,13 +336,6 @@ pub(crate) fn query_group_impl( }; trait_methods.push(Queries::Transparent(method)); } - // error/invalid constructions - (QueryKind::Interned, Some(path)) => { - return Err(syn::Error::new( - path.span(), - "Interned queries cannot be used with an `#[invoke]`".to_string(), - )); - } (QueryKind::Input, Some(path)) => { return Err(syn::Error::new( path.span(), @@ -451,8 +400,6 @@ pub(crate) fn query_group_impl( #(#trait_methods)* #(#setter_methods)* - - #(#lookup_methods)* } }; RemoveAttrsFromTraitMethods.visit_item_trait_mut(&mut item_trait); @@ -485,15 +432,6 @@ where } } -fn make_trait_method(sig: syn::Signature) -> TraitItemFn { - TraitItemFn { - attrs: vec![], - sig: sig.clone(), - semi_token: Some(syn::Token![;](sig.span())), - default: None, - } -} - struct RemoveAttrsFromTraitMethods; impl VisitMut for RemoveAttrsFromTraitMethods { diff --git a/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs b/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs index 83ce8902d0c9d..4221068828af8 100644 --- a/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs +++ b/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs @@ -1,7 +1,7 @@ //! The IR of the `#[query_group]` macro. use quote::{ToTokens, format_ident, quote, quote_spanned}; -use syn::{FnArg, Ident, PatType, Path, Receiver, ReturnType, Type, parse_quote, spanned::Spanned}; +use syn::{FnArg, Ident, PatType, Path, Receiver, ReturnType, parse_quote, spanned::Spanned}; use crate::Cycle; @@ -266,80 +266,11 @@ impl ToTokens for Transparent { method.to_tokens(tokens); } } -pub(crate) struct Intern { - pub(crate) signature: syn::Signature, - pub(crate) pat_and_tys: Vec, - pub(crate) interned_struct_path: Path, -} - -impl ToTokens for Intern { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - let sig = &self.signature; - - let ty = self.pat_and_tys.to_vec(); - - let interned_pat = ty.first().expect("at least one pat; this is a bug"); - let interned_pat = &interned_pat.pat; - - let wrapper_struct = self.interned_struct_path.to_token_stream(); - - let method = quote! { - #sig { - #wrapper_struct::new(self, #interned_pat) - } - }; - - method.to_tokens(tokens); - } -} - -pub(crate) struct Lookup { - pub(crate) signature: syn::Signature, - pub(crate) pat_and_tys: Vec, - pub(crate) return_ty: Type, - pub(crate) interned_struct_path: Path, -} - -impl Lookup { - pub(crate) fn prepare_signature(&mut self) { - let sig = &self.signature; - - let ident = format_ident!("lookup_{}", sig.ident); - - let ty = self.pat_and_tys.to_vec(); - - let interned_key = &self.return_ty; - - let interned_pat = ty.first().expect("at least one pat; this is a bug"); - let interned_return_ty = &interned_pat.ty; - - self.signature = parse_quote!( - fn #ident(&self, id: #interned_key) -> #interned_return_ty - ); - } -} - -impl ToTokens for Lookup { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - let sig = &self.signature; - - let wrapper_struct = self.interned_struct_path.to_token_stream(); - let method = quote! { - #sig { - let zalsa = self.zalsa(); - #wrapper_struct::ingredient(zalsa).data(zalsa, id.as_id()).0.clone() - } - }; - - method.to_tokens(tokens); - } -} #[allow(clippy::large_enum_variant)] pub(crate) enum Queries { TrackedQuery(TrackedQuery), InputQuery(InputQuery), - Intern(Intern), Transparent(Transparent), } @@ -349,7 +280,6 @@ impl ToTokens for Queries { Queries::TrackedQuery(tracked_query) => tracked_query.to_tokens(tokens), Queries::InputQuery(input_query) => input_query.to_tokens(tokens), Queries::Transparent(transparent) => transparent.to_tokens(tokens), - Queries::Intern(intern) => intern.to_tokens(tokens), } } } diff --git a/src/tools/rust-analyzer/crates/query-group-macro/tests/interned.rs b/src/tools/rust-analyzer/crates/query-group-macro/tests/interned.rs deleted file mode 100644 index f738185b1fe7c..0000000000000 --- a/src/tools/rust-analyzer/crates/query-group-macro/tests/interned.rs +++ /dev/null @@ -1,50 +0,0 @@ -use query_group_macro::query_group; - -use expect_test::expect; -use salsa::plumbing::AsId; - -mod logger_db; -use logger_db::LoggerDb; - -#[salsa_macros::interned(no_lifetime)] -pub struct InternedString { - data: String, -} - -#[query_group] -pub trait InternedDB: salsa::Database { - #[salsa::interned] - fn intern_string(&self, data: String) -> InternedString; - - fn interned_len(&self, id: InternedString) -> usize; -} - -fn interned_len(db: &dyn InternedDB, id: InternedString) -> usize { - db.lookup_intern_string(id).len() -} - -#[test] -fn intern_round_trip() { - let db = LoggerDb::default(); - - let id = db.intern_string(String::from("Hello, world!")); - let s = db.lookup_intern_string(id); - - assert_eq!(s.len(), 13); - db.assert_logs(expect![[r#"[]"#]]); -} - -#[test] -fn intern_with_query() { - let db = LoggerDb::default(); - - let id = db.intern_string(String::from("Hello, world!")); - let len = db.interned_len(id); - - assert_eq!(len, 13); - db.assert_logs(expect![[r#" - [ - "salsa_event(WillCheckCancellation)", - "salsa_event(WillExecute { database_key: interned_len_shim(Id(0)) })", - ]"#]]); -} From b647116483b1df816f2ed679fefabc2901a955c2 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 10 May 2026 08:26:57 +0530 Subject: [PATCH 279/289] make substitute_type_bound and substitute_where_pred to return option --- .../hir-expand/src/builtin/derive_macro.rs | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) 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 03c07909e5a73..8b031e364775c 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 @@ -1182,10 +1182,18 @@ fn coerce_pointee_expand( // Otherwise, duplicate only bounds that mention the pointee. let is_pointee = param_name.text() == pointee_param_name.text(); let new_bounds = bounds.bounds().filter_map(|bound| { - let (bound, substituted) = - substitute_type_bound(bound, &pointee_param_name.text(), ADDED_PARAM); - (substituted || is_pointee).then_some(bound) + let new_bound = substitute_type_bound( + bound.clone(), + &pointee_param_name.text(), + ADDED_PARAM, + ); + + if is_pointee { + return new_bound.or(Some(bound)); + } + new_bound }); + let new_bounds_target = if is_pointee { make.name_ref(ADDED_PARAM) } else { @@ -1231,15 +1239,13 @@ fn coerce_pointee_expand( // If the target type references the pointee, duplicate the bound as whole. // Otherwise, duplicate only bounds that mention the pointee. - let (predicate_with_substituted_target, target_substituted) = - substitute_where_pred(&predicate, &pointee_param_name.text(), ADDED_PARAM); - if target_substituted { + if let Some(predicate_with_substituted_target) = + substitute_where_pred(&predicate, &pointee_param_name.text(), ADDED_PARAM) + { new_predicates.push(predicate_with_substituted_target); } else if let Some(bounds) = predicate.type_bound_list() { let new_bounds = bounds.bounds().filter_map(|bound| { - let (bound, substituted) = - substitute_type_bound(bound, &pointee_param_name.text(), ADDED_PARAM); - substituted.then_some(bound) + substitute_type_bound(bound, &pointee_param_name.text(), ADDED_PARAM) }); new_predicates.push(make.where_pred(Either::Right(pred_target), new_bounds)); } @@ -1386,24 +1392,24 @@ fn coerce_pointee_expand( bound: ast::TypeBound, param_name: &str, replacement: &str, - ) -> (ast::TypeBound, bool) { + ) -> Option { let (editor, bound) = SyntaxEditor::with_ast_node(&bound); let substituted = bound .ty() .is_some_and(|ty| substitute_type_in_bound(&editor, ty, param_name, replacement)); if !substituted { - return (bound, false); + return None; } let edit = editor.finish(); - (ast::TypeBound::cast(edit.new_root().clone()).unwrap(), true) + Some(ast::TypeBound::cast(edit.new_root().clone()).unwrap()) } fn substitute_where_pred( predicate: &ast::WherePred, param_name: &str, replacement: &str, - ) -> (ast::WherePred, bool) { + ) -> Option { let (editor, predicate) = SyntaxEditor::with_ast_node(predicate); let substituted = predicate .ty() @@ -1416,11 +1422,11 @@ fn coerce_pointee_expand( } } if !substituted { - return (predicate, false); + return None; } let edit = editor.finish(); - (ast::WherePred::cast(edit.new_root().clone()).unwrap(), true) + Some(ast::WherePred::cast(edit.new_root().clone()).unwrap()) } fn substitute_type_in_bound( From 4fabfc3f00bb0a832fe6381180731f1e3933b47a Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 10 May 2026 08:40:07 +0530 Subject: [PATCH 280/289] add without_mapping factory to bypass direct use of make constructor --- .../src/handlers/convert_closure_to_fn.rs | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index 40c99be95626d..e6d31b966099c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -11,7 +11,7 @@ use syntax::{ ast::{ self, HasArgList, HasGenericParams, HasName, edit::{AstNodeEdit, IndentLevel}, - make, + syntax_factory::SyntaxFactory, }, hacks::parse_expr_from_str, syntax_editor::SyntaxEditor, @@ -205,6 +205,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_, ' expr = peel_ref(expr); } let replacement = wrap_capture_in_deref_if_needed( + make, &expr, &capture_name, capture.kind(), @@ -213,7 +214,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_, ' capture_usages_replacement_map.push((expr, replacement)); } - let capture_as_arg = capture_as_arg(ctx, capture); + let capture_as_arg = capture_as_arg(make, ctx, capture); if is_self { captures_as_args.insert(0, capture_as_arg); } else { @@ -222,7 +223,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_, ' } let (closure_type_params, closure_where_clause) = - compute_closure_type_params(ctx, closure_mentioned_generic_params, &closure); + compute_closure_type_params(make, ctx, closure_mentioned_generic_params, &closure); for (old, new) in capture_usages_replacement_map { editor.replace(old.syntax(), new.syntax()); @@ -236,22 +237,24 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_, ' .and_then(|closure| closure.body()) .unwrap(); + let make = SyntaxFactory::without_mappings(); + let body = if wrap_body_in_block { - make::block_expr([], Some(body.reset_indent().indent(1.into()))) + make.block_expr([], Some(body.reset_indent().indent(1.into()))) } else { ast::BlockExpr::cast(body.syntax().clone()).unwrap() }; - let params = make::param_list(None, params); + let params = make.param_list(None, params); let ret_ty = if ret_ty.is_unit() { None } else { let ret_ty = ret_ty .display_source_code(ctx.db(), module.into(), true) .unwrap_or_else(|_| "_".to_owned()); - Some(make::ret_type(make::ty(&ret_ty))) + Some(make.ret_type(make.ty(&ret_ty))) }; - let mut fn_ = make::fn_( + let mut fn_ = make.fn_( None, None, closure_name_or_default.clone(), @@ -346,6 +349,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_, ' } fn compute_closure_type_params( + make: &SyntaxFactory, ctx: &AssistContext<'_, '_>, mentioned_generic_params: FxHashSet, closure: &ast::ClosureExpr, @@ -472,11 +476,11 @@ fn compute_closure_type_params( })) .collect::>(); let where_clause = - (!include_where_bounds.is_empty()).then(|| make::where_clause(include_where_bounds)); + (!include_where_bounds.is_empty()).then(|| make.where_clause(include_where_bounds)); // FIXME: Consider generic parameters that do not appear in params/return type/captures but // written explicitly inside the closure. - (Some(make::generic_param_list(include_params)), where_clause) + (Some(make.generic_param_list(include_params)), where_clause) } fn peel_parens(mut expr: ast::Expr) -> ast::Expr { @@ -497,12 +501,13 @@ fn peel_ref(mut expr: ast::Expr) -> ast::Expr { } fn wrap_capture_in_deref_if_needed( + make: &SyntaxFactory, expr: &ast::Expr, capture_name: &ast::Name, capture_kind: CaptureKind, is_ref: bool, ) -> ast::Expr { - let capture_name = make::expr_path(make::path_from_text(&capture_name.text())); + let capture_name = make.expr_path(make.path_from_text(&capture_name.text())); if capture_kind == CaptureKind::Move || is_ref { return capture_name; } @@ -524,10 +529,14 @@ fn wrap_capture_in_deref_if_needed( if does_autoderef { return capture_name; } - make::expr_prefix(T![*], capture_name).into() + make.expr_prefix(T![*], capture_name).into() } -fn capture_as_arg(ctx: &AssistContext<'_, '_>, capture: &ClosureCapture<'_>) -> ast::Expr { +fn capture_as_arg( + make: &SyntaxFactory, + ctx: &AssistContext<'_, '_>, + capture: &ClosureCapture<'_>, +) -> ast::Expr { let place = parse_expr_from_str( &capture.display_place_source_code(ctx.db(), ctx.edition()), ctx.edition(), @@ -543,7 +552,7 @@ fn capture_as_arg(ctx: &AssistContext<'_, '_>, capture: &ClosureCapture<'_>) -> { return expr.expr().expect("`display_place_source_code()` produced an invalid expr"); } - make::expr_ref(place, needs_mut) + make.expr_ref(place, needs_mut) } fn handle_calls( From 9d01bf83f9d68dea3d98e7d837a0f4ce651fe210 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 10 May 2026 17:04:59 +1000 Subject: [PATCH 281/289] Remove some dead code for dumping MIR for a single DefId --- compiler/rustc_driver_impl/src/pretty.rs | 4 ++-- compiler/rustc_middle/src/mir/graphviz.rs | 10 ++++----- compiler/rustc_middle/src/mir/pretty.rs | 22 +++++--------------- compiler/rustc_middle/src/ty/mod.rs | 3 ++- compiler/rustc_mir_transform/src/dump_mir.rs | 4 ++-- 5 files changed, 15 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 79b6fc59978f3..3d3906637b8b1 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -301,12 +301,12 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { } Mir => { let mut out = Vec::new(); - write_mir_pretty(ex.tcx(), None, &mut out).unwrap(); + write_mir_pretty(ex.tcx(), &mut out).unwrap(); String::from_utf8(out).unwrap() } MirCFG => { let mut out = Vec::new(); - write_mir_graphviz(ex.tcx(), None, &mut out).unwrap(); + write_mir_graphviz(ex.tcx(), &mut out).unwrap(); String::from_utf8(out).unwrap() } StableMir => { diff --git a/compiler/rustc_middle/src/mir/graphviz.rs b/compiler/rustc_middle/src/mir/graphviz.rs index 30a781b025fd5..4002d0ebd392d 100644 --- a/compiler/rustc_middle/src/mir/graphviz.rs +++ b/compiler/rustc_middle/src/mir/graphviz.rs @@ -4,24 +4,22 @@ use gsgdt::GraphvizSettings; use rustc_graphviz as dot; use super::generic_graph::mir_fn_to_generic_graph; -use super::pretty::dump_mir_def_ids; use crate::mir::*; /// Write a graphviz DOT graph of a list of MIRs. -pub fn write_mir_graphviz(tcx: TyCtxt<'_>, single: Option, w: &mut W) -> io::Result<()> +pub fn write_mir_graphviz(tcx: TyCtxt<'_>, w: &mut W) -> io::Result<()> where W: Write, { - let def_ids = dump_mir_def_ids(tcx, single); - - let mirs = def_ids + let mirs = tcx + .mir_keys(()) .iter() .filter(|&&def_id| !tcx.is_trivial_const(def_id)) .flat_map(|&def_id| { if tcx.is_const_fn(def_id) { vec![tcx.optimized_mir(def_id), tcx.mir_for_ctfe(def_id)] } else { - vec![tcx.instance_mir(ty::InstanceKind::Item(def_id))] + vec![tcx.instance_mir(ty::InstanceKind::Item(def_id.to_def_id()))] } }) .collect::>(); diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index e1bc29d5c3684..70ad7fc1c3d6c 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -312,13 +312,9 @@ impl<'a, 'tcx> MirDumper<'a, 'tcx> { /////////////////////////////////////////////////////////////////////////// // Whole MIR bodies -/// Write out a human-readable textual representation for the given MIR, with the default -/// [PrettyPrintMirOptions]. -pub fn write_mir_pretty<'tcx>( - tcx: TyCtxt<'tcx>, - single: Option, - w: &mut dyn io::Write, -) -> io::Result<()> { +/// Write out a human-readable textual representation of this crate's MIR, +/// with the default [`PrettyPrintMirOptions`]. +pub fn write_mir_pretty<'tcx>(tcx: TyCtxt<'tcx>, w: &mut dyn io::Write) -> io::Result<()> { let writer = MirWriter::new(tcx); writeln!(w, "// WARNING: This output format is intended for human consumers only")?; @@ -326,7 +322,7 @@ pub fn write_mir_pretty<'tcx>( writeln!(w, "// HINT: See also -Z dump-mir for MIR at specific points during compilation.")?; let mut first = true; - for def_id in dump_mir_def_ids(tcx, single) { + for &def_id in tcx.mir_keys(()) { if first { first = false; } else { @@ -360,7 +356,7 @@ pub fn write_mir_pretty<'tcx>( } writeln!(w, ": {} = const {};", ty, Const::Val(val, ty))?; } else { - let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id)); + let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id.to_def_id())); render_body(w, instance_mir)?; } } @@ -698,14 +694,6 @@ fn write_user_type_annotations( Ok(()) } -pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option) -> Vec { - if let Some(i) = single { - vec![i] - } else { - tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect() - } -} - /////////////////////////////////////////////////////////////////////////// // Basic blocks and their parts (statements, terminators, ...) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 701e1102f33f1..93233efae6650 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2073,7 +2073,8 @@ impl<'tcx> TyCtxt<'tcx> { /// /// Even if this returns `true`, constness may still be unstable! #[inline] - pub fn is_const_fn(self, def_id: DefId) -> bool { + pub fn is_const_fn(self, def_id: impl IntoQueryKey) -> bool { + let def_id = def_id.into_query_key(); matches!( self.def_kind(def_id), DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Closure diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs index e4fcbaa483d04..50d4e9894e74a 100644 --- a/compiler/rustc_mir_transform/src/dump_mir.rs +++ b/compiler/rustc_mir_transform/src/dump_mir.rs @@ -25,11 +25,11 @@ pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> { match tcx.output_filenames(()).path(OutputType::Mir) { OutFileName::Stdout => { let mut f = io::stdout(); - write_mir_pretty(tcx, None, &mut f)?; + write_mir_pretty(tcx, &mut f)?; } OutFileName::Real(path) => { let mut f = File::create_buffered(&path)?; - write_mir_pretty(tcx, None, &mut f)?; + write_mir_pretty(tcx, &mut f)?; if tcx.sess.opts.json_artifact_notifications { tcx.dcx().emit_artifact_notification(&path, "mir"); } From d1c3e91a7e3ab431c40dcca68c5bc80a119aa5a6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 10 May 2026 12:03:47 +0200 Subject: [PATCH 282/289] prepare fs tests for miri --- library/std/src/fs/tests.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 7b53f2ead1c0d..068f341a54b15 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -856,10 +856,12 @@ fn recursive_mkdir_failure() { #[test] fn concurrent_recursive_mkdir() { - for _ in 0..100 { + let count = if cfg!(miri) { 10 } else { 100 }; + let nest = if cfg!(miri) { 10 } else { 40 }; + for _ in 0..count { let dir = tmpdir(); let mut dir = dir.join("a"); - for _ in 0..40 { + for _ in 0..nest { dir = dir.join("a"); } let mut join = vec![]; @@ -1869,7 +1871,7 @@ fn create_dir_long_paths() { fn read_large_dir() { let tmpdir = tmpdir(); - let count = 32 * 1024; + let count = if cfg!(miri) { 1024 } else { 32 * 1024 }; for i in 0..count { check!(fs::File::create(tmpdir.join(&i.to_string()))); } @@ -1954,6 +1956,7 @@ fn test_eq_windows_file_type() { /// Regression test for https://github.com/rust-lang/rust/issues/50619. #[test] #[cfg(target_os = "linux")] +#[cfg_attr(miri, ignore)] // Cannot spawn processes on Miri fn test_read_dir_infinite_loop() { use crate::io::ErrorKind; use crate::process::Command; From 5901bb8d2daa774b825a8b535729afe5e7e0d017 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 10 May 2026 12:45:07 +0200 Subject: [PATCH 283/289] remove some miri test gates --- library/std/src/fs/tests.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 068f341a54b15..83136288431ee 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1,10 +1,7 @@ use rand::RngCore; -#[cfg(not(miri))] use super::Dir; use crate::fs::{self, File, FileTimes, OpenOptions, TryLockError}; -#[cfg(not(miri))] -use crate::io; use crate::io::prelude::*; use crate::io::{BorrowedBuf, ErrorKind, SeekFrom}; use crate::mem::MaybeUninit; @@ -20,7 +17,7 @@ use crate::path::Path; use crate::sync::Arc; use crate::test_helpers::{TempDir, tmpdir}; use crate::time::{Duration, Instant, SystemTime}; -use crate::{assert_matches, env, str, thread}; +use crate::{assert_matches, env, io, str, thread}; macro_rules! check { ($e:expr) => { @@ -2533,8 +2530,6 @@ fn test_fs_set_times_nofollow() { } #[test] -// FIXME: libc calls fail on miri -#[cfg(not(miri))] fn test_dir_smoke_test() { let tmpdir = tmpdir(); let dir = Dir::open(tmpdir.path()); @@ -2542,8 +2537,6 @@ fn test_dir_smoke_test() { } #[test] -// FIXME: libc calls fail on miri -#[cfg(not(miri))] fn test_dir_read_file() { let tmpdir = tmpdir(); let mut f = check!(File::create(tmpdir.join("foo.txt"))); From e8eb80b9de9bacf2809e9b068c1b4a97cbea8736 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Sun, 10 May 2026 12:19:26 +0100 Subject: [PATCH 284/289] Improve doc comments for f32::ceil() and f32::floor() Previously ::floor() included an example showing behaviour for negative values, but ::ceil() did not. Ensure both have examples of the negative case, for both f32 and f64. Whilst we're here, tweak the wording slightly so it reads better. --- library/std/src/num/f32.rs | 6 ++++-- library/std/src/num/f64.rs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index 4f38d3be52f7d..3c1778cae6e22 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -26,7 +26,7 @@ use crate::sys::cmath; #[cfg(not(test))] impl f32 { - /// Returns the largest integer less than or equal to `self`. + /// Returns the largest integer that is less than or equal to `self`. /// /// This function always returns the precise result. /// @@ -50,7 +50,7 @@ impl f32 { core::f32::math::floor(self) } - /// Returns the smallest integer greater than or equal to `self`. + /// Returns the smallest integer that is greater than or equal to `self`. /// /// This function always returns the precise result. /// @@ -59,9 +59,11 @@ impl f32 { /// ``` /// let f = 3.01_f32; /// let g = 4.0_f32; + /// let h = -3.01_f32; /// /// assert_eq!(f.ceil(), 4.0); /// assert_eq!(g.ceil(), 4.0); + /// assert_eq!(h.ceil(), -3.0); /// ``` #[doc(alias = "ceiling")] #[rustc_allow_incoherent_impl] diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 8a771185f6fe4..af262474f1b0f 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -26,7 +26,7 @@ use crate::sys::cmath; #[cfg(not(test))] impl f64 { - /// Returns the largest integer less than or equal to `self`. + /// Returns the largest integer that is less than or equal to `self`. /// /// This function always returns the precise result. /// @@ -50,7 +50,7 @@ impl f64 { core::f64::math::floor(self) } - /// Returns the smallest integer greater than or equal to `self`. + /// Returns the smallest integer that is greater than or equal to `self`. /// /// This function always returns the precise result. /// @@ -59,9 +59,11 @@ impl f64 { /// ``` /// let f = 3.01_f64; /// let g = 4.0_f64; + /// let h = -3.01_f64; /// /// assert_eq!(f.ceil(), 4.0); /// assert_eq!(g.ceil(), 4.0); + /// assert_eq!(h.ceil(), -3.0); /// ``` #[doc(alias = "ceiling")] #[rustc_allow_incoherent_impl] From 4b15312c3adba212d540fc8cc4d7a53877f05a17 Mon Sep 17 00:00:00 2001 From: WaterWhisperer Date: Sun, 10 May 2026 14:56:38 +0800 Subject: [PATCH 285/289] feat: add diagnostic for E0529 --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 5 ++ .../crates/hir-ty/src/infer/pat.rs | 5 +- .../crates/hir-ty/src/infer/unify.rs | 1 + .../hir-ty/src/mir/lower/pattern_matching.rs | 11 +++- .../crates/hir/src/diagnostics.rs | 11 ++++ .../handlers/expected_array_or_slice_pat.rs | 50 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 7 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 48c04297009f2..1185073316372 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -295,6 +295,11 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] has_rest: bool, }, + ExpectedArrayOrSlicePat { + #[type_visitable(ignore)] + pat: PatId, + found: StoredTy, + }, DuplicateField { #[type_visitable(ignore)] field: ExprOrPatId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index ca2a0d87f7a0a..f21438647c14f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -1620,7 +1620,10 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; TyKind::Slice(element_ty) => (element_ty, Some(expected), expected), // The expected type must be an array or slice, but was neither, so error. _ => { - // FIXME: Emit an error: expected an array or a slice. + self.push_diagnostic(InferenceDiagnostic::ExpectedArrayOrSlicePat { + pat, + found: expected.store(), + }); let err = self.types.types.error; (err, Some(err), err) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 168da95600d69..f9ad76b0c12c0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -578,6 +578,7 @@ pub(super) mod resolve_completely { self.resolve_completely(diagnostic); if let InferenceDiagnostic::ExpectedFunction { found: ty, .. } + | InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. } | InferenceDiagnostic::UnresolvedField { receiver: ty, .. } | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic && ty.as_ref().references_non_lt_error() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index e7fa036f23c67..c924c5bdf0fdf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -248,9 +248,18 @@ impl<'db> MirLowerCtx<'_, 'db> { (current, current_else) } Pat::Slice { prefix, slice, suffix } => { + let pat_ty = self.infer.pat_ty(pattern); + // FIXME: MIR lowering should be skipped for bodies with inference errors. Once + // that happens, this recovery for invalid slice patterns can be removed. + if !matches!(pat_ty.kind(), TyKind::Array(..) | TyKind::Slice(_)) { + return Err(MirLowerError::TypeError( + "non array or slice type matched with slice pattern", + )); + } + if mode == MatchingMode::Check { // emit runtime length check for slice - if let TyKind::Slice(_) = self.infer.pat_ty(pattern).kind() { + if let TyKind::Slice(_) = pat_ty.kind() { let pattern_len = prefix.len() + suffix.len(); let place_len: Place = self .temp(Ty::new_usize(self.interner()), current, pattern.into())? diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index d56d4b7431cad..a044f24587bae 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -103,6 +103,7 @@ diagnostics![AnyDiagnostic<'db> -> AwaitOutsideOfAsync, BreakOutsideOfLoop, CastToUnsized<'db>, + ExpectedArrayOrSlicePat<'db>, ExpectedFunction<'db>, FunctionalRecordUpdateOnNonStruct, GenericDefaultRefersToSelf, @@ -296,6 +297,12 @@ pub struct MismatchedArrayPatLen { pub has_rest: bool, } +#[derive(Debug)] +pub struct ExpectedArrayOrSlicePat<'db> { + pub pat: InFile, + pub found: Type<'db>, +} + #[derive(Debug)] pub struct ExpectedFunction<'db> { pub call: InFile, @@ -777,6 +784,10 @@ impl<'db> AnyDiagnostic<'db> { let pat = pat_syntax(pat)?.map(Into::into); MismatchedArrayPatLen { pat, expected, found, has_rest }.into() } + InferenceDiagnostic::ExpectedArrayOrSlicePat { pat, found } => { + let pat = pat_syntax(*pat)?.map(Into::into); + ExpectedArrayOrSlicePat { pat, found: Type::new(db, def, found.as_ref()) }.into() + } &InferenceDiagnostic::DuplicateField { field: expr, variant } => { let expr_or_pat = match expr { ExprOrPatId::ExprId(expr) => { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs new file mode 100644 index 0000000000000..ab2c3ccd1215b --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs @@ -0,0 +1,50 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: expected-array-or-slice-pat +// +// This diagnostic is triggered when an array or slice pattern is matched +// against a type that is neither an array nor a slice. +pub(crate) fn expected_array_or_slice_pat( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::ExpectedArrayOrSlicePat<'_>, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0529"), + format!( + "expected an array or slice, found {}", + d.found.display(ctx.sema.db, ctx.display_target) + ), + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn expected_array_or_slice() { + check_diagnostics( + r#" +fn f([_a, _b]: i32) {} + //^^^^^^^^ error: expected an array or slice, found i32 +"#, + ); + } + + #[test] + fn expected_array_or_slice_let_pattern() { + check_diagnostics( + r#" +fn f(x: i32) { + let [_a, _b] = x; + //^^^^^^^^ error: expected an array or slice, found i32 +} +"#, + ); + } +} 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 300b5e824d0cb..aa8dc02de665e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -34,6 +34,7 @@ mod handlers { pub(crate) mod break_outside_of_loop; pub(crate) mod duplicate_field; pub(crate) mod elided_lifetimes_in_path; + pub(crate) mod expected_array_or_slice_pat; pub(crate) mod expected_function; pub(crate) mod functional_record_update_on_non_struct; pub(crate) mod generic_args_prohibited; @@ -425,6 +426,7 @@ pub fn semantic_diagnostics( let d = match diag { AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d), AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), + AnyDiagnostic::ExpectedArrayOrSlicePat(d) => handlers::expected_array_or_slice_pat::expected_array_or_slice_pat(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), AnyDiagnostic::FunctionalRecordUpdateOnNonStruct(d) => handlers::functional_record_update_on_non_struct::functional_record_update_on_non_struct(&ctx, &d), AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) { From a07cc548e43869de0c4aa047242c182757a750af Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 8 May 2026 14:41:27 +0300 Subject: [PATCH 286/289] resolve: Module-related refactorings --- .../rustc_resolve/src/build_reduced_graph.rs | 11 +- compiler/rustc_resolve/src/diagnostics.rs | 41 ++-- compiler/rustc_resolve/src/ident.rs | 24 +-- compiler/rustc_resolve/src/imports.rs | 4 +- compiler/rustc_resolve/src/late.rs | 2 +- .../rustc_resolve/src/late/diagnostics.rs | 13 +- compiler/rustc_resolve/src/lib.rs | 202 ++++++++++-------- compiler/rustc_resolve/src/macros.rs | 10 +- 8 files changed, 164 insertions(+), 143 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 61da931df4d5d..4fb274188a8cf 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -253,7 +253,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match vis.kind { ast::VisibilityKind::Public => Ok(Visibility::Public), ast::VisibilityKind::Inherited => { - Ok(match parent_scope.module.kind { + Ok(match parent_scope.module.expect_local().kind { // Any inherited visibility resolved directly inside an enum or trait // (i.e. variants, fields, and trait items) inherits from the visibility // of the enum or trait. @@ -535,7 +535,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { root_id: NodeId, vis: Visibility, ) { - let current_module = self.parent_scope.module; + let current_module = self.parent_scope.module.expect_local(); let import = self.r.arenas.alloc_import(ImportData { kind, parent_scope: self.parent_scope, @@ -560,7 +560,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { if target.name != kw::Underscore { self.r.per_ns(|this, ns| { let key = BindingKey::new(IdentKey::new(target), ns); - this.resolution_or_default(current_module, key, target.span) + this.resolution_or_default(current_module.to_module(), key, target.span) .borrow_mut(this) .single_imports .insert(import); @@ -1136,7 +1136,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { if let Some(Attribute::Parsed(AttributeKind::MacroUse { span, arguments })) = AttributeParser::parse_limited(self.r.tcx.sess, &item.attrs, &[sym::macro_use]) { - if self.parent_scope.module.parent.is_some() { + if self.parent_scope.module.expect_local().parent.is_some() { self.r .dcx() .emit_err(errors::ExternCrateLoadingMacroNotAtCrateRoot { span: item.span }); @@ -1247,7 +1247,8 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { /// directly into its parent scope's module. pub(crate) fn visit_invoc_in_module(&mut self, id: NodeId) -> MacroRulesScopeRef<'ra> { let invoc_id = self.visit_invoc(id); - self.parent_scope.module.unexpanded_invocations.borrow_mut(self.r).insert(invoc_id); + let module = self.parent_scope.module.expect_local(); + module.unexpanded_invocations.borrow_mut(self.r).insert(invoc_id); self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id)) } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 5e52ebcc3f194..4ba6106bf09e6 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -240,7 +240,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return self.report_conflict(ident, ns, new_binding, old_binding); } - let container = match old_binding.parent_module.unwrap().kind { + let container = match old_binding.parent_module.unwrap().expect_local().kind { // Avoid using TyCtxt::def_kind_descr in the resolver, because it // indirectly *calls* the resolver, and would cause a query cycle. ModuleKind::Def(kind, def_id, _, _) => kind.descr(def_id), @@ -2597,7 +2597,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let module = module.to_module(); current_module.is_ancestor_of(module) && current_module != module }) - .flat_map(|(_, module)| module.kind.name()), + .flat_map(|(_, module)| module.name()), ) .chain( self.extern_module_map @@ -2607,7 +2607,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let module = module.to_module(); current_module.is_ancestor_of(module) && current_module != module }) - .flat_map(|(_, module)| module.kind.name()), + .flat_map(|(_, module)| module.name()), ) .filter(|c| !c.to_string().is_empty()) .collect::>(); @@ -2631,8 +2631,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) -> (String, String, Option) { let is_last = failed_segment_idx == path.len() - 1; let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; - let module_res = match module { - Some(ModuleOrUniformRoot::Module(module)) => module.res(), + let module_def_id = match module { + Some(ModuleOrUniformRoot::Module(module)) => module.opt_def_id(), _ => None, }; let scope = match &path[..failed_segment_idx] { @@ -2647,7 +2647,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; let message = format!("cannot find `{ident}` in {scope}"); - if module_res == self.graph_root.res() { + if module_def_id == Some(CRATE_DEF_ID.to_def_id()) { let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); candidates @@ -3145,7 +3145,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if !kinds.contains(MacroKinds::BANG) { return None; } - let module_name = crate_module.kind.name().unwrap_or(kw::Crate); + let module_name = crate_module.name().unwrap_or(kw::Crate); let import_snippet = match import.kind { ImportKind::Single { source, target, .. } if source != target => { format!("{source} as {target}") @@ -3290,20 +3290,23 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return cached; } visited.insert(parent_module, false); - let m = r.expect_module(parent_module); let mut res = false; - for importer in m.glob_importers.borrow().iter() { - if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() { - if next_parent_module == module - || comes_from_same_module_for_glob( - r, - next_parent_module, - module, - visited, - ) + let m = r.expect_module(parent_module); + if m.is_local() { + for importer in m.glob_importers.borrow().iter() { + if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() { - res = true; - break; + if next_parent_module == module + || comes_from_same_module_for_glob( + r, + next_parent_module, + module, + visited, + ) + { + res = true; + break; + } } } } diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index b33b77d43999d..49461651412b2 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -643,10 +643,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Err(ControlFlow::Break(..)) => return decl, } } - Scope::ModuleGlobs(module, _) - if let ModuleKind::Def(_, def_id, _, _) = module.kind - && !def_id.is_local() => - { + Scope::ModuleGlobs(module, _) if !module.is_local() => { // Fast path: external module decoding only creates non-glob declarations. Err(Determined) } @@ -699,9 +696,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Scope::MacroUsePrelude => match self.macro_use_prelude.get(&ident.name).cloned() { Some(decl) => Ok(decl), - None => Err(Determinacy::determined( - self.graph_root.unexpanded_invocations.borrow().is_empty(), - )), + None => Err(Determinacy::determined(!self.graph_root.has_unexpanded_invocations())), }, Scope::BuiltinAttrs => match self.builtin_attr_decls.get(&ident.name) { Some(decl) => Ok(*decl), @@ -714,9 +709,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { finalize.is_some(), ) { Some(decl) => Ok(decl), - None => Err(Determinacy::determined( - self.graph_root.unexpanded_invocations.borrow().is_empty(), - )), + None => { + Err(Determinacy::determined(!self.graph_root.has_unexpanded_invocations())) + } } } Scope::ExternPreludeFlags => { @@ -1126,6 +1121,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return if accessible { Ok(binding) } else { Err(ControlFlow::Break(Determined)) }; } + // In extern modules everything is determined from the start. + if !module.is_local() { + return Err(ControlFlow::Continue(Determined)); + }; + // Check if one of single imports can still define the name, block if it can. if self.reborrow().single_import_can_define_name( &resolution, @@ -1139,7 +1139,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // Check if one of unexpanded macros can still define the name. - if !module.unexpanded_invocations.borrow().is_empty() { + if module.has_unexpanded_invocations() { return Err(ControlFlow::Continue(Undetermined)); } @@ -1223,7 +1223,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // scopes we return `Undetermined` with `ControlFlow::Continue`. // Check if one of unexpanded macros can still define the name, // if it can then our "no resolution" result is not determined and can be invalidated. - if !module.unexpanded_invocations.borrow().is_empty() { + if module.has_unexpanded_invocations() { return Err(ControlFlow::Continue(Undetermined)); } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index b16347af7ba0c..3b948f2bed194 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1790,7 +1790,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // Add to module's glob_importers - module.glob_importers.borrow_mut_unchecked().push(import); + if module.is_local() { + module.glob_importers.borrow_mut_unchecked().push(import); + } // Ensure that `resolutions` isn't borrowed during `try_define`, // since it might get updated via a glob cycle. diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 3337a4626b040..79c95fe713ed6 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1477,7 +1477,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // During late resolution we only track the module component of the parent scope, // although it may be useful to track other components as well for diagnostics. let graph_root = resolver.graph_root; - let parent_scope = ParentScope::module(graph_root.to_module(), resolver.arenas); + let parent_scope = ParentScope::module(graph_root, resolver.arenas); let start_rib_kind = RibKind::Module(graph_root); LateResolutionVisitor { r: resolver, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 6f86759d46c5f..d7ce4f731e21b 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -38,8 +38,8 @@ use crate::late::{ }; use crate::ty::fast_reject::SimplifiedType; use crate::{ - Finalize, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, PathSource, Res, - Resolver, ScopeSet, Segment, errors, path_names_to_string, + Finalize, Module, ModuleOrUniformRoot, ParentScope, PathResult, PathSource, Res, Resolver, + ScopeSet, Segment, errors, path_names_to_string, }; /// A field or associated item from self type suggested in case of resolution failure. @@ -1698,7 +1698,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { && let TyKind::Path(_, self_ty_path) = &self_ty.kind && let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None, source) - && let ModuleKind::Def(DefKind::Trait, ..) = module.kind + && module.def_kind() == Some(DefKind::Trait) && trait_ref.path.span == span && let PathSource::Trait(_) = source && let Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)) = res @@ -3014,12 +3014,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn find_module(&self, def_id: DefId) -> Option<(Module<'ra>, ImportSuggestion)> { let mut result = None; let mut seen_modules = FxHashSet::default(); - let root_did = self.r.graph_root.def_id(); - let mut worklist = vec![( - self.r.graph_root.to_module(), - ThinVec::new(), - root_did.is_local() || !self.r.tcx.is_doc_hidden(root_did), - )]; + let mut worklist = vec![(self.r.graph_root.to_module(), ThinVec::new(), true)]; while let Some((in_module, path_segments, doc_visible)) = worklist.pop() { // abort if the module is already found diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 879a4dd3983a6..44a72fec56b95 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -61,7 +61,6 @@ use rustc_hir::definitions::{PerParentDisambiguatorState, PerParentDisambiguator use rustc_hir::{PrimTy, TraitCandidate, find_attr}; use rustc_index::bit_set::DenseBitSet; use rustc_metadata::creader::CStore; -use rustc_middle::bug; use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport}; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::query::Providers; @@ -69,6 +68,7 @@ use rustc_middle::ty::{ self, DelegationInfo, MainDefinition, RegisteredTools, ResolverAstLowering, ResolverGlobalCtxt, TyCtxt, TyCtxtFeed, Visibility, }; +use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; use rustc_session::lint::builtin::PRIVATE_MACRO_USE; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; @@ -172,9 +172,9 @@ struct ParentScope<'ra> { impl<'ra> ParentScope<'ra> { /// Creates a parent scope with the passed argument used as the module scope component, /// and other scope components set to default empty values. - fn module(module: Module<'ra>, arenas: &'ra ResolverArenas<'ra>) -> ParentScope<'ra> { + fn module(module: LocalModule<'ra>, arenas: &'ra ResolverArenas<'ra>) -> ParentScope<'ra> { ParentScope { - module, + module: module.to_module(), expansion: LocalExpnId::ROOT, macro_rules: arenas.alloc_macro_rules_scope(MacroRulesScope::Empty), derives: &[], @@ -547,20 +547,23 @@ enum ModuleKind { } impl ModuleKind { - /// Get name of the module. - fn name(&self) -> Option { - match *self { - ModuleKind::Block => None, - ModuleKind::Def(.., name) => name, - } - } - fn opt_def_id(&self) -> Option { match self { ModuleKind::Def(_, def_id, _, _) => Some(*def_id), _ => None, } } + + fn def_id(&self) -> DefId { + self.opt_def_id().expect("`Module::def_id` is called on a block module") + } + + fn is_local(&self) -> bool { + match self { + ModuleKind::Def(_, def_id, ..) => def_id.is_local(), + ModuleKind::Block => true, + } + } } /// Combination of a symbol and its macros 2.0 normalized hygiene context. @@ -723,11 +726,16 @@ impl<'ra> ModuleData<'ra> { expansion: ExpnId, span: Span, no_implicit_prelude: bool, - self_decl: Option>, + vis: Visibility, + arenas: &'ra ResolverArenas<'ra>, ) -> Self { - let is_foreign = match kind { - ModuleKind::Def(_, def_id, _, _) => !def_id.is_local(), - ModuleKind::Block => false, + let is_foreign = !kind.is_local(); + let self_decl = match kind { + ModuleKind::Def(def_kind, def_id, ..) => { + let expn_id = expansion.as_local().unwrap_or(LocalExpnId::ROOT); + Some(arenas.new_def_decl(Res::Def(def_kind, def_id), vis, span, expn_id, parent)) + } + ModuleKind::Block => None, }; ModuleData { parent, @@ -746,12 +754,28 @@ impl<'ra> ModuleData<'ra> { } } + /// Get name of the module. + fn name(&self) -> Option { + match self.kind { + ModuleKind::Block => None, + ModuleKind::Def(.., name) => name, + } + } + fn opt_def_id(&self) -> Option { self.kind.opt_def_id() } fn def_id(&self) -> DefId { - self.opt_def_id().expect("`ModuleData::def_id` is called on a block module") + self.kind.def_id() + } + + fn is_local(&self) -> bool { + self.kind.is_local() + } + + fn has_unexpanded_invocations(&self) -> bool { + !self.unexpanded_invocations.borrow().is_empty() } fn res(&self) -> Option { @@ -760,6 +784,13 @@ impl<'ra> ModuleData<'ra> { _ => None, } } + + fn def_kind(&self) -> Option { + match self.kind { + ModuleKind::Def(def_kind, ..) => Some(def_kind), + ModuleKind::Block => None, + } + } } impl<'ra> Module<'ra> { @@ -813,16 +844,16 @@ impl<'ra> Module<'ra> { // `self` resolves to the first module ancestor that `is_normal`. fn is_normal(self) -> bool { - matches!(self.kind, ModuleKind::Def(DefKind::Mod, _, _, _)) + self.def_kind() == Some(DefKind::Mod) } fn is_trait(self) -> bool { - matches!(self.kind, ModuleKind::Def(DefKind::Trait, _, _, _)) + matches!(self.def_kind(), Some(DefKind::Trait)) } fn nearest_item_scope(self) -> Module<'ra> { - match self.kind { - ModuleKind::Def(DefKind::Enum | DefKind::Trait, ..) => { + match self.def_kind() { + Some(DefKind::Enum | DefKind::Trait) => { self.parent.expect("enum or trait module without a parent") } _ => self, @@ -862,7 +893,7 @@ impl<'ra> Module<'ra> { fn expect_local(self) -> LocalModule<'ra> { match self.kind { ModuleKind::Def(_, def_id, _, _) if !def_id.is_local() => { - panic!("`Module::expect_local` is called on a non-local module: {self:?}") + span_bug!(self.span, "unexpected extern module: {self:?}") } ModuleKind::Def(..) | ModuleKind::Block => LocalModule(self.0), } @@ -873,19 +904,49 @@ impl<'ra> Module<'ra> { match self.kind { ModuleKind::Def(_, def_id, _, _) if !def_id.is_local() => ExternModule(self.0), ModuleKind::Def(..) | ModuleKind::Block => { - panic!("`Module::expect_extern` is called on a local module: {self:?}") + span_bug!(self.span, "unexpected local module: {self:?}") } } } } impl<'ra> LocalModule<'ra> { + fn new( + parent: Option>, + kind: ModuleKind, + vis: Visibility, + expn_id: ExpnId, + span: Span, + no_implicit_prelude: bool, + arenas: &'ra ResolverArenas<'ra>, + ) -> LocalModule<'ra> { + assert!(kind.is_local()); + let parent = parent.map(|m| m.to_module()); + let data = ModuleData::new(parent, kind, expn_id, span, no_implicit_prelude, vis, arenas); + LocalModule(Interned::new_unchecked(arenas.modules.alloc(data))) + } + fn to_module(self) -> Module<'ra> { Module(self.0) } } impl<'ra> ExternModule<'ra> { + fn new( + parent: Option>, + kind: ModuleKind, + vis: Visibility, + expn_id: ExpnId, + span: Span, + no_implicit_prelude: bool, + arenas: &'ra ResolverArenas<'ra>, + ) -> ExternModule<'ra> { + assert!(!kind.is_local()); + let parent = parent.map(|m| m.to_module()); + let data = ModuleData::new(parent, kind, expn_id, span, no_implicit_prelude, vis, arenas); + ExternModule(Interned::new_unchecked(arenas.modules.alloc(data))) + } + fn to_module(self) -> Module<'ra> { Module(self.0) } @@ -917,9 +978,9 @@ impl<'ra> std::ops::Deref for ExternModule<'ra> { impl<'ra> fmt::Debug for Module<'ra> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.kind { - ModuleKind::Block => write!(f, "block"), - ModuleKind::Def(..) => write!(f, "{:?}", self.res()), + match self.res() { + None => write!(f, "block"), + Some(res) => write!(f, "{:?}", res), } } } @@ -1208,8 +1269,7 @@ impl<'ra> DeclData<'ra> { fn determined(&self) -> bool { match &self.kind { DeclKind::Import { source_decl, import, .. } if import.is_glob() => { - import.parent_scope.module.unexpanded_invocations.borrow().is_empty() - && source_decl.determined() + !import.parent_scope.module.has_unexpanded_invocations() && source_decl.determined() } _ => true, } @@ -1538,31 +1598,6 @@ impl<'ra> ResolverArenas<'ra> { self.new_def_decl(res, Visibility::Public, span, expn_id, None) } - fn new_module( - &'ra self, - parent: Option>, - kind: ModuleKind, - vis: Visibility, - expn_id: ExpnId, - span: Span, - no_implicit_prelude: bool, - ) -> Module<'ra> { - let self_decl = match kind { - ModuleKind::Def(def_kind, def_id, _, _) => { - let expn_id = expn_id.as_local().unwrap_or(LocalExpnId::ROOT); - Some(self.new_def_decl(Res::Def(def_kind, def_id), vis, span, expn_id, parent)) - } - ModuleKind::Block => None, - }; - Module(Interned::new_unchecked(self.modules.alloc(ModuleData::new( - parent, - kind, - expn_id, - span, - no_implicit_prelude, - self_decl, - )))) - } fn alloc_decl(&'ra self, data: DeclData<'ra>) -> Decl<'ra> { Interned::new_unchecked(self.dropless.alloc(data)) } @@ -1724,26 +1759,26 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { arenas: &'ra ResolverArenas<'ra>, ) -> Resolver<'ra, 'tcx> { let root_def_id = CRATE_DEF_ID.to_def_id(); - let graph_root = arenas.new_module( + let graph_root = LocalModule::new( None, ModuleKind::Def(DefKind::Mod, root_def_id, CRATE_NODE_ID, None), Visibility::Public, ExpnId::root(), crate_span, attr::contains_name(attrs, sym::no_implicit_prelude), + arenas, ); - let graph_root = graph_root.expect_local(); let local_modules = vec![graph_root]; let local_module_map = FxIndexMap::from_iter([(CRATE_DEF_ID, graph_root)]); - let empty_module = arenas.new_module( + let empty_module = LocalModule::new( None, ModuleKind::Def(DefKind::Mod, root_def_id, CRATE_NODE_ID, None), Visibility::Public, ExpnId::root(), DUMMY_SP, true, + arenas, ); - let empty_module = empty_module.expect_local(); let mut node_id_to_def_id = NodeMap::default(); let crate_feed = tcx.create_local_crate_def_id(crate_span); @@ -1825,7 +1860,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .. }; - let root_parent_scope = ParentScope::module(graph_root.to_module(), resolver.arenas); + let root_parent_scope = ParentScope::module(graph_root, resolver.arenas); resolver.invocation_parent_scopes.insert(LocalExpnId::ROOT, root_parent_scope); resolver.feed_visibility(crate_feed, Visibility::Public); @@ -1840,13 +1875,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { span: Span, no_implicit_prelude: bool, ) -> LocalModule<'ra> { - let parent = parent.map(|m| m.to_module()); let vis = kind.opt_def_id().map_or(Visibility::Public, |def_id| self.tcx.visibility(def_id)); - let module = self - .arenas - .new_module(parent, kind, vis, expn_id, span, no_implicit_prelude) - .expect_local(); + let module = + LocalModule::new(parent, kind, vis, expn_id, span, no_implicit_prelude, self.arenas); self.local_modules.push(module); if let Some(def_id) = module.opt_def_id() { self.local_module_map.insert(def_id.expect_local(), module); @@ -1862,14 +1894,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { span: Span, no_implicit_prelude: bool, ) -> ExternModule<'ra> { - let parent = parent.map(|m| m.to_module()); - let vis = - kind.opt_def_id().map_or(Visibility::Public, |def_id| self.tcx.visibility(def_id)); - let module = self - .arenas - .new_module(parent, kind, vis, expn_id, span, no_implicit_prelude) - .expect_extern(); - self.extern_module_map.borrow_mut().insert(module.def_id(), module); + let def_id = kind.def_id(); + let module = ExternModule::new( + parent, + kind, + self.tcx.visibility(def_id), + expn_id, + span, + no_implicit_prelude, + self.arenas, + ); + self.extern_module_map.borrow_mut().insert(def_id, module); module } @@ -2220,7 +2255,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Do not report the lint if the macro name resolves in stdlib prelude // even without the problematic `macro_use` import. let found_in_stdlib_prelude = self.prelude.is_some_and(|prelude| { - let empty_module = self.empty_module.to_module(); + let empty_module = self.empty_module; let arenas = self.arenas; self.cm() .maybe_resolve_ident_in_module( @@ -2341,7 +2376,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { "resolve_crate_root({:?}): got module {:?} ({:?}) (ident.span = {:?})", ident, module, - module.kind.name(), + module.name(), ident.span ); module @@ -2586,12 +2621,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return; } - let module = self.graph_root.to_module(); + let module = self.graph_root; let ident = Ident::with_dummy_span(sym::main); let parent_scope = &ParentScope::module(module, self.arenas); let Ok(name_binding) = self.cm().maybe_resolve_ident_in_module( - ModuleOrUniformRoot::Module(module), + ModuleOrUniformRoot::Module(module.to_module()), ident, ValueNS, parent_scope, @@ -2685,22 +2720,9 @@ fn path_names_to_string(path: &Path) -> String { /// A somewhat inefficient routine to obtain the name of a module. fn module_to_string(mut module: Module<'_>) -> Option { let mut names = Vec::new(); - loop { - if let ModuleKind::Def(.., name) = module.kind { - if let Some(parent) = module.parent { - // `unwrap` is safe: the presence of a parent means it's not the crate root. - names.push(name.unwrap()); - module = parent - } else { - break; - } - } else { - names.push(sym::opaque_module_name_placeholder); - let Some(parent) = module.parent else { - return None; - }; - module = parent; - } + while let Some(parent) = module.parent { + names.push(module.name().unwrap_or(sym::opaque_module_name_placeholder)); + module = parent; } if names.is_empty() { return None; diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index f4464138ef1fc..206e32f61b485 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -175,10 +175,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { fn resolve_dollar_crates(&self) { hygiene::update_dollar_crate_names(|ctxt| { let ident = Ident::new(kw::DollarCrate, DUMMY_SP.with_ctxt(ctxt)); - match self.resolve_crate_root(ident).kind { - ModuleKind::Def(.., name) if let Some(name) = name => name, - _ => kw::Crate, - } + self.resolve_crate_root(ident).name().unwrap_or(kw::Crate) }); } @@ -193,7 +190,8 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { let output_macro_rules_scope = collect_definitions(self, fragment, parent_scope); self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope); - parent_scope.module.unexpanded_invocations.borrow_mut(self).remove(&expansion); + let module = parent_scope.module.expect_local(); + module.unexpanded_invocations.borrow_mut(self).remove(&expansion); if let Some(unexpanded_invocations) = self.impl_unexpanded_invocations.get_mut(&self.invocation_parent(expansion)) { @@ -523,7 +521,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { star_span: Span, ) -> Result)>, Indeterminate> { let target_trait = self.expect_module(trait_def_id); - if !target_trait.unexpanded_invocations.borrow().is_empty() { + if target_trait.has_unexpanded_invocations() { return Err(Indeterminate); } // FIXME: Instead of waiting try generating all trait methods, and pruning From 8643183f9359fe7b428f37288956a66af4611ad1 Mon Sep 17 00:00:00 2001 From: jyn Date: Sun, 10 May 2026 20:18:11 +0200 Subject: [PATCH 287/289] bootstrap: Don't panic on `x install --set build.extended=true` This was a regression from 155732. The Cargo submodule wasn't checked out, so reading from Cargo.toml didn't work. Return a fake version during dry-runs to avoid unnecessarily checking out submodules. --- src/bootstrap/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 6518224576c9d..fd3e88e1a36f4 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1679,6 +1679,9 @@ impl Build { /// Returns the `a.b.c` version that the given package is at. fn release_num(&self, package: &str) -> String { + if self.config.dry_run() { + return "0.0.0 (dry-run)".into(); + } let toml_file_name = self.src.join(format!("src/tools/{package}/Cargo.toml")); let toml = t!(fs::read_to_string(toml_file_name)); for line in toml.lines() { From 5a52e12664a5e042ca935794f661b5dc7b10b95f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 11 May 2026 03:31:13 +0200 Subject: [PATCH 288/289] Fix unwanted "Available on XX-bit only" --- library/core/src/num/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 890f5ab2286c6..fdd4831336179 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -460,6 +460,7 @@ impl i128 { midpoint_impl! { i128, signed } } +#[doc(auto_cfg = false)] #[cfg(target_pointer_width = "16")] impl isize { int_impl! { @@ -485,6 +486,7 @@ impl isize { midpoint_impl! { isize, i32, signed } } +#[doc(auto_cfg = false)] #[cfg(target_pointer_width = "32")] impl isize { int_impl! { @@ -510,6 +512,7 @@ impl isize { midpoint_impl! { isize, i64, signed } } +#[doc(auto_cfg = false)] #[cfg(target_pointer_width = "64")] impl isize { int_impl! { @@ -1334,6 +1337,7 @@ impl u128 { carrying_carryless_mul_impl! { u128, u256 } } +#[doc(auto_cfg = false)] #[cfg(target_pointer_width = "16")] impl usize { uint_impl! { @@ -1365,6 +1369,7 @@ impl usize { carrying_carryless_mul_impl! { usize, u32 } } +#[doc(auto_cfg = false)] #[cfg(target_pointer_width = "32")] impl usize { uint_impl! { @@ -1396,6 +1401,7 @@ impl usize { carrying_carryless_mul_impl! { usize, u64 } } +#[doc(auto_cfg = false)] #[cfg(target_pointer_width = "64")] impl usize { uint_impl! { From 519f35a7170a76787a6b76241f2c025a527a6ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 11 May 2026 09:55:44 +0300 Subject: [PATCH 289/289] Prepare for merging from rust-lang/rust This updates the rust-version file to 8afb6a8b1b32fce2f8aa7520517833338dc36c5e. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index fb590a192fca0..1e8579c04511c 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -1d72d7e8136faaebad3a85eeed432e6ea1b2ffab +8afb6a8b1b32fce2f8aa7520517833338dc36c5e