Merge pull request #22238 from MavenRain/fixme-array-pat-incorrect-length ide-diagnostics: emit error for mismatched array pattern length
diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs index 5dc410b..7757d53 100644 --- a/crates/hir-def/src/attrs.rs +++ b/crates/hir-def/src/attrs.rs
@@ -137,6 +137,7 @@ "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 @@ "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 @@ const MACRO_STYLE_PARENTHESES = 1 << 48; const PREFER_UNDERSCORE_IMPORT = 1 << 49; + + const IS_MUST_USE = 1 << 50; } }
diff --git a/crates/hir-def/src/nameres/attr_resolution.rs b/crates/hir-def/src/nameres/attr_resolution.rs index 062b55f..5aabd7d 100644 --- a/crates/hir-def/src/nameres/attr_resolution.rs +++ b/crates/hir-def/src/nameres/attr_resolution.rs
@@ -9,7 +9,6 @@ }; use span::SyntaxContext; use syntax::ast; -use triomphe::Arc; use crate::{ AstIdWithPath, MacroId, ModuleId, UnresolvedMacro, @@ -126,7 +125,7 @@ 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/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs index 60ec303..08f672a 100644 --- a/crates/hir-def/src/nameres/tests/incremental.rs +++ b/crates/hir-def/src/nameres/tests/incremental.rs
@@ -227,7 +227,7 @@ "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 @@ "file_item_tree_query", "ast_id_map", "parse_macro_expansion", - "macro_arg_shim", + "macro_arg", ] "#]], expect![[r#" @@ -249,7 +249,7 @@ "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 @@ "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 @@ "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 @@ "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 @@ "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 @@ "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 @@ "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 @@ "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 @@ "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/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index 57c7874..4b26c1f 100644 --- a/crates/hir-expand/src/db.rs +++ b/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 @@ tt, }; /// This is just to ensure the types of smart_macro_arg and macro_arg are the same -type MacroArgResult = (Arc<tt::TopSubtree>, 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 @@ /// 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<DeclarativeMacroExpander>), + DeclarativeMacro(&'db DeclarativeMacroExpander), /// Stuff like `line!` and `file!`. BuiltIn(BuiltinFnLikeExpander), /// Built-in eagerly expanded fn-like macros (`include!`, `concat!`, etc.) @@ -99,34 +100,37 @@ /// 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<ast::Macro>, - ) -> Arc<DeclarativeMacroExpander>; + ) -> &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<Arc<tt::TopSubtree>>; + #[salsa::transparent] + fn expand_proc_macro(&self, call: MacroCallId) -> &ExpandResult<tt::TopSubtree>; /// 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 @@ #[salsa::invoke_interned(proc_macro_span)] fn proc_macro_span(&self, fun: AstId<ast::Fn>) -> 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<Arc<ExpandResult<Arc<[SyntaxError]>>>>; + ) -> Option<ExpandResult<Arc<[SyntaxError]>>>; #[salsa::transparent] fn syntax_context(&self, file: HirFileId, edition: Edition) -> SyntaxContext; @@ -179,7 +183,7 @@ 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 @@ 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_error( db: &dyn ExpandDatabase, macro_call_id: MacroCallId, -) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>> { +) -> Option<ExpandResult<Arc<[SyntaxError]>>> { let e: ExpandResult<Arc<[SyntaxError]>> = 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 @@ /// /// 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 @@ } } +#[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 @@ 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 @@ // 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 @@ 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 @@ } } -enum CowArc<T> { - Arc(Arc<T>), - Owned(T), -} - fn macro_expand( db: &dyn ExpandDatabase, macro_call_id: MacroCallId, loc: MacroCallLoc, -) -> ExpandResult<(CowArc<tt::TopSubtree>, 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 @@ // 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 @@ } 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 @@ // 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<ast::Fn>) -> Span { @@ -643,10 +640,8 @@ span_map.span_for_range(range) } -fn expand_proc_macro( - db: &dyn ExpandDatabase, - id: MacroCallId, -) -> ExpandResult<Arc<tt::TopSubtree>> { +#[salsa_macros::tracked(returns(ref))] +fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<tt::TopSubtree> { 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 @@ 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 @@ // 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/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs index aa745a2..ead10a5 100644 --- a/crates/hir-expand/src/declarative.rs +++ b/crates/hir-expand/src/declarative.rs
@@ -10,7 +10,6 @@ ast::{self, HasAttrs}, }; use syntax_bridge::DocCommentDesugarMode; -use triomphe::Arc; use crate::{ AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, Lookup, MacroCallId, @@ -78,12 +77,16 @@ .map_err(Into::into), } } +} +#[salsa::tracked] +impl DeclarativeMacroExpander { + #[salsa::tracked(returns(ref))] pub(crate) fn expander( db: &dyn ExpandDatabase, def_crate: Crate, id: AstId<ast::Macro>, - ) -> Arc<DeclarativeMacroExpander> { + ) -> DeclarativeMacroExpander { let (root, map) = crate::db::parse_with_map(db, id.file_id); let root = root.syntax_node(); @@ -177,6 +180,6 @@ 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/crates/hir-expand/src/eager.rs b/crates/hir-expand/src/eager.rs index 6e95ff0..dddef17 100644 --- a/crates/hir-expand/src/eager.rs +++ b/crates/hir-expand/src/eager.rs
@@ -22,7 +22,6 @@ 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 @@ let mut subtree = syntax_bridge::syntax_node_to_token_tree( &expanded_eager_input, arg_map, - span, + *span, DocCommentDesugarMode::Mbe, ); @@ -104,11 +103,11 @@ 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/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index b51ec39..939104b 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs
@@ -14,7 +14,7 @@ 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 @@ /// This is the information needed to reverse the fixups. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct SyntaxFixupUndoInfo { - // FIXME: ThinArc<[Subtree]> - original: Option<Arc<Box<[TopSubtree]>>>, + original: Option<ThinVec<TopSubtree>>, } impl SyntaxFixupUndoInfo { @@ -59,7 +58,7 @@ let mut append = FxHashMap::<SyntaxElement, _>::default(); let mut remove = FxHashSet::<SyntaxElement>::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 @@ } } } + 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 @@ } 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/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index fc2a074..403e544 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs
@@ -279,7 +279,7 @@ #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct EagerCallInfo { /// The expanded argument of the eager macro. - arg: Arc<tt::TopSubtree>, + 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<ExpandError>, @@ -296,7 +296,7 @@ /// for the eager input macro file. // FIXME: This is being interned, subtrees can vary quickly differing just slightly causing // leakage problems here - eager: Option<Arc<EagerCallInfo>>, + eager: Option<Box<EagerCallInfo>>, }, Derive { ast_id: AstId<ast::Adt>, @@ -311,7 +311,7 @@ Attr { ast_id: AstId<ast::Item>, // FIXME: This shouldn't be here, we can derive this from `invoc_attr_index`. - attr_args: Option<Arc<tt::TopSubtree>>, + attr_args: Option<Box<tt::TopSubtree>>, /// 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/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 80e7e05..87633ad 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs
@@ -16,7 +16,6 @@ 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 @@ 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/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 2e130c1..5dba53a 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs
@@ -37,33 +37,44 @@ // 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<Arc<MirBody>, 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<Arc<MirBody>, 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<Arc<MirBody>, 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<Arc<MirBody>, 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<Arc<[BorrowckResult]>, 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 @@ env: StoredParamEnvAndCrate, ) -> Result<Arc<Layout>, LayoutError>; - #[salsa::invoke(crate::layout::target_data_layout_query)] - fn target_data_layout(&self, krate: Crate) -> Result<Arc<TargetDataLayout>, 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<DynCompatibilityViolation>;
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index 93772ca..be29926 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -7,7 +7,9 @@ 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 @@ }, display::{DisplayTarget, HirDisplay}, next_solver::{ - DbInterner, ParamEnv, Ty, TyKind, TypingMode, + CallableIdWrapper, DbInterner, ParamEnv, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, InferCtxt}, }, }; @@ -67,6 +69,9 @@ RemoveUnnecessaryElse { if_expr: ExprId, }, + UnusedMustUse { + expr: ExprId, + }, } impl BodyValidationDiagnostic { @@ -328,54 +333,73 @@ 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; - }; - 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; - } - - let mut have_errors = false; - let deconstructed_pat = self.lower_pattern(&cx, pat, &mut have_errors); - - // optimization, wildcard trivially hold - if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) { - continue; - } - - 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 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, }; - 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()), - ), - }); + if let Some(diag) = diag { + self.diagnostics.push(diag); } } } + fn check_non_exhaustive_let<'a>( + &self, + cx: &MatchCheckCtx<'a, 'db>, + pattern_arena: &'a Arena<DeconstructedPat<'a, 'db>>, + pat: PatId, + initializer: Option<ExprId>, + ) -> Option<BodyValidationDiagnostic> { + 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; + } + + let mut have_errors = false; + let deconstructed_pat = self.lower_pattern(cx, pat, &mut have_errors); + + // 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>( &self, cx: &MatchCheckCtx<'a, 'db>, @@ -391,6 +415,37 @@ pattern } + fn check_unused_must_use(&self, expr: ExprId) -> Option<BodyValidationDiagnostic> { + 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/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 2df190b..c300388 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs
@@ -974,7 +974,7 @@ return f.write_str("<target-layout-not-available>"); }; 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("<failed-to-detect-variant>"); };
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index d2759dd..da0be81 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs
@@ -167,7 +167,7 @@ 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 @@ repr.packed(), &args, trait_env.as_ref(), - &target, + target, ); } }
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 6090ddf..e77fb13 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs
@@ -29,7 +29,7 @@ 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/crates/hir-ty/src/layout/target.rs b/crates/hir-ty/src/layout/target.rs index 1752b56..26fa73e 100644 --- a/crates/hir-ty/src/layout/target.rs +++ b/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<Arc<TargetDataLayout>, TargetLoadError> { +) -> Result<TargetDataLayout, TargetLoadError> { 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/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index a8e06f3..67cc67f 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs
@@ -47,9 +47,6 @@ 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<BasicBlock>;
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index 17715d3..dcd06ae 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/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 @@ #[derive(Debug, Clone, PartialEq, Eq)] pub struct BorrowckResult { - pub mir_body: Arc<MirBody>, + owner: Either<DefWithBodyId, InternedClosureId>, pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>, pub moved_out_of_ref: Vec<MovedOutOfRef>, pub partially_moved: Vec<PartiallyMoved>, pub borrow_regions: Vec<BorrowRegion>, } -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<MirBody>) -> BorrowckResult, - mut merge_from_closures: impl FnMut(&mut BorrowckResult, &BorrowckResult), -) -> Result<Arc<[BorrowckResult]>, MirLowerError> { - fn for_closure( - db: &dyn HirDatabase, + mut cb: impl FnMut(&'db MirBody, Either<DefWithBodyId, InternedClosureId>) -> BorrowckResult, + mut merge_from_closures: impl FnMut( + (&mut BorrowckResult, &'db MirBody), + (&BorrowckResult, &'db MirBody), + ), +) -> Result<Box<[BorrowckResult]>, MirLowerError> { + fn for_closure<'db>( + db: &'db dyn HirDatabase, c: InternedClosureId, - results: &mut Vec<BorrowckResult>, - cb: &mut impl FnMut(Arc<MirBody>) -> BorrowckResult, - merge_from_closures: &mut impl FnMut(&mut BorrowckResult, &BorrowckResult), + results: &mut Vec<(BorrowckResult, &'db MirBody)>, + cb: &mut impl FnMut(&'db MirBody, Either<DefWithBodyId, InternedClosureId>) -> 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<Arc<[BorrowckResult]>, MirLowerError> { +) -> Result<Box<[BorrowckResult]>, 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 @@ 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/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 87d64e5..104b90e 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs
@@ -156,26 +156,26 @@ } } -struct StackFrame { - locals: Locals, +struct StackFrame<'a> { + locals: Locals<'a>, destination: Option<BasicBlockId>, prev_stack_ptr: usize, span: (MirSpan, DefWithBodyId), } #[derive(Clone)] -enum MirOrDynIndex { - Mir(Arc<MirBody>), +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<TargetDataLayout>, + target_data_layout: &'db TargetDataLayout, stack: Vec<u8>, heap: Vec<u8>, - code_stack: Vec<StackFrame>, + code_stack: Vec<StackFrame<'a>>, /// 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<StaticId, Address>, @@ -190,11 +190,11 @@ layout_cache: RefCell<FxHashMap<Ty<'db>, Arc<Layout>>>, projected_ty_cache: RefCell<FxHashMap<(Ty<'db>, PlaceElem), Ty<'db>>>, not_special_fn_cache: RefCell<FxHashSet<FunctionId>>, - mir_or_dyn_index_cache: RefCell<FxHashMap<(FunctionId, GenericArgs<'db>), MirOrDynIndex>>, + mir_or_dyn_index_cache: RefCell<FxHashMap<(FunctionId, GenericArgs<'db>), 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<FxHashMap<DefWithBodyId, Vec<Locals>>>, + unused_locals_store: RefCell<FxHashMap<DefWithBodyId, Vec<Locals<'a>>>>, cached_ptr_size: usize, cached_fn_trait_func: Option<FunctionId>, cached_fn_mut_trait_func: Option<FunctionId>, @@ -237,17 +237,21 @@ 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<'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 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 @@ } #[derive(Debug)] -struct Locals { +struct Locals<'a> { ptr: ArenaMap<LocalId, Interval>, - body: Arc<MirBody>, + body: &'a MirBody, drop_flags: DropFlags, } @@ -596,9 +606,9 @@ } } -pub fn interpret_mir<'db>( +pub fn interpret_mir<'a, 'db: 'a>( db: &'db dyn HirDatabase, - body: Arc<MirBody>, + 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 @@ if evaluator.ptr_size() != size_of::<usize>() { 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 @@ #[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<ParamEnvAndCrate<'db>>, - ) -> 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 @@ 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 @@ 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<IntervalOrOwned>)> { 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 @@ 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 @@ 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 @@ fn interpret_mir( &mut self, - body: Arc<MirBody>, + body: &'a MirBody, args: impl Iterator<Item = IntervalOrOwned>, ) -> Result<'db, Interval> { if let Some(it) = self.stack_depth_limit.checked_sub(1) { @@ -917,8 +927,8 @@ 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 @@ fn fill_locals_for_body( &mut self, body: &MirBody, - locals: &mut Locals, + locals: &mut Locals<'a>, args: impl Iterator<Item = IntervalOrOwned>, ) -> Result<'db, ()> { let mut remain_args = body.param_locals.len(); @@ -1096,19 +1106,15 @@ fn create_locals_for_body( &mut self, - body: &Arc<MirBody>, + body: &'a MirBody, destination: Option<Interval>, - ) -> 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 @@ 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 @@ 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 @@ &mut self, it: VariantId, subst: GenericArgs<'db>, - locals: &Locals, + locals: &Locals<'a>, ) -> Result<'db, (usize, Arc<Layout>, Option<(usize, usize, i128)>)> { let adt = it.adt_id(self.db); if let DefWithBodyId::VariantId(f) = locals.body.owner @@ -1851,7 +1857,7 @@ 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 @@ 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 @@ #[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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ &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 @@ 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 @@ 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 @@ 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 @@ bytes: Interval, destination: Interval, args: &[IntervalAndTy<'db>], - locals: &Locals, + locals: &Locals<'a>, target_bb: Option<BasicBlockId>, span: MirSpan, - ) -> Result<'db, Option<StackFrame>> { + ) -> Result<'db, Option<StackFrame<'a>>> { 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 @@ generic_args: GenericArgs<'db>, destination: Interval, args: &[IntervalAndTy<'db>], - locals: &Locals, + locals: &Locals<'a>, span: MirSpan, - ) -> Result<'db, Option<StackFrame>> { + ) -> Result<'db, Option<StackFrame<'a>>> { let mir_body = self .db .monomorphized_mir_body_for_closure( @@ -2688,10 +2698,10 @@ generic_args: GenericArgs<'db>, destination: Interval, args: &[IntervalAndTy<'db>], - locals: &Locals, + locals: &Locals<'a>, target_bb: Option<BasicBlockId>, span: MirSpan, - ) -> Result<'db, Option<StackFrame>> { + ) -> Result<'db, Option<StackFrame<'a>>> { match def { CallableDefId::FunctionId(def) => { if self.detect_fn_trait(def).is_some() { @@ -2746,9 +2756,9 @@ &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 @@ mut def: FunctionId, args: &[IntervalAndTy<'db>], generic_args: GenericArgs<'db>, - locals: &Locals, + locals: &Locals<'a>, destination: Interval, target_bb: Option<BasicBlockId>, span: MirSpan, - ) -> Result<'db, Option<StackFrame>> { + ) -> Result<'db, Option<StackFrame<'a>>> { if self.detect_and_exec_special_function( def, args, @@ -2854,18 +2864,18 @@ fn exec_looked_up_function( &mut self, - mir_body: Arc<MirBody>, - locals: &Locals, + mir_body: &'a MirBody, + locals: &Locals<'a>, def: FunctionId, arg_bytes: impl Iterator<Item = IntervalOrOwned>, span: MirSpan, destination: Interval, target_bb: Option<BasicBlockId>, - ) -> Result<'db, Option<StackFrame>> { + ) -> Result<'db, Option<StackFrame<'a>>> { 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 @@ def: FunctionId, args: &[IntervalAndTy<'db>], generic_args: GenericArgs<'db>, - locals: &Locals, + locals: &Locals<'a>, destination: Interval, target_bb: Option<BasicBlockId>, span: MirSpan, - ) -> Result<'db, Option<StackFrame>> { + ) -> Result<'db, Option<StackFrame<'a>>> { let func = args .first() .ok_or_else(|| MirEvalError::InternalError("fn trait with no arg".into()))?; @@ -2954,7 +2964,7 @@ } } - 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 @@ } } - 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 @@ fn run_drop_glue_deep( &mut self, ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, addr: Address, _metadata: &[u8], span: MirSpan,
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 4154760..a0978bd 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -29,13 +29,13 @@ 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 @@ def: FunctionId, args: &[IntervalAndTy<'db>], self_ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, destination: Interval, span: MirSpan, ) -> Result<'db, ()> { @@ -190,7 +190,7 @@ layout: Arc<Layout>, addr: Address, def: FunctionId, - locals: &Locals, + locals: &Locals<'a>, destination: Interval, span: MirSpan, ) -> Result<'db, ()> { @@ -296,7 +296,7 @@ it: EvalLangItem, generic_args: GenericArgs<'db>, args: &[IntervalAndTy<'db>], - locals: &Locals, + locals: &Locals<'a>, span: MirSpan, ) -> Result<'db, Vec<u8>> { use EvalLangItem::*; @@ -368,7 +368,7 @@ id: i64, args: &[IntervalAndTy<'db>], destination: Interval, - _locals: &Locals, + _locals: &Locals<'a>, _span: MirSpan, ) -> Result<'db, ()> { match id { @@ -399,7 +399,7 @@ 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 @@ 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 @@ &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 @@ 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/crates/hir-ty/src/mir/eval/shim/simd.rs b/crates/hir-ty/src/mir/eval/shim/simd.rs index 6e20562..2458597 100644 --- a/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/crates/hir-ty/src/mir/eval/shim/simd.rs
@@ -6,7 +6,7 @@ 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 @@ args: &[IntervalAndTy<'db>], _generic_args: GenericArgs<'db>, destination: Interval, - _locals: &Locals, + _locals: &Locals<'a>, _span: MirSpan, ) -> Result<'db, ()> { match name {
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 9c9cf0a..b2a7eaa 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs
@@ -25,7 +25,6 @@ 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 @@ }) } +#[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<MirBody>> { +) -> 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 @@ 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<MirBody>> { +#[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 @@ 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<MirBody>> { +) -> 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/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs index dfce8ca..b352943 100644 --- a/crates/hir-ty/src/mir/monomorphization.rs +++ b/crates/hir-ty/src/mir/monomorphization.rs
@@ -12,7 +12,6 @@ use rustc_type_ir::{ FallibleTypeFolder, TypeFlags, TypeFoldable, TypeSuperFoldable, TypeVisitableExt, }; -use triomphe::Arc; use crate::{ ParamEnvAndCrate, @@ -240,38 +239,50 @@ } } +#[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<Arc<MirBody>, MirLowerError> { +) -> Result<MirBody, MirLowerError> { 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<Arc<MirBody>, MirLowerError> { +) -> Result<MirBody, MirLowerError> { 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<Arc<MirBody>, MirLowerError> { +) -> Result<MirBody, MirLowerError> { 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<MirBody, MirLowerError> { + Err(MirLowerError::Loop) }
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index de5ee22..c224f20 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs
@@ -160,7 +160,7 @@ 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/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 5d3e117..168854d 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs
@@ -87,6 +87,7 @@ PrivateField, RemoveTrailingReturn, RemoveUnnecessaryElse, + UnusedMustUse, ReplaceFilterMapNextWithFindMap, TraitImplIncorrectSafety, TraitImplMissingAssocItems, @@ -407,6 +408,11 @@ } #[derive(Debug)] +pub struct UnusedMustUse { + pub expr: InFile<ExprOrPatPtr>, +} + +#[derive(Debug)] pub struct CastToUnsized<'db> { pub expr: InFile<ExprOrPatPtr>, pub cast_ty: Type<'db>, @@ -637,6 +643,11 @@ ); } } + BodyValidationDiagnostic::UnusedMustUse { expr } => { + if let Ok(source_ptr) = source_map.expr_syntax(expr) { + return Some(UnusedMustUse { expr: source_ptr }.into()); + } + } } None }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 6f55086..b74f594 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs
@@ -1120,7 +1120,7 @@ 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 @@ Type::new(db, var_id, ty) } - pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> { + pub fn layout<'db>(&self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, LayoutError> { db.layout_of_ty( self.ty(db).ty.store(), param_env_from_has_crate( @@ -1696,7 +1696,7 @@ self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit)) } - pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> { + pub fn layout<'db>(self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, LayoutError> { Adt::from(self).layout(db) } @@ -1783,7 +1783,7 @@ db.const_eval_discriminant(self.into()) } - pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> { + pub fn layout<'db>(&self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, 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 @@ has_non_default_type_params(db, self.into()) } - pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> { + pub fn layout<'db>(self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, 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 @@ 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<SyntaxNodePtr> = match moof.span { mir::MirSpan::ExprId(e) => match source_map.expr_syntax(e) { @@ -6530,7 +6530,7 @@ .collect() } - pub fn layout(&self, db: &'db dyn HirDatabase) -> Result<Layout, LayoutError> { + pub fn layout(&self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, 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 @@ } #[derive(Clone, Debug, Eq, PartialEq)] -pub struct Layout(Arc<TyLayout>, Arc<TargetDataLayout>); +pub struct Layout<'db>(Arc<TyLayout>, &'db TargetDataLayout); -impl Layout { +impl<'db> Layout<'db> { pub fn size(&self) -> u64 { self.0.size.bytes() } @@ -6714,7 +6714,7 @@ } pub fn niches(&self) -> Option<u128> { - Some(self.0.largest_niche?.available(&*self.1)) + Some(self.0.largest_niche?.available(self.1)) } pub fn field_offset(&self, field: Field) -> Option<u64> { @@ -6803,7 +6803,7 @@ 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/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs index b11f7a1..99ad1e0 100644 --- a/crates/hir/src/term_search/tactics.rs +++ b/crates/hir/src/term_search/tactics.rs
@@ -56,11 +56,12 @@ 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/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index a8aa0d6..44123dc 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -34,7 +34,7 @@ use crate::{ AssistId, assist_context::{AssistContext, Assists}, - utils::generate_impl, + utils::generate_impl_with_item, }; // Assist: extract_function @@ -161,8 +161,8 @@ 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() }
diff --git a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index e99dd81..3902ee1 100644 --- a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs
@@ -91,6 +91,23 @@ 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::<Vec<_>>(); + (!items.is_empty()).then(|| make.assoc_item_list(items)) + }); + let impl_ = make.impl_trait( cfg_attrs(&traitd), is_unsafe, @@ -103,23 +120,9 @@ 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(
diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index d95456f..5299680 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs
@@ -560,7 +560,7 @@ 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/crates/ide-diagnostics/src/handlers/unused_must_use.rs b/crates/ide-diagnostics/src/handlers/unused_must_use.rs new file mode 100644 index 0000000..4b9ecf2 --- /dev/null +++ b/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/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index d029b8d..3f803a8 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs
@@ -83,6 +83,7 @@ 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. @@ -463,6 +464,7 @@ 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/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 4f60321..e08bbc5 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs
@@ -1136,12 +1136,12 @@ } } -fn render_memory_layout( +fn render_memory_layout<'db>( config: Option<MemoryLayoutHoverConfig>, - layout: impl FnOnce() -> Result<Layout, LayoutError>, - offset: impl FnOnce(&Layout) -> Option<u64>, - padding: impl FnOnce(&Layout) -> Option<(&str, u64)>, - tag: impl FnOnce(&Layout) -> Option<usize>, + layout: impl FnOnce() -> Result<Layout<'db>, LayoutError>, + offset: impl FnOnce(&Layout<'db>) -> Option<u64>, + padding: impl for<'a> FnOnce(&'a Layout<'db>) -> Option<(&'a str, u64)>, + tag: impl FnOnce(&Layout<'db>) -> Option<usize>, ) -> Option<String> { let config = config?; let layout = layout().ok()?;
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 2c6116f..6d1e141 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs
@@ -16,7 +16,7 @@ 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 @@ 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 @@ 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 @@ Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) } +fn rename_elided_lifetime( + position: FilePosition, + lifetime_token: syntax::SyntaxToken, + new_name: &str, +) -> RenameResult<SourceChange> { + 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 @@ "#, ); } + + #[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<T>(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<T> Foo<'_$0, T> {} +"#, + r#" +struct Foo<'a, T>(&'a str, T); +impl<'a, T> Foo<'a, T> {} +"#, + ); + } }
diff --git a/crates/ide/src/view_memory_layout.rs b/crates/ide/src/view_memory_layout.rs index 47ca616..1b9df97 100644 --- a/crates/ide/src/view_memory_layout.rs +++ b/crates/ide/src/view_memory_layout.rs
@@ -108,7 +108,7 @@ nodes: &mut Vec<MemoryLayoutNode>, db: &RootDatabase, ty: &Type<'_>, - layout: &Layout, + layout: &Layout<'_>, parent_idx: usize, display_target: DisplayTarget, ) {
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index c0053a3..db3c3c5 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs
@@ -332,6 +332,7 @@ module_path, mul_assign, mul, + must_use, naked_asm, ne, neg,
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 5396826..a893f65 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -10,11 +10,11 @@ SyntaxNode, SyntaxToken, algo::{self, neighbor}, ast::{self, edit::IndentLevel, make}, - syntax_editor::SyntaxEditor, + syntax_editor::{self, SyntaxEditor}, ted, }; -use super::{GenericParam, HasName}; +use super::HasName; pub trait AttrsOwnerEdit: ast::HasAttrs { fn remove_attrs_and_docs(&self) { @@ -43,24 +43,6 @@ impl<T: ast::HasAttrs> 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 @@ } } - /// Find the params corresponded to generic arg - pub fn find_generic_arg(&self, generic_arg: &ast::GenericArg) -> Option<GenericParam> { - 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 { @@ -124,44 +77,10 @@ } } -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 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] { @@ -371,26 +290,35 @@ /// /// 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<SyntaxElement> = 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); } } @@ -429,32 +357,7 @@ 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) { + 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()); @@ -547,6 +450,35 @@ 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())
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index e8f5c95..718e5e2 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs
@@ -294,12 +294,7 @@ (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()))), } }
diff --git a/crates/syntax/src/syntax_editor/edits.rs b/crates/syntax/src/syntax_editor/edits.rs index 28e8cee..a684c0b 100644 --- a/crates/syntax/src/syntax_editor/edits.rs +++ b/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 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, - ); + /// 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 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), - ); + 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); }