From 840cd38f8dfddfab316a136f9b24badfae8b70de Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 4 Jun 2025 10:55:08 +0800 Subject: [PATCH 001/170] Add ide-assist: remove else branches --- .../src/handlers/remove_else_branches.rs | 90 +++++++++++++++++++ .../crates/ide-assists/src/lib.rs | 2 + .../crates/ide-assists/src/tests/generated.rs | 40 +++++++++ 3 files changed, 132 insertions(+) create mode 100644 src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_else_branches.rs 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 new file mode 100644 index 000000000000..6a02c37015d3 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_else_branches.rs @@ -0,0 +1,90 @@ +use syntax::{AstNode, SyntaxKind, T, TextRange, ast}; + +use crate::{AssistContext, AssistId, Assists}; + +// Assist: remove_else_branches +// +// Removes the `else` keyword and else branches. +// +// ``` +// fn main() { +// if true { +// let _ = 2; +// } $0else { +// unreachable!(); +// } +// } +// ``` +// -> +// ``` +// fn main() { +// if true { +// let _ = 2; +// } +// } +// ``` +// --- +// ``` +// fn main() { +// let _x = 2 $0else { unreachable!() }; +// } +// ``` +// -> +// ``` +// fn main() { +// let _x = 2; +// } +// ``` +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::() + .and_then(|if_expr| if_expr.else_branch()?.syntax().clone().into()) + .or_else(|| { + ctx.find_node_at_range::()? + .let_else()? + .block_expr()? + .syntax() + .clone() + .into() + })?; + + let target = TextRange::cover(else_token.text_range(), else_branches.text_range()); + acc.add( + AssistId::refactor("remove_else_branches"), + "Remove `else` branches", + target, + |builder| { + let mut editor = builder.make_editor(&else_token.parent().unwrap()); + match else_token.prev_token() { + Some(it) if it.kind() == SyntaxKind::WHITESPACE => editor.delete(it), + _ => (), + } + match else_token.next_token() { + Some(it) if it.kind() == SyntaxKind::WHITESPACE => editor.delete(it), + _ => (), + } + editor.delete(else_token); + editor.delete(else_branches); + builder.add_file_edits(ctx.vfs_file_id(), editor); + }, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::check_assist_not_applicable; + + #[test] + fn test_remove_else_branches_not_on_else_token() { + check_assist_not_applicable( + remove_else_branches, + r#" +fn main() { + let _x = 2 else {$0 unreachable!() }; +} +"#, + ); + } +} 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 c2604432032d..f4d5136c1995 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -199,6 +199,7 @@ mod handlers { mod qualify_path; mod raw_string; mod remove_dbg; + mod remove_else_branches; mod remove_mut; mod remove_parentheses; mod remove_underscore; @@ -337,6 +338,7 @@ mod handlers { raw_string::remove_hash, remove_dbg::remove_dbg, remove_mut::remove_mut, + remove_else_branches::remove_else_branches, remove_parentheses::remove_parentheses, remove_underscore::remove_underscore, remove_unused_imports::remove_unused_imports, 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 72f7195cbd77..74c663450c69 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 @@ -2738,6 +2738,46 @@ fn main() { ) } +#[test] +fn doctest_remove_else_branches() { + check_doc_test( + "remove_else_branches", + r#####" +fn main() { + if true { + let _ = 2; + } $0else { + unreachable!(); + } +} +"#####, + r#####" +fn main() { + if true { + let _ = 2; + } +} +"#####, + ) +} + +#[test] +fn doctest_remove_else_branches_1() { + check_doc_test( + "remove_else_branches", + r#####" +fn main() { + let _x = 2 $0else { unreachable!() }; +} +"#####, + r#####" +fn main() { + let _x = 2; +} +"#####, + ) +} + #[test] fn doctest_remove_hash() { check_doc_test( From 2969b0e2c5c70e27833baff42cd19dc809ac8615 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 2 Sep 2025 15:45:58 +0800 Subject: [PATCH 002/170] Fix extract multiple item in impl for extract_module Example --- ```rust struct Foo; impl Foo { $0fn foo() {} fn bar() {}$0 fn baz() {} } ``` **Before this PR**: ```rust struct Foo; impl Foo { mod modname { pub(crate) fn foo() {} pub(crate) fn bar() {} } fn baz() {} } ``` **After this PR**: ```rust struct Foo; impl Foo { fn baz() {} } mod modname { use super::Foo; impl Foo { pub(crate) fn foo() {} pub(crate) fn bar() {} } } ``` --- .../src/handlers/extract_module.rs | 152 +++++++++++------- 1 file changed, 92 insertions(+), 60 deletions(-) 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 dad19bfb8a2c..a17ae4885e62 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 @@ -1,4 +1,4 @@ -use std::ops::RangeInclusive; +use std::{iter::once, ops::RangeInclusive}; use hir::{HasSource, ModuleSource}; use ide_db::{ @@ -63,19 +63,6 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti syntax::NodeOrToken::Token(t) => t.parent()?, }; - //If the selection is inside impl block, we need to place new module outside impl block, - //as impl blocks cannot contain modules - - let mut impl_parent: Option = None; - let mut impl_child_count: usize = 0; - if let Some(parent_assoc_list) = node.parent() - && let Some(parent_impl) = parent_assoc_list.parent() - && let Some(impl_) = ast::Impl::cast(parent_impl) - { - impl_child_count = parent_assoc_list.children().count(); - impl_parent = Some(impl_); - } - let mut curr_parent_module: Option = None; if let Some(mod_syn_opt) = node.ancestors().find(|it| ast::Module::can_cast(it.kind())) { curr_parent_module = ast::Module::cast(mod_syn_opt); @@ -94,7 +81,22 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti return None; } - let old_item_indent = module.body_items[0].indent_level(); + let mut old_item_indent = module.body_items[0].indent_level(); + let old_items: Vec<_> = module.use_items.iter().chain(&module.body_items).cloned().collect(); + + // If the selection is inside impl block, we need to place new module outside impl block, + // as impl blocks cannot contain modules + + let mut impl_parent: Option = None; + let mut impl_child_count: usize = 0; + if let Some(parent_assoc_list) = module.body_items[0].syntax().parent() + && let Some(parent_impl) = parent_assoc_list.parent() + && let Some(impl_) = ast::Impl::cast(parent_impl) + { + impl_child_count = parent_assoc_list.children().count(); + old_item_indent = impl_.indent_level(); + impl_parent = Some(impl_); + } acc.add( AssistId::refactor_extract("extract_module"), @@ -127,7 +129,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti let import_items = module.resolve_imports(curr_parent_module, ctx); module.change_visibility(record_fields); - let module_def = generate_module_def(&impl_parent, module, old_item_indent).to_string(); + let module_def = generate_module_def(&impl_parent, &module).indent(old_item_indent); let mut usages_to_be_processed_for_cur_file = vec![]; for (file_id, usages) in usages_to_be_processed { @@ -149,27 +151,32 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti if let Some(impl_) = impl_parent { // Remove complete impl block if it has only one child (as such it will be empty // after deleting that child) - let node_to_be_removed = if impl_child_count == 1 { - impl_.syntax() + let nodes_to_be_removed = if impl_child_count == old_items.len() { + vec![impl_.syntax()] } else { //Remove selected node - &node + old_items.iter().map(|it| it.syntax()).collect() }; - builder.delete(node_to_be_removed.text_range()); - // Remove preceding indentation from node - if let Some(range) = indent_range_before_given_node(node_to_be_removed) { - builder.delete(range); + for node_to_be_removed in nodes_to_be_removed { + builder.delete(node_to_be_removed.text_range()); + // Remove preceding indentation from node + if let Some(range) = indent_range_before_given_node(node_to_be_removed) { + builder.delete(range); + } } - builder.insert(impl_.syntax().text_range().end(), format!("\n\n{module_def}")); + builder.insert( + impl_.syntax().text_range().end(), + format!("\n\n{old_item_indent}{module_def}"), + ); } else { for import_item in import_items { if !module_text_range.contains_range(import_item) { builder.delete(import_item); } } - builder.replace(module_text_range, module_def) + builder.replace(module_text_range, module_def.to_string()) } }, ) @@ -177,34 +184,35 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti fn generate_module_def( parent_impl: &Option, - module: Module, - old_indent: IndentLevel, + Module { name, body_items, use_items }: &Module, ) -> ast::Module { - let Module { name, body_items, use_items } = module; - let items = if let Some(self_ty) = parent_impl.as_ref().and_then(|imp| imp.self_ty()) { + let items: Vec<_> = if let Some(impl_) = parent_impl.as_ref() + && let Some(self_ty) = impl_.self_ty() + { let assoc_items = body_items - .into_iter() + .iter() .map(|item| item.syntax().clone()) .filter_map(ast::AssocItem::cast) .map(|it| it.indent(IndentLevel(1))) .collect_vec(); - let assoc_item_list = make::assoc_item_list(Some(assoc_items)); - let impl_ = make::impl_(None, None, None, self_ty.clone(), None, Some(assoc_item_list)); + let assoc_item_list = make::assoc_item_list(Some(assoc_items)).clone_for_update(); + let impl_ = impl_.reset_indent(); + ted::replace(impl_.get_or_create_assoc_item_list().syntax(), assoc_item_list.syntax()); // Add the import for enum/struct corresponding to given impl block let use_impl = make_use_stmt_of_node_with_super(self_ty.syntax()); - let mut module_body_items = use_items; - module_body_items.insert(0, use_impl); - module_body_items.push(ast::Item::Impl(impl_)); - module_body_items + once(use_impl) + .chain(use_items.iter().cloned()) + .chain(once(ast::Item::Impl(impl_))) + .collect() } else { - [use_items, body_items].concat() + use_items.iter().chain(body_items).cloned().collect() }; let items = items.into_iter().map(|it| it.reset_indent().indent(IndentLevel(1))).collect_vec(); let module_body = make::item_list(Some(items)); let module_name = make::name(name); - make::mod_(module_name, Some(module_body)).indent(old_indent) + make::mod_(module_name, Some(module_body)) } fn make_use_stmt_of_node_with_super(node_syntax: &SyntaxNode) -> ast::Item { @@ -1400,28 +1408,54 @@ mod modname { fn test_if_inside_impl_block_generate_module_outside() { check_assist( extract_module, - r" - struct A {} + r"struct A {} impl A { -$0fn foo() {}$0 + $0fn foo() {}$0 fn bar() {} } ", - r" - struct A {} + r"struct A {} impl A { fn bar() {} } -mod modname { - use super::A; + mod modname { + use super::A; - impl A { - pub(crate) fn foo() {} - } -} + impl A { + pub(crate) fn foo() {} + } + } + ", + ); + + check_assist( + extract_module, + r"struct A {} + + impl A { + $0fn foo() {} + fn bar() {}$0 + fn baz() {} + } + ", + r"struct A {} + + impl A { + fn baz() {} + } + + mod modname { + use super::A; + + impl A { + pub(crate) fn foo() {} + + pub(crate) fn bar() {} + } + } ", ) } @@ -1430,27 +1464,25 @@ mod modname { fn test_if_inside_impl_block_generate_module_outside_but_impl_block_having_one_child() { check_assist( extract_module, - r" - struct A {} + r"struct A {} struct B {} impl A { $0fn foo(x: B) {}$0 } ", - r" - struct A {} + r"struct A {} struct B {} -mod modname { - use super::A; + mod modname { + use super::A; - use super::B; + use super::B; - impl A { - pub(crate) fn foo(x: B) {} - } -} + impl A { + pub(crate) fn foo(x: B) {} + } + } ", ) } From 39628ff5a42d14afc75f57dedcc430c043468836 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 8 Sep 2025 06:12:51 +0200 Subject: [PATCH 003/170] remove untrue/contentious statement One can use unstable features on stable too, like via an env var --- src/doc/rustc-dev-guide/src/contributing.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/contributing.md b/src/doc/rustc-dev-guide/src/contributing.md index 347608d305aa..764452b6e544 100644 --- a/src/doc/rustc-dev-guide/src/contributing.md +++ b/src/doc/rustc-dev-guide/src/contributing.md @@ -52,9 +52,7 @@ channels: stable, beta, and nightly. - **Stable**: this is the latest stable release for general usage. - **Beta**: this is the next release (will be stable within 6 weeks). -- **Nightly**: follows the `master` branch of the repo. This is the only - channel where unstable, incomplete, or experimental features are usable with - feature gates. +- **Nightly**: follows the `master` branch of the repo. See [this chapter on implementing new features](./implementing_new_features.md) for more information. From 419902412b4a970cb12401675a2d7471508e4cc7 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 20 Sep 2025 11:17:24 +0800 Subject: [PATCH 004/170] Add ide-assist: flip_range_expr Flips operands of a range expression. Example --- ```rust fn main() { let _ = 90..$02; } ``` -> ```rust fn main() { let _ = 2..90; } ``` --- ```rust fn main() { let _ = 90..$0; } ``` -> ```rust fn main() { let _ = ..90; } ``` --- .../ide-assists/src/handlers/flip_binexpr.rs | 71 ++++++++++++++++++- .../crates/ide-assists/src/lib.rs | 1 + .../crates/ide-assists/src/tests/generated.rs | 34 +++++++++ 3 files changed, 105 insertions(+), 1 deletion(-) 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 247e8109abc9..8f2306e9037e 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 @@ -1,6 +1,7 @@ use syntax::{ SyntaxKind, T, - ast::{self, AstNode, BinExpr, syntax_factory::SyntaxFactory}, + ast::{self, AstNode, BinExpr, RangeItem, syntax_factory::SyntaxFactory}, + syntax_editor::Position, }; use crate::{AssistContext, AssistId, Assists}; @@ -87,6 +88,74 @@ impl From for FlipAction { } } +// Assist: flip_range_expr +// +// Flips operands of a range expression. +// +// ``` +// fn main() { +// let _ = 90..$02; +// } +// ``` +// -> +// ``` +// fn main() { +// let _ = 2..90; +// } +// ``` +// --- +// ``` +// fn main() { +// let _ = 90..$0; +// } +// ``` +// -> +// ``` +// fn main() { +// let _ = ..90; +// } +// ``` +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(); + let end = range_expr.end(); + + if !op.text_range().contains_range(ctx.selection_trimmed()) { + return None; + } + if start.is_none() && end.is_none() { + return None; + } + + acc.add( + AssistId::refactor_rewrite("flip_range_expr"), + "Flip range expression", + op.text_range(), + |builder| { + let mut edit = builder.make_editor(range_expr.syntax()); + + match (start, end) { + (Some(start), Some(end)) => { + edit.replace(start.syntax(), end.syntax()); + edit.replace(end.syntax(), start.syntax()); + } + (Some(start), None) => { + edit.delete(start.syntax()); + edit.insert(Position::after(&op), start.syntax().clone_for_update()); + } + (None, Some(end)) => { + edit.delete(end.syntax()); + edit.insert(Position::before(&op), end.syntax().clone_for_update()); + } + (None, None) => (), + } + + builder.add_file_edits(ctx.vfs_file_id(), edit); + }, + ) +} + #[cfg(test)] mod tests { use super::*; 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 4682c0473238..0e160af652f9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -283,6 +283,7 @@ mod handlers { extract_type_alias::extract_type_alias, fix_visibility::fix_visibility, flip_binexpr::flip_binexpr, + flip_binexpr::flip_range_expr, flip_comma::flip_comma, flip_or_pattern::flip_or_pattern, flip_trait_bound::flip_trait_bound, 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 91348be97eb7..7b042ed4dc99 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 @@ -1298,6 +1298,40 @@ fn foo() { ) } +#[test] +fn doctest_flip_range_expr() { + check_doc_test( + "flip_range_expr", + r#####" +fn main() { + let _ = 90..$02; +} +"#####, + r#####" +fn main() { + let _ = 2..90; +} +"#####, + ) +} + +#[test] +fn doctest_flip_range_expr_1() { + check_doc_test( + "flip_range_expr", + r#####" +fn main() { + let _ = 90..$0; +} +"#####, + r#####" +fn main() { + let _ = ..90; +} +"#####, + ) +} + #[test] fn doctest_flip_trait_bound() { check_doc_test( From 404f7499b36709e47e703a6d8cbc68e55c24bbab Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 20 Sep 2025 21:41:27 +0800 Subject: [PATCH 005/170] Fix shorthand field pat for destructure_tuple_binding Example --- ```rust struct S { field: (i32, i32) } fn main() { let S { $0field } = S { field: (2, 3) }; let v = field.0 + field.1; } ``` **Before this PR**: ```rust struct S { field: (i32, i32) } fn main() { let S { ($0_0, _1) } = S { field: (2, 3) }; let v = _0 + _1; } ``` **After this PR**: ```rust struct S { field: (i32, i32) } fn main() { let S { field: ($0_0, _1) } = S { field: (2, 3) }; let v = _0 + _1; } ``` --- .../src/handlers/destructure_tuple_binding.rs | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) 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 f09389f8302f..e2afc0bf130e 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 @@ -7,6 +7,7 @@ use ide_db::{ }; use itertools::Itertools; use syntax::{ + T, ast::{self, AstNode, FieldExpr, HasName, IdentPat, make}, ted, }; @@ -179,6 +180,11 @@ fn edit_tuple_assignment( .map(|name| ast::Pat::from(make::ident_pat(is_ref, is_mut, make::name(name)))); make::tuple_pat(fields).clone_for_update() }; + let is_shorthand_field = ident_pat + .name() + .as_ref() + .and_then(ast::RecordPatField::for_field_name) + .is_some_and(|field| field.colon_token().is_none()); if let Some(cap) = ctx.config.snippet_cap { // place cursor on first tuple name @@ -190,12 +196,13 @@ fn edit_tuple_assignment( } } - AssignmentEdit { ident_pat, tuple_pat, in_sub_pattern } + AssignmentEdit { ident_pat, tuple_pat, in_sub_pattern, is_shorthand_field } } struct AssignmentEdit { ident_pat: ast::IdentPat, tuple_pat: ast::TuplePat, in_sub_pattern: bool, + is_shorthand_field: bool, } impl AssignmentEdit { @@ -203,6 +210,9 @@ impl AssignmentEdit { // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)` if self.in_sub_pattern { self.ident_pat.set_pat(Some(self.tuple_pat.into())) + } else if self.is_shorthand_field { + ted::insert(ted::Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax()); + ted::insert_raw(ted::Position::after(self.ident_pat.syntax()), make::token(T![:])); } else { ted::replace(self.ident_pat.syntax(), self.tuple_pat.syntax()) } @@ -799,6 +809,48 @@ fn main() { ) } + #[test] + fn in_record_shorthand_field() { + check_assist( + assist, + r#" +struct S { field: (i32, i32) } +fn main() { + let S { $0field } = S { field: (2, 3) }; + let v = field.0 + field.1; +} + "#, + r#" +struct S { field: (i32, i32) } +fn main() { + let S { field: ($0_0, _1) } = S { field: (2, 3) }; + let v = _0 + _1; +} + "#, + ) + } + + #[test] + fn in_record_field() { + check_assist( + assist, + r#" +struct S { field: (i32, i32) } +fn main() { + let S { field: $0t } = S { field: (2, 3) }; + let v = t.0 + t.1; +} + "#, + r#" +struct S { field: (i32, i32) } +fn main() { + let S { field: ($0_0, _1) } = S { field: (2, 3) }; + let v = _0 + _1; +} + "#, + ) + } + #[test] fn in_nested_tuple() { check_assist( From c0dc979549ef4b973e0e275bafa4edf3e5cf4c8b Mon Sep 17 00:00:00 2001 From: dianne Date: Fri, 26 Sep 2025 20:10:08 -0700 Subject: [PATCH 006/170] do not lifetime-extend array/slice indices When lowering non-overloaded indexing operations to MIR, this uses the temporary lifetime of the index expression for the index, rather than applying the temporary lifetime of the indexing operation as a whole to the index. --- .../src/builder/expr/as_place.rs | 5 ++--- ....dereference_indexing.GVN.panic-abort.diff | 2 +- ...dereference_indexing.GVN.panic-unwind.diff | 2 +- ...e_ptr_same_provenance.GVN.panic-abort.diff | 4 ++-- ..._ptr_same_provenance.GVN.panic-unwind.diff | 4 ++-- tests/mir-opt/issue_91633.fun.built.after.mir | 2 +- ...egion_subtyping_basic.main.nll.0.32bit.mir | 22 +++++++++---------- ...egion_subtyping_basic.main.nll.0.64bit.mir | 22 +++++++++---------- 8 files changed, 31 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 5a6bd2f413c2..02d0cd0ed9a1 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -454,7 +454,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { index, mutability, fake_borrow_temps, - expr.temp_lifetime, expr_span, source_info, ), @@ -625,7 +624,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { index: ExprId, mutability: Mutability, fake_borrow_temps: Option<&mut Vec>, - temp_lifetime: TempLifetime, expr_span: Span, source_info: SourceInfo, ) -> BlockAnd> { @@ -639,7 +637,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Making this a *fresh* temporary means we do not have to worry about // the index changing later: Nothing will ever change this temporary. // The "retagging" transformation (for Stacked Borrows) relies on this. - let idx = unpack!(block = self.as_temp(block, temp_lifetime, index, Mutability::Not)); + let index_lifetime = self.thir[index].temp_lifetime; + let idx = unpack!(block = self.as_temp(block, index_lifetime, index, Mutability::Not)); block = self.bounds_check(block, &base_place, idx, expr_span, source_info); diff --git a/tests/mir-opt/gvn.dereference_indexing.GVN.panic-abort.diff b/tests/mir-opt/gvn.dereference_indexing.GVN.panic-abort.diff index 9bdcc2f108a6..9ae244851594 100644 --- a/tests/mir-opt/gvn.dereference_indexing.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.dereference_indexing.GVN.panic-abort.diff @@ -41,6 +41,7 @@ - StorageDead(_4); + _3 = &_1[_4]; + nop; + StorageDead(_6); StorageLive(_8); StorageLive(_9); _9 = copy (*_3); @@ -51,7 +52,6 @@ StorageDead(_9); StorageDead(_8); _0 = const (); - StorageDead(_6); StorageDead(_3); return; } diff --git a/tests/mir-opt/gvn.dereference_indexing.GVN.panic-unwind.diff b/tests/mir-opt/gvn.dereference_indexing.GVN.panic-unwind.diff index f38bc51adc48..82cdf3f7cd20 100644 --- a/tests/mir-opt/gvn.dereference_indexing.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.dereference_indexing.GVN.panic-unwind.diff @@ -41,6 +41,7 @@ - StorageDead(_4); + _3 = &_1[_4]; + nop; + StorageDead(_6); StorageLive(_8); StorageLive(_9); _9 = copy (*_3); @@ -51,7 +52,6 @@ StorageDead(_9); StorageDead(_8); _0 = const (); - StorageDead(_6); StorageDead(_3); return; } diff --git a/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-abort.diff b/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-abort.diff index 1b305e746f5e..0165136fda4c 100644 --- a/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-abort.diff @@ -89,6 +89,7 @@ - StorageDead(_4); + _3 = copy _4; + nop; + StorageDead(_8); StorageDead(_7); StorageDead(_5); StorageLive(_10); @@ -116,6 +117,7 @@ - StorageDead(_11); + _10 = copy _11; + nop; + StorageDead(_15); StorageDead(_14); StorageDead(_12); StorageLive(_17); @@ -251,9 +253,7 @@ StorageDead(_43); StorageDead(_42); _0 = const (); - StorageDead(_15); StorageDead(_10); - StorageDead(_8); StorageDead(_3); StorageDead(_1); return; diff --git a/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-unwind.diff b/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-unwind.diff index e418ecf25bd4..bbaab46b4756 100644 --- a/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-unwind.diff @@ -89,6 +89,7 @@ - StorageDead(_4); + _3 = copy _4; + nop; + StorageDead(_8); StorageDead(_7); StorageDead(_5); StorageLive(_10); @@ -116,6 +117,7 @@ - StorageDead(_11); + _10 = copy _11; + nop; + StorageDead(_15); StorageDead(_14); StorageDead(_12); StorageLive(_17); @@ -251,9 +253,7 @@ StorageDead(_43); StorageDead(_42); _0 = const (); - StorageDead(_15); StorageDead(_10); - StorageDead(_8); StorageDead(_3); StorageDead(_1); return; diff --git a/tests/mir-opt/issue_91633.fun.built.after.mir b/tests/mir-opt/issue_91633.fun.built.after.mir index d2fc438d3e81..7468e591b37b 100644 --- a/tests/mir-opt/issue_91633.fun.built.after.mir +++ b/tests/mir-opt/issue_91633.fun.built.after.mir @@ -23,8 +23,8 @@ fn fun(_1: &[T]) -> &T { bb1: { _2 = &(*_1)[_3]; FakeRead(ForLet(None), _2); - _0 = &(*_2); StorageDead(_3); + _0 = &(*_2); StorageDead(_2); return; } diff --git a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir index 42b388033360..c069ce3fdf3c 100644 --- a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir +++ b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir @@ -5,20 +5,20 @@ | '?1 | Local | ['?1] | | Inferred Region Values -| '?0 | U0 | {bb0[0..=8], bb1[0..=7], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=5], bb7[0], '?0, '?1} -| '?1 | U0 | {bb0[0..=8], bb1[0..=7], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=5], bb7[0], '?1} -| '?2 | U0 | {bb1[0..=7], bb2[0..=2]} -| '?3 | U0 | {bb1[1..=7], bb2[0..=2]} -| '?4 | U0 | {bb1[4..=7], bb2[0..=2]} +| '?0 | U0 | {bb0[0..=8], bb1[0..=8], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=4], bb7[0], '?0, '?1} +| '?1 | U0 | {bb0[0..=8], bb1[0..=8], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=4], bb7[0], '?1} +| '?2 | U0 | {bb1[0..=8], bb2[0..=2]} +| '?3 | U0 | {bb1[1..=8], bb2[0..=2]} +| '?4 | U0 | {bb1[5..=8], bb2[0..=2]} | | Inference Constraints -| '?0 live at {bb0[0..=8], bb1[0..=7], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=5], bb7[0]} -| '?1 live at {bb0[0..=8], bb1[0..=7], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=5], bb7[0]} +| '?0 live at {bb0[0..=8], bb1[0..=8], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=4], bb7[0]} +| '?1 live at {bb0[0..=8], bb1[0..=8], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=4], bb7[0]} | '?2 live at {bb1[0]} -| '?3 live at {bb1[1..=3]} -| '?4 live at {bb1[4..=7], bb2[0..=2]} +| '?3 live at {bb1[1..=4]} +| '?4 live at {bb1[5..=8], bb2[0..=2]} | '?2: '?3 due to Assignment at Single(bb1[0]) ($DIR/region_subtyping_basic.rs:19:13: 19:18 (#0) -| '?3: '?4 due to Assignment at Single(bb1[3]) ($DIR/region_subtyping_basic.rs:20:13: 20:14 (#0) +| '?3: '?4 due to Assignment at Single(bb1[4]) ($DIR/region_subtyping_basic.rs:20:13: 20:14 (#0) | | Borrows | bw0: issued at bb1[0] in '?2 @@ -59,6 +59,7 @@ fn main() -> () { bb1: { _2 = &'?2 _1[_3]; FakeRead(ForLet(None), _2); + StorageDead(_3); StorageLive(_5); _5 = copy _2; FakeRead(ForLet(None), _5); @@ -95,7 +96,6 @@ fn main() -> () { bb6: { StorageDead(_6); StorageDead(_5); - StorageDead(_3); StorageDead(_2); StorageDead(_1); return; diff --git a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir index 15395fd470e9..a866521e7c97 100644 --- a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir +++ b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir @@ -5,20 +5,20 @@ | '?1 | Local | ['?1] | | Inferred Region Values -| '?0 | U0 | {bb0[0..=8], bb1[0..=7], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=5], bb7[0], '?0, '?1} -| '?1 | U0 | {bb0[0..=8], bb1[0..=7], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=5], bb7[0], '?1} -| '?2 | U0 | {bb1[0..=7], bb2[0..=2]} -| '?3 | U0 | {bb1[1..=7], bb2[0..=2]} -| '?4 | U0 | {bb1[4..=7], bb2[0..=2]} +| '?0 | U0 | {bb0[0..=8], bb1[0..=8], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=4], bb7[0], '?0, '?1} +| '?1 | U0 | {bb0[0..=8], bb1[0..=8], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=4], bb7[0], '?1} +| '?2 | U0 | {bb1[0..=8], bb2[0..=2]} +| '?3 | U0 | {bb1[1..=8], bb2[0..=2]} +| '?4 | U0 | {bb1[5..=8], bb2[0..=2]} | | Inference Constraints -| '?0 live at {bb0[0..=8], bb1[0..=7], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=5], bb7[0]} -| '?1 live at {bb0[0..=8], bb1[0..=7], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=5], bb7[0]} +| '?0 live at {bb0[0..=8], bb1[0..=8], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=4], bb7[0]} +| '?1 live at {bb0[0..=8], bb1[0..=8], bb2[0..=3], bb3[0..=3], bb4[0..=1], bb5[0..=2], bb6[0..=4], bb7[0]} | '?2 live at {bb1[0]} -| '?3 live at {bb1[1..=3]} -| '?4 live at {bb1[4..=7], bb2[0..=2]} +| '?3 live at {bb1[1..=4]} +| '?4 live at {bb1[5..=8], bb2[0..=2]} | '?2: '?3 due to Assignment at Single(bb1[0]) ($DIR/region_subtyping_basic.rs:19:13: 19:18 (#0) -| '?3: '?4 due to Assignment at Single(bb1[3]) ($DIR/region_subtyping_basic.rs:20:13: 20:14 (#0) +| '?3: '?4 due to Assignment at Single(bb1[4]) ($DIR/region_subtyping_basic.rs:20:13: 20:14 (#0) | | Borrows | bw0: issued at bb1[0] in '?2 @@ -59,6 +59,7 @@ fn main() -> () { bb1: { _2 = &'?2 _1[_3]; FakeRead(ForLet(None), _2); + StorageDead(_3); StorageLive(_5); _5 = copy _2; FakeRead(ForLet(None), _5); @@ -95,7 +96,6 @@ fn main() -> () { bb6: { StorageDead(_6); StorageDead(_5); - StorageDead(_3); StorageDead(_2); StorageDead(_1); return; From 5647e83ba1bae599c7e6e40c4712fb66ee45f7dd Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 27 Sep 2025 12:46:11 +0800 Subject: [PATCH 007/170] =?UTF-8?q?Add=20`doc=20=3D=20include=5Fstr!("?= =?UTF-8?q?=E2=80=A6")`=20completion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/completions/attribute.rs | 3 ++- .../ide-completion/src/tests/attribute.rs | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) 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 e174b0c8922a..297ce3339e02 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 @@ -231,7 +231,7 @@ const fn attr( macro_rules! attrs { // attributes applicable to all items [@ { item $($tt:tt)* } {$($acc:tt)*}] => { - attrs!(@ { $($tt)* } { $($acc)*, "deprecated", "doc", "dochidden", "docalias", "must_use", "no_mangle" }) + attrs!(@ { $($tt)* } { $($acc)*, "deprecated", "doc", "dochidden", "docalias", "docinclude", "must_use", "no_mangle" }) }; // attributes applicable to all adts [@ { adt $($tt:tt)* } {$($acc:tt)*}] => { @@ -345,6 +345,7 @@ const ATTRIBUTES: &[AttrCompletion] = &[ attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)), attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)), attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)), + attr(r#"doc = include_str!("…")"#, Some("docinclude"), Some(r#"doc = include_str!("$0")"#)), attr("expect(…)", Some("expect"), Some("expect(${0:lint})")), attr( r#"export_name = "…""#, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index 30e1e108c6c4..cd660e496f27 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -33,6 +33,7 @@ pub struct Foo(#[m$0] i32); at diagnostic::do_not_recommend at diagnostic::on_unimplemented at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -85,6 +86,7 @@ struct Foo; at deprecated at derive(…) at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -158,6 +160,7 @@ fn attr_on_source_file() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -189,6 +192,7 @@ fn attr_on_module() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -212,6 +216,7 @@ fn attr_on_module() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -238,6 +243,7 @@ fn attr_on_macro_rules() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -264,6 +270,7 @@ fn attr_on_macro_def() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -288,6 +295,7 @@ fn attr_on_extern_crate() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -313,6 +321,7 @@ fn attr_on_use() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -337,6 +346,7 @@ fn attr_on_type_alias() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -368,6 +378,7 @@ struct Foo; at derive(…) at derive_const macro derive_const at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -396,6 +407,7 @@ fn attr_on_enum() { at deprecated at derive(…) at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -422,6 +434,7 @@ fn attr_on_const() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -446,6 +459,7 @@ fn attr_on_static() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -476,6 +490,7 @@ fn attr_on_trait() { at deprecated at diagnostic::on_unimplemented at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -502,6 +517,7 @@ fn attr_on_impl() { at deprecated at diagnostic::do_not_recommend at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -522,6 +538,7 @@ fn attr_on_impl() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -548,6 +565,7 @@ fn attr_with_qualifier() { at deprecated at do_not_recommend at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -566,6 +584,7 @@ fn attr_with_qualifier() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -616,6 +635,7 @@ fn attr_on_extern_block() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -637,6 +657,7 @@ fn attr_on_extern_block() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -682,6 +703,7 @@ fn attr_on_fn() { at deny(…) at deprecated at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) @@ -724,6 +746,7 @@ fn attr_in_source_file_end() { at diagnostic::do_not_recommend at diagnostic::on_unimplemented at doc = "…" + at doc = include_str!("…") at doc(alias = "…") at doc(hidden) at expect(…) From 6390c89472e2feb255a20257b3d7ecd522eece36 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 27 Sep 2025 12:03:04 +0530 Subject: [PATCH 008/170] Use FileId::MAX for id assertion in PathInterner::intern --- src/tools/rust-analyzer/crates/vfs/src/path_interner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/vfs/src/path_interner.rs b/src/tools/rust-analyzer/crates/vfs/src/path_interner.rs index 64f51976053d..225bfc7218b4 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/path_interner.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/path_interner.rs @@ -28,7 +28,7 @@ impl PathInterner { /// - Else, returns a newly allocated id. pub(crate) fn intern(&mut self, path: VfsPath) -> FileId { let (id, _added) = self.map.insert_full(path); - assert!(id < u32::MAX as usize); + assert!(id < FileId::MAX as usize); FileId(id as u32) } From f6eb4ea86b043dce17d6b4388b67e72f8fd33b00 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 30 Sep 2025 13:04:01 +0800 Subject: [PATCH 009/170] Fix let-expr in lhs for convert_to_guarded_return Example --- ```rust fn main() { if$0 let Ok(x) = Err(92) && let Ok(y) = Ok(37) && x < 30 && let Some(y) = Some(8) { foo(x, y); } } ``` **Before this PR**: ```rust fn main() { let Ok(x) = Err(92) else { return }; if !(let Ok(y) = Ok(37) && x < 30) { return; } let Some(y) = Some(8) else { return }; foo(x, y); } ``` **After this PR**: ```rust fn main() { let Ok(x) = Err(92) else { return }; let Ok(y) = Ok(37) else { return }; if x >= 30 { return; } let Some(y) = Some(8) else { return }; foo(x, y); } ``` --- .../src/handlers/convert_to_guarded_return.rs | 104 +++++++++++++++++- 1 file changed, 98 insertions(+), 6 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 82213ae3217e..6b5a37513cbe 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 @@ -254,20 +254,25 @@ fn early_expression( fn flat_let_chain(mut expr: ast::Expr) -> Vec { let mut chains = vec![]; + let mut reduce_cond = |rhs| { + if !matches!(rhs, ast::Expr::LetExpr(_)) + && let Some(last) = chains.pop_if(|last| !matches!(last, ast::Expr::LetExpr(_))) + { + chains.push(make::expr_bin_op(rhs, ast::BinaryOp::LogicOp(ast::LogicOp::And), last)); + } else { + chains.push(rhs); + } + }; while let ast::Expr::BinExpr(bin_expr) = &expr && bin_expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) && let (Some(lhs), Some(rhs)) = (bin_expr.lhs(), bin_expr.rhs()) { - if let Some(last) = chains.pop_if(|last| !matches!(last, ast::Expr::LetExpr(_))) { - chains.push(make::expr_bin_op(rhs, ast::BinaryOp::LogicOp(ast::LogicOp::And), last)); - } else { - chains.push(rhs); - } + reduce_cond(rhs); expr = lhs; } - chains.push(expr); + reduce_cond(expr); chains.reverse(); chains } @@ -493,6 +498,93 @@ fn main() { let Some(y) = Some(8) else { return }; foo(x, y); } +"#, + ); + + check_assist( + convert_to_guarded_return, + r#" +fn main() { + if$0 let Ok(x) = Err(92) + && let Ok(y) = Ok(37) + && x < 30 + && let Some(y) = Some(8) + { + foo(x, y); + } +} +"#, + r#" +fn main() { + let Ok(x) = Err(92) else { return }; + let Ok(y) = Ok(37) else { return }; + if x >= 30 { + return; + } + let Some(y) = Some(8) else { return }; + foo(x, y); +} +"#, + ); + + check_assist( + convert_to_guarded_return, + r#" +fn main() { + if$0 cond + && let Ok(x) = Err(92) + && let Ok(y) = Ok(37) + && x < 30 + && let Some(y) = Some(8) + { + foo(x, y); + } +} +"#, + r#" +fn main() { + if !cond { + return; + } + let Ok(x) = Err(92) else { return }; + let Ok(y) = Ok(37) else { return }; + if x >= 30 { + return; + } + let Some(y) = Some(8) else { return }; + foo(x, y); +} +"#, + ); + + check_assist( + convert_to_guarded_return, + r#" +fn main() { + if$0 cond + && foo() + && let Ok(x) = Err(92) + && let Ok(y) = Ok(37) + && x < 30 + && let Some(y) = Some(8) + { + foo(x, y); + } +} +"#, + r#" +fn main() { + if !(cond && foo()) { + return; + } + let Ok(x) = Err(92) else { return }; + let Ok(y) = Ok(37) else { return }; + if x >= 30 { + return; + } + let Some(y) = Some(8) else { return }; + foo(x, y); +} "#, ); } From 26cf7899b7d5643af8492cc3a88681d43a649840 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 1 Oct 2025 12:24:50 +0800 Subject: [PATCH 010/170] Add applicable on `else` for invert_if Example --- ```rust fn f() { if cond { 3 * 2 } e$0lse { 1 } } ``` -> ```rust fn f() { if !cond { 1 } else { 3 * 2 } } ``` --- .../crates/ide-assists/src/handlers/invert_if.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) 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 7576d2fab976..bf82d8df9b58 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 @@ -27,7 +27,9 @@ use crate::{ // } // ``` pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let if_keyword = ctx.find_token_syntax_at_offset(T![if])?; + let if_keyword = ctx + .find_token_syntax_at_offset(T![if]) + .or_else(|| ctx.find_token_syntax_at_offset(T![else]))?; let expr = ast::IfExpr::cast(if_keyword.parent()?)?; let if_range = if_keyword.text_range(); let cursor_in_range = if_range.contains_range(ctx.selection_trimmed()); @@ -111,6 +113,15 @@ mod tests { ) } + #[test] + fn invert_if_on_else_keyword() { + check_assist( + invert_if, + "fn f() { if cond { 3 * 2 } e$0lse { 1 } }", + "fn f() { if !cond { 1 } else { 3 * 2 } }", + ) + } + #[test] fn invert_if_doesnt_apply_with_cursor_not_on_if() { check_assist_not_applicable(invert_if, "fn f() { if !$0cond { 3 * 2 } else { 1 } }") From 1eb80f1d3c9cd30dcba45f4c6b3e49a1a7b50b50 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 4 Oct 2025 21:10:24 +0200 Subject: [PATCH 011/170] restore helpful explanation --- src/doc/rustc-dev-guide/src/contributing.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/doc/rustc-dev-guide/src/contributing.md b/src/doc/rustc-dev-guide/src/contributing.md index 764452b6e544..b927c9a79274 100644 --- a/src/doc/rustc-dev-guide/src/contributing.md +++ b/src/doc/rustc-dev-guide/src/contributing.md @@ -53,6 +53,8 @@ channels: stable, beta, and nightly. - **Stable**: this is the latest stable release for general usage. - **Beta**: this is the next release (will be stable within 6 weeks). - **Nightly**: follows the `master` branch of the repo. + This is the only channel where unstable, incomplete, or experimental features + should be used (with feature gates). See [this chapter on implementing new features](./implementing_new_features.md) for more information. From 92eef5348ac32ac8934bc9397f11b1fd6bdb2eea Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 12 Oct 2025 11:13:11 +0300 Subject: [PATCH 012/170] Migrate inhabitedness checking to the new solver --- .../diagnostics/match_check/pat_analysis.rs | 29 +++- .../crates/hir-ty/src/inhabitedness.rs | 150 ++++++++++-------- .../crates/hir-ty/src/mir/lower.rs | 6 +- .../crates/hir-ty/src/next_solver/def_id.rs | 38 ++++- .../crates/hir-ty/src/next_solver/interner.rs | 4 +- 5 files changed, 139 insertions(+), 88 deletions(-) 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 eb20d3c51ff4..76f50c194835 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 @@ -19,6 +19,11 @@ use crate::{ db::HirDatabase, infer::normalize, inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from}, + next_solver::{ + DbInterner, TypingMode, + infer::{DbInternerInferExt, InferCtxt}, + mapping::ChalkToNextSolver, + }, }; use super::{FieldPat, Pat, PatKind}; @@ -28,7 +33,7 @@ use Constructor::*; // Re-export r-a-specific versions of all these types. pub(crate) type DeconstructedPat<'db> = rustc_pattern_analysis::pat::DeconstructedPat>; -pub(crate) type MatchArm<'db> = rustc_pattern_analysis::MatchArm<'db, MatchCheckCtx<'db>>; +pub(crate) type MatchArm<'a, 'db> = rustc_pattern_analysis::MatchArm<'a, MatchCheckCtx<'db>>; pub(crate) type WitnessPat<'db> = rustc_pattern_analysis::pat::WitnessPat>; /// [Constructor] uses this in unimplemented variants. @@ -71,6 +76,7 @@ pub(crate) struct MatchCheckCtx<'db> { pub(crate) db: &'db dyn HirDatabase, exhaustive_patterns: bool, env: Arc>, + infcx: InferCtxt<'db>, } impl<'db> MatchCheckCtx<'db> { @@ -82,15 +88,17 @@ impl<'db> MatchCheckCtx<'db> { ) -> Self { let def_map = module.crate_def_map(db); let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns); - Self { module, body, db, exhaustive_patterns, env } + let interner = DbInterner::new_with(db, Some(env.krate), env.block); + let infcx = interner.infer_ctxt().build(TypingMode::typeck_for_body(interner, body.into())); + Self { module, body, db, exhaustive_patterns, env, infcx } } - pub(crate) fn compute_match_usefulness( + pub(crate) fn compute_match_usefulness<'a>( &self, - arms: &[MatchArm<'db>], + arms: &[MatchArm<'a, 'db>], scrut_ty: Ty, known_valid_scrutinee: Option, - ) -> Result, ()> { + ) -> Result, ()> { if scrut_ty.contains_unknown() { return Err(()); } @@ -107,7 +115,12 @@ impl<'db> MatchCheckCtx<'db> { } fn is_uninhabited(&self, ty: &Ty) -> bool { - is_ty_uninhabited_from(self.db, ty, self.module, self.env.clone()) + is_ty_uninhabited_from( + &self.infcx, + ty.to_nextsolver(self.infcx.interner), + self.module, + self.env.clone(), + ) } /// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`. @@ -429,9 +442,9 @@ impl PatCx for MatchCheckCtx<'_> { let mut variants = IndexVec::with_capacity(enum_data.variants.len()); for &(variant, _, _) in enum_data.variants.iter() { let is_uninhabited = is_enum_variant_uninhabited_from( - cx.db, + &cx.infcx, variant, - subst, + subst.to_nextsolver(cx.infcx.interner), cx.module, self.env.clone(), ); 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 826f19cf0b68..7ebc2df6f75d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -1,60 +1,62 @@ //! Type inhabitedness logic. use std::ops::ControlFlow::{self, Break, Continue}; -use chalk_ir::{ - DebruijnIndex, - visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, -}; use hir_def::{AdtId, EnumVariantId, ModuleId, VariantId, visibility::Visibility}; use rustc_hash::FxHashSet; +use rustc_type_ir::{ + TypeSuperVisitable, TypeVisitable, TypeVisitor, + inherent::{AdtDef, IntoKind}, +}; use triomphe::Arc; use crate::{ - AliasTy, Binders, Interner, Substitution, TraitEnvironment, Ty, TyKind, + TraitEnvironment, consteval::try_const_usize, db::HirDatabase, - next_solver::{DbInterner, mapping::ChalkToNextSolver}, + next_solver::{ + DbInterner, EarlyBinder, GenericArgs, Ty, TyKind, + infer::{InferCtxt, traits::ObligationCause}, + obligation_ctxt::ObligationCtxt, + }, }; // FIXME: Turn this into a query, it can be quite slow /// Checks whether a type is visibly uninhabited from a particular module. -pub(crate) fn is_ty_uninhabited_from( - db: &dyn HirDatabase, - ty: &Ty, +pub(crate) fn is_ty_uninhabited_from<'db>( + infcx: &InferCtxt<'db>, + ty: Ty<'db>, target_mod: ModuleId, - env: Arc>, + env: Arc>, ) -> bool { let _p = tracing::info_span!("is_ty_uninhabited_from", ?ty).entered(); - let mut uninhabited_from = - UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default(), env }; - let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST); + let mut uninhabited_from = UninhabitedFrom::new(infcx, target_mod, env); + let inhabitedness = ty.visit_with(&mut uninhabited_from); inhabitedness == BREAK_VISIBLY_UNINHABITED } // FIXME: Turn this into a query, it can be quite slow /// Checks whether a variant is visibly uninhabited from a particular module. -pub(crate) fn is_enum_variant_uninhabited_from( - db: &dyn HirDatabase, +pub(crate) fn is_enum_variant_uninhabited_from<'db>( + infcx: &InferCtxt<'db>, variant: EnumVariantId, - subst: &Substitution, + subst: GenericArgs<'db>, target_mod: ModuleId, - env: Arc>, + env: Arc>, ) -> bool { let _p = tracing::info_span!("is_enum_variant_uninhabited_from").entered(); - let mut uninhabited_from = - UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default(), env }; + let mut uninhabited_from = UninhabitedFrom::new(infcx, target_mod, env); let inhabitedness = uninhabited_from.visit_variant(variant.into(), subst); inhabitedness == BREAK_VISIBLY_UNINHABITED } -struct UninhabitedFrom<'a> { +struct UninhabitedFrom<'a, 'db> { target_mod: ModuleId, - recursive_ty: FxHashSet, + recursive_ty: FxHashSet>, // guard for preventing stack overflow in non trivial non terminating types max_depth: usize, - db: &'a dyn HirDatabase, - env: Arc>, + infcx: &'a InferCtxt<'db>, + env: Arc>, } const CONTINUE_OPAQUELY_INHABITED: ControlFlow = Continue(()); @@ -62,63 +64,73 @@ const BREAK_VISIBLY_UNINHABITED: ControlFlow = Break(Visibly #[derive(PartialEq, Eq)] struct VisiblyUninhabited; -impl TypeVisitor for UninhabitedFrom<'_> { - type BreakTy = VisiblyUninhabited; +impl<'db> TypeVisitor> for UninhabitedFrom<'_, 'db> { + type Result = ControlFlow; - fn as_dyn(&mut self) -> &mut dyn TypeVisitor { - self - } - - fn visit_ty( - &mut self, - ty: &Ty, - outer_binder: DebruijnIndex, - ) -> ControlFlow { - if self.recursive_ty.contains(ty) || self.max_depth == 0 { + fn visit_ty(&mut self, mut ty: Ty<'db>) -> ControlFlow { + if self.recursive_ty.contains(&ty) || self.max_depth == 0 { // rustc considers recursive types always inhabited. I think it is valid to consider // recursive types as always uninhabited, but we should do what rustc is doing. return CONTINUE_OPAQUELY_INHABITED; } - self.recursive_ty.insert(ty.clone()); + self.recursive_ty.insert(ty); self.max_depth -= 1; - let interner = DbInterner::new_with(self.db, None, None); - let r = match ty.kind(Interner) { - TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst), + + if matches!(ty.kind(), TyKind::Alias(..)) { + let mut ocx = ObligationCtxt::new(self.infcx); + match ocx.structurally_normalize_ty(&ObligationCause::dummy(), self.env.env, ty) { + Ok(it) => ty = it, + Err(_) => return CONTINUE_OPAQUELY_INHABITED, + } + } + + let r = match ty.kind() { + TyKind::Adt(adt, subst) => self.visit_adt(adt.def_id().0, subst), TyKind::Never => BREAK_VISIBLY_UNINHABITED, - TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder), - TyKind::Array(item_ty, len) => { - match try_const_usize(self.db, len.to_nextsolver(interner)) { - Some(0) | None => CONTINUE_OPAQUELY_INHABITED, - Some(1..) => item_ty.super_visit_with(self, outer_binder), - } - } - TyKind::Alias(AliasTy::Projection(projection)) => { - // FIXME: I think this currently isn't used for monomorphized bodies, so there is no need to handle - // `TyKind::AssociatedType`, but perhaps in the future it will. - let normalized = self.db.normalize_projection(projection.clone(), self.env.clone()); - self.visit_ty(&normalized, outer_binder) - } + TyKind::Tuple(..) => ty.super_visit_with(self), + TyKind::Array(item_ty, len) => match try_const_usize(self.infcx.interner.db, len) { + Some(0) | None => CONTINUE_OPAQUELY_INHABITED, + Some(1..) => item_ty.super_visit_with(self), + }, _ => CONTINUE_OPAQUELY_INHABITED, }; - self.recursive_ty.remove(ty); + self.recursive_ty.remove(&ty); self.max_depth += 1; r } - - fn interner(&self) -> Interner { - Interner - } } -impl UninhabitedFrom<'_> { - fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow { +impl<'a, 'db> UninhabitedFrom<'a, 'db> { + fn new( + infcx: &'a InferCtxt<'db>, + target_mod: ModuleId, + env: Arc>, + ) -> Self { + Self { target_mod, recursive_ty: FxHashSet::default(), max_depth: 500, infcx, env } + } + + #[inline] + fn interner(&self) -> DbInterner<'db> { + self.infcx.interner + } + + #[inline] + fn db(&self) -> &'db dyn HirDatabase { + self.interner().db + } + + fn visit_adt( + &mut self, + adt: AdtId, + subst: GenericArgs<'db>, + ) -> ControlFlow { // An ADT is uninhabited iff all its variants uninhabited. match adt { // rustc: For now, `union`s are never considered uninhabited. AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED, AdtId::StructId(s) => self.visit_variant(s.into(), subst), AdtId::EnumId(e) => { - let enum_data = e.enum_variants(self.db); + let enum_data = e.enum_variants(self.db()); for &(variant, _, _) in enum_data.variants.iter() { let variant_inhabitedness = self.visit_variant(variant.into(), subst); @@ -135,17 +147,17 @@ impl UninhabitedFrom<'_> { fn visit_variant( &mut self, variant: VariantId, - subst: &Substitution, + subst: GenericArgs<'db>, ) -> ControlFlow { - let variant_data = variant.fields(self.db); + let variant_data = variant.fields(self.db()); let fields = variant_data.fields(); if fields.is_empty() { return CONTINUE_OPAQUELY_INHABITED; } let is_enum = matches!(variant, VariantId::EnumVariantId(..)); - let field_tys = self.db.field_types(variant); - let field_vis = if is_enum { None } else { Some(self.db.field_visibilities(variant)) }; + let field_tys = self.db().field_types_ns(variant); + let field_vis = if is_enum { None } else { Some(self.db().field_visibilities(variant)) }; for (fid, _) in fields.iter() { self.visit_field(field_vis.as_ref().map(|it| it[fid]), &field_tys[fid], subst)?; @@ -156,12 +168,12 @@ impl UninhabitedFrom<'_> { fn visit_field( &mut self, vis: Option, - ty: &Binders, - subst: &Substitution, + ty: &EarlyBinder<'db, Ty<'db>>, + subst: GenericArgs<'db>, ) -> ControlFlow { - if vis.is_none_or(|it| it.is_visible_from(self.db, self.target_mod)) { - let ty = ty.clone().substitute(Interner, subst); - ty.visit_with(self, DebruijnIndex::INNERMOST) + if vis.is_none_or(|it| it.is_visible_from(self.db(), self.target_mod)) { + let ty = ty.instantiate(self.interner(), subst); + ty.visit_with(self) } else { CONTINUE_OPAQUELY_INHABITED } 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 92f9cd42615e..1439c43e99e8 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 @@ -43,7 +43,6 @@ use crate::{ next_solver::{ Const, DbInterner, ParamConst, Region, TyKind, TypingMode, UnevaluatedConst, infer::{DbInternerInferExt, InferCtxt}, - mapping::NextSolverToChalk, }, traits::FnTrait, }; @@ -303,6 +302,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let resolver = owner.resolver(db); let env = db.trait_environment_for_body(owner); let interner = DbInterner::new_with(db, Some(env.krate), env.block); + // FIXME(next-solver): Is `non_body_analysis()` correct here? Don't we want to reveal opaque types defined by this body? let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); MirLowerCtx { @@ -1766,8 +1766,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn is_uninhabited(&self, expr_id: ExprId) -> bool { is_ty_uninhabited_from( - self.db, - &self.infer[expr_id].to_chalk(self.interner()), + &self.infcx, + self.infer[expr_id], self.owner.module(self.db), self.env.clone(), ) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index 789be3b731b1..8525d4bc96e6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -1,8 +1,8 @@ //! Definition of `SolverDefId` use hir_def::{ - AdtId, CallableDefId, ConstId, EnumId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, - ImplId, StaticId, StructId, TraitId, TypeAliasId, UnionId, + AdtId, CallableDefId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, + GeneralConstId, GenericDefId, ImplId, StaticId, StructId, TraitId, TypeAliasId, UnionId, }; use rustc_type_ir::inherent; use stdx::impl_from; @@ -29,6 +29,8 @@ pub enum SolverDefId { InternedClosureId(InternedClosureId), InternedCoroutineId(InternedCoroutineId), InternedOpaqueTyId(InternedOpaqueTyId), + EnumVariantId(EnumVariantId), + // FIXME(next-solver): Do we need the separation of `Ctor`? It duplicates some variants. Ctor(Ctor), } @@ -73,6 +75,16 @@ impl std::fmt::Debug for SolverDefId { SolverDefId::InternedOpaqueTyId(id) => { f.debug_tuple("InternedOpaqueTyId").field(&id).finish() } + SolverDefId::EnumVariantId(id) => { + let parent_enum = id.loc(db).parent; + f.debug_tuple("EnumVariantId") + .field(&format_args!( + "\"{}::{}\"", + db.enum_signature(parent_enum).name.as_str(), + parent_enum.enum_variants(db).variant_name_by_id(id).unwrap().as_str() + )) + .finish() + } SolverDefId::Ctor(Ctor::Struct(id)) => { f.debug_tuple("Ctor").field(&db.struct_signature(id).name.as_str()).finish() } @@ -101,6 +113,7 @@ impl_from!( InternedClosureId, InternedCoroutineId, InternedOpaqueTyId, + EnumVariantId, Ctor for SolverDefId ); @@ -129,6 +142,18 @@ impl From for SolverDefId { } } +impl From for SolverDefId { + #[inline] + fn from(value: DefWithBodyId) -> Self { + match value { + DefWithBodyId::FunctionId(id) => id.into(), + DefWithBodyId::StaticId(id) => id.into(), + DefWithBodyId::ConstId(id) => id.into(), + DefWithBodyId::VariantId(id) => id.into(), + } + } +} + impl TryFrom for GenericDefId { type Error = SolverDefId; @@ -141,10 +166,11 @@ impl TryFrom for GenericDefId { SolverDefId::StaticId(static_id) => GenericDefId::StaticId(static_id), SolverDefId::TraitId(trait_id) => GenericDefId::TraitId(trait_id), SolverDefId::TypeAliasId(type_alias_id) => GenericDefId::TypeAliasId(type_alias_id), - SolverDefId::InternedClosureId(_) => return Err(value), - SolverDefId::InternedCoroutineId(_) => return Err(value), - SolverDefId::InternedOpaqueTyId(_) => return Err(value), - SolverDefId::Ctor(_) => return Err(value), + SolverDefId::InternedClosureId(_) + | SolverDefId::InternedCoroutineId(_) + | SolverDefId::InternedOpaqueTyId(_) + | SolverDefId::EnumVariantId(_) + | SolverDefId::Ctor(_) => return Err(value), }) } } 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 3fd8e7b39dd7..cfa8b5b8a7f7 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 @@ -1211,6 +1211,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { | SolverDefId::AdtId(_) | SolverDefId::TraitId(_) | SolverDefId::ImplId(_) + | SolverDefId::EnumVariantId(..) | SolverDefId::Ctor(..) | SolverDefId::InternedOpaqueTyId(..) => panic!(), }; @@ -1969,8 +1970,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, defining_anchor: Self::LocalDefId, ) -> Self::LocalDefIds { - // FIXME(next-solver) - unimplemented!() + Default::default() } type Probe = rustc_type_ir::solve::inspect::Probe>; From 5736f47b032a2d4021ee3b1084689d4e084d0a4e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 12 Oct 2025 12:25:14 +0300 Subject: [PATCH 013/170] Migrate hir-ty body validation diagnostics to the next solver --- .../crates/hir-ty/src/chalk_ext.rs | 59 +---- .../crates/hir-ty/src/diagnostics/expr.rs | 154 +++++++------ .../hir-ty/src/diagnostics/match_check.rs | 111 +++++---- .../diagnostics/match_check/pat_analysis.rs | 216 +++++++++--------- .../rust-analyzer/crates/hir-ty/src/lib.rs | 2 +- 5 files changed, 247 insertions(+), 295 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index e9960374c6f5..ea3ed1589d75 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -1,32 +1,22 @@ //! Various extensions traits for Chalk types. -use chalk_ir::Mutability; use hir_def::{FunctionId, ItemContainerId, Lookup, TraitId}; use crate::{ - AdtId, Binders, CallableDefId, CallableSig, DynTy, Interner, Lifetime, ProjectionTy, - Substitution, ToChalk, TraitRef, Ty, TyKind, TypeFlags, WhereClause, db::HirDatabase, - from_assoc_type_id, from_chalk_trait_id, generics::generics, to_chalk_trait_id, - utils::ClosureSubst, + Binders, CallableDefId, CallableSig, DynTy, Interner, ProjectionTy, Substitution, ToChalk, + TraitRef, Ty, TyKind, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, + generics::generics, to_chalk_trait_id, utils::ClosureSubst, }; pub(crate) trait TyExt { fn is_unit(&self) -> bool; fn is_unknown(&self) -> bool; - fn contains_unknown(&self) -> bool; - fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; fn as_tuple(&self) -> Option<&Substitution>; fn as_fn_def(&self, db: &dyn HirDatabase) -> Option; - fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>; fn callable_def(&self, db: &dyn HirDatabase) -> Option; fn callable_sig(&self, db: &dyn HirDatabase) -> Option; - - fn strip_references(&self) -> &Ty; - - /// If this is a `dyn Trait`, returns that trait. - fn dyn_trait(&self) -> Option; } impl TyExt for Ty { @@ -38,17 +28,6 @@ impl TyExt for Ty { matches!(self.kind(Interner), TyKind::Error) } - fn contains_unknown(&self) -> bool { - self.data(Interner).flags.contains(TypeFlags::HAS_ERROR) - } - - fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> { - match self.kind(Interner) { - TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)), - _ => None, - } - } - fn as_tuple(&self) -> Option<&Substitution> { match self.kind(Interner) { TyKind::Tuple(_, substs) => Some(substs), @@ -63,13 +42,6 @@ impl TyExt for Ty { } } - fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> { - match self.kind(Interner) { - TyKind::Ref(mutability, lifetime, ty) => Some((ty, lifetime.clone(), *mutability)), - _ => None, - } - } - fn callable_def(&self, db: &dyn HirDatabase) -> Option { match self.kind(Interner) { &TyKind::FnDef(def, ..) => Some(ToChalk::from_chalk(db, def)), @@ -85,31 +57,6 @@ impl TyExt for Ty { _ => None, } } - - fn dyn_trait(&self) -> Option { - let trait_ref = match self.kind(Interner) { - // The principal trait bound should be the first element of the bounds. This is an - // invariant ensured by `TyLoweringContext::lower_dyn_trait()`. - // FIXME: dyn types may not have principal trait and we don't want to return auto trait - // here. - TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().first().and_then(|b| { - match b.skip_binders() { - WhereClause::Implemented(trait_ref) => Some(trait_ref), - _ => None, - } - }), - _ => None, - }?; - Some(from_chalk_trait_id(trait_ref.trait_id)) - } - - fn strip_references(&self) -> &Ty { - let mut t: &Ty = self; - while let TyKind::Ref(_mutability, _lifetime, ty) = t.kind(Interner) { - t = ty; - } - t - } } pub trait ProjectionTyExt { 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 7b6fb994ecaf..0eca0c09d690 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,6 +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 syntax::{ AstNode, ast::{self, UnaryOp}, @@ -23,16 +24,18 @@ use tracing::debug; use triomphe::Arc; use typed_arena::Arena; -use crate::next_solver::DbInterner; -use crate::next_solver::mapping::NextSolverToChalk; use crate::{ - Adjust, InferenceResult, Interner, TraitEnvironment, Ty, TyExt, TyKind, + Adjust, InferenceResult, TraitEnvironment, db::HirDatabase, diagnostics::match_check::{ self, pat_analysis::{self, DeconstructedPat, MatchCheckCtx, WitnessPat}, }, display::{DisplayTarget, HirDisplay}, + next_solver::{ + DbInterner, Ty, TyKind, TypingMode, + infer::{DbInternerInferExt, InferCtxt}, + }, }; pub(crate) use hir_def::{ @@ -77,6 +80,8 @@ impl BodyValidationDiagnostic { let body = db.body(owner); let env = db.trait_environment_for_body(owner); let interner = DbInterner::new_with(db, Some(env.krate), env.block); + let infcx = + interner.infer_ctxt().build(TypingMode::typeck_for_body(interner, owner.into())); let mut validator = ExprValidator { owner, body, @@ -84,9 +89,9 @@ impl BodyValidationDiagnostic { diagnostics: Vec::new(), validate_lints, env, - interner, + infcx, }; - validator.validate_body(db); + validator.validate_body(); validator.diagnostics } } @@ -98,11 +103,17 @@ struct ExprValidator<'db> { env: Arc>, diagnostics: Vec, validate_lints: bool, - interner: DbInterner<'db>, + infcx: InferCtxt<'db>, } impl<'db> ExprValidator<'db> { - fn validate_body(&mut self, db: &'db dyn HirDatabase) { + #[inline] + fn db(&self) -> &'db dyn HirDatabase { + self.infcx.interner.db + } + + fn validate_body(&mut self) { + let db = self.db(); let mut filter_map_next_checker = None; // we'll pass &mut self while iterating over body.exprs, so they need to be disjoint let body = Arc::clone(&self.body); @@ -124,19 +135,19 @@ impl<'db> ExprValidator<'db> { match expr { Expr::Match { expr, arms } => { - self.validate_match(id, *expr, arms, db); + self.validate_match(id, *expr, arms); } Expr::Call { .. } | Expr::MethodCall { .. } => { - self.validate_call(db, id, expr, &mut filter_map_next_checker); + self.validate_call(id, expr, &mut filter_map_next_checker); } Expr::Closure { body: body_expr, .. } => { self.check_for_trailing_return(*body_expr, &body); } Expr::If { .. } => { - self.check_for_unnecessary_else(id, expr, db); + self.check_for_unnecessary_else(id, expr); } Expr::Block { .. } | Expr::Async { .. } | Expr::Unsafe { .. } => { - self.validate_block(db, expr); + self.validate_block(expr); } _ => {} } @@ -157,10 +168,9 @@ impl<'db> ExprValidator<'db> { fn validate_call( &mut self, - db: &dyn HirDatabase, call_id: ExprId, expr: &Expr, - filter_map_next_checker: &mut Option, + filter_map_next_checker: &mut Option>, ) { if !self.validate_lints { return; @@ -176,8 +186,9 @@ impl<'db> ExprValidator<'db> { None => return, }; - let checker = filter_map_next_checker - .get_or_insert_with(|| FilterMapNextChecker::new(&self.owner.resolver(db), db)); + let checker = filter_map_next_checker.get_or_insert_with(|| { + FilterMapNextChecker::new(&self.owner.resolver(self.db()), self.db()) + }); if checker.check(call_id, receiver, &callee).is_some() { self.diagnostics.push(BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { @@ -186,27 +197,20 @@ impl<'db> ExprValidator<'db> { } if let Some(receiver_ty) = self.infer.type_of_expr_with_adjust(*receiver) { - checker.prev_receiver_ty = Some(receiver_ty.to_chalk(self.interner)); + checker.prev_receiver_ty = Some(receiver_ty); } } } - fn validate_match( - &mut self, - match_expr: ExprId, - scrutinee_expr: ExprId, - arms: &[MatchArm], - db: &dyn HirDatabase, - ) { + fn validate_match(&mut self, match_expr: ExprId, scrutinee_expr: ExprId, arms: &[MatchArm]) { let Some(scrut_ty) = self.infer.type_of_expr_with_adjust(scrutinee_expr) else { return; }; - let scrut_ty = scrut_ty.to_chalk(self.interner); - if scrut_ty.contains_unknown() { + if scrut_ty.references_non_lt_error() { return; } - let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db, self.env.clone()); + let cx = MatchCheckCtx::new(self.owner.module(self.db()), &self.infcx, self.env.clone()); let pattern_arena = Arena::new(); let mut m_arms = Vec::with_capacity(arms.len()); @@ -217,8 +221,7 @@ impl<'db> ExprValidator<'db> { let Some(pat_ty) = self.infer.type_of_pat_with_adjust(arm.pat) else { return; }; - let pat_ty = pat_ty.to_chalk(self.interner); - if pat_ty.contains_unknown() { + if pat_ty.references_non_lt_error() { return; } @@ -235,14 +238,14 @@ impl<'db> ExprValidator<'db> { if (pat_ty == scrut_ty || scrut_ty .as_reference() - .map(|(match_expr_ty, ..)| *match_expr_ty == pat_ty) + .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty) .unwrap_or(false)) && types_of_subpatterns_do_match(arm.pat, &self.body, &self.infer) { // If we had a NotUsefulMatchArm diagnostic, we could // check the usefulness of each pattern as we added it // to the matrix here. - let pat = self.lower_pattern(&cx, arm.pat, db, &mut has_lowering_errors); + let pat = self.lower_pattern(&cx, arm.pat, &mut has_lowering_errors); let m_arm = pat_analysis::MatchArm { pat: pattern_arena.alloc(pat), has_guard: arm.guard.is_some(), @@ -258,15 +261,12 @@ impl<'db> ExprValidator<'db> { return; } - let known_valid_scrutinee = Some(self.is_known_valid_scrutinee(scrutinee_expr, db)); - let report = match cx.compute_match_usefulness( - m_arms.as_slice(), - scrut_ty.clone(), - known_valid_scrutinee, - ) { - Ok(report) => report, - Err(()) => return, - }; + let known_valid_scrutinee = Some(self.is_known_valid_scrutinee(scrutinee_expr)); + let report = + match cx.compute_match_usefulness(m_arms.as_slice(), scrut_ty, known_valid_scrutinee) { + Ok(report) => report, + Err(()) => return, + }; // FIXME Report unreachable arms // https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200 @@ -277,10 +277,10 @@ impl<'db> ExprValidator<'db> { match_expr, uncovered_patterns: missing_match_arms( &cx, - &scrut_ty, + scrut_ty, witnesses, m_arms.is_empty(), - self.owner.krate(db), + self.owner.krate(self.db()), ), }); } @@ -291,7 +291,9 @@ impl<'db> ExprValidator<'db> { // While the above function in rustc uses thir exprs, r-a doesn't have them. // So, the logic here is getting same result as "hir lowering + match with lowered thir" // with "hir only" - fn is_known_valid_scrutinee(&self, scrutinee_expr: ExprId, db: &dyn HirDatabase) -> bool { + fn is_known_valid_scrutinee(&self, scrutinee_expr: ExprId) -> bool { + let db = self.db(); + if self .infer .expr_adjustments @@ -311,20 +313,18 @@ impl<'db> ExprValidator<'db> { ); value_or_partial.is_none_or(|v| !matches!(v, ValueNs::StaticId(_))) } - Expr::Field { expr, .. } => { - match self.infer.type_of_expr[*expr].to_chalk(self.interner).kind(Interner) { - TyKind::Adt(adt, ..) if matches!(adt.0, AdtId::UnionId(_)) => false, - _ => self.is_known_valid_scrutinee(*expr, db), - } - } - Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base, db), - Expr::Cast { expr, .. } => self.is_known_valid_scrutinee(*expr, db), + Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind() { + TyKind::Adt(adt, ..) if matches!(adt.def_id().0, AdtId::UnionId(_)) => false, + _ => self.is_known_valid_scrutinee(*expr), + }, + Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base), + Expr::Cast { expr, .. } => self.is_known_valid_scrutinee(*expr), Expr::Missing => false, _ => true, } } - fn validate_block(&mut self, db: &dyn HirDatabase, expr: &Expr) { + fn validate_block(&mut self, expr: &Expr) { let (Expr::Block { statements, .. } | Expr::Async { statements, .. } | Expr::Unsafe { statements, .. }) = expr @@ -332,7 +332,7 @@ impl<'db> ExprValidator<'db> { return; }; let pattern_arena = Arena::new(); - let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db, self.env.clone()); + let cx = MatchCheckCtx::new(self.owner.module(self.db()), &self.infcx, self.env.clone()); for stmt in &**statements { let &Statement::Let { pat, initializer, else_branch: None, .. } = stmt else { continue; @@ -342,13 +342,12 @@ impl<'db> ExprValidator<'db> { } let Some(initializer) = initializer else { continue }; let Some(ty) = self.infer.type_of_expr_with_adjust(initializer) else { continue }; - let ty = ty.to_chalk(self.interner); - if ty.contains_unknown() { + if ty.references_non_lt_error() { continue; } let mut have_errors = false; - let deconstructed_pat = self.lower_pattern(&cx, pat, db, &mut have_errors); + let deconstructed_pat = self.lower_pattern(&cx, pat, &mut have_errors); // optimization, wildcard trivially hold if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) { @@ -360,7 +359,7 @@ impl<'db> ExprValidator<'db> { has_guard: false, arm_data: (), }; - let report = match cx.compute_match_usefulness(&[match_arm], ty.clone(), None) { + let report = match cx.compute_match_usefulness(&[match_arm], ty, None) { Ok(v) => v, Err(e) => { debug!(?e, "match usefulness error"); @@ -373,24 +372,23 @@ impl<'db> ExprValidator<'db> { pat, uncovered_patterns: missing_match_arms( &cx, - &ty, + ty, witnesses, false, - self.owner.krate(db), + self.owner.krate(self.db()), ), }); } } } - fn lower_pattern<'p>( + fn lower_pattern<'a>( &self, - cx: &MatchCheckCtx<'p>, + cx: &MatchCheckCtx<'a, 'db>, pat: PatId, - db: &dyn HirDatabase, have_errors: &mut bool, - ) -> DeconstructedPat<'p> { - let mut patcx = match_check::PatCtxt::new(db, &self.infer, &self.body); + ) -> DeconstructedPat<'a, 'db> { + let mut patcx = match_check::PatCtxt::new(self.db(), &self.infer, &self.body); let pattern = patcx.lower_pattern(pat); let pattern = cx.lower_pat(&pattern); if !patcx.errors.is_empty() { @@ -434,7 +432,7 @@ impl<'db> ExprValidator<'db> { } } - fn check_for_unnecessary_else(&mut self, id: ExprId, expr: &Expr, db: &dyn HirDatabase) { + fn check_for_unnecessary_else(&mut self, id: ExprId, expr: &Expr) { if !self.validate_lints { return; } @@ -453,11 +451,11 @@ impl<'db> ExprValidator<'db> { && last_then_expr_ty.is_never() { // Only look at sources if the then branch diverges and we have an else branch. - let source_map = db.body_with_source_map(self.owner).1; + let source_map = self.db().body_with_source_map(self.owner).1; let Ok(source_ptr) = source_map.expr_syntax(id) else { return; }; - let root = source_ptr.file_syntax(db); + let root = source_ptr.file_syntax(self.db()); let either::Left(ast::Expr::IfExpr(if_expr)) = source_ptr.value.to_node(&root) else { return; @@ -491,15 +489,15 @@ impl<'db> ExprValidator<'db> { } } -struct FilterMapNextChecker { +struct FilterMapNextChecker<'db> { filter_map_function_id: Option, next_function_id: Option, prev_filter_map_expr_id: Option, - prev_receiver_ty: Option>, + prev_receiver_ty: Option>, } -impl FilterMapNextChecker { - fn new(resolver: &hir_def::resolver::Resolver<'_>, db: &dyn HirDatabase) -> Self { +impl<'db> FilterMapNextChecker<'db> { + fn new(resolver: &hir_def::resolver::Resolver<'db>, db: &'db dyn HirDatabase) -> Self { // Find and store the FunctionIds for Iterator::filter_map and Iterator::next let (next_function_id, filter_map_function_id) = match LangItem::IteratorNext .resolve_function(db, resolver.krate()) @@ -639,15 +637,19 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul !has_type_mismatches } -fn missing_match_arms<'p>( - cx: &MatchCheckCtx<'p>, - scrut_ty: &Ty, - witnesses: Vec>, +fn missing_match_arms<'a, 'db>( + cx: &MatchCheckCtx<'a, 'db>, + scrut_ty: Ty<'a>, + witnesses: Vec>, arms_is_empty: bool, krate: Crate, ) -> String { - struct DisplayWitness<'a, 'p>(&'a WitnessPat<'p>, &'a MatchCheckCtx<'p>, DisplayTarget); - impl fmt::Display for DisplayWitness<'_, '_> { + struct DisplayWitness<'a, 'b, 'db>( + &'a WitnessPat<'b, 'db>, + &'a MatchCheckCtx<'b, 'db>, + DisplayTarget, + ); + impl fmt::Display for DisplayWitness<'_, '_, '_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let DisplayWitness(witness, cx, display_target) = *self; let pat = cx.hoist_witness_pat(witness); 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 af541ffa342e..af6795e6018a 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 @@ -9,7 +9,6 @@ mod pat_util; pub(crate) mod pat_analysis; -use chalk_ir::Mutability; use hir_def::{ AdtId, EnumVariantId, LocalFieldId, Lookup, VariantId, expr_store::{Body, path::Path}, @@ -17,16 +16,16 @@ use hir_def::{ item_tree::FieldsShape, }; use hir_expand::name::Name; +use rustc_type_ir::inherent::{IntoKind, SliceLike}; use span::Edition; use stdx::{always, never}; -use crate::next_solver::DbInterner; -use crate::next_solver::mapping::NextSolverToChalk; use crate::{ - InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, + InferenceResult, db::HirDatabase, display::{HirDisplay, HirDisplayError, HirFormatter}, infer::BindingMode, + next_solver::{GenericArgs, Mutability, Ty, TyKind}, }; use self::pat_util::EnumerateAndAdjustIterator; @@ -41,46 +40,46 @@ pub(crate) enum PatternError { } #[derive(Clone, Debug, PartialEq)] -pub(crate) struct FieldPat { +pub(crate) struct FieldPat<'db> { pub(crate) field: LocalFieldId, - pub(crate) pattern: Pat, + pub(crate) pattern: Pat<'db>, } #[derive(Clone, Debug, PartialEq)] -pub(crate) struct Pat { - pub(crate) ty: Ty, - pub(crate) kind: Box, +pub(crate) struct Pat<'db> { + pub(crate) ty: Ty<'db>, + pub(crate) kind: Box>, } /// Close relative to `rustc_mir_build::thir::pattern::PatKind` #[derive(Clone, Debug, PartialEq)] -pub(crate) enum PatKind { +pub(crate) enum PatKind<'db> { Wild, Never, /// `x`, `ref x`, `x @ P`, etc. Binding { name: Name, - subpattern: Option, + subpattern: Option>, }, /// `Foo(...)` or `Foo{...}` or `Foo`, where `Foo` is a variant name from an ADT with /// multiple variants. Variant { - substs: Substitution, + substs: GenericArgs<'db>, enum_variant: EnumVariantId, - subpatterns: Vec, + subpatterns: Vec>, }, /// `(...)`, `Foo(...)`, `Foo{...}`, or `Foo`, where `Foo` is a variant name from an ADT with /// a single variant. Leaf { - subpatterns: Vec, + subpatterns: Vec>, }, /// `&P`, `&mut P`, etc. Deref { - subpattern: Pat, + subpattern: Pat<'db>, }, // FIXME: for now, only bool literals are implemented @@ -91,28 +90,27 @@ pub(crate) enum PatKind { /// An or-pattern, e.g. `p | q`. /// Invariant: `pats.len() >= 2`. Or { - pats: Vec, + pats: Vec>, }, } -pub(crate) struct PatCtxt<'db> { +pub(crate) struct PatCtxt<'a, 'db> { db: &'db dyn HirDatabase, - infer: &'db InferenceResult<'db>, - body: &'db Body, + infer: &'a InferenceResult<'db>, + body: &'a Body, pub(crate) errors: Vec, - interner: DbInterner<'db>, } -impl<'a> PatCtxt<'a> { +impl<'a, 'db> PatCtxt<'a, 'db> { pub(crate) fn new( - db: &'a dyn HirDatabase, - infer: &'a InferenceResult<'a>, + db: &'db dyn HirDatabase, + infer: &'a InferenceResult<'db>, body: &'a Body, ) -> Self { - Self { db, infer, body, errors: Vec::new(), interner: DbInterner::new_with(db, None, None) } + Self { db, infer, body, errors: Vec::new() } } - pub(crate) fn lower_pattern(&mut self, pat: PatId) -> Pat { + pub(crate) fn lower_pattern(&mut self, pat: PatId) -> Pat<'db> { // XXX(iDawer): Collecting pattern adjustments feels imprecise to me. // When lowering of & and box patterns are implemented this should be tested // in a manner of `match_ergonomics_issue_9095` test. @@ -121,15 +119,12 @@ impl<'a> PatCtxt<'a> { let unadjusted_pat = self.lower_pattern_unadjusted(pat); self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold( unadjusted_pat, - |subpattern, ref_ty| Pat { - ty: ref_ty.to_chalk(self.interner).clone(), - kind: Box::new(PatKind::Deref { subpattern }), - }, + |subpattern, ref_ty| Pat { ty: *ref_ty, kind: Box::new(PatKind::Deref { subpattern }) }, ) } - fn lower_pattern_unadjusted(&mut self, pat: PatId) -> Pat { - let mut ty = self.infer[pat].to_chalk(self.interner); + fn lower_pattern_unadjusted(&mut self, pat: PatId) -> Pat<'db> { + let mut ty = self.infer[pat]; let variant = self.infer.variant_resolution_for_pat(pat); let kind = match self.body[pat] { @@ -142,8 +137,8 @@ impl<'a> PatCtxt<'a> { } hir_def::hir::Pat::Tuple { ref args, ellipsis } => { - let arity = match *ty.kind(Interner) { - TyKind::Tuple(arity, _) => arity, + let arity = match ty.kind() { + TyKind::Tuple(tys) => tys.len(), _ => { never!("unexpected type for tuple pattern: {:?}", ty); self.errors.push(PatternError::UnexpectedType); @@ -156,10 +151,10 @@ impl<'a> PatCtxt<'a> { hir_def::hir::Pat::Bind { id, subpat, .. } => { let bm = self.infer.binding_modes[pat]; - ty = self.infer[id].to_chalk(self.interner); + ty = self.infer[id]; let name = &self.body[id].name; - match (bm, ty.kind(Interner)) { - (BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty.clone(), + match (bm, ty.kind()) { + (BindingMode::Ref(_), TyKind::Ref(_, rty, _)) => ty = rty, (BindingMode::Ref(_), _) => { never!( "`ref {}` has wrong type {:?}", @@ -167,7 +162,7 @@ impl<'a> PatCtxt<'a> { ty ); self.errors.push(PatternError::UnexpectedType); - return Pat { ty: ty.clone(), kind: PatKind::Wild.into() }; + return Pat { ty, kind: PatKind::Wild.into() }; } _ => (), } @@ -177,7 +172,7 @@ impl<'a> PatCtxt<'a> { hir_def::hir::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => { let expected_len = variant.unwrap().fields(self.db).fields().len(); let subpatterns = self.lower_tuple_subpats(args, expected_len, ellipsis); - self.lower_variant_or_leaf(pat, &ty, subpatterns) + self.lower_variant_or_leaf(pat, ty, subpatterns) } hir_def::hir::Pat::Record { ref args, .. } if variant.is_some() => { @@ -193,7 +188,7 @@ impl<'a> PatCtxt<'a> { }) .collect(); match subpatterns { - Some(subpatterns) => self.lower_variant_or_leaf(pat, &ty, subpatterns), + Some(subpatterns) => self.lower_variant_or_leaf(pat, ty, subpatterns), None => { self.errors.push(PatternError::MissingField); PatKind::Wild @@ -213,7 +208,7 @@ impl<'a> PatCtxt<'a> { } }; - Pat { ty: ty.clone(), kind: Box::new(kind) } + Pat { ty, kind: Box::new(kind) } } fn lower_tuple_subpats( @@ -221,7 +216,7 @@ impl<'a> PatCtxt<'a> { pats: &[PatId], expected_len: usize, ellipsis: Option, - ) -> Vec { + ) -> Vec> { if pats.len() > expected_len { self.errors.push(PatternError::ExtraFields); return Vec::new(); @@ -236,28 +231,28 @@ impl<'a> PatCtxt<'a> { .collect() } - fn lower_patterns(&mut self, pats: &[PatId]) -> Vec { + fn lower_patterns(&mut self, pats: &[PatId]) -> Vec> { pats.iter().map(|&p| self.lower_pattern(p)).collect() } - fn lower_opt_pattern(&mut self, pat: Option) -> Option { + fn lower_opt_pattern(&mut self, pat: Option) -> Option> { pat.map(|p| self.lower_pattern(p)) } fn lower_variant_or_leaf( &mut self, pat: PatId, - ty: &Ty, - subpatterns: Vec, - ) -> PatKind { + ty: Ty<'db>, + subpatterns: Vec>, + ) -> PatKind<'db> { match self.infer.variant_resolution_for_pat(pat) { Some(variant_id) => { if let VariantId::EnumVariantId(enum_variant) = variant_id { - let substs = match ty.kind(Interner) { - TyKind::Adt(_, substs) => substs.clone(), + let substs = match ty.kind() { + TyKind::Adt(_, substs) => substs, kind => { always!( - matches!(kind, TyKind::FnDef(..) | TyKind::Error), + matches!(kind, TyKind::FnDef(..) | TyKind::Error(_)), "inappropriate type for def: {:?}", ty ); @@ -277,13 +272,13 @@ impl<'a> PatCtxt<'a> { } } - fn lower_path(&mut self, pat: PatId, _path: &Path) -> Pat { - let ty = self.infer[pat].to_chalk(self.interner); + fn lower_path(&mut self, pat: PatId, _path: &Path) -> Pat<'db> { + let ty = self.infer[pat]; - let pat_from_kind = |kind| Pat { ty: ty.clone(), kind: Box::new(kind) }; + let pat_from_kind = |kind| Pat { ty, kind: Box::new(kind) }; match self.infer.variant_resolution_for_pat(pat) { - Some(_) => pat_from_kind(self.lower_variant_or_leaf(pat, &ty, Vec::new())), + Some(_) => pat_from_kind(self.lower_variant_or_leaf(pat, ty, Vec::new())), None => { self.errors.push(PatternError::UnresolvedVariant); pat_from_kind(PatKind::Wild) @@ -291,7 +286,7 @@ impl<'a> PatCtxt<'a> { } } - fn lower_lit(&mut self, expr: hir_def::hir::ExprId) -> PatKind { + fn lower_lit(&mut self, expr: hir_def::hir::ExprId) -> PatKind<'db> { use hir_def::hir::{Expr, Literal::Bool}; match self.body[expr] { @@ -304,7 +299,7 @@ impl<'a> PatCtxt<'a> { } } -impl HirDisplay for Pat { +impl HirDisplay for Pat<'_> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match &*self.kind { PatKind::Wild => write!(f, "_"), @@ -402,7 +397,7 @@ impl HirDisplay for Pat { }) }); f.write_joined(subpats, ", ")?; - if let (TyKind::Tuple(..), 1) = (self.ty.kind(Interner), num_fields) { + if let (TyKind::Tuple(..), 1) = (self.ty.kind(), num_fields) { write!(f, ",")?; } write!(f, ")")?; @@ -411,8 +406,8 @@ impl HirDisplay for Pat { Ok(()) } PatKind::Deref { subpattern } => { - match self.ty.kind(Interner) { - &TyKind::Ref(mutbl, ..) => { + match self.ty.kind() { + TyKind::Ref(.., mutbl) => { write!(f, "&{}", if mutbl == Mutability::Mut { "mut " } else { "" })? } _ => never!("{:?} is a bad Deref pattern type", self.ty), 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 76f50c194835..f0efadeafcea 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 @@ -1,28 +1,26 @@ //! Interface with `rustc_pattern_analysis`. -use std::cell::LazyCell; -use std::fmt; +use std::{cell::LazyCell, fmt}; -use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId}; +use hir_def::{EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId}; use intern::sym; use rustc_pattern_analysis::{ IndexVec, PatCx, PrivateUninhabitedField, constructor::{Constructor, ConstructorSet, VariantVisibility}, usefulness::{PlaceValidity, UsefulnessReport, compute_match_usefulness}, }; +use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike}; use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; use crate::{ - AdtId, Interner, Scalar, TraitEnvironment, Ty, TyExt, TyKind, + TraitEnvironment, db::HirDatabase, - infer::normalize, inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from}, next_solver::{ - DbInterner, TypingMode, - infer::{DbInternerInferExt, InferCtxt}, - mapping::ChalkToNextSolver, + Ty, TyKind, + infer::{InferCtxt, traits::ObligationCause}, }, }; @@ -31,10 +29,12 @@ use super::{FieldPat, Pat, PatKind}; use Constructor::*; // Re-export r-a-specific versions of all these types. -pub(crate) type DeconstructedPat<'db> = - rustc_pattern_analysis::pat::DeconstructedPat>; -pub(crate) type MatchArm<'a, 'db> = rustc_pattern_analysis::MatchArm<'a, MatchCheckCtx<'db>>; -pub(crate) type WitnessPat<'db> = rustc_pattern_analysis::pat::WitnessPat>; +pub(crate) type DeconstructedPat<'a, 'db> = + rustc_pattern_analysis::pat::DeconstructedPat>; +pub(crate) type MatchArm<'a, 'b, 'db> = + rustc_pattern_analysis::MatchArm<'b, MatchCheckCtx<'a, 'db>>; +pub(crate) type WitnessPat<'a, 'db> = + rustc_pattern_analysis::pat::WitnessPat>; /// [Constructor] uses this in unimplemented variants. /// It allows porting match expressions from upstream algorithm without losing semantics. @@ -70,40 +70,37 @@ impl rustc_pattern_analysis::Idx for EnumVariantContiguousIndex { } #[derive(Clone)] -pub(crate) struct MatchCheckCtx<'db> { +pub(crate) struct MatchCheckCtx<'a, 'db> { module: ModuleId, - body: DefWithBodyId, pub(crate) db: &'db dyn HirDatabase, exhaustive_patterns: bool, env: Arc>, - infcx: InferCtxt<'db>, + infcx: &'a InferCtxt<'db>, } -impl<'db> MatchCheckCtx<'db> { +impl<'a, 'db> MatchCheckCtx<'a, 'db> { pub(crate) fn new( module: ModuleId, - body: DefWithBodyId, - db: &'db dyn HirDatabase, + infcx: &'a InferCtxt<'db>, env: Arc>, ) -> 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 interner = DbInterner::new_with(db, Some(env.krate), env.block); - let infcx = interner.infer_ctxt().build(TypingMode::typeck_for_body(interner, body.into())); - Self { module, body, db, exhaustive_patterns, env, infcx } + Self { module, db, exhaustive_patterns, env, infcx } } - pub(crate) fn compute_match_usefulness<'a>( + pub(crate) fn compute_match_usefulness<'b>( &self, - arms: &[MatchArm<'a, 'db>], - scrut_ty: Ty, + arms: &[MatchArm<'a, 'b, 'db>], + scrut_ty: Ty<'db>, known_valid_scrutinee: Option, - ) -> Result, ()> { - if scrut_ty.contains_unknown() { + ) -> Result, ()> { + if scrut_ty.references_non_lt_error() { return Err(()); } for arm in arms { - if arm.pat.ty().contains_unknown() { + if arm.pat.ty().references_non_lt_error() { return Err(()); } } @@ -114,13 +111,8 @@ impl<'db> MatchCheckCtx<'db> { compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit) } - fn is_uninhabited(&self, ty: &Ty) -> bool { - is_ty_uninhabited_from( - &self.infcx, - ty.to_nextsolver(self.infcx.interner), - self.module, - self.env.clone(), - ) + fn is_uninhabited(&self, ty: Ty<'db>) -> bool { + is_ty_uninhabited_from(self.infcx, ty, self.module, self.env.clone()) } /// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`. @@ -153,23 +145,27 @@ impl<'db> MatchCheckCtx<'db> { // This lists the fields of a variant along with their types. fn list_variant_fields( &self, - ty: &Ty, + ty: Ty<'db>, variant: VariantId, - ) -> impl Iterator { + ) -> impl Iterator)> { let (_, substs) = ty.as_adt().unwrap(); - let field_tys = self.db.field_types(variant); + let field_tys = self.db.field_types_ns(variant); let fields_len = variant.fields(self.db).fields().len() as u32; (0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).map(move |fid| { - let ty = field_tys[fid].clone().substitute(Interner, substs); - let ty = normalize(self.db, self.db.trait_environment_for_body(self.body), ty); + let ty = field_tys[fid].instantiate(self.infcx.interner, substs); + let ty = self + .infcx + .at(&ObligationCause::dummy(), self.env.env) + .deeply_normalize(ty) + .unwrap_or(ty); (fid, ty) }) } - pub(crate) fn lower_pat(&self, pat: &Pat) -> DeconstructedPat<'db> { - let singleton = |pat: DeconstructedPat<'db>| vec![pat.at_index(0)]; + pub(crate) fn lower_pat(&self, pat: &Pat<'db>) -> DeconstructedPat<'a, 'db> { + let singleton = |pat: DeconstructedPat<'a, 'db>| vec![pat.at_index(0)]; let ctor; let mut fields: Vec<_>; let arity; @@ -182,7 +178,7 @@ impl<'db> MatchCheckCtx<'db> { arity = 0; } PatKind::Deref { subpattern } => { - ctor = match pat.ty.kind(Interner) { + ctor = match pat.ty.kind() { TyKind::Ref(..) => Ref, _ => { never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty); @@ -200,12 +196,13 @@ impl<'db> MatchCheckCtx<'db> { self.lower_pat(&pat.pattern).at_index(idx as usize) }) .collect(); - match pat.ty.kind(Interner) { - TyKind::Tuple(_, substs) => { + match pat.ty.kind() { + TyKind::Tuple(substs) => { ctor = Struct; - arity = substs.len(Interner); + arity = substs.len(); } - &TyKind::Adt(AdtId(adt), _) => { + TyKind::Adt(adt_def, _) => { + let adt = adt_def.def_id().0; ctor = match pat.kind.as_ref() { PatKind::Leaf { .. } if matches!(adt, hir_def::AdtId::UnionId(_)) => { UnionField @@ -253,15 +250,15 @@ impl<'db> MatchCheckCtx<'db> { arity = pats.len(); } } - DeconstructedPat::new(ctor, fields, arity, pat.ty.clone(), ()) + DeconstructedPat::new(ctor, fields, arity, pat.ty, ()) } - pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'db>) -> Pat { + pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'a, 'db>) -> Pat<'db> { let mut subpatterns = pat.iter_fields().map(|p| self.hoist_witness_pat(p)); let kind = match pat.ctor() { &Bool(value) => PatKind::LiteralBool { value }, IntRange(_) => unimplemented!(), - Struct | Variant(_) | UnionField => match pat.ty().kind(Interner) { + Struct | Variant(_) | UnionField => match pat.ty().kind() { TyKind::Tuple(..) => PatKind::Leaf { subpatterns: subpatterns .zip(0u32..) @@ -272,15 +269,16 @@ impl<'db> MatchCheckCtx<'db> { .collect(), }, TyKind::Adt(adt, substs) => { - let variant = Self::variant_id_for_adt(self.db, pat.ctor(), adt.0).unwrap(); + let variant = + Self::variant_id_for_adt(self.db, pat.ctor(), adt.def_id().0).unwrap(); let subpatterns = self - .list_variant_fields(pat.ty(), variant) + .list_variant_fields(*pat.ty(), variant) .zip(subpatterns) .map(|((field, _ty), pattern)| FieldPat { field, pattern }) .collect(); if let VariantId::EnumVariantId(enum_variant) = variant { - PatKind::Variant { substs: substs.clone(), enum_variant, subpatterns } + PatKind::Variant { substs, enum_variant, subpatterns } } else { PatKind::Leaf { subpatterns } } @@ -306,13 +304,13 @@ impl<'db> MatchCheckCtx<'db> { PatKind::Wild } }; - Pat { ty: pat.ty().clone(), kind: Box::new(kind) } + Pat { ty: *pat.ty(), kind: Box::new(kind) } } } -impl PatCx for MatchCheckCtx<'_> { +impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> { type Error = (); - type Ty = Ty; + type Ty = Ty<'db>; type VariantIdx = EnumVariantContiguousIndex; type StrLit = Void; type ArmData = (); @@ -328,10 +326,11 @@ impl PatCx for MatchCheckCtx<'_> { ty: &Self::Ty, ) -> usize { match ctor { - Struct | Variant(_) | UnionField => match *ty.kind(Interner) { - TyKind::Tuple(arity, ..) => arity, - TyKind::Adt(AdtId(adt), ..) => { - let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap(); + Struct | Variant(_) | UnionField => match ty.kind() { + 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(); variant.fields(self.db).fields().len() } _ => { @@ -359,24 +358,24 @@ impl PatCx for MatchCheckCtx<'_> { ) -> impl ExactSizeIterator { let single = |ty| smallvec![(ty, PrivateUninhabitedField(false))]; let tys: SmallVec<[_; 2]> = match ctor { - Struct | Variant(_) | UnionField => match ty.kind(Interner) { - TyKind::Tuple(_, substs) => { - let tys = substs.iter(Interner).map(|ty| ty.assert_ty_ref(Interner)); - tys.cloned().map(|ty| (ty, PrivateUninhabitedField(false))).collect() + Struct | Variant(_) | UnionField => match ty.kind() { + TyKind::Tuple(substs) => { + substs.iter().map(|ty| (ty, PrivateUninhabitedField(false))).collect() } - TyKind::Ref(.., rty) => single(rty.clone()), - &TyKind::Adt(AdtId(adt), ..) => { + TyKind::Ref(_, rty, _) => single(rty), + TyKind::Adt(adt_def, ..) => { + let adt = adt_def.def_id().0; let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap(); let visibilities = LazyCell::new(|| self.db.field_visibilities(variant)); - self.list_variant_fields(ty, variant) + self.list_variant_fields(*ty, variant) .map(move |(fid, ty)| { let is_visible = || { matches!(adt, hir_def::AdtId::EnumId(..)) || visibilities[fid].is_visible_from(self.db, self.module) }; - let is_uninhabited = self.is_uninhabited(&ty); + let is_uninhabited = self.is_uninhabited(ty); let private_uninhabited = is_uninhabited && !is_visible(); (ty, PrivateUninhabitedField(private_uninhabited)) }) @@ -384,14 +383,14 @@ impl PatCx for MatchCheckCtx<'_> { } ty_kind => { never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind); - single(ty.clone()) + single(*ty) } }, - Ref => match ty.kind(Interner) { - TyKind::Ref(.., rty) => single(rty.clone()), + Ref => match ty.kind() { + TyKind::Ref(_, rty, _) => single(rty), ty_kind => { never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind); - single(ty.clone()) + single(*ty) } }, Slice(_) => unreachable!("Found a `Slice` constructor in match checking"), @@ -427,42 +426,51 @@ impl PatCx for MatchCheckCtx<'_> { // returned list of constructors. // Invariant: this is empty if and only if the type is uninhabited (as determined by // `cx.is_uninhabited()`). - Ok(match ty.kind(Interner) { - TyKind::Scalar(Scalar::Bool) => ConstructorSet::Bool, - TyKind::Scalar(Scalar::Char) => unhandled(), - TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(), + Ok(match ty.kind() { + TyKind::Bool => ConstructorSet::Bool, + TyKind::Char => unhandled(), + TyKind::Int(..) | TyKind::Uint(..) => unhandled(), TyKind::Array(..) | TyKind::Slice(..) => unhandled(), - &TyKind::Adt(AdtId(adt @ hir_def::AdtId::EnumId(enum_id)), ref subst) => { - let enum_data = enum_id.enum_variants(cx.db); - let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt); + TyKind::Adt(adt_def, subst) => { + let adt = adt_def.def_id().0; + match adt { + hir_def::AdtId::EnumId(enum_id) => { + let enum_data = enum_id.enum_variants(cx.db); + let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt); - if enum_data.variants.is_empty() && !is_declared_nonexhaustive { - ConstructorSet::NoConstructors - } else { - let mut variants = IndexVec::with_capacity(enum_data.variants.len()); - for &(variant, _, _) in enum_data.variants.iter() { - let is_uninhabited = is_enum_variant_uninhabited_from( - &cx.infcx, - variant, - subst.to_nextsolver(cx.infcx.interner), - cx.module, - self.env.clone(), - ); - let visibility = if is_uninhabited { - VariantVisibility::Empty + if enum_data.variants.is_empty() && !is_declared_nonexhaustive { + ConstructorSet::NoConstructors } else { - VariantVisibility::Visible - }; - variants.push(visibility); - } + let mut variants = IndexVec::with_capacity(enum_data.variants.len()); + for &(variant, _, _) in enum_data.variants.iter() { + let is_uninhabited = is_enum_variant_uninhabited_from( + cx.infcx, + variant, + subst, + cx.module, + self.env.clone(), + ); + let visibility = if is_uninhabited { + VariantVisibility::Empty + } else { + VariantVisibility::Visible + }; + variants.push(visibility); + } - ConstructorSet::Variants { variants, non_exhaustive: is_declared_nonexhaustive } + ConstructorSet::Variants { + variants, + non_exhaustive: is_declared_nonexhaustive, + } + } + } + hir_def::AdtId::UnionId(_) => ConstructorSet::Union, + hir_def::AdtId::StructId(_) => { + ConstructorSet::Struct { empty: cx.is_uninhabited(*ty) } + } } } - TyKind::Adt(AdtId(hir_def::AdtId::UnionId(_)), _) => ConstructorSet::Union, - TyKind::Adt(..) | TyKind::Tuple(..) => { - ConstructorSet::Struct { empty: cx.is_uninhabited(ty) } - } + TyKind::Tuple(..) => ConstructorSet::Struct { empty: cx.is_uninhabited(*ty) }, TyKind::Ref(..) => ConstructorSet::Ref, TyKind::Never => ConstructorSet::NoConstructors, // This type is one for which we cannot list constructors, like `str` or `f64`. @@ -505,14 +513,14 @@ impl PatCx for MatchCheckCtx<'_> { fn report_mixed_deref_pat_ctors( &self, - _deref_pat: &DeconstructedPat<'_>, - _normal_pat: &DeconstructedPat<'_>, + _deref_pat: &DeconstructedPat<'a, 'db>, + _normal_pat: &DeconstructedPat<'a, 'db>, ) { // FIXME(deref_patterns): This could report an error comparable to the one in rustc. } } -impl fmt::Debug for MatchCheckCtx<'_> { +impl fmt::Debug for MatchCheckCtx<'_, '_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("MatchCheckCtx").finish() } 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 734483a823e4..eb01ef104b61 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -124,7 +124,7 @@ pub use utils::{ }; pub use variance::Variance; -use chalk_ir::{AdtId, BoundVar, DebruijnIndex, Safety, Scalar}; +use chalk_ir::{BoundVar, DebruijnIndex, Safety, Scalar}; pub(crate) type ForeignDefId = chalk_ir::ForeignDefId; pub(crate) type AssocTypeId = chalk_ir::AssocTypeId; From 4b247ba0a56049c7987a8ec082442cc79cc0d58a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 12 Oct 2025 14:05:51 +0300 Subject: [PATCH 014/170] Migrate unsafe checker to the new solver --- .../crates/hir-ty/src/chalk_ext.rs | 24 +++--------------- .../hir-ty/src/diagnostics/unsafe_check.rs | 25 +++++++++---------- 2 files changed, 16 insertions(+), 33 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index ea3ed1589d75..6bab30b40cc1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -1,11 +1,11 @@ //! Various extensions traits for Chalk types. -use hir_def::{FunctionId, ItemContainerId, Lookup, TraitId}; +use hir_def::{ItemContainerId, Lookup, TraitId}; use crate::{ - Binders, CallableDefId, CallableSig, DynTy, Interner, ProjectionTy, Substitution, ToChalk, - TraitRef, Ty, TyKind, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, - generics::generics, to_chalk_trait_id, utils::ClosureSubst, + Binders, CallableSig, DynTy, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyKind, + db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, generics::generics, + to_chalk_trait_id, utils::ClosureSubst, }; pub(crate) trait TyExt { @@ -13,9 +13,7 @@ pub(crate) trait TyExt { fn is_unknown(&self) -> bool; fn as_tuple(&self) -> Option<&Substitution>; - fn as_fn_def(&self, db: &dyn HirDatabase) -> Option; - fn callable_def(&self, db: &dyn HirDatabase) -> Option; fn callable_sig(&self, db: &dyn HirDatabase) -> Option; } @@ -35,20 +33,6 @@ impl TyExt for Ty { } } - fn as_fn_def(&self, db: &dyn HirDatabase) -> Option { - match self.callable_def(db) { - Some(CallableDefId::FunctionId(func)) => Some(func), - Some(CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_)) | None => None, - } - } - - fn callable_def(&self, db: &dyn HirDatabase) -> Option { - match self.kind(Interner) { - &TyKind::FnDef(def, ..) => Some(ToChalk::from_chalk(db, def)), - _ => None, - } - } - fn callable_sig(&self, db: &dyn HirDatabase) -> Option { match self.kind(Interner) { TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)), 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 31100e17f846..53524d66a33c 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 @@ -5,22 +5,21 @@ use std::mem; use either::Either; use hir_def::{ - AdtId, DefWithBodyId, FieldId, FunctionId, VariantId, + AdtId, CallableDefId, DefWithBodyId, FieldId, FunctionId, VariantId, expr_store::{Body, path::Path}, hir::{AsmOperand, Expr, ExprId, ExprOrPatId, InlineAsmKind, Pat, PatId, Statement, UnaryOp}, resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs}, signatures::StaticFlags, type_ref::Rawness, }; +use rustc_type_ir::inherent::IntoKind; use span::Edition; -use crate::next_solver::DbInterner; -use crate::next_solver::mapping::NextSolverToChalk; -use crate::utils::TargetFeatureIsSafeInTarget; use crate::{ - InferenceResult, Interner, TargetFeatures, TyExt, TyKind, + InferenceResult, TargetFeatures, db::HirDatabase, - utils::{is_fn_unsafe_to_call, target_feature_is_safe_in_target}, + next_solver::{CallableIdWrapper, TyKind, abi::Safety}, + utils::{TargetFeatureIsSafeInTarget, is_fn_unsafe_to_call, target_feature_is_safe_in_target}, }; #[derive(Debug, Default)] @@ -151,7 +150,6 @@ struct UnsafeVisitor<'db> { /// On some targets (WASM), calling safe functions with `#[target_feature]` is always safe, even when /// the target feature is not enabled. This flag encodes that. target_feature_is_safe: TargetFeatureIsSafeInTarget, - interner: DbInterner<'db>, } impl<'db> UnsafeVisitor<'db> { @@ -186,7 +184,6 @@ impl<'db> UnsafeVisitor<'db> { def_target_features, edition, target_feature_is_safe, - interner: DbInterner::new_with(db, None, None), } } @@ -289,12 +286,14 @@ impl<'db> UnsafeVisitor<'db> { let inside_assignment = mem::replace(&mut self.inside_assignment, false); match expr { &Expr::Call { callee, .. } => { - let callee = self.infer[callee].to_chalk(self.interner); - if let Some(func) = callee.as_fn_def(self.db) { + let callee = self.infer[callee]; + if let TyKind::FnDef(CallableIdWrapper(CallableDefId::FunctionId(func)), _) = + callee.kind() + { self.check_call(current, func); } - if let TyKind::Function(fn_ptr) = callee.kind(Interner) - && fn_ptr.sig.safety == chalk_ir::Safety::Unsafe + if let TyKind::FnPtr(_, hdr) = callee.kind() + && hdr.safety == Safety::Unsafe { self.on_unsafe_op(current.into(), UnsafetyReason::UnsafeFnCall); } @@ -342,7 +341,7 @@ impl<'db> UnsafeVisitor<'db> { } } Expr::UnaryOp { expr, op: UnaryOp::Deref } => { - if let TyKind::Raw(..) = &self.infer[*expr].to_chalk(self.interner).kind(Interner) { + if let TyKind::RawPtr(..) = self.infer[*expr].kind() { self.on_unsafe_op(current.into(), UnsafetyReason::RawPtrDeref); } } From aa711f622f96b74ac9e4f477884b2b018cab6c5e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 12 Oct 2025 16:15:13 +0300 Subject: [PATCH 015/170] Make `lookup_impl_method()` query transparent The bulk of the work is trait solving and cached in the trait solver's cache, and this will save memory. --- src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs | 3 +-- src/tools/rust-analyzer/crates/hir-ty/src/db.rs | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) 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 002e0823b9d1..761d72243e9f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -5,13 +5,12 @@ mod tests; use base_db::Crate; use hir_def::{ - EnumVariantId, GeneralConstId, + EnumVariantId, GeneralConstId, HasModule, StaticId, expr_store::{Body, HygieneId, path::Path}, hir::{Expr, ExprId}, resolver::{Resolver, ValueNs}, type_ref::LiteralConstRef, }; -use hir_def::{HasModule, StaticId}; use hir_expand::Lookup; use rustc_type_ir::{UnevaluatedConst, inherent::IntoKind}; use stdx::never; 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 4b33c8a84a81..6c1d05ab1b50 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -96,6 +96,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { ) -> Result>; #[salsa::invoke(crate::method_resolution::lookup_impl_method_query)] + #[salsa::transparent] fn lookup_impl_method<'db>( &'db self, env: Arc>, From 2df61e02e4c258ba9aaa2b03d9d57c61e55d708f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 12 Oct 2025 16:20:56 +0300 Subject: [PATCH 016/170] Remove unneeded queries --- src/tools/rust-analyzer/crates/hir-ty/src/db.rs | 17 ----------------- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- 2 files changed, 1 insertion(+), 18 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 6c1d05ab1b50..80945adedbfd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -323,23 +323,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { )] fn variances_of(&self, def: GenericDefId) -> Option>; - #[salsa::invoke(crate::traits::normalize_projection_query)] - #[salsa::transparent] - fn normalize_projection( - &self, - projection: crate::ProjectionTy, - env: Arc>, - ) -> Ty; - - #[salsa::invoke(crate::traits::trait_solve_query)] - #[salsa::transparent] - fn trait_solve( - &self, - krate: Crate, - block: Option, - goal: crate::Canonical>, - ) -> NextTraitSolveResult; - // next trait solver #[salsa::invoke(crate::lower_nextsolver::const_param_ty_query)] diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 55da27781db1..fc516a6764a5 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -3692,7 +3692,7 @@ impl GenericDef { }; expr_store_diagnostics(db, acc, &source_map); - push_ty_diagnostics(db, acc, db.generic_defaults_with_diagnostics(def).1, &source_map); + push_ty_diagnostics(db, acc, db.generic_defaults_ns_with_diagnostics(def).1, &source_map); push_ty_diagnostics( db, acc, From bf531aca66a6f9444b26f7b18279a8818a7cdc10 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 13 Oct 2025 00:15:55 +0900 Subject: [PATCH 017/170] minor: Fix creating `rust-analyzer/rust-analyzer` --- .../rust-analyzer/crates/rust-analyzer/src/flycheck.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index b545106fe1cf..73a51bba3d9a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -12,7 +12,7 @@ use cargo_metadata::PackageId; use crossbeam_channel::{Receiver, Sender, select_biased, unbounded}; use ide_db::FxHashSet; use itertools::Itertools; -use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::Deserialize as _; use serde_derive::Deserialize; @@ -432,8 +432,10 @@ impl FlycheckActor { options .target_dir .as_deref() - .unwrap_or("target".as_ref()) - .join(format!("rust-analyzer/flycheck{}", self.id)), + .unwrap_or( + Utf8Path::new("target").join("rust-analyzer").as_path(), + ) + .join(format!("flycheck{}", self.id)), ), _ => None, }, From 55b73e6b06b930d1d357d15beda76c9c3ffa9ab4 Mon Sep 17 00:00:00 2001 From: Elliot Roberts Date: Sun, 12 Oct 2025 15:47:00 -0700 Subject: [PATCH 018/170] enable tt feature in crates/cfg tests --- src/tools/rust-analyzer/Cargo.lock | 1 + src/tools/rust-analyzer/crates/cfg/Cargo.toml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 6625403572b0..16a874ce5d97 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -235,6 +235,7 @@ name = "cfg" version = "0.0.0" dependencies = [ "arbitrary", + "cfg", "expect-test", "intern", "oorandom", diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index af95f86c8352..e17969bd82d4 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -29,5 +29,8 @@ arbitrary = { version = "1.4.1", features = ["derive"] } syntax-bridge.workspace = true syntax.workspace = true +# tt is needed for testing +cfg = { path = ".", default-features = false, features = ["tt"] } + [lints] workspace = true From f35b6dfe9174d6e7e4f783bfbf676741e836d10b Mon Sep 17 00:00:00 2001 From: Michael Gruenewald Date: Tue, 7 Oct 2025 13:59:09 +0200 Subject: [PATCH 019/170] Add lodash --- src/tools/rust-analyzer/editors/code/package-lock.json | 8 ++++++++ src/tools/rust-analyzer/editors/code/package.json | 1 + 2 files changed, 9 insertions(+) diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index e35a159cbc3f..6dd448522379 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -21,6 +21,7 @@ "@stylistic/eslint-plugin": "^4.1.0", "@stylistic/eslint-plugin-js": "^4.1.0", "@tsconfig/strictest": "^2.0.5", + "@types/lodash": "^4.17.20", "@types/node": "~22.13.4", "@types/vscode": "~1.93.0", "@typescript-eslint/eslint-plugin": "^8.25.0", @@ -1388,6 +1389,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.13.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.5.tgz", diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 70687238c854..d659421a0299 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -58,6 +58,7 @@ "@stylistic/eslint-plugin": "^4.1.0", "@stylistic/eslint-plugin-js": "^4.1.0", "@tsconfig/strictest": "^2.0.5", + "@types/lodash": "^4.17.20", "@types/node": "~22.13.4", "@types/vscode": "~1.93.0", "@typescript-eslint/eslint-plugin": "^8.25.0", From 145677f699412386d4956cef1edb3e1b6f980367 Mon Sep 17 00:00:00 2001 From: Michael Gruenewald Date: Tue, 7 Oct 2025 13:55:49 +0200 Subject: [PATCH 020/170] Don't pretend to have a WorkspaceConfiguration if there isn't one --- .../rust-analyzer/editors/code/src/client.ts | 2 +- .../rust-analyzer/editors/code/src/config.ts | 20 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/client.ts b/src/tools/rust-analyzer/editors/code/src/client.ts index 073ff2f4703f..cb71a01138b3 100644 --- a/src/tools/rust-analyzer/editors/code/src/client.ts +++ b/src/tools/rust-analyzer/editors/code/src/client.ts @@ -13,7 +13,7 @@ import { RaLanguageClient } from "./lang_client"; export async function createClient( traceOutputChannel: vscode.OutputChannel, outputChannel: vscode.OutputChannel, - initializationOptions: vscode.WorkspaceConfiguration, + initializationOptions: lc.LanguageClientOptions["initializationOptions"], serverOptions: lc.ServerOptions, config: Config, unlinkedFiles: vscode.Uri[], diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index 3b1b0768d3cf..06e179eb0eb1 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -5,6 +5,7 @@ import * as vscode from "vscode"; import { expectNotUndefined, log, normalizeDriveLetter, unwrapUndefinable } from "./util"; import type { Env } from "./util"; import type { Disposable } from "vscode"; +import { get } from "lodash"; export type RunnableEnvCfgItem = { mask?: string; @@ -12,6 +13,9 @@ export type RunnableEnvCfgItem = { platform?: string | string[]; }; +export type ConfigurationTree = { [key: string]: ConfigurationValue }; +export type ConfigurationValue = undefined | null | boolean | number | string | ConfigurationValue[] | ConfigurationTree; + type ShowStatusBar = "always" | "never" | { documentSelector: vscode.DocumentSelector }; export class Config { @@ -197,7 +201,7 @@ export class Config { * So this getter handles this quirk by not requiring the caller to use postfix `!` */ private get(path: string): T | undefined { - return prepareVSCodeConfig(this.cfg.get(path)); + return prepareVSCodeConfig(get(this.cfg, path)) as T; } get serverPath() { @@ -371,22 +375,20 @@ export class Config { } } -export function prepareVSCodeConfig(resp: T): T { +export function prepareVSCodeConfig(resp: ConfigurationValue): ConfigurationValue { if (Is.string(resp)) { - return substituteVSCodeVariableInString(resp) as T; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } else if (resp && Is.array(resp)) { + return substituteVSCodeVariableInString(resp); + } else if (resp && Is.array(resp)) { return resp.map((val) => { return prepareVSCodeConfig(val); - }) as T; + }); } else if (resp && typeof resp === "object") { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const res: { [key: string]: any } = {}; + const res: ConfigurationTree = {}; for (const key in resp) { const val = resp[key]; res[key] = prepareVSCodeConfig(val); } - return res as T; + return res; } return resp; } From 8ffdc2f84fd6e686804ec00d55eda85b3381e0e8 Mon Sep 17 00:00:00 2001 From: Michael Gruenewald Date: Tue, 7 Oct 2025 14:06:30 +0200 Subject: [PATCH 021/170] Allow other extensions to override the configuration --- .../rust-analyzer/editors/code/src/config.ts | 30 +++++++++++++++---- .../rust-analyzer/editors/code/src/ctx.ts | 6 +++- .../rust-analyzer/editors/code/src/main.ts | 2 ++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index 06e179eb0eb1..340f107ebbab 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -5,7 +5,7 @@ import * as vscode from "vscode"; import { expectNotUndefined, log, normalizeDriveLetter, unwrapUndefinable } from "./util"; import type { Env } from "./util"; import type { Disposable } from "vscode"; -import { get } from "lodash"; +import { cloneDeep, get, merge } from "lodash"; export type RunnableEnvCfgItem = { mask?: string; @@ -23,7 +23,7 @@ export class Config { configureLang: vscode.Disposable | undefined; readonly rootSection = "rust-analyzer"; - private readonly requiresServerReloadOpts = ["server", "files", "showSyntaxTree"].map( + private readonly requiresServerReloadOpts = ["cargo", "server", "files", "showSyntaxTree"].map( (opt) => `${this.rootSection}.${opt}`, ); @@ -31,6 +31,19 @@ export class Config { (opt) => `${this.rootSection}.${opt}`, ); + extensionConfigurations: Map> = new Map(); + + async addExtensionConfiguration(extensionId: string, configuration: Record): Promise { + this.extensionConfigurations.set(extensionId, configuration); + const prefix = `${this.rootSection}.`; + await this.onDidChangeConfiguration({ + affectsConfiguration(section: string, _scope?: vscode.ConfigurationScope): boolean { + // FIXME: questionable + return section.startsWith(prefix) && section.slice(prefix.length) in configuration; + }, + }); + } + constructor(disposables: Disposable[]) { vscode.workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, disposables); this.refreshLogging(); @@ -180,10 +193,15 @@ export class Config { // We don't do runtime config validation here for simplicity. More on stackoverflow: // https://stackoverflow.com/questions/60135780/what-is-the-best-way-to-type-check-the-configuration-for-vscode-extension - private get cfg(): vscode.WorkspaceConfiguration { + private get rawCfg(): vscode.WorkspaceConfiguration { return vscode.workspace.getConfiguration(this.rootSection); } + public get cfg(): ConfigurationTree { + const vsCodeConfig = cloneDeep(this.rawCfg); + return merge(vsCodeConfig, ...this.extensionConfigurations.values()); + } + /** * Beware that postfix `!` operator erases both `null` and `undefined`. * This is why the following doesn't work as expected: @@ -227,7 +245,7 @@ export class Config { } async toggleCheckOnSave() { - const config = this.cfg.inspect("checkOnSave") ?? { key: "checkOnSave" }; + const config = this.rawCfg.inspect("checkOnSave") ?? { key: "checkOnSave" }; let overrideInLanguage; let target; let value; @@ -253,7 +271,7 @@ export class Config { overrideInLanguage = config.defaultLanguageValue; value = config.defaultValue || config.defaultLanguageValue; } - await this.cfg.update("checkOnSave", !(value || false), target || null, overrideInLanguage); + await this.rawCfg.update("checkOnSave", !(value || false), target || null, overrideInLanguage); } get problemMatcher(): string[] { @@ -371,7 +389,7 @@ export class Config { } async setAskBeforeUpdateTest(value: boolean) { - await this.cfg.update("runnables.askBeforeUpdateTest", value, true); + await this.rawCfg.update("runnables.askBeforeUpdateTest", value, true); } } diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index e55754fb9f04..dfbf5b1e47c3 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -150,6 +150,10 @@ export class Ctx implements RustAnalyzerExtensionApi { }); } + async addConfiguration(extensionId: string, configuration: Record): Promise { + await this.config.addExtensionConfiguration(extensionId, configuration); + } + dispose() { this.config.dispose(); this.statusBar.dispose(); @@ -230,7 +234,7 @@ export class Ctx implements RustAnalyzerExtensionApi { debug: run, }; - let rawInitializationOptions = vscode.workspace.getConfiguration("rust-analyzer"); + let rawInitializationOptions = this.config.cfg; if (this.workspace.kind === "Detached Files") { rawInitializationOptions = { diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index 996298524f11..c126a0a105dc 100644 --- a/src/tools/rust-analyzer/editors/code/src/main.ts +++ b/src/tools/rust-analyzer/editors/code/src/main.ts @@ -13,6 +13,8 @@ const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; export interface RustAnalyzerExtensionApi { // FIXME: this should be non-optional readonly client?: lc.LanguageClient; + + addConfiguration(extensionId: string, configuration: Record): Promise; } export async function deactivate() { From c53b566b2afe7f5dbfc04b30873ae156c21808da Mon Sep 17 00:00:00 2001 From: Michael Gruenewald Date: Thu, 9 Oct 2025 14:21:16 +0200 Subject: [PATCH 022/170] Remember configuration overrides by extensions --- .../rust-analyzer/editors/code/src/config.ts | 52 ++++++++++++------- .../rust-analyzer/editors/code/src/ctx.ts | 2 +- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index 340f107ebbab..3afda6008263 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -4,8 +4,7 @@ import * as path from "path"; import * as vscode from "vscode"; import { expectNotUndefined, log, normalizeDriveLetter, unwrapUndefinable } from "./util"; import type { Env } from "./util"; -import type { Disposable } from "vscode"; -import { cloneDeep, get, merge } from "lodash"; +import { cloneDeep, get, merge, pickBy } from "lodash"; export type RunnableEnvCfgItem = { mask?: string; @@ -20,6 +19,7 @@ type ShowStatusBar = "always" | "never" | { documentSelector: vscode.DocumentSel export class Config { readonly extensionId = "rust-lang.rust-analyzer"; + readonly workspaceState: vscode.Memento; configureLang: vscode.Disposable | undefined; readonly rootSection = "rust-analyzer"; @@ -31,21 +31,9 @@ export class Config { (opt) => `${this.rootSection}.${opt}`, ); - extensionConfigurations: Map> = new Map(); - - async addExtensionConfiguration(extensionId: string, configuration: Record): Promise { - this.extensionConfigurations.set(extensionId, configuration); - const prefix = `${this.rootSection}.`; - await this.onDidChangeConfiguration({ - affectsConfiguration(section: string, _scope?: vscode.ConfigurationScope): boolean { - // FIXME: questionable - return section.startsWith(prefix) && section.slice(prefix.length) in configuration; - }, - }); - } - - constructor(disposables: Disposable[]) { - vscode.workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, disposables); + constructor(ctx: vscode.ExtensionContext) { + this.workspaceState = ctx.workspaceState; + vscode.workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, ctx.subscriptions); this.refreshLogging(); this.configureLanguage(); } @@ -54,6 +42,32 @@ export class Config { this.configureLang?.dispose(); } + /// Returns the rust-analyzer-specific workspace configuration, incl. any + /// configuration items overridden by (present) extensions. + get extensionConfigurations(): Record> { + return pickBy( + this.workspaceState.get>("extensionConfigurations", {}), + (_, extensionId) => vscode.extensions.getExtension(extensionId) !== undefined, + ); + } + + async addExtensionConfiguration(extensionId: string, configuration: Record): Promise { + const oldConfiguration = this.cfg; + + const extCfgs = this.extensionConfigurations; + extCfgs[extensionId] = configuration; + await this.workspaceState.update("extensionConfigurations", extCfgs); + + const newConfiguration = this.cfg; + const prefix = `${this.rootSection}.`; + await this.onDidChangeConfiguration({ + affectsConfiguration(section: string, _scope?: vscode.ConfigurationScope): boolean { + return section.startsWith(prefix) && + get(oldConfiguration, section.slice(prefix.length)) !== get(newConfiguration, section.slice(prefix.length)); + }, + }); + } + private refreshLogging() { log.info( "Extension version:", @@ -198,8 +212,7 @@ export class Config { } public get cfg(): ConfigurationTree { - const vsCodeConfig = cloneDeep(this.rawCfg); - return merge(vsCodeConfig, ...this.extensionConfigurations.values()); + return merge(cloneDeep(this.rawCfg), ...Object.values(this.extensionConfigurations)); } /** @@ -209,7 +222,6 @@ export class Config { * ```ts * const nullableNum = vscode * .workspace - * .getConfiguration * .getConfiguration("rust-analyzer") * .get(path)!; * diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index dfbf5b1e47c3..69703efc6964 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -125,7 +125,7 @@ export class Ctx implements RustAnalyzerExtensionApi { extCtx.subscriptions.push(this); this.version = extCtx.extension.packageJSON.version ?? ""; this._serverVersion = ""; - this.config = new Config(extCtx.subscriptions); + this.config = new Config(extCtx); this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); this.updateStatusBarVisibility(vscode.window.activeTextEditor); this.statusBarActiveEditorListener = vscode.window.onDidChangeActiveTextEditor((editor) => From 6de179293805437f3c3359a99129b2468fc185c5 Mon Sep 17 00:00:00 2001 From: Michael Gruenewald Date: Mon, 13 Oct 2025 11:24:06 +0200 Subject: [PATCH 023/170] docs --- .../rust-analyzer/editors/code/src/config.ts | 16 ++++++++++++---- src/tools/rust-analyzer/editors/code/src/main.ts | 4 ++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index 3afda6008263..4c895fb3d09b 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -19,10 +19,11 @@ type ShowStatusBar = "always" | "never" | { documentSelector: vscode.DocumentSel export class Config { readonly extensionId = "rust-lang.rust-analyzer"; - readonly workspaceState: vscode.Memento; - configureLang: vscode.Disposable | undefined; - readonly rootSection = "rust-analyzer"; + configureLang: vscode.Disposable | undefined; + workspaceState: vscode.Memento; + + private readonly rootSection = "rust-analyzer"; private readonly requiresServerReloadOpts = ["cargo", "server", "files", "showSyntaxTree"].map( (opt) => `${this.rootSection}.${opt}`, ); @@ -42,11 +43,14 @@ export class Config { this.configureLang?.dispose(); } + private readonly extensionConfigurationStateKey = "extensionConfigurations"; + /// Returns the rust-analyzer-specific workspace configuration, incl. any /// configuration items overridden by (present) extensions. get extensionConfigurations(): Record> { return pickBy( this.workspaceState.get>("extensionConfigurations", {}), + // ignore configurations from disabled/removed extensions (_, extensionId) => vscode.extensions.getExtension(extensionId) !== undefined, ); } @@ -56,7 +60,7 @@ export class Config { const extCfgs = this.extensionConfigurations; extCfgs[extensionId] = configuration; - await this.workspaceState.update("extensionConfigurations", extCfgs); + await this.workspaceState.update(this.extensionConfigurationStateKey, extCfgs); const newConfiguration = this.cfg; const prefix = `${this.rootSection}.`; @@ -207,10 +211,14 @@ export class Config { // We don't do runtime config validation here for simplicity. More on stackoverflow: // https://stackoverflow.com/questions/60135780/what-is-the-best-way-to-type-check-the-configuration-for-vscode-extension + // Returns the raw configuration for rust-analyzer as returned by vscode. This + // should only be used when modifications to the user/workspace configuration + // are required. private get rawCfg(): vscode.WorkspaceConfiguration { return vscode.workspace.getConfiguration(this.rootSection); } + // Returns the final configuration to use, with extension configuration overrides merged in. public get cfg(): ConfigurationTree { return merge(cloneDeep(this.rawCfg), ...Object.values(this.extensionConfigurations)); } diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index c126a0a105dc..1b512696ac57 100644 --- a/src/tools/rust-analyzer/editors/code/src/main.ts +++ b/src/tools/rust-analyzer/editors/code/src/main.ts @@ -14,6 +14,10 @@ export interface RustAnalyzerExtensionApi { // FIXME: this should be non-optional readonly client?: lc.LanguageClient; + // Allows adding a configuration override from another extension. + // `configuration` is a `rust-analyzer` subtree of the vscode configuration + // that gets merged with the workspace/user configuration. `extensionId` is + // used to only merge configuration override from present extensions. addConfiguration(extensionId: string, configuration: Record): Promise; } From e44c3c4f2607ca5971df5eb79615d10bc46350b2 Mon Sep 17 00:00:00 2001 From: Michael Gruenewald Date: Mon, 13 Oct 2025 14:29:31 +0200 Subject: [PATCH 024/170] Don't override users' settings --- .../rust-analyzer/editors/code/src/config.ts | 16 ++++++++++++++-- src/tools/rust-analyzer/editors/code/src/main.ts | 6 +++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index 4c895fb3d09b..b97a47d5b984 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -4,7 +4,7 @@ import * as path from "path"; import * as vscode from "vscode"; import { expectNotUndefined, log, normalizeDriveLetter, unwrapUndefinable } from "./util"; import type { Env } from "./util"; -import { cloneDeep, get, merge, pickBy } from "lodash"; +import { cloneDeep, get, pickBy, set } from "lodash"; export type RunnableEnvCfgItem = { mask?: string; @@ -220,7 +220,19 @@ export class Config { // Returns the final configuration to use, with extension configuration overrides merged in. public get cfg(): ConfigurationTree { - return merge(cloneDeep(this.rawCfg), ...Object.values(this.extensionConfigurations)); + const finalConfig = cloneDeep(this.rawCfg); + for (const [extensionId, items] of Object.entries(this.extensionConfigurations)) { + for (const [k, v] of Object.entries(items)) { + const i = this.rawCfg.inspect(k); + if (i?.workspaceValue !== undefined || i?.workspaceFolderValue !== undefined || i?.globalValue !== undefined) { + log.trace(`Ignoring configuration override for ${k} from extension ${extensionId}`); + continue; + } + log.trace(`Extension ${extensionId} overrides configuration ${k} to `, v); + set(finalConfig, k, v); + } + } + return finalConfig; } /** diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index 1b512696ac57..190f5866d0ea 100644 --- a/src/tools/rust-analyzer/editors/code/src/main.ts +++ b/src/tools/rust-analyzer/editors/code/src/main.ts @@ -15,9 +15,9 @@ export interface RustAnalyzerExtensionApi { readonly client?: lc.LanguageClient; // Allows adding a configuration override from another extension. - // `configuration` is a `rust-analyzer` subtree of the vscode configuration - // that gets merged with the workspace/user configuration. `extensionId` is - // used to only merge configuration override from present extensions. + // `extensionId` is used to only merge configuration override from present + // extensions. `configuration` is map of rust-analyzer-specific setting + // overrides, e.g., `{"cargo.cfgs": ["foo", "bar"]}`. addConfiguration(extensionId: string, configuration: Record): Promise; } From 38c4d80c08b7d9a8e275656709e77b1310c68a28 Mon Sep 17 00:00:00 2001 From: Michael Gruenewald Date: Mon, 13 Oct 2025 14:45:01 +0200 Subject: [PATCH 025/170] Format fixes --- .../rust-analyzer/editors/code/src/config.ts | 49 +++++++++++++++---- .../rust-analyzer/editors/code/src/ctx.ts | 5 +- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index b97a47d5b984..c0a1b3f02e36 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -13,7 +13,14 @@ export type RunnableEnvCfgItem = { }; export type ConfigurationTree = { [key: string]: ConfigurationValue }; -export type ConfigurationValue = undefined | null | boolean | number | string | ConfigurationValue[] | ConfigurationTree; +export type ConfigurationValue = + | undefined + | null + | boolean + | number + | string + | ConfigurationValue[] + | ConfigurationTree; type ShowStatusBar = "always" | "never" | { documentSelector: vscode.DocumentSelector }; @@ -34,7 +41,11 @@ export class Config { constructor(ctx: vscode.ExtensionContext) { this.workspaceState = ctx.workspaceState; - vscode.workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, ctx.subscriptions); + vscode.workspace.onDidChangeConfiguration( + this.onDidChangeConfiguration, + this, + ctx.subscriptions, + ); this.refreshLogging(); this.configureLanguage(); } @@ -49,13 +60,19 @@ export class Config { /// configuration items overridden by (present) extensions. get extensionConfigurations(): Record> { return pickBy( - this.workspaceState.get>("extensionConfigurations", {}), + this.workspaceState.get>( + "extensionConfigurations", + {}, + ), // ignore configurations from disabled/removed extensions (_, extensionId) => vscode.extensions.getExtension(extensionId) !== undefined, ); } - async addExtensionConfiguration(extensionId: string, configuration: Record): Promise { + async addExtensionConfiguration( + extensionId: string, + configuration: Record, + ): Promise { const oldConfiguration = this.cfg; const extCfgs = this.extensionConfigurations; @@ -66,8 +83,11 @@ export class Config { const prefix = `${this.rootSection}.`; await this.onDidChangeConfiguration({ affectsConfiguration(section: string, _scope?: vscode.ConfigurationScope): boolean { - return section.startsWith(prefix) && - get(oldConfiguration, section.slice(prefix.length)) !== get(newConfiguration, section.slice(prefix.length)); + return ( + section.startsWith(prefix) && + get(oldConfiguration, section.slice(prefix.length)) !== + get(newConfiguration, section.slice(prefix.length)) + ); }, }); } @@ -224,8 +244,14 @@ export class Config { for (const [extensionId, items] of Object.entries(this.extensionConfigurations)) { for (const [k, v] of Object.entries(items)) { const i = this.rawCfg.inspect(k); - if (i?.workspaceValue !== undefined || i?.workspaceFolderValue !== undefined || i?.globalValue !== undefined) { - log.trace(`Ignoring configuration override for ${k} from extension ${extensionId}`); + if ( + i?.workspaceValue !== undefined || + i?.workspaceFolderValue !== undefined || + i?.globalValue !== undefined + ) { + log.trace( + `Ignoring configuration override for ${k} from extension ${extensionId}`, + ); continue; } log.trace(`Extension ${extensionId} overrides configuration ${k} to `, v); @@ -303,7 +329,12 @@ export class Config { overrideInLanguage = config.defaultLanguageValue; value = config.defaultValue || config.defaultLanguageValue; } - await this.rawCfg.update("checkOnSave", !(value || false), target || null, overrideInLanguage); + await this.rawCfg.update( + "checkOnSave", + !(value || false), + target || null, + overrideInLanguage, + ); } get problemMatcher(): string[] { diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index 69703efc6964..a7b7be03b5d8 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -150,7 +150,10 @@ export class Ctx implements RustAnalyzerExtensionApi { }); } - async addConfiguration(extensionId: string, configuration: Record): Promise { + async addConfiguration( + extensionId: string, + configuration: Record, + ): Promise { await this.config.addExtensionConfiguration(extensionId, configuration); } From 25ed1c5bfb9db3dce709ec7a579fa07b168e63d7 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 14 Oct 2025 21:30:34 +0300 Subject: [PATCH 026/170] Migrate `Display` impls to the next solver --- .../crates/hir-ty/src/builder.rs | 222 +--- .../crates/hir-ty/src/chalk_ext.rs | 44 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 1 - .../hir-ty/src/diagnostics/match_check.rs | 29 +- .../crates/hir-ty/src/display.rs | 1155 ++++++----------- .../crates/hir-ty/src/generics.rs | 7 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 30 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 128 +- .../crates/hir-ty/src/lower_nextsolver.rs | 142 +- .../hir-ty/src/lower_nextsolver/path.rs | 29 +- .../crates/hir-ty/src/mir/pretty.rs | 6 +- .../crates/hir-ty/src/next_solver/consts.rs | 6 +- .../hir-ty/src/next_solver/predicate.rs | 20 + .../rust-analyzer/crates/hir-ty/src/traits.rs | 90 +- .../rust-analyzer/crates/hir-ty/src/utils.rs | 16 +- .../rust-analyzer/crates/hir/src/display.rs | 179 ++- .../crates/ide/src/hover/tests.rs | 2 +- .../rust-analyzer/crates/ide/src/moniker.rs | 2 +- .../crates/ide/src/navigation_target.rs | 2 +- 19 files changed, 689 insertions(+), 1421 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs index 706bbe856c67..5c4eb8475bbc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs @@ -1,15 +1,14 @@ //! `TyBuilder`, a helper for building instances of `Ty` and related types. use chalk_ir::{ - AdtId, DebruijnIndex, Scalar, - cast::{Cast, CastTo, Caster}, + DebruijnIndex, Scalar, + cast::{Cast, Caster}, }; -use hir_def::{GenericDefId, GenericParamId, TraitId, TypeAliasId, builtin_type::BuiltinType}; +use hir_def::{GenericDefId, GenericParamId, TraitId, builtin_type::BuiltinType}; use smallvec::SmallVec; use crate::{ - BoundVar, CallableSig, GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, - TraitRef, Ty, TyDefId, TyExt, TyKind, + BoundVar, GenericArg, GenericArgData, Interner, Substitution, TraitRef, Ty, TyKind, consteval::unknown_const_as_generic, db::HirDatabase, error_lifetime, @@ -19,18 +18,18 @@ use crate::{ DbInterner, EarlyBinder, mapping::{ChalkToNextSolver, NextSolverToChalk}, }, - primitive, to_assoc_type_id, to_chalk_trait_id, + primitive, to_chalk_trait_id, }; #[derive(Debug, Clone, PartialEq, Eq)] -pub enum ParamKind { +pub(crate) enum ParamKind { Type, Lifetime, Const(Ty), } /// This is a builder for `Ty` or anything that needs a `Substitution`. -pub struct TyBuilder { +pub(crate) struct TyBuilder { /// The `data` field is used to keep track of what we're building (e.g. an /// ADT, a `TraitRef`, ...). data: D, @@ -60,10 +59,6 @@ impl TyBuilder { Self { data, vec: SmallVec::with_capacity(param_kinds.len()), param_kinds, parent_subst } } - fn new_empty(data: D) -> Self { - TyBuilder::new(data, SmallVec::new(), None) - } - fn build_internal(self) -> (D, Substitution) { assert_eq!( self.vec.len(), @@ -83,35 +78,15 @@ impl TyBuilder { (self.data, subst) } - pub fn build_into_subst(self) -> Substitution { - self.build_internal().1 - } - - pub fn push(mut self, arg: impl CastTo) -> Self { - assert!(self.remaining() > 0); - let arg = arg.cast(Interner); - let expected_kind = &self.param_kinds[self.vec.len()]; - - let arg_kind = match arg.data(Interner) { - GenericArgData::Ty(_) => ParamKind::Type, - GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"), - GenericArgData::Const(c) => { - let c = c.data(Interner); - ParamKind::Const(c.ty.clone()) - } - }; - assert_eq!(*expected_kind, arg_kind); - - self.vec.push(arg); - - self - } - - pub fn remaining(&self) -> usize { + pub(crate) fn remaining(&self) -> usize { self.param_kinds.len() - self.vec.len() } - pub fn fill_with_bound_vars(self, debruijn: DebruijnIndex, starting_from: usize) -> Self { + pub(crate) fn fill_with_bound_vars( + self, + debruijn: DebruijnIndex, + starting_from: usize, + ) -> Self { // self.fill is inlined to make borrow checker happy let mut this = self; let other = &this.param_kinds[this.vec.len()..]; @@ -129,22 +104,6 @@ impl TyBuilder { this } - pub fn fill_with_unknown(self) -> Self { - let interner = DbInterner::conjure(); - // self.fill is inlined to make borrow checker happy - let mut this = self; - let filler = this.param_kinds[this.vec.len()..].iter().map(|x| match x { - ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner), - ParamKind::Const(ty) => { - unknown_const_as_generic(ty.to_nextsolver(interner)).to_chalk(interner) - } - ParamKind::Lifetime => error_lifetime().cast(Interner), - }); - this.vec.extend(filler.casted(Interner)); - assert_eq!(this.remaining(), 0); - this - } - #[tracing::instrument(skip_all)] pub(crate) fn fill_with_inference_vars(self, table: &mut InferenceTable<'_>) -> Self { self.fill(|x| { @@ -157,7 +116,7 @@ impl TyBuilder { }) } - pub fn fill(mut self, filler: impl FnMut(&ParamKind) -> GenericArg) -> Self { + pub(crate) fn fill(mut self, filler: impl FnMut(&ParamKind) -> GenericArg) -> Self { self.vec.extend(self.param_kinds[self.vec.len()..].iter().map(filler)); assert_eq!(self.remaining(), 0); self @@ -174,28 +133,11 @@ impl TyBuilder { } impl TyBuilder<()> { - pub fn unit() -> Ty { - TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner) - } - - // FIXME: rustc's ty is dependent on the adt type, maybe we need to do that as well - pub fn discr_ty() -> Ty { - TyKind::Scalar(chalk_ir::Scalar::Int(chalk_ir::IntTy::I128)).intern(Interner) - } - - pub fn bool() -> Ty { - TyKind::Scalar(chalk_ir::Scalar::Bool).intern(Interner) - } - - pub fn usize() -> Ty { + pub(crate) fn usize() -> Ty { TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(Interner) } - pub fn fn_ptr(sig: CallableSig) -> Ty { - TyKind::Function(sig.to_fn_ptr()).intern(Interner) - } - - pub fn builtin(builtin: BuiltinType) -> Ty { + pub(crate) fn builtin(builtin: BuiltinType) -> Ty { match builtin { BuiltinType::Char => TyKind::Scalar(Scalar::Char).intern(Interner), BuiltinType::Bool => TyKind::Scalar(Scalar::Bool).intern(Interner), @@ -212,16 +154,10 @@ impl TyBuilder<()> { } } - pub fn slice(argument: Ty) -> Ty { - TyKind::Slice(argument).intern(Interner) - } - - pub fn placeholder_subst(db: &dyn HirDatabase, def: impl Into) -> Substitution { - let params = generics(db, def.into()); - params.placeholder_subst(db) - } - - pub fn unknown_subst(db: &dyn HirDatabase, def: impl Into) -> Substitution { + pub(crate) fn unknown_subst( + db: &dyn HirDatabase, + def: impl Into, + ) -> Substitution { let interner = DbInterner::conjure(); let params = generics(db, def.into()); Substitution::from_iter( @@ -239,7 +175,7 @@ impl TyBuilder<()> { } #[tracing::instrument(skip_all)] - pub fn subst_for_def( + pub(crate) fn subst_for_def( db: &dyn HirDatabase, def: impl Into, parent_subst: Option, @@ -257,114 +193,25 @@ impl TyBuilder<()> { TyBuilder::new((), params, parent_subst) } - pub fn build(self) -> Substitution { + pub(crate) fn build(self) -> Substitution { let ((), subst) = self.build_internal(); subst } } -impl TyBuilder { - pub fn adt(db: &dyn HirDatabase, def: hir_def::AdtId) -> TyBuilder { - TyBuilder::subst_for_def(db, def, None).with_data(def) - } - - pub fn fill_with_defaults( - mut self, - db: &dyn HirDatabase, - mut fallback: impl FnMut() -> Ty, - ) -> Self { - let interner = DbInterner::conjure(); - // Note that we're building ADT, so we never have parent generic parameters. - let defaults = db.generic_defaults(self.data.into()); - - if let Some(defaults) = defaults.get(self.vec.len()..) { - for default_ty in defaults { - // NOTE(skip_binders): we only check if the arg type is error type. - if let Some(x) = default_ty.skip_binders().ty(Interner) - && x.is_unknown() - { - self.vec.push(fallback().cast(Interner)); - continue; - } - // Each default can only depend on the previous parameters. - self.vec.push(default_ty.clone().substitute(Interner, &*self.vec).cast(Interner)); - } - } - - // The defaults may be missing if no param has default, so fill that. - let filler = self.param_kinds[self.vec.len()..].iter().map(|x| match x { - ParamKind::Type => fallback().cast(Interner), - ParamKind::Const(ty) => { - unknown_const_as_generic(ty.to_nextsolver(interner)).to_chalk(interner) - } - ParamKind::Lifetime => error_lifetime().cast(Interner), - }); - self.vec.extend(filler.casted(Interner)); - - self - } - - pub fn build(self) -> Ty { - let (adt, subst) = self.build_internal(); - TyKind::Adt(AdtId(adt), subst).intern(Interner) - } -} - -pub struct Tuple(usize); -impl TyBuilder { - pub fn tuple(size: usize) -> TyBuilder { - TyBuilder::new(Tuple(size), std::iter::repeat_n(ParamKind::Type, size).collect(), None) - } - - pub fn build(self) -> Ty { - let (Tuple(size), subst) = self.build_internal(); - TyKind::Tuple(size, subst).intern(Interner) - } - - pub fn tuple_with(elements: I) -> Ty - where - I: IntoIterator, - ::IntoIter: ExactSizeIterator, - { - let elements = elements.into_iter(); - let len = elements.len(); - let mut b = - TyBuilder::new(Tuple(len), std::iter::repeat_n(ParamKind::Type, len).collect(), None); - for e in elements { - b = b.push(e); - } - b.build() - } -} - impl TyBuilder { - pub fn trait_ref(db: &dyn HirDatabase, def: TraitId) -> TyBuilder { + pub(crate) fn trait_ref(db: &dyn HirDatabase, def: TraitId) -> TyBuilder { TyBuilder::subst_for_def(db, def, None).with_data(def) } - pub fn build(self) -> TraitRef { + pub(crate) fn build(self) -> TraitRef { let (trait_id, substitution) = self.build_internal(); TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution } } } -impl TyBuilder { - pub fn assoc_type_projection( - db: &dyn HirDatabase, - def: TypeAliasId, - parent_subst: Option, - ) -> TyBuilder { - TyBuilder::subst_for_def(db, def, parent_subst).with_data(def) - } - - pub fn build(self) -> ProjectionTy { - let (type_alias, substitution) = self.build_internal(); - ProjectionTy { associated_ty_id: to_assoc_type_id(type_alias), substitution } - } -} - impl<'db, T: rustc_type_ir::TypeFoldable>> TyBuilder> { - pub fn build(self, interner: DbInterner<'db>) -> T { + pub(crate) fn build(self, interner: DbInterner<'db>) -> T { let (b, subst) = self.build_internal(); let args: crate::next_solver::GenericArgs<'db> = subst.to_nextsolver(interner); b.instantiate(interner, args) @@ -372,24 +219,7 @@ impl<'db, T: rustc_type_ir::TypeFoldable>> TyBuilder TyBuilder>> { - pub fn def_ty( - db: &'db dyn HirDatabase, - def: TyDefId, - parent_subst: Option, - ) -> TyBuilder>> { - let poly_ty = db.ty(def); - let id: GenericDefId = match def { - TyDefId::BuiltinType(_) => { - assert!(parent_subst.is_none()); - return TyBuilder::new_empty(poly_ty); - } - TyDefId::AdtId(id) => id.into(), - TyDefId::TypeAliasId(id) => id.into(), - }; - TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_ty) - } - - pub fn impl_self_ty( + pub(crate) fn impl_self_ty( db: &'db dyn HirDatabase, def: hir_def::ImplId, ) -> TyBuilder>> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 6bab30b40cc1..a315f699ddaa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -3,47 +3,11 @@ use hir_def::{ItemContainerId, Lookup, TraitId}; use crate::{ - Binders, CallableSig, DynTy, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyKind, - db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, generics::generics, - to_chalk_trait_id, utils::ClosureSubst, + Binders, DynTy, Interner, ProjectionTy, Substitution, TraitRef, Ty, db::HirDatabase, + from_assoc_type_id, from_chalk_trait_id, generics::generics, to_chalk_trait_id, }; -pub(crate) trait TyExt { - fn is_unit(&self) -> bool; - fn is_unknown(&self) -> bool; - - fn as_tuple(&self) -> Option<&Substitution>; - - fn callable_sig(&self, db: &dyn HirDatabase) -> Option; -} - -impl TyExt for Ty { - fn is_unit(&self) -> bool { - matches!(self.kind(Interner), TyKind::Tuple(0, _)) - } - - fn is_unknown(&self) -> bool { - matches!(self.kind(Interner), TyKind::Error) - } - - fn as_tuple(&self) -> Option<&Substitution> { - match self.kind(Interner) { - TyKind::Tuple(_, substs) => Some(substs), - _ => None, - } - } - - fn callable_sig(&self, db: &dyn HirDatabase) -> Option { - match self.kind(Interner) { - TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)), - TyKind::FnDef(def, parameters) => Some(CallableSig::from_def(db, *def, parameters)), - TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty(db).callable_sig(db), - _ => None, - } - } -} - -pub trait ProjectionTyExt { +pub(crate) trait ProjectionTyExt { fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef; fn trait_(&self, db: &dyn HirDatabase) -> TraitId; fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty; @@ -88,7 +52,7 @@ impl DynTyExt for DynTy { } } -pub trait TraitRefExt { +pub(crate) trait TraitRefExt { fn hir_trait_id(&self) -> TraitId; } 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 80945adedbfd..7ad76f35b1f2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -24,7 +24,6 @@ use crate::{ lower::{Diagnostics, GenericDefaults, GenericPredicates}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, - traits::NextTraitSolveResult, }; #[query_group::query_group] 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 af6795e6018a..80b65ace77cd 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 @@ -18,7 +18,7 @@ use hir_def::{ use hir_expand::name::Name; use rustc_type_ir::inherent::{IntoKind, SliceLike}; use span::Edition; -use stdx::{always, never}; +use stdx::{always, never, variance::PhantomCovariantLifetime}; use crate::{ InferenceResult, @@ -299,8 +299,8 @@ impl<'a, 'db> PatCtxt<'a, 'db> { } } -impl HirDisplay for Pat<'_> { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Pat<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { match &*self.kind { PatKind::Wild => write!(f, "_"), PatKind::Never => write!(f, "!"), @@ -356,7 +356,7 @@ impl HirDisplay for Pat<'_> { .filter(|p| !matches!(*p.pattern.kind, PatKind::Wild)) .map(|p| { printed += 1; - WriteWith(|f| { + WriteWith::new(|f| { write!( f, "{}: ", @@ -382,7 +382,7 @@ impl HirDisplay for Pat<'_> { if num_fields != 0 || variant.is_none() { write!(f, "(")?; let subpats = (0..num_fields).map(|i| { - WriteWith(move |f| { + WriteWith::new(move |f| { let fid = LocalFieldId::from_raw((i as u32).into()); if let Some(p) = subpatterns.get(i) && p.field == fid @@ -420,15 +420,24 @@ impl HirDisplay for Pat<'_> { } } -struct WriteWith(F) +struct WriteWith<'db, F>(F, PhantomCovariantLifetime<'db>) where - F: Fn(&mut HirFormatter<'_>) -> Result<(), HirDisplayError>; + F: Fn(&mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError>; -impl HirDisplay for WriteWith +impl<'db, F> WriteWith<'db, F> where - F: Fn(&mut HirFormatter<'_>) -> Result<(), HirDisplayError>, + F: Fn(&mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError>, { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + fn new(f: F) -> Self { + Self(f, PhantomCovariantLifetime::new()) + } +} + +impl<'db, F> HirDisplay<'db> for WriteWith<'db, F> +where + F: Fn(&mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError>, +{ + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { (self.0)(f) } } 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 bcd93c6699cc..210e1ac52e58 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -8,7 +8,6 @@ use std::{ }; use base_db::Crate; -use chalk_ir::{BoundVar, Safety, TyKind}; use either::Either; use hir_def::{ FindPathConfig, GeneralConstId, GenericDefId, HasModule, LocalFieldId, Lookup, ModuleDefId, @@ -36,39 +35,33 @@ use rustc_apfloat::{ Float, ieee::{Half as f16, Quad as f128}, }; +use rustc_ast_ir::FloatTy; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, CoroutineArgsParts, RegionKind, - inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike}, + AliasTyKind, CoroutineArgsParts, RegionKind, Upcast, + inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _, Tys as _}, }; use smallvec::SmallVec; use span::Edition; use stdx::never; use triomphe::Arc; -use crate::next_solver::infer::traits::ObligationCause; -use crate::next_solver::{infer::DbInternerInferExt, mapping::NextSolverToChalk}; use crate::{ - AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const, ConstScalar, - ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, - LifetimeOutlives, MemoryMap, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, - TraitEnvironment, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, consteval, + CallableDefId, FnAbi, ImplTraitId, MemoryMap, TraitEnvironment, consteval, db::{HirDatabase, InternedClosure}, - from_assoc_type_id, from_placeholder_idx, generics::generics, - infer::normalize, layout::Layout, - lt_from_placeholder_idx, mir::pad16, next_solver::{ - BoundExistentialPredicate, DbInterner, GenericArgs, SolverDefId, - mapping::{ - ChalkToNextSolver, convert_args_for_result, convert_const_for_result, - convert_region_for_result, convert_ty_for_result, - }, + AliasTy, Clause, ClauseKind, Const, ConstKind, DbInterner, EarlyBinder, + ExistentialPredicate, FnSig, GenericArg, GenericArgs, PolyFnSig, Region, SolverDefId, Term, + TraitRef, Ty, TyKind, TypingMode, + abi::Safety, + infer::{DbInternerInferExt, traits::ObligationCause}, + mapping::ChalkToNextSolver, }, - primitive, to_assoc_type_id, - utils::{self, ClosureSubst, detect_variant_from_bytes}, + primitive, + utils::{self, detect_variant_from_bytes}, }; pub trait HirWrite: fmt::Write { @@ -82,9 +75,10 @@ impl HirWrite for String {} // `core::Formatter` will ignore metadata impl HirWrite for fmt::Formatter<'_> {} -pub struct HirFormatter<'a> { +pub struct HirFormatter<'a, 'db> { /// The database handle - pub db: &'a dyn HirDatabase, + pub db: &'db dyn HirDatabase, + pub interner: DbInterner<'db>, /// The sink to write into fmt: &'a mut dyn HirWrite, /// A buffer to intercept writes with, this allows us to track the overall size of the formatted output. @@ -103,7 +97,7 @@ pub struct HirFormatter<'a> { display_lifetimes: DisplayLifetime, display_kind: DisplayKind, display_target: DisplayTarget, - bounds_formatting_ctx: BoundsFormattingCtx, + bounds_formatting_ctx: BoundsFormattingCtx<'db>, } // FIXME: To consider, ref and dyn trait lifetimes can be omitted if they are `'_`, path args should @@ -121,7 +115,7 @@ pub enum DisplayLifetime { } #[derive(Default)] -enum BoundsFormattingCtx { +enum BoundsFormattingCtx<'db> { Entered { /// We can have recursive bounds like the following case: /// ```ignore @@ -131,14 +125,14 @@ enum BoundsFormattingCtx { /// ``` /// So, record the projection types met while formatting bounds and //. prevent recursing into their bounds to avoid infinite loops. - projection_tys_met: FxHashSet, + projection_tys_met: FxHashSet>, }, #[default] Exited, } -impl BoundsFormattingCtx { - fn contains(&mut self, proj: &ProjectionTy) -> bool { +impl<'db> BoundsFormattingCtx<'db> { + fn contains(&self, proj: &AliasTy<'db>) -> bool { match self { BoundsFormattingCtx::Entered { projection_tys_met } => { projection_tys_met.contains(proj) @@ -148,7 +142,7 @@ impl BoundsFormattingCtx { } } -impl HirFormatter<'_> { +impl<'db> HirFormatter<'_, 'db> { fn start_location_link(&mut self, location: ModuleDefId) { self.fmt.start_location_link(location); } @@ -159,7 +153,7 @@ impl HirFormatter<'_> { fn format_bounds_with T>( &mut self, - target: ProjectionTy, + target: AliasTy<'db>, format_bounds: F, ) -> T { match self.bounds_formatting_ctx { @@ -181,52 +175,28 @@ impl HirFormatter<'_> { } } - fn render_lifetime(&self, lifetime: &Lifetime) -> bool { + fn render_region(&self, lifetime: Region<'db>) -> bool { match self.display_lifetimes { DisplayLifetime::Always => true, - DisplayLifetime::OnlyStatic => matches!(***lifetime.interned(), LifetimeData::Static), + DisplayLifetime::OnlyStatic => matches!(lifetime.kind(), RegionKind::ReStatic), DisplayLifetime::OnlyNamed => { - matches!(***lifetime.interned(), LifetimeData::Placeholder(_)) + matches!(lifetime.kind(), RegionKind::ReEarlyParam(_)) } - DisplayLifetime::OnlyNamedOrStatic => matches!( - ***lifetime.interned(), - LifetimeData::Static | LifetimeData::Placeholder(_) - ), - DisplayLifetime::Never => false, - } - } - - fn render_region(&self, lifetime: crate::next_solver::Region<'_>) -> bool { - match self.display_lifetimes { - DisplayLifetime::Always => true, - DisplayLifetime::OnlyStatic => { - matches!(lifetime.kind(), rustc_type_ir::RegionKind::ReStatic) + DisplayLifetime::OnlyNamedOrStatic => { + matches!(lifetime.kind(), RegionKind::ReStatic | RegionKind::ReEarlyParam(_)) } - DisplayLifetime::OnlyNamed => { - matches!( - lifetime.kind(), - rustc_type_ir::RegionKind::RePlaceholder(_) - | rustc_type_ir::RegionKind::ReEarlyParam(_) - ) - } - DisplayLifetime::OnlyNamedOrStatic => matches!( - lifetime.kind(), - rustc_type_ir::RegionKind::ReStatic - | rustc_type_ir::RegionKind::RePlaceholder(_) - | rustc_type_ir::RegionKind::ReEarlyParam(_) - ), DisplayLifetime::Never => false, } } } -pub trait HirDisplay { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError>; +pub trait HirDisplay<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError>; /// Returns a `Display`able type that is human-readable. fn into_displayable<'a>( &'a self, - db: &'a dyn HirDatabase, + db: &'db dyn HirDatabase, max_size: Option, limited_size: Option, omit_verbose_types: bool, @@ -234,7 +204,7 @@ pub trait HirDisplay { display_kind: DisplayKind, closure_style: ClosureStyle, show_container_bounds: bool, - ) -> HirDisplayWrapper<'a, Self> + ) -> HirDisplayWrapper<'a, 'db, Self> where Self: Sized, { @@ -260,9 +230,9 @@ pub trait HirDisplay { /// Use this for showing types to the user (e.g. diagnostics) fn display<'a>( &'a self, - db: &'a dyn HirDatabase, + db: &'db dyn HirDatabase, display_target: DisplayTarget, - ) -> HirDisplayWrapper<'a, Self> + ) -> HirDisplayWrapper<'a, 'db, Self> where Self: Sized, { @@ -284,10 +254,10 @@ pub trait HirDisplay { /// Use this for showing types to the user where space is constrained (e.g. doc popups) fn display_truncated<'a>( &'a self, - db: &'a dyn HirDatabase, + db: &'db dyn HirDatabase, max_size: Option, display_target: DisplayTarget, - ) -> HirDisplayWrapper<'a, Self> + ) -> HirDisplayWrapper<'a, 'db, Self> where Self: Sized, { @@ -309,10 +279,10 @@ pub trait HirDisplay { /// Use this for showing definitions which may contain too many items, like `trait`, `struct`, `enum` fn display_limited<'a>( &'a self, - db: &'a dyn HirDatabase, + db: &'db dyn HirDatabase, limited_size: Option, display_target: DisplayTarget, - ) -> HirDisplayWrapper<'a, Self> + ) -> HirDisplayWrapper<'a, 'db, Self> where Self: Sized, { @@ -334,13 +304,16 @@ pub trait HirDisplay { /// Use this when generating code (e.g. assists) fn display_source_code<'a>( &'a self, - db: &'a dyn HirDatabase, + db: &'db dyn HirDatabase, module_id: ModuleId, allow_opaque: bool, ) -> Result { let mut result = String::new(); + let interner = + DbInterner::new_with(db, Some(module_id.krate()), module_id.containing_block()); match self.hir_fmt(&mut HirFormatter { db, + interner, fmt: &mut result, buf: String::with_capacity(20), curr_size: 0, @@ -364,9 +337,9 @@ pub trait HirDisplay { /// Returns a String representation of `self` for test purposes fn display_test<'a>( &'a self, - db: &'a dyn HirDatabase, + db: &'db dyn HirDatabase, display_target: DisplayTarget, - ) -> HirDisplayWrapper<'a, Self> + ) -> HirDisplayWrapper<'a, 'db, Self> where Self: Sized, { @@ -388,10 +361,10 @@ pub trait HirDisplay { /// the container for functions fn display_with_container_bounds<'a>( &'a self, - db: &'a dyn HirDatabase, + db: &'db dyn HirDatabase, show_container_bounds: bool, display_target: DisplayTarget, - ) -> HirDisplayWrapper<'a, Self> + ) -> HirDisplayWrapper<'a, 'db, Self> where Self: Sized, { @@ -410,7 +383,7 @@ pub trait HirDisplay { } } -impl HirFormatter<'_> { +impl<'db> HirFormatter<'_, 'db> { pub fn krate(&self) -> Crate { self.display_target.krate } @@ -419,7 +392,7 @@ impl HirFormatter<'_> { self.display_target.edition } - pub fn write_joined( + pub fn write_joined>( &mut self, iter: impl IntoIterator, sep: &str, @@ -536,8 +509,8 @@ impl From for HirDisplayError { } } -pub struct HirDisplayWrapper<'a, T> { - db: &'a dyn HirDatabase, +pub struct HirDisplayWrapper<'a, 'db, T> { + db: &'db dyn HirDatabase, t: &'a T, max_size: Option, limited_size: Option, @@ -564,10 +537,17 @@ pub enum ClosureStyle { Hide, } -impl HirDisplayWrapper<'_, T> { +impl<'db, T: HirDisplay<'db>> HirDisplayWrapper<'_, 'db, T> { pub fn write_to(&self, f: &mut F) -> Result<(), HirDisplayError> { + let krate = self.display_target.krate; + let block = match self.display_kind { + DisplayKind::SourceCode { target_module_id, .. } => target_module_id.containing_block(), + DisplayKind::Diagnostics | DisplayKind::Test => None, + }; + let interner = DbInterner::new_with(self.db, Some(krate), block); self.t.hir_fmt(&mut HirFormatter { db: self.db, + interner, fmt: f, buf: String::with_capacity(self.max_size.unwrap_or(20)), curr_size: 0, @@ -594,9 +574,9 @@ impl HirDisplayWrapper<'_, T> { } } -impl fmt::Display for HirDisplayWrapper<'_, T> +impl<'db, T> fmt::Display for HirDisplayWrapper<'_, 'db, T> where - T: HirDisplay, + T: HirDisplay<'db>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.write_to(f) { @@ -614,196 +594,146 @@ where const TYPE_HINT_TRUNCATION: &str = "…"; -impl HirDisplay for &T { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db, T: HirDisplay<'db>> HirDisplay<'db> for &T { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { HirDisplay::hir_fmt(*self, f) } } -impl HirDisplay for Interned { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db, T: HirDisplay<'db> + Internable> HirDisplay<'db> for Interned { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { HirDisplay::hir_fmt(self.as_ref(), f) } } -impl HirDisplay for ProjectionTy { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - if f.should_truncate() { - return write!(f, "{TYPE_HINT_TRUNCATION}"); - } - let trait_ref = self.trait_ref(f.db); - let self_ty = trait_ref.self_type_parameter(Interner); +fn write_projection<'db>( + f: &mut HirFormatter<'_, 'db>, + alias: &AliasTy<'db>, +) -> Result<(), HirDisplayError> { + 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::Placeholder(idx) = self_ty.kind(Interner) - && !f.bounds_formatting_ctx.contains(self) - { - let db = f.db; - let id = from_placeholder_idx(db, *idx).0; - let generics = generics(db, id.parent); - - let substs = generics.placeholder_subst(db); - let bounds = db - .generic_predicates(id.parent) - .iter() - .map(|pred| pred.clone().substitute(Interner, &substs)) + // 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 = + f.db.generic_predicates_ns(param.id.parent()) + .instantiate_identity() + .into_iter() + .flatten() .filter(|wc| { - let ty = match wc.skip_binders() { - WhereClause::Implemented(tr) => tr.self_type_parameter(Interner), - WhereClause::TypeOutlives(t) => t.ty.clone(), - // We shouldn't be here if these exist - WhereClause::AliasEq(_) | WhereClause::LifetimeOutlives(_) => { - return false; - } + let ty = match wc.kind().skip_binder() { + ClauseKind::Trait(tr) => tr.self_ty(), + ClauseKind::TypeOutlives(t) => t.0, + _ => return false, }; - let TyKind::Alias(AliasTy::Projection(proj)) = ty.kind(Interner) else { + let TyKind::Alias(AliasTyKind::Projection, a) = ty.kind() else { return false; }; - proj == self + a == *alias }) .collect::>(); - if !bounds.is_empty() { - return f.format_bounds_with(self.clone(), |f| { - write_bounds_like_dyn_trait_with_prefix( - f, - "impl", - Either::Left( - &TyKind::Alias(AliasTy::Projection(self.clone())).intern(Interner), - ), - &bounds, - SizedByDefault::NotSized, - ) - }); - } + if !bounds.is_empty() { + return f.format_bounds_with(*alias, |f| { + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + Either::Left(Ty::new_alias(f.interner, AliasTyKind::Projection, *alias)), + &bounds, + SizedByDefault::NotSized, + ) + }); } - - write!(f, "<")?; - self_ty.hir_fmt(f)?; - write!(f, " as ")?; - trait_ref.hir_fmt(f)?; - write!( - f, - ">::{}", - f.db.type_alias_signature(from_assoc_type_id(self.associated_ty_id)) - .name - .display(f.db, f.edition()) - )?; - let proj_params = - &self.substitution.as_slice(Interner)[trait_ref.substitution.len(Interner)..]; - 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, + ">::{}", + f.db.type_alias_signature(alias.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 HirDisplay for OpaqueTy { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - if f.should_truncate() { - return write!(f, "{TYPE_HINT_TRUNCATION}"); - } - - self.substitution.at(Interner, 0).hir_fmt(f) - } -} - -impl HirDisplay for GenericArg { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - match self.interned() { - crate::GenericArgData::Ty(ty) => ty.hir_fmt(f), - crate::GenericArgData::Lifetime(lt) => lt.hir_fmt(f), - crate::GenericArgData::Const(c) => c.hir_fmt(f), +impl<'db> HirDisplay<'db> for GenericArg<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { + match self { + GenericArg::Ty(ty) => ty.hir_fmt(f), + GenericArg::Lifetime(lt) => lt.hir_fmt(f), + GenericArg::Const(c) => c.hir_fmt(f), } } } -impl<'db> HirDisplay for crate::next_solver::GenericArg<'db> { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Const<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { match self.kind() { - rustc_type_ir::GenericArgKind::Type(ty) => ty.hir_fmt(f), - rustc_type_ir::GenericArgKind::Lifetime(lt) => lt.hir_fmt(f), - rustc_type_ir::GenericArgKind::Const(c) => c.hir_fmt(f), - } - } -} - -impl HirDisplay for Const { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - let c = self.to_nextsolver(DbInterner::new_with(f.db, None, None)); - c.hir_fmt(f) - } -} - -impl<'db> HirDisplay for crate::next_solver::Const<'db> { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - match self.kind() { - rustc_type_ir::ConstKind::Placeholder(_) => write!(f, ""), - rustc_type_ir::ConstKind::Bound(db, bound_const) => { + ConstKind::Placeholder(_) => write!(f, ""), + ConstKind::Bound(db, bound_const) => { write!(f, "?{}.{}", db.as_u32(), bound_const.var.as_u32()) } - rustc_type_ir::ConstKind::Infer(..) => write!(f, "#c#"), - rustc_type_ir::ConstKind::Param(param) => { + ConstKind::Infer(..) => write!(f, "#c#"), + ConstKind::Param(param) => { let generics = generics(f.db, param.id.parent()); let param_data = &generics[param.id.local_id()]; write!(f, "{}", param_data.name().unwrap().display(f.db, f.edition()))?; Ok(()) } - rustc_type_ir::ConstKind::Value(const_bytes) => render_const_scalar_ns( + ConstKind::Value(const_bytes) => render_const_scalar( f, &const_bytes.value.inner().memory, &const_bytes.value.inner().memory_map, const_bytes.ty, ), - rustc_type_ir::ConstKind::Unevaluated(unev) => { + ConstKind::Unevaluated(unev) => { let c = match unev.def { SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), _ => unreachable!(), }; write!(f, "{}", c.name(f.db))?; - hir_fmt_generics_ns(f, unev.args.as_slice(), c.generic_def(f.db), None)?; + hir_fmt_generics(f, unev.args.as_slice(), c.generic_def(f.db), None)?; Ok(()) } - rustc_type_ir::ConstKind::Error(..) => f.write_char('_'), - rustc_type_ir::ConstKind::Expr(..) => write!(f, ""), + ConstKind::Error(..) => f.write_char('_'), + ConstKind::Expr(..) => write!(f, ""), } } } -fn render_const_scalar( - f: &mut HirFormatter<'_>, +fn render_const_scalar<'db>( + f: &mut HirFormatter<'_, 'db>, b: &[u8], - memory_map: &MemoryMap<'_>, - ty: &Ty, + memory_map: &MemoryMap<'db>, + ty: Ty<'db>, ) -> Result<(), HirDisplayError> { let trait_env = TraitEnvironment::empty(f.krate()); - let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); - let ty = normalize(f.db, trait_env.clone(), ty.clone()); - let ty = ty.to_nextsolver(interner); - render_const_scalar_inner(f, b, memory_map, ty, trait_env) -} - -fn render_const_scalar_ns( - f: &mut HirFormatter<'_>, - b: &[u8], - memory_map: &MemoryMap<'_>, - ty: crate::next_solver::Ty<'_>, -) -> Result<(), HirDisplayError> { - let trait_env = TraitEnvironment::empty(f.krate()); - let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); - let infcx = interner.infer_ctxt().build(rustc_type_ir::TypingMode::PostAnalysis); + let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis); let ty = infcx.at(&ObligationCause::new(), trait_env.env).deeply_normalize(ty).unwrap_or(ty); render_const_scalar_inner(f, b, memory_map, ty, trait_env) } fn render_const_scalar_inner<'db>( - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, b: &[u8], - memory_map: &MemoryMap<'_>, - ty: crate::next_solver::Ty<'db>, + memory_map: &MemoryMap<'db>, + ty: Ty<'db>, trait_env: Arc>, ) -> Result<(), HirDisplayError> { - use rustc_type_ir::TyKind; + use TyKind; match ty.kind() { TyKind::Bool => write!(f, "{}", b[0] != 0), TyKind::Char => { @@ -822,7 +752,7 @@ fn render_const_scalar_inner<'db>( write!(f, "{it}") } TyKind::Float(fl) => match fl { - rustc_type_ir::FloatTy::F16 => { + FloatTy::F16 => { // FIXME(#17451): Replace with builtins once they are stabilised. let it = f16::from_bits(u16::from_le_bytes(b.try_into().unwrap()).into()); let s = it.to_string(); @@ -833,15 +763,15 @@ fn render_const_scalar_inner<'db>( write!(f, "{s}") } } - rustc_type_ir::FloatTy::F32 => { + FloatTy::F32 => { let it = f32::from_le_bytes(b.try_into().unwrap()); write!(f, "{it:?}") } - rustc_type_ir::FloatTy::F64 => { + FloatTy::F64 => { let it = f64::from_le_bytes(b.try_into().unwrap()); write!(f, "{it:?}") } - rustc_type_ir::FloatTy::F128 => { + FloatTy::F128 => { // FIXME(#17451): Replace with builtins once they are stabilised. let it = f128::from_bits(u128::from_le_bytes(b.try_into().unwrap())); let s = it.to_string(); @@ -890,7 +820,7 @@ fn render_const_scalar_inner<'db>( f.write_str(", ")?; } let offset = size_one * i; - render_const_scalar_ns(f, &bytes[offset..offset + size_one], memory_map, ty)?; + render_const_scalar(f, &bytes[offset..offset + size_one], memory_map, ty)?; } f.write_str("]") } @@ -908,7 +838,7 @@ fn render_const_scalar_inner<'db>( return f.write_str(""); }; f.write_str("&")?; - render_const_scalar_ns(f, bytes, memory_map, t) + render_const_scalar(f, bytes, memory_map, t) } TyKind::Adt(adt, _) if b.len() == 2 * size_of::() => match adt.def_id().0 { hir_def::AdtId::StructId(s) => { @@ -938,7 +868,7 @@ fn render_const_scalar_inner<'db>( return f.write_str(""); }; f.write_str("&")?; - render_const_scalar_ns(f, bytes, memory_map, t) + render_const_scalar(f, bytes, memory_map, t) } }, TyKind::Tuple(tys) => { @@ -959,7 +889,7 @@ fn render_const_scalar_inner<'db>( continue; }; let size = layout.size.bytes_usize(); - render_const_scalar_ns(f, &b[offset..offset + size], memory_map, ty)?; + render_const_scalar(f, &b[offset..offset + size], memory_map, ty)?; } f.write_str(")") } @@ -972,7 +902,7 @@ fn render_const_scalar_inner<'db>( hir_def::AdtId::StructId(s) => { let data = f.db.struct_signature(s); write!(f, "{}", data.name.display(f.db, f.edition()))?; - let field_types = f.db.field_types(s.into()); + let field_types = f.db.field_types_ns(s.into()); render_variant_after_name( s.fields(f.db), f, @@ -1004,7 +934,7 @@ fn render_const_scalar_inner<'db>( .1 .display(f.db, f.edition()) )?; - let field_types = f.db.field_types(var_id.into()); + let field_types = f.db.field_types_ns(var_id.into()); render_variant_after_name( var_id.fields(f.db), f, @@ -1041,7 +971,7 @@ fn render_const_scalar_inner<'db>( f.write_str(", ")?; } let offset = size_one * i; - render_const_scalar_ns(f, &b[offset..offset + size_one], memory_map, ty)?; + render_const_scalar(f, &b[offset..offset + size_one], memory_map, ty)?; } f.write_str("]") } @@ -1067,28 +997,24 @@ fn render_const_scalar_inner<'db>( fn render_variant_after_name<'db>( data: &VariantFields, - f: &mut HirFormatter<'_>, - field_types: &ArenaMap>, + f: &mut HirFormatter<'_, 'db>, + field_types: &ArenaMap>>, trait_env: Arc>, layout: &Layout, - args: GenericArgs<'_>, + args: GenericArgs<'db>, b: &[u8], - memory_map: &MemoryMap<'_>, + memory_map: &MemoryMap<'db>, ) -> Result<(), HirDisplayError> { - let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); match data.shape { FieldsShape::Record | FieldsShape::Tuple => { - let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| { + let render_field = |f: &mut HirFormatter<'_, 'db>, id: LocalFieldId| { let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize(); - let ty = field_types[id] - .clone() - .substitute(Interner, &convert_args_for_result(interner, args.as_slice())); - let Ok(layout) = f.db.layout_of_ty(ty.to_nextsolver(interner), trait_env.clone()) - else { + let ty = field_types[id].instantiate(f.interner, args); + let Ok(layout) = f.db.layout_of_ty(ty, trait_env.clone()) else { return f.write_str(""); }; let size = layout.size.bytes_usize(); - render_const_scalar(f, &b[offset..offset + size], memory_map, &ty) + render_const_scalar(f, &b[offset..offset + size], memory_map, ty) }; let mut it = data.fields().iter(); if matches!(data.shape, FieldsShape::Record) { @@ -1120,33 +1046,17 @@ fn render_variant_after_name<'db>( } } -impl HirDisplay for BoundVar { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - write!(f, "?{}.{}", self.debruijn.depth(), self.index) - } -} - -impl HirDisplay for Ty { +impl<'db> HirDisplay<'db> for Ty<'db> { fn hir_fmt( &self, - f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_>, + f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_, 'db>, ) -> Result<(), HirDisplayError> { - let ty = self.to_nextsolver(DbInterner::new_with(db, None, None)); - ty.hir_fmt(f) - } -} - -impl<'db> HirDisplay for crate::next_solver::Ty<'db> { - fn hir_fmt( - &self, - f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_>, - ) -> Result<(), HirDisplayError> { - let interner = DbInterner::new_with(db, None, None); + let interner = f.interner; if f.should_truncate() { return write!(f, "{TYPE_HINT_TRUNCATION}"); } - use rustc_type_ir::TyKind; + use TyKind; match self.kind() { TyKind::Never => write!(f, "!")?, TyKind::Str => write!(f, "str")?, @@ -1164,14 +1074,14 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { write!(f, "[")?; t.hir_fmt(f)?; write!(f, "; ")?; - convert_const_for_result(interner, c).hir_fmt(f)?; + c.hir_fmt(f)?; write!(f, "]")?; } kind @ (TyKind::RawPtr(t, m) | TyKind::Ref(_, t, m)) => { if let TyKind::Ref(l, _, _) = kind { f.write_char('&')?; if f.render_region(l) { - convert_region_for_result(interner, l).hir_fmt(f)?; + l.hir_fmt(f)?; f.write_char(' ')?; } match m { @@ -1190,32 +1100,18 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } // FIXME: all this just to decide whether to use parentheses... - let contains_impl_fn = |bounds: &[QuantifiedWhereClause]| { - bounds.iter().any(|bound| { - if let WhereClause::Implemented(trait_ref) = bound.skip_binders() { - let trait_ = trait_ref.hir_trait_id(); - fn_traits(db, trait_).any(|it| it == trait_) - } else { - false - } - }) - }; - let contains_impl_fn_ns = |bounds: &[BoundExistentialPredicate<'_>]| { - bounds.iter().any(|bound| match bound.skip_binder() { - rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { - let trait_ = trait_ref.def_id.0; - fn_traits(db, trait_).any(|it| it == trait_) - } - _ => false, - }) - }; let (preds_to_print, has_impl_fn_pred) = match t.kind() { TyKind::Dynamic(bounds, region) => { + let contains_impl_fn = + bounds.iter().any(|bound| match bound.skip_binder() { + ExistentialPredicate::Trait(trait_ref) => { + let trait_ = trait_ref.def_id.0; + fn_traits(db, trait_).any(|it| it == trait_) + } + _ => false, + }); let render_lifetime = f.render_region(region); - ( - bounds.len() + render_lifetime as usize, - contains_impl_fn_ns(bounds.as_slice()), - ) + (bounds.len() + render_lifetime as usize, contains_impl_fn) } TyKind::Alias(AliasTyKind::Opaque, ty) => { let opaque_ty_id = match ty.def_id { @@ -1225,28 +1121,25 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id); if let ImplTraitId::ReturnTypeImplTrait(func, idx) = impl_trait_id { let datas = db - .return_type_impl_traits(func) + .return_type_impl_traits_ns(func) .expect("impl trait id without data"); - let data = - (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); - let bounds = data.substitute( - Interner, - &convert_args_for_result(interner, ty.args.as_slice()), - ); - let mut len = bounds.skip_binders().len(); + let data = (*datas).as_ref().map_bound(|rpit| { + &rpit.impl_traits[idx.to_nextsolver(interner)].predicates + }); + let bounds = + || data.iter_instantiated_copied(f.interner, ty.args.as_slice()); + let mut len = bounds().count(); // Don't count Sized but count when it absent // (i.e. when explicit ?Sized bound is set). let default_sized = SizedByDefault::Sized { anchor: func.krate(db) }; - let sized_bounds = bounds - .skip_binders() - .iter() + let sized_bounds = bounds() .filter(|b| { matches!( - b.skip_binders(), - WhereClause::Implemented(trait_ref) + b.kind().skip_binder(), + ClauseKind::Trait(trait_ref) if default_sized.is_sized_trait( - trait_ref.hir_trait_id(), + trait_ref.def_id().0, db, ), ) @@ -1259,7 +1152,15 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } } - (len, contains_impl_fn(bounds.skip_binders())) + let contains_impl_fn = bounds().any(|bound| { + if let ClauseKind::Trait(trait_ref) = bound.kind().skip_binder() { + let trait_ = trait_ref.def_id().0; + fn_traits(db, trait_).any(|it| it == trait_) + } else { + false + } + }); + (len, contains_impl_fn) } else { (0, false) } @@ -1291,31 +1192,28 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } } TyKind::FnPtr(sig, header) => { - let sig = CallableSig::from_fn_sig_and_header(interner, sig, header); + let sig = sig.with(header); sig.hir_fmt(f)?; } TyKind::FnDef(def, args) => { let def = def.0; - let sig = db - .callable_item_signature(def) - .instantiate(interner, args) - .skip_binder() - .to_chalk(interner); + let sig = db.callable_item_signature(def).instantiate(interner, args); if f.display_kind.is_source_code() { // `FnDef` is anonymous and there's no surface syntax for it. Show it as a // function pointer type. return sig.hir_fmt(f); } - if let Safety::Unsafe = sig.safety { + if let Safety::Unsafe = sig.safety() { write!(f, "unsafe ")?; } - if !matches!(sig.abi, FnAbi::Rust | FnAbi::RustCall) { + if !matches!(sig.abi(), FnAbi::Rust | FnAbi::RustCall) { f.write_str("extern \"")?; - f.write_str(sig.abi.as_str())?; + f.write_str(sig.abi().as_str())?; f.write_str("\" ")?; } + let sig = sig.skip_binder(); write!(f, "fn ")?; f.start_location_link(def.into()); match def { @@ -1338,13 +1236,12 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { }; f.end_location_link(); - let parameters = convert_args_for_result(interner, args.as_slice()); - if parameters.len(Interner) > 0 { + if args.len() > 0 { 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 parameters = parameters.as_slice(Interner); + let parameters = args.as_slice(); debug_assert_eq!( parameters.len(), parent_len + self_param as usize + type_ + const_ + impl_ + lifetime @@ -1389,9 +1286,9 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } } write!(f, "(")?; - f.write_joined(sig.params(), ", ")?; + f.write_joined(sig.inputs(), ", ")?; write!(f, ")")?; - let ret = sig.ret(); + let ret = sig.output(); if !ret.is_unit() { write!(f, " -> ")?; ret.hir_fmt(f)?; @@ -1434,27 +1331,9 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } f.end_location_link(); - hir_fmt_generics( - f, - convert_args_for_result(interner, parameters.as_slice()).as_slice(Interner), - Some(def.def_id().0.into()), - None, - )?; - } - TyKind::Alias(AliasTyKind::Projection, alias_ty) => { - let type_alias = match alias_ty.def_id { - SolverDefId::TypeAliasId(id) => id, - _ => unreachable!(), - }; - let parameters = convert_args_for_result(interner, alias_ty.args.as_slice()); - - let projection_ty = ProjectionTy { - associated_ty_id: to_assoc_type_id(type_alias), - substitution: parameters.clone(), - }; - - projection_ty.hir_fmt(f)?; + hir_fmt_generics(f, parameters.as_slice(), Some(def.def_id().0.into()), None)?; } + TyKind::Alias(AliasTyKind::Projection, alias_ty) => write_projection(f, &alias_ty)?, TyKind::Foreign(alias) => { let type_alias = db.type_alias_signature(alias.0); f.start_location_link(alias.0.into()); @@ -1466,7 +1345,6 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { SolverDefId::InternedOpaqueTyId(id) => id, _ => unreachable!(), }; - let parameters = convert_args_for_result(interner, alias_ty.args.as_slice()); if !f.display_kind.allows_opaque() { return Err(HirDisplayError::DisplaySourceCodeError( DisplaySourceCodeError::OpaqueType, @@ -1475,32 +1353,41 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id); match impl_trait_id { ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let datas = - db.return_type_impl_traits(func).expect("impl trait id without data"); - let data = - (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); - let bounds = data.substitute(Interner, ¶meters); + let datas = db + .return_type_impl_traits_ns(func) + .expect("impl trait id without data"); + let data = (*datas).as_ref().map_bound(|rpit| { + &rpit.impl_traits[idx.to_nextsolver(interner)].predicates + }); + let bounds = data + .iter_instantiated_copied(interner, alias_ty.args.as_slice()) + .collect::>(); let krate = func.krate(db); write_bounds_like_dyn_trait_with_prefix( f, "impl", - Either::Left(&convert_ty_for_result(interner, *self)), - bounds.skip_binders(), + Either::Left(*self), + &bounds, SizedByDefault::Sized { anchor: krate }, )?; // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution } ImplTraitId::TypeAliasImplTrait(alias, idx) => { - let datas = - db.type_alias_impl_traits(alias).expect("impl trait id without data"); - let data = (*datas).as_ref().map(|it| it.impl_traits[idx].bounds.clone()); - let bounds = data.substitute(Interner, ¶meters); + let datas = db + .type_alias_impl_traits_ns(alias) + .expect("impl trait id without data"); + let data = (*datas).as_ref().map_bound(|rpit| { + &rpit.impl_traits[idx.to_nextsolver(interner)].predicates + }); + let bounds = data + .iter_instantiated_copied(interner, alias_ty.args.as_slice()) + .collect::>(); let krate = alias.krate(db); write_bounds_like_dyn_trait_with_prefix( f, "impl", - Either::Left(&convert_ty_for_result(interner, *self)), - bounds.skip_binders(), + Either::Left(*self), + &bounds, SizedByDefault::Sized { anchor: krate }, )?; } @@ -1528,14 +1415,13 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { f.end_location_link(); } write!(f, " = ")?; - parameters.at(Interner, 0).hir_fmt(f)?; + alias_ty.args.type_at(0).hir_fmt(f)?; write!(f, ">")?; } } } TyKind::Closure(id, substs) => { let id = id.0; - let substs = convert_args_for_result(interner, substs.as_slice()); if f.display_kind.is_source_code() { if !f.display_kind.allows_opaque() { return Err(HirDisplayError::DisplaySourceCodeError( @@ -1556,12 +1442,16 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } ClosureStyle::ClosureWithSubst => { write!(f, "{{closure#{:?}}}", salsa::plumbing::AsId::as_id(&id).index())?; - return hir_fmt_generics(f, substs.as_slice(Interner), None, None); + return hir_fmt_generics(f, substs.as_slice(), None, None); } _ => (), } - let sig = ClosureSubst(&substs).sig_ty(db).callable_sig(db); + let sig = substs + .split_closure_args_untupled() + .closure_sig_as_fn_ptr_ty + .callable_sig(interner); if let Some(sig) = sig { + let sig = sig.skip_binder(); let InternedClosure(def, _) = db.lookup_intern_closure(id); let infer = db.infer(def); let (_, kind) = infer.closure_info(id); @@ -1570,22 +1460,22 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { ClosureStyle::RANotation => write!(f, "|")?, _ => unreachable!(), } - if sig.params().is_empty() { + if sig.inputs().is_empty() { } else if f.should_truncate() { write!(f, "{TYPE_HINT_TRUNCATION}")?; } else { - f.write_joined(sig.params(), ", ")?; + f.write_joined(sig.inputs(), ", ")?; }; match f.closure_style { ClosureStyle::ImplFn => write!(f, ")")?, ClosureStyle::RANotation => write!(f, "|")?, _ => unreachable!(), } - if f.closure_style == ClosureStyle::RANotation || !sig.ret().is_unit() { + if f.closure_style == ClosureStyle::RANotation || !sig.output().is_unit() { write!(f, " -> ")?; // FIXME: We display `AsyncFn` as `-> impl Future`, but this is hard to fix because // we don't have a trait environment here, required to normalize `::Output`. - sig.ret().hir_fmt(f)?; + sig.output().hir_fmt(f)?; } } else { write!(f, "{{closure}}")?; @@ -1593,6 +1483,8 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } TyKind::Placeholder(_) => write!(f, "{{placeholder}}")?, 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 param_data = &generics[param.id.local_id()]; match param_data { @@ -1608,35 +1500,23 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { )? } TypeParamProvenance::ArgumentImplTrait => { - let substs = generics.placeholder_subst(db); let bounds = db - .generic_predicates(param.id.parent()) - .iter() - .map(|pred| pred.clone().substitute(Interner, &substs)) - .filter(|wc| match wc.skip_binders() { - WhereClause::Implemented(tr) => { - tr.self_type_parameter(Interner) - == convert_ty_for_result(interner, *self) - } - WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(proj), - ty: _, - }) => { - proj.self_type_parameter(db) - == convert_ty_for_result(interner, *self) - } - WhereClause::AliasEq(_) => false, - WhereClause::TypeOutlives(to) => { - to.ty == convert_ty_for_result(interner, *self) - } - WhereClause::LifetimeOutlives(_) => false, + .generic_predicates_ns(param.id.parent()) + .instantiate_identity() + .into_iter() + .flatten() + .filter(|wc| match wc.kind().skip_binder() { + ClauseKind::Trait(tr) => tr.self_ty() == *self, + ClauseKind::Projection(proj) => proj.self_ty() == *self, + ClauseKind::TypeOutlives(to) => to.0 == *self, + _ => false, }) .collect::>(); let krate = param.id.parent().module(db).krate(); write_bounds_like_dyn_trait_with_prefix( f, "impl", - Either::Left(&convert_ty_for_result(interner, *self)), + Either::Left(*self), &bounds, SizedByDefault::Sized { anchor: krate }, )?; @@ -1647,42 +1527,34 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } } } - TyKind::Bound(debruijn_index, ty) => { - let idx = chalk_ir::BoundVar { - debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), - index: ty.var.as_usize(), - }; - idx.hir_fmt(f)? + TyKind::Bound(debruijn, ty) => { + write!(f, "?{}.{}", debruijn.as_usize(), ty.var.as_usize())? } - TyKind::Dynamic(..) => { - let ty = convert_ty_for_result(interner, *self); - let chalk_ir::TyKind::Dyn(dyn_ty) = ty.kind(Interner) else { unreachable!() }; - // Reorder bounds to satisfy `write_bounds_like_dyn_trait()`'s expectation. - // FIXME: `Iterator::partition_in_place()` or `Vec::extract_if()` may make it - // more efficient when either of them hits stable. - let mut bounds: SmallVec<[_; 4]> = - dyn_ty.bounds.skip_binders().iter(Interner).cloned().collect(); - let (auto_traits, others): (SmallVec<[_; 4]>, _) = - bounds.drain(1..).partition(|b| b.skip_binders().trait_id().is_some()); - bounds.extend(others); - bounds.extend(auto_traits); + TyKind::Dynamic(bounds, region) => { + // We want to put auto traits after principal traits, regardless of their written order. + let mut bounds_to_display = SmallVec::<[_; 4]>::new(); + let mut auto_trait_bounds = SmallVec::<[_; 4]>::new(); + for bound in bounds.iter() { + let clause = bound.with_self_ty(interner, *self); + match bound.skip_binder() { + ExistentialPredicate::Trait(_) | ExistentialPredicate::Projection(_) => { + bounds_to_display.push(clause); + } + ExistentialPredicate::AutoTrait(_) => auto_trait_bounds.push(clause), + } + } + bounds_to_display.append(&mut auto_trait_bounds); - if f.render_lifetime(&dyn_ty.lifetime) { - // we skip the binders in `write_bounds_like_dyn_trait_with_prefix` - bounds.push(Binders::empty( - Interner, - chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { - ty: ty.clone(), - lifetime: dyn_ty.lifetime.clone(), - }), - )); + if f.render_region(region) { + bounds_to_display + .push(rustc_type_ir::OutlivesPredicate(*self, region).upcast(interner)); } write_bounds_like_dyn_trait_with_prefix( f, "dyn", - Either::Left(&ty), - &bounds, + Either::Left(*self), + &bounds_to_display, SizedByDefault::NotSized, )?; } @@ -1722,11 +1594,11 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } } -fn hir_fmt_generics( - f: &mut HirFormatter<'_>, - parameters: &[GenericArg], +fn hir_fmt_generics<'db>( + f: &mut HirFormatter<'_, 'db>, + parameters: &[GenericArg<'db>], generic_def: Option, - self_: Option<&Ty>, + self_: Option>, ) -> Result<(), HirDisplayError> { if parameters.is_empty() { return Ok(()); @@ -1743,70 +1615,23 @@ fn hir_fmt_generics( Ok(()) } -fn hir_fmt_generics_ns<'db>( - f: &mut HirFormatter<'_>, - parameters: &[crate::next_solver::GenericArg<'db>], +fn generic_args_sans_defaults<'ga, 'db>( + f: &mut HirFormatter<'_, 'db>, generic_def: Option, - self_: Option>, -) -> Result<(), HirDisplayError> { - if parameters.is_empty() { - return Ok(()); - } - - let parameters_to_write = generic_args_sans_defaults_ns(f, generic_def, parameters); - - if !parameters_to_write.is_empty() { - write!(f, "<")?; - hir_fmt_generic_arguments_ns(f, parameters_to_write, self_)?; - write!(f, ">")?; - } - - Ok(()) -} - -fn generic_args_sans_defaults<'ga>( - f: &mut HirFormatter<'_>, - generic_def: Option, - parameters: &'ga [GenericArg], -) -> &'ga [GenericArg] { + parameters: &'ga [GenericArg<'db>], +) -> &'ga [GenericArg<'db>] { if f.display_kind.is_source_code() || f.omit_verbose_types() { - match generic_def - .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) - .filter(|it| !it.is_empty()) - { + match generic_def.map(|generic_def_id| f.db.generic_defaults_ns(generic_def_id)) { None => parameters, Some(default_parameters) => { - let should_show = |arg: &GenericArg, i: usize| { - let is_err = |arg: &GenericArg| match arg.data(Interner) { - chalk_ir::GenericArgData::Lifetime(it) => { - *it.data(Interner) == LifetimeData::Error - } - chalk_ir::GenericArgData::Ty(it) => *it.kind(Interner) == TyKind::Error, - chalk_ir::GenericArgData::Const(it) => matches!( - it.data(Interner).value, - ConstValue::Concrete(ConcreteConst { - interned: ConstScalar::Unknown, - .. - }) - ), - }; - // if the arg is error like, render it to inform the user - if is_err(arg) { - return true; - } - // otherwise, if the arg is equal to the param default, hide it (unless the - // default is an error which can happen for the trait Self type) - match default_parameters.get(i) { - None => true, - Some(default_parameter) => { - // !is_err(default_parameter.skip_binders()) - // && - arg != &default_parameter.clone().substitute(Interner, ¶meters[..i]) - } + let should_show = |arg: GenericArg<'db>, i: usize| match default_parameters.get(i) { + None => true, + Some(default_parameter) => { + arg != default_parameter.instantiate(f.interner, ¶meters[..i]) } }; let mut default_from = 0; - for (i, parameter) in parameters.iter().enumerate() { + for (i, ¶meter) in parameters.iter().enumerate() { if should_show(parameter, i) { default_from = i + 1; } @@ -1820,114 +1645,30 @@ fn generic_args_sans_defaults<'ga>( } fn hir_fmt_generic_args<'db>( - f: &mut HirFormatter<'_>, - parameters: &[crate::next_solver::GenericArg<'db>], + f: &mut HirFormatter<'_, 'db>, + parameters: &[GenericArg<'db>], generic_def: Option, - self_: Option>, + self_: Option>, ) -> Result<(), HirDisplayError> { if parameters.is_empty() { return Ok(()); } - let parameters_to_write = generic_args_sans_defaults_ns(f, generic_def, parameters); + let parameters_to_write = generic_args_sans_defaults(f, generic_def, parameters); if !parameters_to_write.is_empty() { write!(f, "<")?; - hir_fmt_generic_arguments_ns(f, parameters_to_write, self_)?; + hir_fmt_generic_arguments(f, parameters_to_write, self_)?; write!(f, ">")?; } Ok(()) } -fn generic_args_sans_defaults_ns<'ga, 'db>( - f: &mut HirFormatter<'_>, - generic_def: Option, - parameters: &'ga [crate::next_solver::GenericArg<'db>], -) -> &'ga [crate::next_solver::GenericArg<'db>] { - let interner = DbInterner::new_with(f.db, Some(f.krate()), None); - if f.display_kind.is_source_code() || f.omit_verbose_types() { - match generic_def - .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) - .filter(|it| !it.is_empty()) - { - None => parameters, - Some(default_parameters) => { - let should_show = |arg: &crate::next_solver::GenericArg<'db>, i: usize| { - let is_err = |arg: &crate::next_solver::GenericArg<'db>| match arg.kind() { - rustc_type_ir::GenericArgKind::Lifetime(it) => { - matches!(it.kind(), RegionKind::ReError(..)) - } - rustc_type_ir::GenericArgKind::Type(it) => { - matches!(it.kind(), rustc_type_ir::TyKind::Error(..)) - } - rustc_type_ir::GenericArgKind::Const(it) => { - matches!(it.kind(), rustc_type_ir::ConstKind::Error(..),) - } - }; - // if the arg is error like, render it to inform the user - if is_err(arg) { - return true; - } - // otherwise, if the arg is equal to the param default, hide it (unless the - // default is an error which can happen for the trait Self type) - match default_parameters.get(i) { - None => true, - Some(default_parameter) => { - // !is_err(default_parameter.skip_binders()) - // && - arg != &default_parameter - .clone() - .substitute( - Interner, - &convert_args_for_result(interner, ¶meters[..i]), - ) - .to_nextsolver(interner) - } - } - }; - let mut default_from = 0; - for (i, parameter) in parameters.iter().enumerate() { - if should_show(parameter, i) { - default_from = i + 1; - } - } - ¶meters[0..default_from] - } - } - } else { - parameters - } -} - -fn hir_fmt_generic_arguments( - f: &mut HirFormatter<'_>, - parameters: &[GenericArg], - self_: Option<&Ty>, -) -> Result<(), HirDisplayError> { - let mut first = true; - let lifetime_offset = parameters.iter().position(|arg| arg.lifetime(Interner).is_some()); - - let (ty_or_const, lifetimes) = match lifetime_offset { - Some(offset) => parameters.split_at(offset), - None => (parameters, &[][..]), - }; - for generic_arg in lifetimes.iter().chain(ty_or_const) { - if !mem::take(&mut first) { - write!(f, ", ")?; - } - match self_ { - self_ @ Some(_) if generic_arg.ty(Interner) == self_ => write!(f, "Self")?, - _ => generic_arg.hir_fmt(f)?, - } - } - Ok(()) -} - -fn hir_fmt_generic_arguments_ns<'db>( - f: &mut HirFormatter<'_>, - parameters: &[crate::next_solver::GenericArg<'db>], - self_: Option>, +fn hir_fmt_generic_arguments<'db>( + f: &mut HirFormatter<'_, 'db>, + parameters: &[GenericArg<'db>], + self_: Option>, ) -> Result<(), HirDisplayError> { let mut first = true; let lifetime_offset = parameters.iter().position(|arg| arg.region().is_some()); @@ -1948,9 +1689,28 @@ fn hir_fmt_generic_arguments_ns<'db>( Ok(()) } -impl HirDisplay for CallableSig { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - let CallableSig { params_and_return: _, is_varargs, safety, abi: _ } = *self; +fn hir_fmt_tys<'db>( + f: &mut HirFormatter<'_, 'db>, + tys: &[Ty<'db>], + self_: Option>, +) -> Result<(), HirDisplayError> { + let mut first = true; + + for ty in tys { + if !mem::take(&mut first) { + write!(f, ", ")?; + } + match self_ { + Some(self_) if *ty == self_ => write!(f, "Self")?, + _ => ty.hir_fmt(f)?, + } + } + Ok(()) +} + +impl<'db> HirDisplay<'db> for PolyFnSig<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { + let FnSig { inputs_and_output, c_variadic, safety, abi: _ } = self.skip_binder(); if let Safety::Unsafe = safety { write!(f, "unsafe ")?; } @@ -1961,16 +1721,16 @@ impl HirDisplay for CallableSig { // f.write_str("\" ")?; // } write!(f, "fn(")?; - f.write_joined(self.params(), ", ")?; - if is_varargs { - if self.params().is_empty() { + f.write_joined(inputs_and_output.inputs(), ", ")?; + if c_variadic { + if inputs_and_output.inputs().is_empty() { write!(f, "...")?; } else { write!(f, ", ...")?; } } write!(f, ")")?; - let ret = self.ret(); + let ret = inputs_and_output.output(); if !ret.is_unit() { write!(f, " -> ")?; ret.hir_fmt(f)?; @@ -1979,6 +1739,15 @@ impl HirDisplay for CallableSig { } } +impl<'db> HirDisplay<'db> for Term<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { + match self { + Term::Ty(it) => it.hir_fmt(f), + Term::Const(it) => it.hir_fmt(f), + } + } +} + fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator + '_ { let krate = trait_.lookup(db).container.krate(); utils::fn_traits(db, krate) @@ -2002,11 +1771,11 @@ impl SizedByDefault { } } -pub fn write_bounds_like_dyn_trait_with_prefix( - f: &mut HirFormatter<'_>, +pub fn write_bounds_like_dyn_trait_with_prefix<'db>( + f: &mut HirFormatter<'_, 'db>, prefix: &str, - this: Either<&Ty, &Lifetime>, - predicates: &[QuantifiedWhereClause], + this: Either, Region<'db>>, + predicates: &[Clause<'db>], default_sized: SizedByDefault, ) -> Result<(), HirDisplayError> { write!(f, "{prefix}")?; @@ -2020,10 +1789,10 @@ pub fn write_bounds_like_dyn_trait_with_prefix( } } -fn write_bounds_like_dyn_trait( - f: &mut HirFormatter<'_>, - this: Either<&Ty, &Lifetime>, - predicates: &[QuantifiedWhereClause], +fn write_bounds_like_dyn_trait<'db>( + f: &mut HirFormatter<'_, 'db>, + this: Either, Region<'db>>, + predicates: &[Clause<'db>], default_sized: SizedByDefault, ) -> Result<(), HirDisplayError> { // Note: This code is written to produce nice results (i.e. @@ -2036,10 +1805,10 @@ fn write_bounds_like_dyn_trait( let mut angle_open = false; let mut is_fn_trait = false; let mut is_sized = false; - for p in predicates.iter() { - match p.skip_binders() { - WhereClause::Implemented(trait_ref) => { - let trait_ = trait_ref.hir_trait_id(); + for p in predicates { + match p.kind().skip_binder() { + ClauseKind::Trait(trait_ref) => { + let trait_ = trait_ref.def_id().0; if default_sized.is_sized_trait(trait_, f.db) { is_sized = true; if matches!(default_sized, SizedByDefault::Sized { .. }) { @@ -2064,31 +1833,30 @@ fn write_bounds_like_dyn_trait( write!(f, "{}", f.db.trait_signature(trait_).name.display(f.db, f.edition()))?; f.end_location_link(); if is_fn_trait { - if let [self_, params @ ..] = trait_ref.substitution.as_slice(Interner) - && let Some(args) = - params.first().and_then(|it| it.assert_ty_ref(Interner).as_tuple()) + if let [_self, params @ ..] = trait_ref.trait_ref.args.as_slice() + && let Some(args) = params.first().and_then(|it| it.ty()?.as_tuple()) { write!(f, "(")?; - hir_fmt_generic_arguments(f, args.as_slice(Interner), self_.ty(Interner))?; + hir_fmt_tys(f, args.as_slice(), Some(trait_ref.trait_ref.self_ty()))?; write!(f, ")")?; } } else { let params = generic_args_sans_defaults( f, Some(trait_.into()), - trait_ref.substitution.as_slice(Interner), + trait_ref.trait_ref.args.as_slice(), ); - if let [self_, params @ ..] = params + if let [_self, params @ ..] = params && !params.is_empty() { write!(f, "<")?; - hir_fmt_generic_arguments(f, params, self_.ty(Interner))?; + hir_fmt_generic_arguments(f, params, Some(trait_ref.trait_ref.self_ty()))?; // there might be assoc type bindings, so we leave the angle brackets open angle_open = true; } } } - WhereClause::TypeOutlives(to) if Either::Left(&to.ty) == this => { + ClauseKind::TypeOutlives(to) if Either::Left(to.0) == this => { if !is_fn_trait && angle_open { write!(f, ">")?; angle_open = false; @@ -2096,10 +1864,9 @@ fn write_bounds_like_dyn_trait( if !first { write!(f, " + ")?; } - to.lifetime.hir_fmt(f)?; + to.1.hir_fmt(f)?; } - WhereClause::TypeOutlives(_) => {} - WhereClause::LifetimeOutlives(lo) if Either::Right(&lo.a) == this => { + ClauseKind::RegionOutlives(lo) if Either::Right(lo.0) == this => { if !is_fn_trait && angle_open { write!(f, ">")?; angle_open = false; @@ -2107,17 +1874,16 @@ fn write_bounds_like_dyn_trait( if !first { write!(f, " + ")?; } - lo.b.hir_fmt(f)?; + lo.1.hir_fmt(f)?; } - WhereClause::LifetimeOutlives(_) => {} - WhereClause::AliasEq(alias_eq) if is_fn_trait => { + ClauseKind::Projection(projection) if is_fn_trait => { is_fn_trait = false; - if !alias_eq.ty.is_unit() { + if !projection.term.as_type().is_some_and(|it| it.is_unit()) { write!(f, " -> ")?; - alias_eq.ty.hir_fmt(f)?; + projection.term.hir_fmt(f)?; } } - WhereClause::AliasEq(AliasEq { ty, alias }) => { + ClauseKind::Projection(projection) => { // in types in actual Rust, these will always come // after the corresponding Implemented predicate if angle_open { @@ -2126,28 +1892,22 @@ fn write_bounds_like_dyn_trait( write!(f, "<")?; angle_open = true; } - if let AliasTy::Projection(proj) = alias { - let assoc_ty_id = from_assoc_type_id(proj.associated_ty_id); - let type_alias = f.db.type_alias_signature(assoc_ty_id); - f.start_location_link(assoc_ty_id.into()); - write!(f, "{}", type_alias.name.display(f.db, f.edition()))?; - f.end_location_link(); + let assoc_ty_id = projection.def_id().expect_type_alias(); + let type_alias = f.db.type_alias_signature(assoc_ty_id); + f.start_location_link(assoc_ty_id.into()); + write!(f, "{}", type_alias.name.display(f.db, f.edition()))?; + f.end_location_link(); - let proj_arg_count = generics(f.db, assoc_ty_id.into()).len_self(); - let parent_len = proj.substitution.len(Interner) - proj_arg_count; - if proj_arg_count > 0 { - write!(f, "<")?; - hir_fmt_generic_arguments( - f, - &proj.substitution.as_slice(Interner)[parent_len..], - None, - )?; - write!(f, ">")?; - } - write!(f, " = ")?; + let own_args = projection.projection_term.own_args(f.interner); + if !own_args.is_empty() { + write!(f, "<")?; + hir_fmt_generic_arguments(f, own_args.as_slice(), None)?; + write!(f, ">")?; } - ty.hir_fmt(f)?; + write!(f, " = ")?; + projection.term.hir_fmt(f)?; } + _ => {} } first = false; } @@ -2177,154 +1937,49 @@ fn write_bounds_like_dyn_trait( Ok(()) } -impl HirDisplay for TraitRef { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - let trait_ = self.hir_trait_id(); - f.start_location_link(trait_.into()); - write!(f, "{}", f.db.trait_signature(trait_).name.display(f.db, f.edition()))?; - f.end_location_link(); - let substs = self.substitution.as_slice(Interner); - hir_fmt_generics(f, &substs[1..], None, substs[0].ty(Interner)) - } -} - -impl<'db> HirDisplay for crate::next_solver::TraitRef<'db> { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for TraitRef<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { let trait_ = self.def_id.0; f.start_location_link(trait_.into()); write!(f, "{}", f.db.trait_signature(trait_).name.display(f.db, f.edition()))?; f.end_location_link(); let substs = self.args.as_slice(); - hir_fmt_generic_args(f, &substs[1..], None, substs[0].ty()) + hir_fmt_generic_args(f, &substs[1..], None, Some(self.self_ty())) } } -impl HirDisplay for WhereClause { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - if f.should_truncate() { - return write!(f, "{TYPE_HINT_TRUNCATION}"); - } - - match self { - WhereClause::Implemented(trait_ref) => { - trait_ref.self_type_parameter(Interner).hir_fmt(f)?; - write!(f, ": ")?; - trait_ref.hir_fmt(f)?; - } - WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => { - write!(f, "<")?; - let trait_ref = &projection_ty.trait_ref(f.db); - trait_ref.self_type_parameter(Interner).hir_fmt(f)?; - write!(f, " as ")?; - trait_ref.hir_fmt(f)?; - write!(f, ">::",)?; - let type_alias = from_assoc_type_id(projection_ty.associated_ty_id); - f.start_location_link(type_alias.into()); - write!( - f, - "{}", - f.db.type_alias_signature(type_alias).name.display(f.db, f.edition()), - )?; - f.end_location_link(); - write!(f, " = ")?; - ty.hir_fmt(f)?; - } - WhereClause::AliasEq(_) => write!(f, "{{error}}")?, - - // FIXME implement these - WhereClause::TypeOutlives(..) => {} - WhereClause::LifetimeOutlives(..) => {} - } - Ok(()) - } -} - -impl HirDisplay for LifetimeOutlives { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - self.a.hir_fmt(f)?; - write!(f, ": ")?; - self.b.hir_fmt(f) - } -} - -impl HirDisplay for Lifetime { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - self.interned().hir_fmt(f) - } -} - -impl HirDisplay for LifetimeData { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - match self { - LifetimeData::Placeholder(idx) => { - let id = lt_from_placeholder_idx(f.db, *idx).0; - let generics = generics(f.db, id.parent); - let param_data = &generics[id.local_id]; - write!(f, "{}", param_data.name.display(f.db, f.edition()))?; - Ok(()) - } - LifetimeData::BoundVar(idx) => idx.hir_fmt(f), - LifetimeData::InferenceVar(_) => write!(f, "_"), - LifetimeData::Static => write!(f, "'static"), - LifetimeData::Error => { - if cfg!(test) { - write!(f, "'?") - } else { - write!(f, "'_") - } - } - LifetimeData::Erased => write!(f, "'"), - LifetimeData::Phantom(void, _) => match *void {}, - } - } -} - -impl<'db> HirDisplay for crate::next_solver::Region<'db> { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Region<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { match self.kind() { - rustc_type_ir::RegionKind::ReEarlyParam(param) => { + RegionKind::ReEarlyParam(param) => { let generics = generics(f.db, param.id.parent); let param_data = &generics[param.id.local_id]; write!(f, "{}", param_data.name.display(f.db, f.edition()))?; Ok(()) } - rustc_type_ir::RegionKind::ReBound(db, idx) => { + RegionKind::ReBound(db, idx) => { write!(f, "?{}.{}", db.as_u32(), idx.var.as_u32()) } - rustc_type_ir::RegionKind::ReVar(_) => write!(f, "_"), - rustc_type_ir::RegionKind::ReStatic => write!(f, "'static"), - rustc_type_ir::RegionKind::ReError(..) => { + RegionKind::ReVar(_) => write!(f, "_"), + RegionKind::ReStatic => write!(f, "'static"), + RegionKind::ReError(..) => { if cfg!(test) { write!(f, "'?") } else { write!(f, "'_") } } - rustc_type_ir::RegionKind::ReErased => write!(f, "'"), - rustc_type_ir::RegionKind::RePlaceholder(_) => write!(f, ""), - rustc_type_ir::RegionKind::ReLateParam(_) => write!(f, ""), + RegionKind::ReErased => write!(f, "'"), + RegionKind::RePlaceholder(_) => write!(f, ""), + RegionKind::ReLateParam(_) => write!(f, ""), } } } -impl HirDisplay for DomainGoal { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - match self { - DomainGoal::Holds(wc) => { - write!(f, "Holds(")?; - wc.hir_fmt(f)?; - write!(f, ")")?; - } - _ => write!(f, "_")?, - } - Ok(()) - } -} - -pub fn write_visibility( +pub fn write_visibility<'db>( module_id: ModuleId, vis: Visibility, - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, ) -> Result<(), HirDisplayError> { match vis { Visibility::Public => write!(f, "pub "), @@ -2346,28 +2001,30 @@ pub fn write_visibility( } } -pub trait HirDisplayWithExpressionStore { +pub trait HirDisplayWithExpressionStore<'db> { fn hir_fmt( &self, - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore, ) -> Result<(), HirDisplayError>; } -impl HirDisplayWithExpressionStore for &'_ T { +impl<'db, T: ?Sized + HirDisplayWithExpressionStore<'db>> HirDisplayWithExpressionStore<'db> + for &'_ T +{ fn hir_fmt( &self, - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore, ) -> Result<(), HirDisplayError> { T::hir_fmt(&**self, f, store) } } -pub fn hir_display_with_store<'a, T: HirDisplayWithExpressionStore + 'a>( +pub fn hir_display_with_store<'a, 'db, T: HirDisplayWithExpressionStore<'db> + 'a>( value: T, store: &'a ExpressionStore, -) -> impl HirDisplay + 'a { +) -> impl HirDisplay<'db> + 'a { ExpressionStoreAdapter(value, store) } @@ -2379,15 +2036,15 @@ impl<'a, T> ExpressionStoreAdapter<'a, T> { } } -impl HirDisplay for ExpressionStoreAdapter<'_, T> { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db, T: HirDisplayWithExpressionStore<'db>> HirDisplay<'db> for ExpressionStoreAdapter<'_, T> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { T::hir_fmt(&self.0, f, self.1) } } -impl HirDisplayWithExpressionStore for LifetimeRefId { +impl<'db> HirDisplayWithExpressionStore<'db> for LifetimeRefId { fn hir_fmt( &self, - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore, ) -> Result<(), HirDisplayError> { match &store[*self] { @@ -2407,10 +2064,10 @@ impl HirDisplayWithExpressionStore for LifetimeRefId { } } -impl HirDisplayWithExpressionStore for TypeRefId { +impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { fn hir_fmt( &self, - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore, ) -> Result<(), HirDisplayError> { match &store[*self] { @@ -2536,10 +2193,10 @@ impl HirDisplayWithExpressionStore for TypeRefId { } } -impl HirDisplayWithExpressionStore for ConstRef { +impl<'db> HirDisplayWithExpressionStore<'db> for ConstRef { fn hir_fmt( &self, - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, _store: &ExpressionStore, ) -> Result<(), HirDisplayError> { // FIXME @@ -2549,10 +2206,10 @@ impl HirDisplayWithExpressionStore for ConstRef { } } -impl HirDisplayWithExpressionStore for TypeBound { +impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { fn hir_fmt( &self, - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore, ) -> Result<(), HirDisplayError> { match self { @@ -2593,10 +2250,10 @@ impl HirDisplayWithExpressionStore for TypeBound { } } -impl HirDisplayWithExpressionStore for Path { +impl<'db> HirDisplayWithExpressionStore<'db> for Path { fn hir_fmt( &self, - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore, ) -> Result<(), HirDisplayError> { match (self.type_anchor(), self.kind()) { @@ -2745,10 +2402,10 @@ impl HirDisplayWithExpressionStore for Path { } } -impl HirDisplayWithExpressionStore for hir_def::expr_store::path::GenericArg { +impl<'db> HirDisplayWithExpressionStore<'db> for hir_def::expr_store::path::GenericArg { fn hir_fmt( &self, - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore, ) -> Result<(), HirDisplayError> { match self { 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 e179e41b1cbe..2053a099ed78 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -130,11 +130,16 @@ impl Generics { /// Returns total number of generic parameters in scope, including those from parent. pub(crate) fn len(&self) -> usize { - let parent = self.parent_generics().map_or(0, Generics::len); + let parent = self.len_parent(); let child = self.params.len(); parent + child } + #[inline] + pub(crate) fn len_parent(&self) -> usize { + 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() 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 72498681aca5..041799be9602 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -55,8 +55,7 @@ use stdx::never; use triomphe::Arc; use crate::{ - ImplTraitId, IncorrectGenericsLenKind, Interner, PathLoweringDiagnostic, TargetFeatures, - TraitEnvironment, + ImplTraitId, IncorrectGenericsLenKind, PathLoweringDiagnostic, TargetFeatures, db::{HirDatabase, InternedClosureId, InternedOpaqueTyId}, generics::Generics, infer::{ @@ -77,7 +76,7 @@ use crate::{ DefineOpaqueTypes, traits::{Obligation, ObligationCause}, }, - mapping::{ChalkToNextSolver, NextSolverToChalk}, + mapping::ChalkToNextSolver, }, traits::FnTrait, utils::TargetFeatureIsSafeInTarget, @@ -166,31 +165,6 @@ pub(crate) fn infer_cycle_result( }) } -/// Fully normalize all the types found within `ty` in context of `owner` body definition. -/// -/// This is appropriate to use only after type-check: it assumes -/// that normalization will succeed, for example. -#[tracing::instrument(level = "debug", skip(db))] -pub(crate) fn normalize( - db: &dyn HirDatabase, - trait_env: Arc>, - ty: crate::Ty, -) -> crate::Ty { - // FIXME: TypeFlags::HAS_CT_PROJECTION is not implemented in chalk, so TypeFlags::HAS_PROJECTION only - // works for the type case, so we check array unconditionally. Remove the array part - // when the bug in chalk becomes fixed. - if !ty.data(Interner).flags.intersects(crate::TypeFlags::HAS_PROJECTION) - && !matches!(ty.kind(Interner), crate::TyKind::Array(..)) - { - return ty; - } - let mut table = unify::InferenceTable::new(db, trait_env); - - let ty_with_vars = table.normalize_associated_types_in(ty.to_nextsolver(table.interner())); - table.select_obligations_where_possible(); - table.resolve_completely(ty_with_vars).to_chalk(table.interner()) -} - /// Binding modes inferred for patterns. /// #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] 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 eb01ef104b61..7277617bce8a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -81,22 +81,17 @@ use syntax::ast::{ConstArg, make}; use traits::FnTrait; use triomphe::Arc; -#[cfg(not(debug_assertions))] -use crate::next_solver::ErrorGuaranteed; use crate::{ + builder::{ParamKind, TyBuilder}, + chalk_ext::*, db::HirDatabase, display::{DisplayTarget, HirDisplay}, generics::Generics, infer::unify::InferenceTable, - next_solver::{ - DbInterner, - mapping::{ChalkToNextSolver, NextSolverToChalk, convert_ty_for_result}, - }, + next_solver::DbInterner, }; pub use autoderef::autoderef; -pub use builder::{ParamKind, TyBuilder}; -pub use chalk_ext::*; pub use infer::{ Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref, PointerCast, @@ -156,7 +151,6 @@ pub(crate) type GenericArgData = chalk_ir::GenericArgData; pub(crate) type Ty = chalk_ir::Ty; pub type TyKind = chalk_ir::TyKind; -pub(crate) type TypeFlags = chalk_ir::TypeFlags; pub(crate) type DynTy = chalk_ir::DynTy; pub(crate) type FnPointer = chalk_ir::FnPointer; pub(crate) use chalk_ir::FnSubst; // a re-export so we don't lose the tuple constructor @@ -174,7 +168,6 @@ pub(crate) type ConstValue = chalk_ir::ConstValue; pub(crate) type Const = chalk_ir::Const; pub(crate) type ConstData = chalk_ir::ConstData; -pub(crate) type ConcreteConst = chalk_ir::ConcreteConst; pub(crate) type TraitRef = chalk_ir::TraitRef; pub(crate) type QuantifiedWhereClause = Binders; @@ -382,7 +375,7 @@ pub(crate) fn variable_kinds_from_iter( /// A function signature as seen by type inference: Several parameter types and /// one return type. #[derive(Clone, PartialEq, Eq, Debug)] -pub struct CallableSig { +pub(crate) struct CallableSig { params_and_return: Arc<[Ty]>, is_varargs: bool, safety: Safety, @@ -534,112 +527,6 @@ impl FnAbi { } } -/// A polymorphic function signature. -pub type PolyFnSig = Binders; - -impl CallableSig { - pub fn from_params_and_return( - params: impl Iterator, - ret: Ty, - is_varargs: bool, - safety: Safety, - abi: FnAbi, - ) -> CallableSig { - let mut params_and_return = Vec::with_capacity(params.size_hint().0 + 1); - params_and_return.extend(params); - params_and_return.push(ret); - CallableSig { params_and_return: params_and_return.into(), is_varargs, safety, abi } - } - - pub fn from_def(db: &dyn HirDatabase, def: FnDefId, substs: &Substitution) -> CallableSig { - let callable_def = ToChalk::from_chalk(db, def); - let interner = DbInterner::new_with(db, None, None); - let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); - let sig = db.callable_item_signature(callable_def); - sig.instantiate(interner, args).skip_binder().to_chalk(interner) - } - pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig { - CallableSig { - // FIXME: what to do about lifetime params? -> return PolyFnSig - params_and_return: Arc::from_iter( - fn_ptr - .substitution - .clone() - .shifted_out_to(Interner, DebruijnIndex::ONE) - .expect("unexpected lifetime vars in fn ptr") - .0 - .as_slice(Interner) - .iter() - .map(|arg| arg.assert_ty_ref(Interner).clone()), - ), - is_varargs: fn_ptr.sig.variadic, - safety: fn_ptr.sig.safety, - abi: fn_ptr.sig.abi, - } - } - pub fn from_fn_sig_and_header<'db>( - interner: DbInterner<'db>, - sig: crate::next_solver::Binder<'db, rustc_type_ir::FnSigTys>>, - header: rustc_type_ir::FnHeader>, - ) -> CallableSig { - CallableSig { - // FIXME: what to do about lifetime params? -> return PolyFnSig - params_and_return: Arc::from_iter( - sig.skip_binder() - .inputs_and_output - .iter() - .map(|t| convert_ty_for_result(interner, t)), - ), - is_varargs: header.c_variadic, - safety: match header.safety { - next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, - next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, - }, - abi: header.abi, - } - } - - pub fn to_fn_ptr(&self) -> FnPointer { - FnPointer { - num_binders: 0, - sig: FnSig { abi: self.abi, safety: self.safety, variadic: self.is_varargs }, - substitution: FnSubst(Substitution::from_iter( - Interner, - self.params_and_return.iter().cloned(), - )), - } - } - - pub fn abi(&self) -> FnAbi { - self.abi - } - - pub fn params(&self) -> &[Ty] { - &self.params_and_return[0..self.params_and_return.len() - 1] - } - - pub fn ret(&self) -> &Ty { - &self.params_and_return[self.params_and_return.len() - 1] - } -} - -impl TypeFoldable for CallableSig { - fn try_fold_with( - self, - folder: &mut dyn chalk_ir::fold::FallibleTypeFolder, - outer_binder: DebruijnIndex, - ) -> Result { - let vec = self.params_and_return.to_vec(); - let folded = vec.try_fold_with(folder, outer_binder)?; - Ok(CallableSig { - params_and_return: folded.into(), - is_varargs: self.is_varargs, - safety: self.safety, - abi: self.abi, - }) - } -} - #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum ImplTraitId { ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx), // FIXME(next-solver): Should be crate::nextsolver::ImplTraitIdx. @@ -764,7 +651,12 @@ where #[cfg(debug_assertions)] let error = || Err(()); #[cfg(not(debug_assertions))] - let error = || Ok(crate::next_solver::Ty::new_error(self.interner, ErrorGuaranteed)); + let error = || { + Ok(crate::next_solver::Ty::new_error( + self.interner, + crate::next_solver::ErrorGuaranteed, + )) + }; match t.kind() { crate::next_solver::TyKind::Error(_) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index abca6b6bb9e5..aced46bf806b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -17,17 +17,20 @@ use std::{ use base_db::Crate; use either::Either; -use hir_def::hir::generics::GenericParamDataRef; -use hir_def::item_tree::FieldsShape; use hir_def::{ - AdtId, AssocItemId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, - GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, - StructId, TraitId, TypeAliasId, TypeOrConstParamId, VariantId, + AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, + FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, + LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, + TypeParamId, VariantId, expr_store::{ ExpressionStore, path::{GenericArg, Path}, }, - hir::generics::{TypeOrConstParamData, WherePredicate}, + hir::generics::{ + GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance, + WherePredicate, + }, + item_tree::FieldsShape, lang_item::LangItem, resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, signatures::{FunctionSignature, TraitFlags, TypeAliasFlags}, @@ -36,7 +39,6 @@ use hir_def::{ TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, }, }; -use hir_def::{ConstId, LifetimeParamId, StaticId, TypeParamId}; use hir_expand::name::Name; use intern::{Symbol, sym}; use la_arena::{Arena, ArenaMap, Idx}; @@ -48,20 +50,17 @@ use rustc_type_ir::{ AliasTyKind, ConstKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, OutlivesPredicate, TyKind::{self}, - TypeVisitableExt, + TypeFoldable, TypeFolder, TypeVisitableExt, Upcast, inherent::{GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, Ty as _}, }; -use rustc_type_ir::{TypeFoldable, TypeFolder, Upcast}; use salsa::plumbing::AsId; use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; -use crate::ValueTyDefId; -use crate::next_solver::ParamConst; use crate::{ FnAbi, ImplTraitId, Interner, ParamKind, TraitEnvironment, TyDefId, TyLoweringDiagnostic, - TyLoweringDiagnosticKind, + TyLoweringDiagnosticKind, ValueTyDefId, consteval::{intern_const_ref, path_to_const, unknown_const_as_generic}, db::HirDatabase, generics::{Generics, generics, trait_self_param_idx}, @@ -69,8 +68,8 @@ use crate::{ next_solver::{ AdtDef, AliasTy, Binder, BoundExistentialPredicates, BoundRegionKind, BoundTyKind, BoundVarKind, BoundVarKinds, Clause, Clauses, Const, DbInterner, EarlyBinder, - EarlyParamRegion, ErrorGuaranteed, GenericArgs, ParamEnv, PolyFnSig, Predicate, Region, - SolverDefId, TraitPredicate, TraitRef, Ty, Tys, + EarlyParamRegion, ErrorGuaranteed, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Predicate, + Region, SolverDefId, TraitPredicate, TraitRef, Ty, Tys, abi::Safety, mapping::{ChalkToNextSolver, convert_ty_for_result}, }, @@ -187,8 +186,9 @@ pub struct TyLoweringContext<'db, 'a> { pub(crate) unsized_types: FxHashSet>, pub(crate) diagnostics: Vec, lifetime_elision: LifetimeElisionKind<'db>, - /// We disallow referencing generic parameters that have an index greater than or equal to this number. - disallow_params_after: u32, + /// When lowering the defaults for generic params, this contains the index of the currently lowered param. + /// We disallow referring to later params, or to ADT's `Self`. + lowering_param_default: Option, } impl<'db, 'a> TyLoweringContext<'db, 'a> { @@ -213,7 +213,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { unsized_types: FxHashSet::default(), diagnostics: Vec::new(), lifetime_elision, - disallow_params_after: u32::MAX, + lowering_param_default: None, } } @@ -249,8 +249,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self } - pub(crate) fn disallow_params_after(&mut self, after: u32) { - self.disallow_params_after = after; + pub(crate) fn lowering_param_default(&mut self, index: u32) { + self.lowering_param_default = Some(index); } pub(crate) fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) { @@ -333,8 +333,13 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.generics.get_or_init(|| generics(self.db, self.def)) } + fn param_index_is_disallowed(&self, index: u32) -> bool { + self.lowering_param_default + .is_some_and(|disallow_params_after| index >= disallow_params_after) + } + fn type_param(&mut self, id: TypeParamId, index: u32, name: Symbol) -> Ty<'db> { - if index >= self.disallow_params_after { + if self.param_index_is_disallowed(index) { // FIXME: Report an error. Ty::new_error(self.interner, ErrorGuaranteed) } else { @@ -343,7 +348,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } fn const_param(&mut self, id: ConstParamId, index: u32) -> Const<'db> { - if index >= self.disallow_params_after { + if self.param_index_is_disallowed(index) { // FIXME: Report an error. Const::error(self.interner) } else { @@ -352,7 +357,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } fn region_param(&mut self, id: LifetimeParamId, index: u32) -> Region<'db> { - if index >= self.disallow_params_after { + if self.param_index_is_disallowed(index) { // FIXME: Report an error. Region::error(self.interner) } else { @@ -394,7 +399,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { type_data .name .as_ref() - .map_or_else(|| sym::MISSING_NAME.clone(), |d| d.symbol().clone()), + .map_or_else(|| sym::MISSING_NAME, |d| d.symbol().clone()), ) } &TypeRef::RawPtr(inner, mutability) => { @@ -1603,8 +1608,6 @@ where for pred in maybe_parent_generics.where_predicates() { tracing::debug!(?pred); if filter(maybe_parent_generics.def()) { - // We deliberately use `generics` and not `maybe_parent_generics` here. This is not a mistake! - // If we use the parent generics predicates.extend(ctx.lower_where_predicate( pred, false, @@ -1619,49 +1622,53 @@ where let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); if let Some(sized_trait) = sized_trait { - let (mut generics, mut def_id) = - (crate::next_solver::generics::generics(db, def.into()), def); - loop { - if filter(def_id) { - let self_idx = trait_self_param_idx(db, def_id); - for (idx, p) in generics.own_params.iter().enumerate() { - if let Some(self_idx) = self_idx - && p.index() as usize == self_idx - { - continue; - } - let GenericParamId::TypeParamId(param_id) = p.id else { - continue; - }; - let idx = idx as u32 + generics.parent_count as u32; - let param_ty = Ty::new_param(interner, param_id, idx, p.name.clone()); - if explicitly_unsized_tys.contains(¶m_ty) { - continue; - } - let trait_ref = TraitRef::new_from_args( - interner, - sized_trait.into(), - GenericArgs::new_from_iter(interner, [param_ty.into()]), - ); - let clause = Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), - )); - predicates.push(clause); - } + let mut add_sized_clause = |param_idx, param_id, param_data| { + let ( + GenericParamId::TypeParamId(param_id), + GenericParamDataRef::TypeParamData(param_data), + ) = (param_id, param_data) + else { + return; + }; + + if param_data.provenance == TypeParamProvenance::TraitSelf { + return; } - if let Some(g) = generics.parent { - generics = crate::next_solver::generics::generics(db, g.into()); - def_id = g; - } else { - break; + let param_name = param_data + .name + .as_ref() + .map_or_else(|| sym::MISSING_NAME, |name| name.symbol().clone()); + let param_ty = Ty::new_param(interner, param_id, param_idx, param_name); + if explicitly_unsized_tys.contains(¶m_ty) { + return; } + let trait_ref = TraitRef::new_from_args( + interner, + sized_trait.into(), + GenericArgs::new_from_iter(interner, [param_ty.into()]), + ); + let clause = Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )); + predicates.push(clause); + }; + if generics.parent_generics().is_some_and(|parent| filter(parent.def())) { + generics.iter_parent().enumerate().for_each(|(param_idx, (param_id, param_data))| { + add_sized_clause(param_idx as u32, param_id, param_data); + }); + } + if filter(def) { + let parent_params_len = generics.len_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); + }); } } @@ -1860,10 +1867,7 @@ pub(crate) fn generic_defaults_with_diagnostics_query( p: GenericParamDataRef<'_>, generic_params: &Generics, ) -> (Option>>, bool) { - // Each default can only refer to previous parameters. - // Type variable default referring to parameter coming - // after it is forbidden. - ctx.disallow_params_after(idx as u32); + ctx.lowering_param_default(idx as u32); 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_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs index ef2c392f0861..6bfe266b460c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -314,7 +314,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { self.lower_ty_relative_path(ty, Some(resolution), infer_args) } - fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) { + /// This returns whether to keep the resolution (`true`) of throw it (`false`). + #[must_use] + fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) -> bool { let mut prohibit_generics_on_resolved = |reason| { if self.current_or_prev_segment.args_and_bindings.is_some() { let segment = self.current_segment_u32(); @@ -333,7 +335,13 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam) } TypeNs::AdtSelfType(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + 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. + return false; + } } TypeNs::BuiltinType(_) => { prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy) @@ -346,6 +354,8 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { | TypeNs::TypeAliasId(_) | TypeNs::TraitId(_) => {} } + + true } pub(crate) fn resolve_path_in_type_ns_fully(&mut self) -> Option { @@ -379,11 +389,6 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { self.current_or_prev_segment = segments.get(resolved_segment_idx).expect("should have resolved segment"); - if matches!(self.path, Path::BarePath(..)) { - // Bare paths cannot have generics, so skip them as an optimization. - return Some((resolution, remaining_index)); - } - for (i, mod_segment) in module_segments.iter().enumerate() { if mod_segment.args_and_bindings.is_some() { self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { @@ -403,7 +408,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }); } - self.handle_type_ns_resolution(&resolution); + if !self.handle_type_ns_resolution(&resolution) { + return None; + } Some((resolution, remaining_index)) } @@ -475,7 +482,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { match resolution { ValueNs::ImplSelf(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy); } // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not // E0109 (generic arguments provided for a type that doesn't accept them) for @@ -499,7 +506,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { } } ResolveValueResult::Partial(resolution, _, _) => { - self.handle_type_ns_resolution(resolution); + if !self.handle_type_ns_resolution(resolution) { + return None; + } } }; Some(res) 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 e46edb815918..0c5a64935e49 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 @@ -118,10 +118,10 @@ enum LocalName<'db> { Binding(Name, LocalId<'db>), } -impl<'db> HirDisplay for LocalName<'db> { +impl<'db> HirDisplay<'db> for LocalName<'db> { fn hir_fmt( &self, - f: &mut crate::display::HirFormatter<'_>, + f: &mut crate::display::HirFormatter<'_, 'db>, ) -> Result<(), crate::display::HirDisplayError> { match self { LocalName::Unknown(l) => write!(f, "_{}", u32::from(l.into_raw())), @@ -489,7 +489,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { } } - fn hir_display<'b, T: HirDisplay>(&self, ty: &'b T) -> impl Display + use<'a, 'b, 'db, T> + fn hir_display<'b, T: HirDisplay<'db>>(&self, ty: &'b T) -> impl Display + use<'a, 'b, 'db, T> where 'db: 'b, { 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 2fc1fc4f45a5..c5a1e7d31546 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 @@ -82,7 +82,11 @@ impl<'db> Const<'db> { } pub fn is_ct_infer(&self) -> bool { - matches!(&self.inner().internee, ConstKind::Infer(_)) + matches!(self.kind(), ConstKind::Infer(_)) + } + + pub fn is_error(&self) -> bool { + matches!(self.kind(), ConstKind::Error(_)) } pub fn is_trivially_wf(self) -> bool { 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 9dda9d06da27..6a0a07705a8c 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 @@ -647,6 +647,26 @@ impl<'db> UpcastFrom, ty::OutlivesPredicate, Reg PredicateKind::Clause(ClauseKind::RegionOutlives(from)).upcast(interner) } } +impl<'db> UpcastFrom, ty::OutlivesPredicate, Ty<'db>>> + for Clause<'db> +{ + fn upcast_from( + from: ty::OutlivesPredicate, Ty<'db>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> UpcastFrom, ty::OutlivesPredicate, Region<'db>>> + for Clause<'db> +{ + fn upcast_from( + from: ty::OutlivesPredicate, Region<'db>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.upcast(interner)) + } +} impl<'db> UpcastFrom, PolyRegionOutlivesPredicate<'db>> for Predicate<'db> { fn upcast_from(from: PolyRegionOutlivesPredicate<'db>, tcx: DbInterner<'db>) -> Self { 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 cd125f3af864..35c8a197f52c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -12,22 +12,20 @@ use intern::sym; use rustc_next_trait_solver::solve::{HasChanged, SolverDelegateEvalExt}; use rustc_type_ir::{ InferCtxtLike, TypingMode, - inherent::{IntoKind, SliceLike, Span as _, Ty as _}, + inherent::{IntoKind, SliceLike, Span as _}, solve::Certainty, }; use span::Edition; -use stdx::never; use triomphe::Arc; use crate::{ - AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, - ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause, + AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTyExt, + TraitRefExt, TyKind, WhereClause, db::HirDatabase, - from_assoc_type_id, next_solver::{ DbInterner, GenericArg, ParamEnv, Predicate, SolverContext, Span, infer::{DbInternerInferExt, InferCtxt, traits::ObligationCause}, - mapping::{ChalkToNextSolver, NextSolverToChalk, convert_canonical_args_for_result}, + mapping::{ChalkToNextSolver, convert_canonical_args_for_result}, obligation_ctxt::ObligationCtxt, util::mini_canonicalize, }, @@ -94,47 +92,6 @@ pub fn structurally_normalize_ty<'db>( ty.replace_infer_with_error(infcx.interner) } -pub(crate) fn normalize_projection_query<'db>( - db: &'db dyn HirDatabase, - projection: ProjectionTy, - env: Arc>, -) -> Ty { - if projection.substitution.iter(Interner).any(|arg| { - arg.ty(Interner) - .is_some_and(|ty| ty.data(Interner).flags.intersects(TypeFlags::HAS_TY_INFER)) - }) { - never!( - "Invoking `normalize_projection_query` with a projection type containing inference var" - ); - return TyKind::Error.intern(Interner); - } - - let interner = DbInterner::new_with(db, Some(env.krate), env.block); - // FIXME(next-solver): I believe this should use `PostAnalysis` (this is only used for IDE things), - // but this causes some bug because of our incorrect impl of `type_of_opaque_hir_typeck()` for TAIT - // and async blocks. - let infcx = interner.infer_ctxt().build(TypingMode::Analysis { - defining_opaque_types_and_generators: crate::next_solver::SolverDefIds::new_from_iter( - interner, - [], - ), - }); - let alias_ty = crate::next_solver::Ty::new_alias( - interner, - rustc_type_ir::AliasTyKind::Projection, - crate::next_solver::AliasTy::new( - interner, - from_assoc_type_id(projection.associated_ty_id).into(), - >>::to_nextsolver(&projection.substitution, interner), - ), - ); - let mut ctxt = crate::next_solver::obligation_ctxt::ObligationCtxt::new(&infcx); - let normalized = ctxt - .structurally_normalize_ty(&ObligationCause::dummy(), env.env, alias_ty) - .unwrap_or(alias_ty); - normalized.replace_infer_with_error(interner).to_chalk(interner) -} - fn identity_subst( binders: chalk_ir::CanonicalVarKinds, ) -> chalk_ir::Canonical> { @@ -165,45 +122,6 @@ fn identity_subst( chalk_ir::Canonical { binders, value: identity_subst } } -/// Solve a trait goal using next trait solver. -pub(crate) fn trait_solve_query( - db: &dyn HirDatabase, - krate: Crate, - block: Option, - goal: Canonical>, -) -> NextTraitSolveResult { - let _p = tracing::info_span!("trait_solve_query", detail = ?match &goal.value.goal.data(Interner) { - GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => db - .trait_signature(it.hir_trait_id()) - .name - .display(db, Edition::LATEST) - .to_string(), - GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_owned(), - _ => "??".to_owned(), - }) - .entered(); - - if let GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(projection_ty), - .. - }))) = &goal.value.goal.data(Interner) - && let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner) - { - // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible - return NextTraitSolveResult::Uncertain(identity_subst(goal.binders.clone())); - } - - // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So - // we should get rid of it when talking to chalk. - let goal = goal - .try_fold_with(&mut UnevaluatedConstEvaluatorFolder { db }, DebruijnIndex::INNERMOST) - .unwrap(); - - // We currently don't deal with universes (I think / hope they're not yet - // relevant for our use cases?) - next_trait_solve(db, krate, block, goal) -} - fn solve_nextsolver<'db>( db: &'db dyn HirDatabase, krate: Crate, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index e989e4c006ff..15359922c80e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -25,8 +25,7 @@ use smallvec::{SmallVec, smallvec}; use span::Edition; use crate::{ - ChalkTraitId, Const, ConstScalar, Interner, Substitution, TargetFeatures, TraitRef, - TraitRefExt, Ty, + ChalkTraitId, Const, ConstScalar, Interner, TargetFeatures, TraitRef, TraitRefExt, consteval::unknown_const, db::HirDatabase, layout::{Layout, TagEncoding}, @@ -192,19 +191,6 @@ pub(super) fn associated_type_by_name_including_super_traits( }) } -pub(crate) struct ClosureSubst<'a>(pub(crate) &'a Substitution); - -impl<'a> ClosureSubst<'a> { - pub(crate) fn sig_ty(&self, db: &dyn HirDatabase) -> Ty { - let interner = DbInterner::new_with(db, None, None); - let subst = - >>::to_nextsolver( - self.0, interner, - ); - subst.split_closure_args_untupled().closure_sig_as_fn_ptr_ty.to_chalk(interner) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Unsafety { Safe, diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 49bf843367d3..b31bb248e839 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -11,14 +11,15 @@ use hir_def::{ type_ref::{TypeBound, TypeRef, TypeRefId}, }; use hir_ty::{ - AliasEq, AliasTy, Interner, ProjectionTyExt, TraitRefExt, TyBuilder, TyKind, WhereClause, db::HirDatabase, display::{ HirDisplay, HirDisplayError, HirDisplayWithExpressionStore, HirFormatter, SizedByDefault, hir_display_with_store, write_bounds_like_dyn_trait_with_prefix, write_visibility, }, + next_solver::ClauseKind, }; use itertools::Itertools; +use rustc_type_ir::inherent::IntoKind; use crate::{ Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum, @@ -27,8 +28,8 @@ use crate::{ TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, Variant, }; -impl HirDisplay for Function { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Function { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { let db = f.db; let data = db.function_signature(self.id); let container = self.as_assoc_item(db).map(|it| it.container(db)); @@ -184,7 +185,10 @@ impl HirDisplay for Function { } } -fn write_impl_header(impl_: &Impl, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +fn write_impl_header<'db>( + impl_: &Impl, + f: &mut HirFormatter<'_, 'db>, +) -> Result<(), HirDisplayError> { let db = f.db; f.write_str("impl")?; @@ -202,8 +206,8 @@ fn write_impl_header(impl_: &Impl, f: &mut HirFormatter<'_>) -> Result<(), HirDi Ok(()) } -impl HirDisplay for SelfParam { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for SelfParam { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { let data = f.db.function_signature(self.func); let param = *data.params.first().unwrap(); match &data.store[param] { @@ -228,8 +232,8 @@ impl HirDisplay for SelfParam { } } -impl HirDisplay for Adt { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Adt { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { match self { Adt::Struct(it) => it.hir_fmt(f), Adt::Union(it) => it.hir_fmt(f), @@ -238,8 +242,8 @@ impl HirDisplay for Adt { } } -impl HirDisplay for Struct { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Struct { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { let module_id = self.module(f.db).id; // FIXME: Render repr if its set explicitly? write_visibility(module_id, self.visibility(f.db), f)?; @@ -279,8 +283,8 @@ impl HirDisplay for Struct { } } -impl HirDisplay for Enum { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Enum { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; f.write_str("enum ")?; write!(f, "{}", self.name(f.db).display(f.db, f.edition()))?; @@ -296,8 +300,8 @@ impl HirDisplay for Enum { } } -impl HirDisplay for Union { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Union { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; f.write_str("union ")?; write!(f, "{}", self.name(f.db).display(f.db, f.edition()))?; @@ -312,12 +316,12 @@ impl HirDisplay for Union { } } -fn write_fields( +fn write_fields<'db>( fields: &[Field], has_where_clause: bool, limit: usize, in_line: bool, - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, ) -> Result<(), HirDisplayError> { let count = fields.len().min(limit); let (indent, separator) = if in_line { ("", ' ') } else { (" ", '\n') }; @@ -346,11 +350,11 @@ fn write_fields( Ok(()) } -fn write_variants( +fn write_variants<'db>( variants: &[Variant], has_where_clause: bool, limit: usize, - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, ) -> Result<(), HirDisplayError> { let count = variants.len().min(limit); f.write_char(if !has_where_clause { ' ' } else { '\n' })?; @@ -386,23 +390,23 @@ fn write_variants( Ok(()) } -impl HirDisplay for Field { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Field { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { write_visibility(self.parent.module(f.db).id, self.visibility(f.db), f)?; write!(f, "{}: ", self.name(f.db).display(f.db, f.edition()))?; self.ty(f.db).hir_fmt(f) } } -impl HirDisplay for TupleField { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for TupleField { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { write!(f, "pub {}: ", self.name().display(f.db, f.edition()))?; self.ty(f.db).hir_fmt(f) } } -impl HirDisplay for Variant { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Variant { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { write!(f, "{}", self.name(f.db).display(f.db, f.edition()))?; let data = self.id.fields(f.db); match data.shape { @@ -431,20 +435,20 @@ impl HirDisplay for Variant { } } -impl HirDisplay for Type<'_> { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Type<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { self.ty.hir_fmt(f) } } -impl HirDisplay for TypeNs<'_> { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for TypeNs<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { self.ty.hir_fmt(f) } } -impl HirDisplay for ExternCrateDecl { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for ExternCrateDecl { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; f.write_str("extern crate ")?; write!(f, "{}", self.name(f.db).display(f.db, f.edition()))?; @@ -455,8 +459,8 @@ impl HirDisplay for ExternCrateDecl { } } -impl HirDisplay for GenericParam { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for GenericParam { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { match self { GenericParam::TypeParam(it) => it.hir_fmt(f), GenericParam::ConstParam(it) => it.hir_fmt(f), @@ -465,8 +469,8 @@ impl HirDisplay for GenericParam { } } -impl HirDisplay for TypeOrConstParam { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for TypeOrConstParam { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { match self.split(f.db) { either::Either::Left(it) => it.hir_fmt(f), either::Either::Right(it) => it.hir_fmt(f), @@ -474,27 +478,22 @@ impl HirDisplay for TypeOrConstParam { } } -impl HirDisplay for TypeParam { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for TypeParam { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { let params = f.db.generic_params(self.id.parent()); let param_data = ¶ms[self.id.local_id()]; - let substs = TyBuilder::placeholder_subst(f.db, self.id.parent()); let krate = self.id.parent().krate(f.db).id; - let ty = TyKind::Placeholder(hir_ty::to_placeholder_idx_no_index(f.db, self.id.into())) - .intern(Interner); - let predicates = f.db.generic_predicates(self.id.parent()); + let ty = self.ty(f.db).ty; + let predicates = f.db.generic_predicates_ns(self.id.parent()); let predicates = predicates - .iter() - .cloned() - .map(|pred| pred.substitute(Interner, &substs)) - .filter(|wc| match wc.skip_binders() { - WhereClause::Implemented(tr) => tr.self_type_parameter(Interner) == ty, - WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), ty: _ }) => { - proj.self_type_parameter(f.db) == ty - } - WhereClause::AliasEq(_) => false, - WhereClause::TypeOutlives(to) => to.ty == ty, - WhereClause::LifetimeOutlives(_) => false, + .instantiate_identity() + .into_iter() + .flatten() + .filter(|wc| match wc.kind().skip_binder() { + ClauseKind::Trait(tr) => tr.self_ty() == ty, + ClauseKind::Projection(proj) => proj.self_ty() == ty, + ClauseKind::TypeOutlives(to) => to.0 == ty, + _ => false, }) .collect::>(); @@ -507,7 +506,7 @@ impl HirDisplay for TypeParam { return write_bounds_like_dyn_trait_with_prefix( f, "impl", - Either::Left(&ty), + Either::Left(ty), &predicates, SizedByDefault::Sized { anchor: krate }, ); @@ -523,23 +522,18 @@ impl HirDisplay for TypeParam { } let sized_trait = LangItem::Sized.resolve_trait(f.db, krate); - let has_only_sized_bound = predicates.iter().all(move |pred| match pred.skip_binders() { - WhereClause::Implemented(it) => Some(it.hir_trait_id()) == sized_trait, - _ => false, - }); + let has_only_sized_bound = + predicates.iter().all(move |pred| match pred.kind().skip_binder() { + ClauseKind::Trait(it) => Some(it.def_id().0) == sized_trait, + _ => false, + }); let has_only_not_sized_bound = predicates.is_empty(); if !has_only_sized_bound || has_only_not_sized_bound { let default_sized = SizedByDefault::Sized { anchor: krate }; write_bounds_like_dyn_trait_with_prefix( f, ":", - Either::Left( - &hir_ty::TyKind::Placeholder(hir_ty::to_placeholder_idx_no_index( - f.db, - self.id.into(), - )) - .intern(Interner), - ), + Either::Left(ty), &predicates, default_sized, )?; @@ -548,22 +542,22 @@ impl HirDisplay for TypeParam { } } -impl HirDisplay for LifetimeParam { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for LifetimeParam { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { write!(f, "{}", self.name(f.db).display(f.db, f.edition())) } } -impl HirDisplay for ConstParam { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for ConstParam { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { write!(f, "const {}: ", self.name(f.db).display(f.db, f.edition()))?; self.ty(f.db).hir_fmt(f) } } -fn write_generic_params( +fn write_generic_params<'db>( def: GenericDefId, - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, ) -> Result<(), HirDisplayError> { let (params, store) = f.db.generic_params_and_store(def); if params.iter_lt().next().is_none() @@ -578,7 +572,7 @@ fn write_generic_params( f.write_char('<')?; let mut first = true; - let mut delim = |f: &mut HirFormatter<'_>| { + let mut delim = |f: &mut HirFormatter<'_, 'db>| { if first { first = false; Ok(()) @@ -622,9 +616,9 @@ fn write_generic_params( Ok(()) } -fn write_where_clause( +fn write_where_clause<'db>( def: GenericDefId, - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, ) -> Result { let (params, store) = f.db.generic_params_and_store(def); if !has_disaplayable_predicates(f.db, ¶ms, &store) { @@ -653,10 +647,10 @@ fn has_disaplayable_predicates( }) } -fn write_where_predicates( +fn write_where_predicates<'db>( params: &GenericParams, store: &ExpressionStore, - f: &mut HirFormatter<'_>, + f: &mut HirFormatter<'_, 'db>, ) -> Result<(), HirDisplayError> { use WherePredicate::*; @@ -717,8 +711,8 @@ fn write_where_predicates( Ok(()) } -impl HirDisplay for Const { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Const { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { let db = f.db; let container = self.as_assoc_item(db).map(|it| it.container(db)); let mut module = self.module(db); @@ -738,8 +732,8 @@ impl HirDisplay for Const { } } -impl HirDisplay for Static { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Static { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; let data = f.db.static_signature(self.id); f.write_str("static ")?; @@ -752,14 +746,14 @@ impl HirDisplay for Static { } } -impl HirDisplay for TraitRef<'_> { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for TraitRef<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { self.trait_ref.hir_fmt(f) } } -impl HirDisplay for Trait { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Trait { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { // FIXME(trait-alias) needs special handling to print the equal sign write_trait_header(self, f)?; let def_id = GenericDefId::TraitId(self.id); @@ -798,7 +792,10 @@ impl HirDisplay for Trait { } } -fn write_trait_header(trait_: &Trait, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +fn write_trait_header<'db>( + trait_: &Trait, + f: &mut HirFormatter<'_, 'db>, +) -> Result<(), HirDisplayError> { write_visibility(trait_.module(f.db).id, trait_.visibility(f.db), f)?; let data = f.db.trait_signature(trait_.id); if data.flags.contains(TraitFlags::UNSAFE) { @@ -812,8 +809,8 @@ fn write_trait_header(trait_: &Trait, f: &mut HirFormatter<'_>) -> Result<(), Hi Ok(()) } -impl HirDisplay for TypeAlias { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for TypeAlias { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; let data = f.db.type_alias_signature(self.id); write!(f, "type {}", data.name.display(f.db, f.edition()))?; @@ -835,8 +832,8 @@ impl HirDisplay for TypeAlias { } } -impl HirDisplay for Module { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Module { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { match self.parent(f.db) { Some(m) => write_visibility(m.id, self.visibility(f.db), f)?, None => { @@ -853,8 +850,8 @@ impl HirDisplay for Module { } } -impl HirDisplay for Crate { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Crate { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { match self.display_name(f.db) { Some(name) => write!(f, "extern crate {name}"), None => f.write_str("extern crate {unknown}"), @@ -862,8 +859,8 @@ impl HirDisplay for Crate { } } -impl HirDisplay for Macro { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +impl<'db> HirDisplay<'db> for Macro { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> { match self.id { hir_def::MacroId::Macro2Id(_) => f.write_str("macro"), hir_def::MacroId::MacroRulesId(_) => f.write_str("macro_rules!"), 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 df1800616803..6c4a074d46d6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -9392,7 +9392,7 @@ fn main(a$0: T) {} *a* ```rust - a: T + a: T ``` --- diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index f1aa03c8f267..4aa9eb98a1e1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -384,7 +384,7 @@ fn def_to_non_local_moniker( }) } -fn display(db: &RootDatabase, module: hir::Module, it: T) -> String { +fn display<'db, T: HirDisplay<'db>>(db: &'db RootDatabase, module: hir::Module, it: T) -> String { match it.display_source_code(db, module.into(), true) { Ok(result) => result, // Fallback on display variant that always succeeds 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 db1298385b11..93090e2a0203 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -382,7 +382,7 @@ impl ToNavFromAst for hir::Trait { impl TryToNav for D where - D: HasSource + ToNavFromAst + Copy + HasDocs + HirDisplay + HasCrate, + D: HasSource + ToNavFromAst + Copy + HasDocs + for<'db> HirDisplay<'db> + HasCrate, D::Ast: ast::HasName, { fn try_to_nav( From b2566ff07bcdf70ac368ebc1e1a1a03ed257ef0d Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 15 Oct 2025 19:26:59 +0800 Subject: [PATCH 027/170] Migrate `add_braces` assist, because edit_in_place uses ted - And fix indent Example --- ```rust fn foo() { { match n { Some(n) $0=> foo( 29, 30, ), _ => () }; } } ``` **Before this PR**: ```rust fn main() { { match n { Some(n) => { foo( 29, 30, ) }, _ => () }; } } ``` **After this PR**: ```rust fn foo() { { match n { Some(n) => { foo( 29, 30, ) }, _ => () }; } } ``` --- .../ide-assists/src/handlers/add_braces.rs | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) 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 5af622eaf28b..d855fb771846 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 @@ -1,7 +1,7 @@ use either::Either; use syntax::{ AstNode, - ast::{self, edit_in_place::Indent, syntax_factory::SyntaxFactory}, + ast::{self, edit::AstNodeEdit, syntax_factory::SyntaxFactory}, }; use crate::{AssistContext, AssistId, Assists}; @@ -43,10 +43,10 @@ pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(expr.syntax()); - let block_expr = make.block_expr(None, Some(expr.clone())); - block_expr.indent(expr.indent_level()); + let new_expr = expr.reset_indent().indent(1.into()); + let block_expr = make.block_expr(None, Some(new_expr)); - editor.replace(expr.syntax(), block_expr.syntax()); + editor.replace(expr.syntax(), block_expr.indent(expr.indent_level()).syntax()); editor.add_mappings(make.finish_with_mappings()); builder.add_file_edits(ctx.vfs_file_id(), editor); @@ -171,6 +171,41 @@ fn foo() { ); } + #[test] + fn multiple_indent() { + check_assist( + add_braces, + r#" +fn foo() { + { + match n { + Some(n) $0=> foo( + 29, + 30, + ), + _ => () + }; + } +} +"#, + r#" +fn foo() { + { + match n { + Some(n) => { + foo( + 29, + 30, + ) + }, + _ => () + }; + } +} +"#, + ); + } + #[test] fn no_assist_for_match_with_braces() { check_assist_not_applicable( From d41a190c687137ce9ec5495e198dc839c2b071be Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 16 Oct 2025 14:29:36 +0800 Subject: [PATCH 028/170] Migrate `add_missing_match_arms` assist, because edit_in_place uses ted - And fix indentations Example --- ```rust fn main() { match None$0 { None => { foo( "foo", "bar", ); } } } ``` **Before this PR**: ```rust fn main() { match None { None => { foo( "foo", "bar", ); } Some(_) => todo!(), } } ``` **After this PR**: ```rust fn main() { match None { None => { foo( "foo", "bar", ); } Some(${1:_}) => ${2:todo!()},$0 } } ``` --- .../src/handlers/add_missing_match_arms.rs | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 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 8802a54e7f24..7843ab9e8f25 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 @@ -8,8 +8,7 @@ use ide_db::syntax_helpers::suggest_name; use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast}; use itertools::Itertools; use syntax::ToSmolStr; -use syntax::ast::edit::IndentLevel; -use syntax::ast::edit_in_place::Indent; +use syntax::ast::edit::{AstNodeEdit, IndentLevel}; use syntax::ast::syntax_factory::SyntaxFactory; use syntax::ast::{self, AstNode, MatchArmList, MatchExpr, Pat, make}; @@ -261,6 +260,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) true } }) + .map(|arm| arm.reset_indent().indent(IndentLevel(1))) .collect(); let first_new_arm_idx = arms.len(); @@ -300,7 +300,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) }; let mut editor = builder.make_editor(&old_place); - new_match_arm_list.indent(IndentLevel::from_node(&old_place)); + let new_match_arm_list = new_match_arm_list.indent(IndentLevel::from_node(&old_place)); editor.replace(old_place, new_match_arm_list.syntax()); if let Some(cap) = ctx.config.snippet_cap { @@ -917,6 +917,39 @@ fn main() { ); } + #[test] + fn partial_fill_option_with_indentation() { + check_assist( + add_missing_match_arms, + r#" +//- minicore: option +fn main() { + match None$0 { + None => { + foo( + "foo", + "bar", + ); + } + } +} +"#, + r#" +fn main() { + match None { + None => { + foo( + "foo", + "bar", + ); + } + Some(${1:_}) => ${2:todo!()},$0 + } +} +"#, + ); + } + #[test] fn partial_fill_or_pat() { check_assist( From e9bba4f598c423125d3608effcfd2fd026b460ef Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 16 Oct 2025 13:08:27 +0300 Subject: [PATCH 029/170] Do not use `force-always-assert` in `xtask install` by default But add a flag to do so. --- src/tools/rust-analyzer/xtask/src/flags.rs | 11 ++++++++++- src/tools/rust-analyzer/xtask/src/install.rs | 16 ++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/flags.rs b/src/tools/rust-analyzer/xtask/src/flags.rs index 72f6215d4c3f..8f70a1861893 100644 --- a/src/tools/rust-analyzer/xtask/src/flags.rs +++ b/src/tools/rust-analyzer/xtask/src/flags.rs @@ -49,6 +49,9 @@ xflags::xflags! { /// build in release with debug info set to 2. optional --dev-rel + /// Make `never!()`, `always!()` etc. panic instead of just logging an error. + optional --force-always-assert + /// Apply PGO optimizations optional --pgo pgo: PgoTrainingCrate } @@ -124,6 +127,7 @@ pub struct Install { pub jemalloc: bool, pub proc_macro_server: bool, pub dev_rel: bool, + pub force_always_assert: bool, pub pgo: Option, } @@ -300,7 +304,12 @@ impl Install { } else { Malloc::System }; - Some(ServerOpt { malloc, dev_rel: self.dev_rel, pgo: self.pgo.clone() }) + Some(ServerOpt { + malloc, + dev_rel: self.dev_rel, + pgo: self.pgo.clone(), + force_always_assert: self.force_always_assert, + }) } pub(crate) fn proc_macro_server(&self) -> Option { if !self.proc_macro_server { diff --git a/src/tools/rust-analyzer/xtask/src/install.rs b/src/tools/rust-analyzer/xtask/src/install.rs index b794f53e761e..975e361ba50b 100644 --- a/src/tools/rust-analyzer/xtask/src/install.rs +++ b/src/tools/rust-analyzer/xtask/src/install.rs @@ -39,6 +39,18 @@ pub(crate) struct ServerOpt { pub(crate) malloc: Malloc, pub(crate) dev_rel: bool, pub(crate) pgo: Option, + pub(crate) force_always_assert: bool, +} + +impl ServerOpt { + fn to_features(&self) -> Vec<&'static str> { + let mut features = Vec::new(); + features.extend(self.malloc.to_features()); + if self.force_always_assert { + features.extend(["--features", "force-always-assert"]); + } + features + } } pub(crate) struct ProcMacroServerOpt { @@ -136,7 +148,7 @@ fn install_client(sh: &Shell, client_opt: ClientOpt) -> anyhow::Result<()> { } fn install_server(sh: &Shell, opts: ServerOpt) -> anyhow::Result<()> { - let features = opts.malloc.to_features(); + let features = &opts.to_features(); let profile = if opts.dev_rel { "dev-rel" } else { "release" }; let mut install_cmd = cmd!( @@ -148,7 +160,7 @@ fn install_server(sh: &Shell, opts: ServerOpt) -> anyhow::Result<()> { let target = detect_target(sh); let build_cmd = cmd!( sh, - "cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target} --profile={profile} --locked --features force-always-assert {features...}" + "cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target} --profile={profile} --locked {features...}" ); let profile = crate::pgo::gather_pgo_profile(sh, build_cmd, &target, train_crate)?; From d75b2ba78366952e4dd5331c464382ed28667021 Mon Sep 17 00:00:00 2001 From: Jesus Checa Hidalgo Date: Mon, 6 Oct 2025 18:34:07 +0200 Subject: [PATCH 030/170] Remove needs-asm-support directive in tests with explicit targets The `needs-asm-support` directive checks whether the host architecture supports inline assembly, not the target architecture. For tests that explicitly specify a target via `--target` in their compile-flags, this directive is incorrect and unnecessary. These tests are cross-compiling to specific targets (like x86_64, arm, aarch64, riscv, etc.) that are already known to have stable asm support. The directive was causing these tests to be incorrectly skipped on hosts that don't support asm, even though the target does. Tests with explicit targets should rely on `needs-llvm-components` to ensure the appropriate backend is available, rather than checking host asm support. Improve documentation about `needs-asm-support` directive. --- .../rustc-dev-guide/src/tests/directives.md | 6 +- tests/ui/asm/aarch64/arm64ec-sve.rs | 1 - tests/ui/asm/aarch64/arm64ec-sve.stderr | 4 +- tests/ui/asm/inline-syntax.arm.stderr | 12 +- tests/ui/asm/inline-syntax.rs | 1 - tests/ui/asm/inline-syntax.x86_64.stderr | 14 +- tests/ui/asm/issue-92378.rs | 1 - tests/ui/asm/issue-99071.rs | 1 - tests/ui/asm/issue-99071.stderr | 2 +- .../bad-reg.loongarch32_ilp32d.stderr | 12 +- .../bad-reg.loongarch32_ilp32s.stderr | 20 +- .../bad-reg.loongarch64_lp64d.stderr | 12 +- .../bad-reg.loongarch64_lp64s.stderr | 20 +- tests/ui/asm/loongarch/bad-reg.rs | 1 - .../ui/asm/naked-functions-instruction-set.rs | 1 - tests/ui/asm/powerpc/bad-reg.aix64.stderr | 226 ++++++++-------- tests/ui/asm/powerpc/bad-reg.powerpc.stderr | 254 +++++++++--------- tests/ui/asm/powerpc/bad-reg.powerpc64.stderr | 246 ++++++++--------- .../ui/asm/powerpc/bad-reg.powerpc64le.stderr | 226 ++++++++-------- tests/ui/asm/powerpc/bad-reg.rs | 1 - tests/ui/asm/riscv/bad-reg.riscv32e.stderr | 66 ++--- tests/ui/asm/riscv/bad-reg.riscv32gc.stderr | 26 +- tests/ui/asm/riscv/bad-reg.riscv32i.stderr | 34 +-- .../ui/asm/riscv/bad-reg.riscv32imafc.stderr | 30 +-- tests/ui/asm/riscv/bad-reg.riscv64gc.stderr | 26 +- tests/ui/asm/riscv/bad-reg.riscv64imac.stderr | 34 +-- tests/ui/asm/riscv/bad-reg.rs | 1 - tests/ui/asm/s390x/bad-reg.rs | 1 - tests/ui/asm/s390x/bad-reg.s390x.stderr | 108 ++++---- .../ui/asm/s390x/bad-reg.s390x_vector.stderr | 94 +++---- .../s390x/bad-reg.s390x_vector_stable.stderr | 126 ++++----- tests/ui/asm/sparc/bad-reg.rs | 1 - tests/ui/asm/sparc/bad-reg.sparc.stderr | 30 +-- tests/ui/asm/sparc/bad-reg.sparc64.stderr | 28 +- tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr | 30 +-- .../feature-gate-asm_experimental_reg.rs | 1 - .../feature-gate-asm_experimental_reg.stderr | 4 +- 37 files changed, 846 insertions(+), 855 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 2c6cfeb0ac70..12ceef0c928d 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -163,8 +163,10 @@ Some examples of `X` in `ignore-X` or `only-X`: The following directives will check rustc build settings and target settings: -- `needs-asm-support` — ignores if it is running on a target that doesn't have - stable support for `asm!` +- `needs-asm-support` — ignores if the **host** architecture doesn't have + stable support for `asm!`. For tests that cross-compile to explicit targets + via `--target`, use `needs-llvm-components` instead to ensure the appropriate + backend is available. - `needs-profiler-runtime` — ignores the test if the profiler runtime was not enabled for the target (`build.profiler = true` in rustc's `bootstrap.toml`) diff --git a/tests/ui/asm/aarch64/arm64ec-sve.rs b/tests/ui/asm/aarch64/arm64ec-sve.rs index f11b9de375ef..651bb5c088c7 100644 --- a/tests/ui/asm/aarch64/arm64ec-sve.rs +++ b/tests/ui/asm/aarch64/arm64ec-sve.rs @@ -1,6 +1,5 @@ //@ add-core-stubs //@ compile-flags: --target arm64ec-pc-windows-msvc -//@ needs-asm-support //@ needs-llvm-components: aarch64 //@ ignore-backends: gcc diff --git a/tests/ui/asm/aarch64/arm64ec-sve.stderr b/tests/ui/asm/aarch64/arm64ec-sve.stderr index cbae807e804a..d654eb4ba1a1 100644 --- a/tests/ui/asm/aarch64/arm64ec-sve.stderr +++ b/tests/ui/asm/aarch64/arm64ec-sve.stderr @@ -1,11 +1,11 @@ error: cannot use register `p0`: x13, x14, x23, x24, x28, v16-v31, p*, ffr cannot be used for Arm64EC - --> $DIR/arm64ec-sve.rs:19:18 + --> $DIR/arm64ec-sve.rs:18:18 | LL | asm!("", out("p0") _); | ^^^^^^^^^^^ error: cannot use register `ffr`: x13, x14, x23, x24, x28, v16-v31, p*, ffr cannot be used for Arm64EC - --> $DIR/arm64ec-sve.rs:21:18 + --> $DIR/arm64ec-sve.rs:20:18 | LL | asm!("", out("ffr") _); | ^^^^^^^^^^^^ diff --git a/tests/ui/asm/inline-syntax.arm.stderr b/tests/ui/asm/inline-syntax.arm.stderr index 14e32c38884c..5b4eb3cc1409 100644 --- a/tests/ui/asm/inline-syntax.arm.stderr +++ b/tests/ui/asm/inline-syntax.arm.stderr @@ -15,7 +15,7 @@ LL | .intel_syntax noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:22:15 + --> $DIR/inline-syntax.rs:21:15 | LL | asm!(".intel_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | .intel_syntax noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:25:15 + --> $DIR/inline-syntax.rs:24:15 | LL | asm!(".intel_syntax aaa noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | .intel_syntax aaa noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:28:15 + --> $DIR/inline-syntax.rs:27:15 | LL | asm!(".att_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^ @@ -51,7 +51,7 @@ LL | .att_syntax noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:31:15 + --> $DIR/inline-syntax.rs:30:15 | LL | asm!(".att_syntax bbb noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | .att_syntax bbb noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:34:15 + --> $DIR/inline-syntax.rs:33:15 | LL | asm!(".intel_syntax noprefix; nop"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,7 +75,7 @@ LL | .intel_syntax noprefix; nop | ^ error: unknown directive - --> $DIR/inline-syntax.rs:40:13 + --> $DIR/inline-syntax.rs:39:13 | LL | .intel_syntax noprefix | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/asm/inline-syntax.rs b/tests/ui/asm/inline-syntax.rs index dae70094e711..4ac0087d5b78 100644 --- a/tests/ui/asm/inline-syntax.rs +++ b/tests/ui/asm/inline-syntax.rs @@ -7,7 +7,6 @@ //@[arm] compile-flags: --target armv7-unknown-linux-gnueabihf //@[arm] build-fail //@[arm] needs-llvm-components: arm -//@ needs-asm-support //@ ignore-backends: gcc #![feature(no_core)] diff --git a/tests/ui/asm/inline-syntax.x86_64.stderr b/tests/ui/asm/inline-syntax.x86_64.stderr index 76f4cc054ef9..2d8091c20441 100644 --- a/tests/ui/asm/inline-syntax.x86_64.stderr +++ b/tests/ui/asm/inline-syntax.x86_64.stderr @@ -1,5 +1,5 @@ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:48:14 + --> $DIR/inline-syntax.rs:47:14 | LL | global_asm!(".intel_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -7,37 +7,37 @@ LL | global_asm!(".intel_syntax noprefix", "nop"); = note: `#[warn(bad_asm_style)]` on by default warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:22:15 + --> $DIR/inline-syntax.rs:21:15 | LL | asm!(".intel_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:25:15 + --> $DIR/inline-syntax.rs:24:15 | LL | asm!(".intel_syntax aaa noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead - --> $DIR/inline-syntax.rs:28:15 + --> $DIR/inline-syntax.rs:27:15 | LL | asm!(".att_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead - --> $DIR/inline-syntax.rs:31:15 + --> $DIR/inline-syntax.rs:30:15 | LL | asm!(".att_syntax bbb noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:34:15 + --> $DIR/inline-syntax.rs:33:15 | LL | asm!(".intel_syntax noprefix; nop"); | ^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:40:13 + --> $DIR/inline-syntax.rs:39:13 | LL | .intel_syntax noprefix | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/asm/issue-92378.rs b/tests/ui/asm/issue-92378.rs index 8f9c91373f9a..1bdaef5a1af7 100644 --- a/tests/ui/asm/issue-92378.rs +++ b/tests/ui/asm/issue-92378.rs @@ -1,7 +1,6 @@ //@ add-core-stubs //@ compile-flags: --target armv5te-unknown-linux-gnueabi //@ needs-llvm-components: arm -//@ needs-asm-support //@ build-pass //@ ignore-backends: gcc diff --git a/tests/ui/asm/issue-99071.rs b/tests/ui/asm/issue-99071.rs index 6eb179daa788..fc7baa677240 100644 --- a/tests/ui/asm/issue-99071.rs +++ b/tests/ui/asm/issue-99071.rs @@ -1,7 +1,6 @@ //@ add-core-stubs //@ compile-flags: --target thumbv6m-none-eabi //@ needs-llvm-components: arm -//@ needs-asm-support //@ ignore-backends: gcc #![feature(no_core)] diff --git a/tests/ui/asm/issue-99071.stderr b/tests/ui/asm/issue-99071.stderr index b869f9a70b4c..1a8074b01d66 100644 --- a/tests/ui/asm/issue-99071.stderr +++ b/tests/ui/asm/issue-99071.stderr @@ -1,5 +1,5 @@ error: cannot use register `r8`: high registers (r8+) can only be used as clobbers in Thumb-1 code - --> $DIR/issue-99071.rs:16:18 + --> $DIR/issue-99071.rs:15:18 | LL | asm!("", in("r8") 0); | ^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr index 8742d4bd82cd..c67c913d2a60 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr @@ -1,35 +1,35 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:27:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:29:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr index e6cb6e40c701..99c071919acf 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr @@ -1,59 +1,59 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:27:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:29:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:41:26 + --> $DIR/bad-reg.rs:40:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:43:26 + --> $DIR/bad-reg.rs:42:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:45:26 + --> $DIR/bad-reg.rs:44:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:47:26 + --> $DIR/bad-reg.rs:46:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr index 8742d4bd82cd..c67c913d2a60 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr @@ -1,35 +1,35 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:27:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:29:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr index e6cb6e40c701..99c071919acf 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr @@ -1,59 +1,59 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:27:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:29:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:41:26 + --> $DIR/bad-reg.rs:40:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:43:26 + --> $DIR/bad-reg.rs:42:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:45:26 + --> $DIR/bad-reg.rs:44:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:47:26 + --> $DIR/bad-reg.rs:46:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.rs b/tests/ui/asm/loongarch/bad-reg.rs index 5c20f9de5472..c39756d7cb1a 100644 --- a/tests/ui/asm/loongarch/bad-reg.rs +++ b/tests/ui/asm/loongarch/bad-reg.rs @@ -1,5 +1,4 @@ //@ add-core-stubs -//@ needs-asm-support //@ revisions: loongarch32_ilp32d loongarch32_ilp32s loongarch64_lp64d loongarch64_lp64s //@[loongarch32_ilp32d] compile-flags: --target loongarch32-unknown-none //@[loongarch32_ilp32d] needs-llvm-components: loongarch diff --git a/tests/ui/asm/naked-functions-instruction-set.rs b/tests/ui/asm/naked-functions-instruction-set.rs index 9a2437389d60..4ee82cfcbb3d 100644 --- a/tests/ui/asm/naked-functions-instruction-set.rs +++ b/tests/ui/asm/naked-functions-instruction-set.rs @@ -1,7 +1,6 @@ //@ add-core-stubs //@ compile-flags: --target armv5te-unknown-linux-gnueabi //@ needs-llvm-components: arm -//@ needs-asm-support //@ build-pass //@ ignore-backends: gcc diff --git a/tests/ui/asm/powerpc/bad-reg.aix64.stderr b/tests/ui/asm/powerpc/bad-reg.aix64.stderr index fb3ed07f5c65..67ad0a5d2c45 100644 --- a/tests/ui/asm/powerpc/bad-reg.aix64.stderr +++ b/tests/ui/asm/powerpc/bad-reg.aix64.stderr @@ -1,137 +1,137 @@ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `r2`: r2 is a system reserved register and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("r2") _); | ^^^^^^^^^^^ error: invalid register `r29`: r29 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:42:18 | LL | asm!("", out("r29") _); | ^^^^^^^^^^^^ error: invalid register `r30`: r30 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:44:18 | LL | asm!("", out("r30") _); | ^^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:47:18 + --> $DIR/bad-reg.rs:46:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `vrsave`: the vrsave register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:49:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", out("vrsave") _); | ^^^^^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:138:18 + --> $DIR/bad-reg.rs:137:18 | LL | asm!("", in("cr") x); | ^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:141:18 + --> $DIR/bad-reg.rs:140:18 | LL | asm!("", out("cr") x); | ^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:144:26 + --> $DIR/bad-reg.rs:143:26 | LL | asm!("/* {} */", in(cr) x); | ^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:147:26 + --> $DIR/bad-reg.rs:146:26 | LL | asm!("/* {} */", out(cr) _); | ^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:151:18 + --> $DIR/bad-reg.rs:150:18 | LL | asm!("", in("ctr") x); | ^^^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:154:18 + --> $DIR/bad-reg.rs:153:18 | LL | asm!("", out("ctr") x); | ^^^^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:157:26 + --> $DIR/bad-reg.rs:156:26 | LL | asm!("/* {} */", in(ctr) x); | ^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:160:26 + --> $DIR/bad-reg.rs:159:26 | LL | asm!("/* {} */", out(ctr) _); | ^^^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:164:18 + --> $DIR/bad-reg.rs:163:18 | LL | asm!("", in("lr") x); | ^^^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:167:18 + --> $DIR/bad-reg.rs:166:18 | LL | asm!("", out("lr") x); | ^^^^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:170:26 + --> $DIR/bad-reg.rs:169:26 | LL | asm!("/* {} */", in(lr) x); | ^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:173:26 + --> $DIR/bad-reg.rs:172:26 | LL | asm!("/* {} */", out(lr) _); | ^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:177:18 + --> $DIR/bad-reg.rs:176:18 | LL | asm!("", in("xer") x); | ^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:180:18 + --> $DIR/bad-reg.rs:179:18 | LL | asm!("", out("xer") x); | ^^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:183:26 + --> $DIR/bad-reg.rs:182:26 | LL | asm!("/* {} */", in(xer) x); | ^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:186:26 + --> $DIR/bad-reg.rs:185:26 | LL | asm!("/* {} */", out(xer) _); | ^^^^^^^^^^ error: register `cr0` conflicts with register `cr` - --> $DIR/bad-reg.rs:190:31 + --> $DIR/bad-reg.rs:189:31 | LL | asm!("", out("cr") _, out("cr0") _); | ----------- ^^^^^^^^^^^^ register `cr0` @@ -139,7 +139,7 @@ LL | asm!("", out("cr") _, out("cr0") _); | register `cr` error: register `cr1` conflicts with register `cr` - --> $DIR/bad-reg.rs:192:31 + --> $DIR/bad-reg.rs:191:31 | LL | asm!("", out("cr") _, out("cr1") _); | ----------- ^^^^^^^^^^^^ register `cr1` @@ -147,7 +147,7 @@ LL | asm!("", out("cr") _, out("cr1") _); | register `cr` error: register `cr2` conflicts with register `cr` - --> $DIR/bad-reg.rs:194:31 + --> $DIR/bad-reg.rs:193:31 | LL | asm!("", out("cr") _, out("cr2") _); | ----------- ^^^^^^^^^^^^ register `cr2` @@ -155,7 +155,7 @@ LL | asm!("", out("cr") _, out("cr2") _); | register `cr` error: register `cr3` conflicts with register `cr` - --> $DIR/bad-reg.rs:196:31 + --> $DIR/bad-reg.rs:195:31 | LL | asm!("", out("cr") _, out("cr3") _); | ----------- ^^^^^^^^^^^^ register `cr3` @@ -163,7 +163,7 @@ LL | asm!("", out("cr") _, out("cr3") _); | register `cr` error: register `cr4` conflicts with register `cr` - --> $DIR/bad-reg.rs:198:31 + --> $DIR/bad-reg.rs:197:31 | LL | asm!("", out("cr") _, out("cr4") _); | ----------- ^^^^^^^^^^^^ register `cr4` @@ -171,7 +171,7 @@ LL | asm!("", out("cr") _, out("cr4") _); | register `cr` error: register `cr5` conflicts with register `cr` - --> $DIR/bad-reg.rs:200:31 + --> $DIR/bad-reg.rs:199:31 | LL | asm!("", out("cr") _, out("cr5") _); | ----------- ^^^^^^^^^^^^ register `cr5` @@ -179,7 +179,7 @@ LL | asm!("", out("cr") _, out("cr5") _); | register `cr` error: register `cr6` conflicts with register `cr` - --> $DIR/bad-reg.rs:202:31 + --> $DIR/bad-reg.rs:201:31 | LL | asm!("", out("cr") _, out("cr6") _); | ----------- ^^^^^^^^^^^^ register `cr6` @@ -187,7 +187,7 @@ LL | asm!("", out("cr") _, out("cr6") _); | register `cr` error: register `cr7` conflicts with register `cr` - --> $DIR/bad-reg.rs:204:31 + --> $DIR/bad-reg.rs:203:31 | LL | asm!("", out("cr") _, out("cr7") _); | ----------- ^^^^^^^^^^^^ register `cr7` @@ -195,7 +195,7 @@ LL | asm!("", out("cr") _, out("cr7") _); | register `cr` error: register `vs0` conflicts with register `f0` - --> $DIR/bad-reg.rs:207:31 + --> $DIR/bad-reg.rs:206:31 | LL | asm!("", out("f0") _, out("vs0") _); | ----------- ^^^^^^^^^^^^ register `vs0` @@ -203,7 +203,7 @@ LL | asm!("", out("f0") _, out("vs0") _); | register `f0` error: register `vs1` conflicts with register `f1` - --> $DIR/bad-reg.rs:209:31 + --> $DIR/bad-reg.rs:208:31 | LL | asm!("", out("f1") _, out("vs1") _); | ----------- ^^^^^^^^^^^^ register `vs1` @@ -211,7 +211,7 @@ LL | asm!("", out("f1") _, out("vs1") _); | register `f1` error: register `vs2` conflicts with register `f2` - --> $DIR/bad-reg.rs:211:31 + --> $DIR/bad-reg.rs:210:31 | LL | asm!("", out("f2") _, out("vs2") _); | ----------- ^^^^^^^^^^^^ register `vs2` @@ -219,7 +219,7 @@ LL | asm!("", out("f2") _, out("vs2") _); | register `f2` error: register `vs3` conflicts with register `f3` - --> $DIR/bad-reg.rs:213:31 + --> $DIR/bad-reg.rs:212:31 | LL | asm!("", out("f3") _, out("vs3") _); | ----------- ^^^^^^^^^^^^ register `vs3` @@ -227,7 +227,7 @@ LL | asm!("", out("f3") _, out("vs3") _); | register `f3` error: register `vs4` conflicts with register `f4` - --> $DIR/bad-reg.rs:215:31 + --> $DIR/bad-reg.rs:214:31 | LL | asm!("", out("f4") _, out("vs4") _); | ----------- ^^^^^^^^^^^^ register `vs4` @@ -235,7 +235,7 @@ LL | asm!("", out("f4") _, out("vs4") _); | register `f4` error: register `vs5` conflicts with register `f5` - --> $DIR/bad-reg.rs:217:31 + --> $DIR/bad-reg.rs:216:31 | LL | asm!("", out("f5") _, out("vs5") _); | ----------- ^^^^^^^^^^^^ register `vs5` @@ -243,7 +243,7 @@ LL | asm!("", out("f5") _, out("vs5") _); | register `f5` error: register `vs6` conflicts with register `f6` - --> $DIR/bad-reg.rs:219:31 + --> $DIR/bad-reg.rs:218:31 | LL | asm!("", out("f6") _, out("vs6") _); | ----------- ^^^^^^^^^^^^ register `vs6` @@ -251,7 +251,7 @@ LL | asm!("", out("f6") _, out("vs6") _); | register `f6` error: register `vs7` conflicts with register `f7` - --> $DIR/bad-reg.rs:221:31 + --> $DIR/bad-reg.rs:220:31 | LL | asm!("", out("f7") _, out("vs7") _); | ----------- ^^^^^^^^^^^^ register `vs7` @@ -259,7 +259,7 @@ LL | asm!("", out("f7") _, out("vs7") _); | register `f7` error: register `vs8` conflicts with register `f8` - --> $DIR/bad-reg.rs:223:31 + --> $DIR/bad-reg.rs:222:31 | LL | asm!("", out("f8") _, out("vs8") _); | ----------- ^^^^^^^^^^^^ register `vs8` @@ -267,7 +267,7 @@ LL | asm!("", out("f8") _, out("vs8") _); | register `f8` error: register `vs9` conflicts with register `f9` - --> $DIR/bad-reg.rs:225:31 + --> $DIR/bad-reg.rs:224:31 | LL | asm!("", out("f9") _, out("vs9") _); | ----------- ^^^^^^^^^^^^ register `vs9` @@ -275,7 +275,7 @@ LL | asm!("", out("f9") _, out("vs9") _); | register `f9` error: register `vs10` conflicts with register `f10` - --> $DIR/bad-reg.rs:227:32 + --> $DIR/bad-reg.rs:226:32 | LL | asm!("", out("f10") _, out("vs10") _); | ------------ ^^^^^^^^^^^^^ register `vs10` @@ -283,7 +283,7 @@ LL | asm!("", out("f10") _, out("vs10") _); | register `f10` error: register `vs11` conflicts with register `f11` - --> $DIR/bad-reg.rs:229:32 + --> $DIR/bad-reg.rs:228:32 | LL | asm!("", out("f11") _, out("vs11") _); | ------------ ^^^^^^^^^^^^^ register `vs11` @@ -291,7 +291,7 @@ LL | asm!("", out("f11") _, out("vs11") _); | register `f11` error: register `vs12` conflicts with register `f12` - --> $DIR/bad-reg.rs:231:32 + --> $DIR/bad-reg.rs:230:32 | LL | asm!("", out("f12") _, out("vs12") _); | ------------ ^^^^^^^^^^^^^ register `vs12` @@ -299,7 +299,7 @@ LL | asm!("", out("f12") _, out("vs12") _); | register `f12` error: register `vs13` conflicts with register `f13` - --> $DIR/bad-reg.rs:233:32 + --> $DIR/bad-reg.rs:232:32 | LL | asm!("", out("f13") _, out("vs13") _); | ------------ ^^^^^^^^^^^^^ register `vs13` @@ -307,7 +307,7 @@ LL | asm!("", out("f13") _, out("vs13") _); | register `f13` error: register `vs14` conflicts with register `f14` - --> $DIR/bad-reg.rs:235:32 + --> $DIR/bad-reg.rs:234:32 | LL | asm!("", out("f14") _, out("vs14") _); | ------------ ^^^^^^^^^^^^^ register `vs14` @@ -315,7 +315,7 @@ LL | asm!("", out("f14") _, out("vs14") _); | register `f14` error: register `vs15` conflicts with register `f15` - --> $DIR/bad-reg.rs:237:32 + --> $DIR/bad-reg.rs:236:32 | LL | asm!("", out("f15") _, out("vs15") _); | ------------ ^^^^^^^^^^^^^ register `vs15` @@ -323,7 +323,7 @@ LL | asm!("", out("f15") _, out("vs15") _); | register `f15` error: register `vs16` conflicts with register `f16` - --> $DIR/bad-reg.rs:239:32 + --> $DIR/bad-reg.rs:238:32 | LL | asm!("", out("f16") _, out("vs16") _); | ------------ ^^^^^^^^^^^^^ register `vs16` @@ -331,7 +331,7 @@ LL | asm!("", out("f16") _, out("vs16") _); | register `f16` error: register `vs17` conflicts with register `f17` - --> $DIR/bad-reg.rs:241:32 + --> $DIR/bad-reg.rs:240:32 | LL | asm!("", out("f17") _, out("vs17") _); | ------------ ^^^^^^^^^^^^^ register `vs17` @@ -339,7 +339,7 @@ LL | asm!("", out("f17") _, out("vs17") _); | register `f17` error: register `vs18` conflicts with register `f18` - --> $DIR/bad-reg.rs:243:32 + --> $DIR/bad-reg.rs:242:32 | LL | asm!("", out("f18") _, out("vs18") _); | ------------ ^^^^^^^^^^^^^ register `vs18` @@ -347,7 +347,7 @@ LL | asm!("", out("f18") _, out("vs18") _); | register `f18` error: register `vs19` conflicts with register `f19` - --> $DIR/bad-reg.rs:245:32 + --> $DIR/bad-reg.rs:244:32 | LL | asm!("", out("f19") _, out("vs19") _); | ------------ ^^^^^^^^^^^^^ register `vs19` @@ -355,7 +355,7 @@ LL | asm!("", out("f19") _, out("vs19") _); | register `f19` error: register `vs20` conflicts with register `f20` - --> $DIR/bad-reg.rs:247:32 + --> $DIR/bad-reg.rs:246:32 | LL | asm!("", out("f20") _, out("vs20") _); | ------------ ^^^^^^^^^^^^^ register `vs20` @@ -363,7 +363,7 @@ LL | asm!("", out("f20") _, out("vs20") _); | register `f20` error: register `vs21` conflicts with register `f21` - --> $DIR/bad-reg.rs:249:32 + --> $DIR/bad-reg.rs:248:32 | LL | asm!("", out("f21") _, out("vs21") _); | ------------ ^^^^^^^^^^^^^ register `vs21` @@ -371,7 +371,7 @@ LL | asm!("", out("f21") _, out("vs21") _); | register `f21` error: register `vs22` conflicts with register `f22` - --> $DIR/bad-reg.rs:251:32 + --> $DIR/bad-reg.rs:250:32 | LL | asm!("", out("f22") _, out("vs22") _); | ------------ ^^^^^^^^^^^^^ register `vs22` @@ -379,7 +379,7 @@ LL | asm!("", out("f22") _, out("vs22") _); | register `f22` error: register `vs23` conflicts with register `f23` - --> $DIR/bad-reg.rs:253:32 + --> $DIR/bad-reg.rs:252:32 | LL | asm!("", out("f23") _, out("vs23") _); | ------------ ^^^^^^^^^^^^^ register `vs23` @@ -387,7 +387,7 @@ LL | asm!("", out("f23") _, out("vs23") _); | register `f23` error: register `vs24` conflicts with register `f24` - --> $DIR/bad-reg.rs:255:32 + --> $DIR/bad-reg.rs:254:32 | LL | asm!("", out("f24") _, out("vs24") _); | ------------ ^^^^^^^^^^^^^ register `vs24` @@ -395,7 +395,7 @@ LL | asm!("", out("f24") _, out("vs24") _); | register `f24` error: register `vs25` conflicts with register `f25` - --> $DIR/bad-reg.rs:257:32 + --> $DIR/bad-reg.rs:256:32 | LL | asm!("", out("f25") _, out("vs25") _); | ------------ ^^^^^^^^^^^^^ register `vs25` @@ -403,7 +403,7 @@ LL | asm!("", out("f25") _, out("vs25") _); | register `f25` error: register `vs26` conflicts with register `f26` - --> $DIR/bad-reg.rs:259:32 + --> $DIR/bad-reg.rs:258:32 | LL | asm!("", out("f26") _, out("vs26") _); | ------------ ^^^^^^^^^^^^^ register `vs26` @@ -411,7 +411,7 @@ LL | asm!("", out("f26") _, out("vs26") _); | register `f26` error: register `vs27` conflicts with register `f27` - --> $DIR/bad-reg.rs:261:32 + --> $DIR/bad-reg.rs:260:32 | LL | asm!("", out("f27") _, out("vs27") _); | ------------ ^^^^^^^^^^^^^ register `vs27` @@ -419,7 +419,7 @@ LL | asm!("", out("f27") _, out("vs27") _); | register `f27` error: register `vs28` conflicts with register `f28` - --> $DIR/bad-reg.rs:263:32 + --> $DIR/bad-reg.rs:262:32 | LL | asm!("", out("f28") _, out("vs28") _); | ------------ ^^^^^^^^^^^^^ register `vs28` @@ -427,7 +427,7 @@ LL | asm!("", out("f28") _, out("vs28") _); | register `f28` error: register `vs29` conflicts with register `f29` - --> $DIR/bad-reg.rs:265:32 + --> $DIR/bad-reg.rs:264:32 | LL | asm!("", out("f29") _, out("vs29") _); | ------------ ^^^^^^^^^^^^^ register `vs29` @@ -435,7 +435,7 @@ LL | asm!("", out("f29") _, out("vs29") _); | register `f29` error: register `vs30` conflicts with register `f30` - --> $DIR/bad-reg.rs:267:32 + --> $DIR/bad-reg.rs:266:32 | LL | asm!("", out("f30") _, out("vs30") _); | ------------ ^^^^^^^^^^^^^ register `vs30` @@ -443,7 +443,7 @@ LL | asm!("", out("f30") _, out("vs30") _); | register `f30` error: register `vs31` conflicts with register `f31` - --> $DIR/bad-reg.rs:269:32 + --> $DIR/bad-reg.rs:268:32 | LL | asm!("", out("f31") _, out("vs31") _); | ------------ ^^^^^^^^^^^^^ register `vs31` @@ -451,7 +451,7 @@ LL | asm!("", out("f31") _, out("vs31") _); | register `f31` error: register `v0` conflicts with register `vs32` - --> $DIR/bad-reg.rs:271:33 + --> $DIR/bad-reg.rs:270:33 | LL | asm!("", out("vs32") _, out("v0") _); | ------------- ^^^^^^^^^^^ register `v0` @@ -459,7 +459,7 @@ LL | asm!("", out("vs32") _, out("v0") _); | register `vs32` error: register `v1` conflicts with register `vs33` - --> $DIR/bad-reg.rs:273:33 + --> $DIR/bad-reg.rs:272:33 | LL | asm!("", out("vs33") _, out("v1") _); | ------------- ^^^^^^^^^^^ register `v1` @@ -467,7 +467,7 @@ LL | asm!("", out("vs33") _, out("v1") _); | register `vs33` error: register `v2` conflicts with register `vs34` - --> $DIR/bad-reg.rs:275:33 + --> $DIR/bad-reg.rs:274:33 | LL | asm!("", out("vs34") _, out("v2") _); | ------------- ^^^^^^^^^^^ register `v2` @@ -475,7 +475,7 @@ LL | asm!("", out("vs34") _, out("v2") _); | register `vs34` error: register `v3` conflicts with register `vs35` - --> $DIR/bad-reg.rs:277:33 + --> $DIR/bad-reg.rs:276:33 | LL | asm!("", out("vs35") _, out("v3") _); | ------------- ^^^^^^^^^^^ register `v3` @@ -483,7 +483,7 @@ LL | asm!("", out("vs35") _, out("v3") _); | register `vs35` error: register `v4` conflicts with register `vs36` - --> $DIR/bad-reg.rs:279:33 + --> $DIR/bad-reg.rs:278:33 | LL | asm!("", out("vs36") _, out("v4") _); | ------------- ^^^^^^^^^^^ register `v4` @@ -491,7 +491,7 @@ LL | asm!("", out("vs36") _, out("v4") _); | register `vs36` error: register `v5` conflicts with register `vs37` - --> $DIR/bad-reg.rs:281:33 + --> $DIR/bad-reg.rs:280:33 | LL | asm!("", out("vs37") _, out("v5") _); | ------------- ^^^^^^^^^^^ register `v5` @@ -499,7 +499,7 @@ LL | asm!("", out("vs37") _, out("v5") _); | register `vs37` error: register `v6` conflicts with register `vs38` - --> $DIR/bad-reg.rs:283:33 + --> $DIR/bad-reg.rs:282:33 | LL | asm!("", out("vs38") _, out("v6") _); | ------------- ^^^^^^^^^^^ register `v6` @@ -507,7 +507,7 @@ LL | asm!("", out("vs38") _, out("v6") _); | register `vs38` error: register `v7` conflicts with register `vs39` - --> $DIR/bad-reg.rs:285:33 + --> $DIR/bad-reg.rs:284:33 | LL | asm!("", out("vs39") _, out("v7") _); | ------------- ^^^^^^^^^^^ register `v7` @@ -515,7 +515,7 @@ LL | asm!("", out("vs39") _, out("v7") _); | register `vs39` error: register `v8` conflicts with register `vs40` - --> $DIR/bad-reg.rs:287:33 + --> $DIR/bad-reg.rs:286:33 | LL | asm!("", out("vs40") _, out("v8") _); | ------------- ^^^^^^^^^^^ register `v8` @@ -523,7 +523,7 @@ LL | asm!("", out("vs40") _, out("v8") _); | register `vs40` error: register `v9` conflicts with register `vs41` - --> $DIR/bad-reg.rs:289:33 + --> $DIR/bad-reg.rs:288:33 | LL | asm!("", out("vs41") _, out("v9") _); | ------------- ^^^^^^^^^^^ register `v9` @@ -531,7 +531,7 @@ LL | asm!("", out("vs41") _, out("v9") _); | register `vs41` error: register `v10` conflicts with register `vs42` - --> $DIR/bad-reg.rs:291:33 + --> $DIR/bad-reg.rs:290:33 | LL | asm!("", out("vs42") _, out("v10") _); | ------------- ^^^^^^^^^^^^ register `v10` @@ -539,7 +539,7 @@ LL | asm!("", out("vs42") _, out("v10") _); | register `vs42` error: register `v11` conflicts with register `vs43` - --> $DIR/bad-reg.rs:293:33 + --> $DIR/bad-reg.rs:292:33 | LL | asm!("", out("vs43") _, out("v11") _); | ------------- ^^^^^^^^^^^^ register `v11` @@ -547,7 +547,7 @@ LL | asm!("", out("vs43") _, out("v11") _); | register `vs43` error: register `v12` conflicts with register `vs44` - --> $DIR/bad-reg.rs:295:33 + --> $DIR/bad-reg.rs:294:33 | LL | asm!("", out("vs44") _, out("v12") _); | ------------- ^^^^^^^^^^^^ register `v12` @@ -555,7 +555,7 @@ LL | asm!("", out("vs44") _, out("v12") _); | register `vs44` error: register `v13` conflicts with register `vs45` - --> $DIR/bad-reg.rs:297:33 + --> $DIR/bad-reg.rs:296:33 | LL | asm!("", out("vs45") _, out("v13") _); | ------------- ^^^^^^^^^^^^ register `v13` @@ -563,7 +563,7 @@ LL | asm!("", out("vs45") _, out("v13") _); | register `vs45` error: register `v14` conflicts with register `vs46` - --> $DIR/bad-reg.rs:299:33 + --> $DIR/bad-reg.rs:298:33 | LL | asm!("", out("vs46") _, out("v14") _); | ------------- ^^^^^^^^^^^^ register `v14` @@ -571,7 +571,7 @@ LL | asm!("", out("vs46") _, out("v14") _); | register `vs46` error: register `v15` conflicts with register `vs47` - --> $DIR/bad-reg.rs:301:33 + --> $DIR/bad-reg.rs:300:33 | LL | asm!("", out("vs47") _, out("v15") _); | ------------- ^^^^^^^^^^^^ register `v15` @@ -579,7 +579,7 @@ LL | asm!("", out("vs47") _, out("v15") _); | register `vs47` error: register `v16` conflicts with register `vs48` - --> $DIR/bad-reg.rs:303:33 + --> $DIR/bad-reg.rs:302:33 | LL | asm!("", out("vs48") _, out("v16") _); | ------------- ^^^^^^^^^^^^ register `v16` @@ -587,7 +587,7 @@ LL | asm!("", out("vs48") _, out("v16") _); | register `vs48` error: register `v17` conflicts with register `vs49` - --> $DIR/bad-reg.rs:305:33 + --> $DIR/bad-reg.rs:304:33 | LL | asm!("", out("vs49") _, out("v17") _); | ------------- ^^^^^^^^^^^^ register `v17` @@ -595,7 +595,7 @@ LL | asm!("", out("vs49") _, out("v17") _); | register `vs49` error: register `v18` conflicts with register `vs50` - --> $DIR/bad-reg.rs:307:33 + --> $DIR/bad-reg.rs:306:33 | LL | asm!("", out("vs50") _, out("v18") _); | ------------- ^^^^^^^^^^^^ register `v18` @@ -603,7 +603,7 @@ LL | asm!("", out("vs50") _, out("v18") _); | register `vs50` error: register `v19` conflicts with register `vs51` - --> $DIR/bad-reg.rs:309:33 + --> $DIR/bad-reg.rs:308:33 | LL | asm!("", out("vs51") _, out("v19") _); | ------------- ^^^^^^^^^^^^ register `v19` @@ -611,7 +611,7 @@ LL | asm!("", out("vs51") _, out("v19") _); | register `vs51` error: register `v20` conflicts with register `vs52` - --> $DIR/bad-reg.rs:311:33 + --> $DIR/bad-reg.rs:310:33 | LL | asm!("", out("vs52") _, out("v20") _); | ------------- ^^^^^^^^^^^^ register `v20` @@ -619,7 +619,7 @@ LL | asm!("", out("vs52") _, out("v20") _); | register `vs52` error: register `v21` conflicts with register `vs53` - --> $DIR/bad-reg.rs:313:33 + --> $DIR/bad-reg.rs:312:33 | LL | asm!("", out("vs53") _, out("v21") _); | ------------- ^^^^^^^^^^^^ register `v21` @@ -627,7 +627,7 @@ LL | asm!("", out("vs53") _, out("v21") _); | register `vs53` error: register `v22` conflicts with register `vs54` - --> $DIR/bad-reg.rs:315:33 + --> $DIR/bad-reg.rs:314:33 | LL | asm!("", out("vs54") _, out("v22") _); | ------------- ^^^^^^^^^^^^ register `v22` @@ -635,7 +635,7 @@ LL | asm!("", out("vs54") _, out("v22") _); | register `vs54` error: register `v23` conflicts with register `vs55` - --> $DIR/bad-reg.rs:317:33 + --> $DIR/bad-reg.rs:316:33 | LL | asm!("", out("vs55") _, out("v23") _); | ------------- ^^^^^^^^^^^^ register `v23` @@ -643,7 +643,7 @@ LL | asm!("", out("vs55") _, out("v23") _); | register `vs55` error: register `v24` conflicts with register `vs56` - --> $DIR/bad-reg.rs:319:33 + --> $DIR/bad-reg.rs:318:33 | LL | asm!("", out("vs56") _, out("v24") _); | ------------- ^^^^^^^^^^^^ register `v24` @@ -651,7 +651,7 @@ LL | asm!("", out("vs56") _, out("v24") _); | register `vs56` error: register `v25` conflicts with register `vs57` - --> $DIR/bad-reg.rs:321:33 + --> $DIR/bad-reg.rs:320:33 | LL | asm!("", out("vs57") _, out("v25") _); | ------------- ^^^^^^^^^^^^ register `v25` @@ -659,7 +659,7 @@ LL | asm!("", out("vs57") _, out("v25") _); | register `vs57` error: register `v26` conflicts with register `vs58` - --> $DIR/bad-reg.rs:323:33 + --> $DIR/bad-reg.rs:322:33 | LL | asm!("", out("vs58") _, out("v26") _); | ------------- ^^^^^^^^^^^^ register `v26` @@ -667,7 +667,7 @@ LL | asm!("", out("vs58") _, out("v26") _); | register `vs58` error: register `v27` conflicts with register `vs59` - --> $DIR/bad-reg.rs:325:33 + --> $DIR/bad-reg.rs:324:33 | LL | asm!("", out("vs59") _, out("v27") _); | ------------- ^^^^^^^^^^^^ register `v27` @@ -675,7 +675,7 @@ LL | asm!("", out("vs59") _, out("v27") _); | register `vs59` error: register `v28` conflicts with register `vs60` - --> $DIR/bad-reg.rs:327:33 + --> $DIR/bad-reg.rs:326:33 | LL | asm!("", out("vs60") _, out("v28") _); | ------------- ^^^^^^^^^^^^ register `v28` @@ -683,7 +683,7 @@ LL | asm!("", out("vs60") _, out("v28") _); | register `vs60` error: register `v29` conflicts with register `vs61` - --> $DIR/bad-reg.rs:329:33 + --> $DIR/bad-reg.rs:328:33 | LL | asm!("", out("vs61") _, out("v29") _); | ------------- ^^^^^^^^^^^^ register `v29` @@ -691,7 +691,7 @@ LL | asm!("", out("vs61") _, out("v29") _); | register `vs61` error: register `v30` conflicts with register `vs62` - --> $DIR/bad-reg.rs:331:33 + --> $DIR/bad-reg.rs:330:33 | LL | asm!("", out("vs62") _, out("v30") _); | ------------- ^^^^^^^^^^^^ register `v30` @@ -699,7 +699,7 @@ LL | asm!("", out("vs62") _, out("v30") _); | register `vs62` error: register `v31` conflicts with register `vs63` - --> $DIR/bad-reg.rs:333:33 + --> $DIR/bad-reg.rs:332:33 | LL | asm!("", out("vs63") _, out("v31") _); | ------------- ^^^^^^^^^^^^ register `v31` @@ -707,13 +707,13 @@ LL | asm!("", out("vs63") _, out("v31") _); | register `vs63` error: cannot use register `r13`: r13 is a reserved register on this target - --> $DIR/bad-reg.rs:41:18 + --> $DIR/bad-reg.rs:40:18 | LL | asm!("", out("r13") _); | ^^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:64:27 + --> $DIR/bad-reg.rs:63:27 | LL | asm!("", in("v0") x); // FIXME: should be ok if vsx is available | ^ @@ -721,7 +721,7 @@ LL | asm!("", in("v0") x); // FIXME: should be ok if vsx is available = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:67:28 + --> $DIR/bad-reg.rs:66:28 | LL | asm!("", out("v0") x); // FIXME: should be ok if vsx is available | ^ @@ -729,7 +729,7 @@ LL | asm!("", out("v0") x); // FIXME: should be ok if vsx is available = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:75:35 + --> $DIR/bad-reg.rs:74:35 | LL | asm!("/* {} */", in(vreg) x); // FIXME: should be ok if vsx is available | ^ @@ -737,7 +737,7 @@ LL | asm!("/* {} */", in(vreg) x); // FIXME: should be ok if vsx is avai = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:105:28 + --> $DIR/bad-reg.rs:104:28 | LL | asm!("", in("vs0") x); // FIXME: should be ok if vsx is available | ^ @@ -745,7 +745,7 @@ LL | asm!("", in("vs0") x); // FIXME: should be ok if vsx is available = note: register class `vsreg` supports these types: f32, f64, i8x16, i16x8, i32x4, i64x2, f32x4, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:108:29 + --> $DIR/bad-reg.rs:107:29 | LL | asm!("", out("vs0") x); // FIXME: should be ok if vsx is available | ^ @@ -753,7 +753,7 @@ LL | asm!("", out("vs0") x); // FIXME: should be ok if vsx is available = note: register class `vsreg` supports these types: f32, f64, i8x16, i16x8, i32x4, i64x2, f32x4, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:115:36 + --> $DIR/bad-reg.rs:114:36 | LL | asm!("/* {} */", in(vsreg) x); // FIXME: should be ok if vsx is available | ^ @@ -761,7 +761,7 @@ LL | asm!("/* {} */", in(vsreg) x); // FIXME: should be ok if vsx is ava = note: register class `vsreg` supports these types: f32, f64, i8x16, i16x8, i32x4, i64x2, f32x4, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:138:27 + --> $DIR/bad-reg.rs:137:27 | LL | asm!("", in("cr") x); | ^ @@ -769,7 +769,7 @@ LL | asm!("", in("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:141:28 + --> $DIR/bad-reg.rs:140:28 | LL | asm!("", out("cr") x); | ^ @@ -777,7 +777,7 @@ LL | asm!("", out("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:144:33 + --> $DIR/bad-reg.rs:143:33 | LL | asm!("/* {} */", in(cr) x); | ^ @@ -785,7 +785,7 @@ LL | asm!("/* {} */", in(cr) x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:151:28 + --> $DIR/bad-reg.rs:150:28 | LL | asm!("", in("ctr") x); | ^ @@ -793,7 +793,7 @@ LL | asm!("", in("ctr") x); = note: register class `ctr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:154:29 + --> $DIR/bad-reg.rs:153:29 | LL | asm!("", out("ctr") x); | ^ @@ -801,7 +801,7 @@ LL | asm!("", out("ctr") x); = note: register class `ctr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:157:34 + --> $DIR/bad-reg.rs:156:34 | LL | asm!("/* {} */", in(ctr) x); | ^ @@ -809,7 +809,7 @@ LL | asm!("/* {} */", in(ctr) x); = note: register class `ctr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:164:27 + --> $DIR/bad-reg.rs:163:27 | LL | asm!("", in("lr") x); | ^ @@ -817,7 +817,7 @@ LL | asm!("", in("lr") x); = note: register class `lr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:167:28 + --> $DIR/bad-reg.rs:166:28 | LL | asm!("", out("lr") x); | ^ @@ -825,7 +825,7 @@ LL | asm!("", out("lr") x); = note: register class `lr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:170:33 + --> $DIR/bad-reg.rs:169:33 | LL | asm!("/* {} */", in(lr) x); | ^ @@ -833,7 +833,7 @@ LL | asm!("/* {} */", in(lr) x); = note: register class `lr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:177:28 + --> $DIR/bad-reg.rs:176:28 | LL | asm!("", in("xer") x); | ^ @@ -841,7 +841,7 @@ LL | asm!("", in("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:180:29 + --> $DIR/bad-reg.rs:179:29 | LL | asm!("", out("xer") x); | ^ @@ -849,7 +849,7 @@ LL | asm!("", out("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:183:34 + --> $DIR/bad-reg.rs:182:34 | LL | asm!("/* {} */", in(xer) x); | ^ diff --git a/tests/ui/asm/powerpc/bad-reg.powerpc.stderr b/tests/ui/asm/powerpc/bad-reg.powerpc.stderr index 1f137b7e180b..64a8f6a3d5b9 100644 --- a/tests/ui/asm/powerpc/bad-reg.powerpc.stderr +++ b/tests/ui/asm/powerpc/bad-reg.powerpc.stderr @@ -1,137 +1,137 @@ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `r2`: r2 is a system reserved register and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("r2") _); | ^^^^^^^^^^^ error: invalid register `r29`: r29 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:42:18 | LL | asm!("", out("r29") _); | ^^^^^^^^^^^^ error: invalid register `r30`: r30 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:44:18 | LL | asm!("", out("r30") _); | ^^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:47:18 + --> $DIR/bad-reg.rs:46:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `vrsave`: the vrsave register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:49:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", out("vrsave") _); | ^^^^^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:138:18 + --> $DIR/bad-reg.rs:137:18 | LL | asm!("", in("cr") x); | ^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:141:18 + --> $DIR/bad-reg.rs:140:18 | LL | asm!("", out("cr") x); | ^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:144:26 + --> $DIR/bad-reg.rs:143:26 | LL | asm!("/* {} */", in(cr) x); | ^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:147:26 + --> $DIR/bad-reg.rs:146:26 | LL | asm!("/* {} */", out(cr) _); | ^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:151:18 + --> $DIR/bad-reg.rs:150:18 | LL | asm!("", in("ctr") x); | ^^^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:154:18 + --> $DIR/bad-reg.rs:153:18 | LL | asm!("", out("ctr") x); | ^^^^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:157:26 + --> $DIR/bad-reg.rs:156:26 | LL | asm!("/* {} */", in(ctr) x); | ^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:160:26 + --> $DIR/bad-reg.rs:159:26 | LL | asm!("/* {} */", out(ctr) _); | ^^^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:164:18 + --> $DIR/bad-reg.rs:163:18 | LL | asm!("", in("lr") x); | ^^^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:167:18 + --> $DIR/bad-reg.rs:166:18 | LL | asm!("", out("lr") x); | ^^^^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:170:26 + --> $DIR/bad-reg.rs:169:26 | LL | asm!("/* {} */", in(lr) x); | ^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:173:26 + --> $DIR/bad-reg.rs:172:26 | LL | asm!("/* {} */", out(lr) _); | ^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:177:18 + --> $DIR/bad-reg.rs:176:18 | LL | asm!("", in("xer") x); | ^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:180:18 + --> $DIR/bad-reg.rs:179:18 | LL | asm!("", out("xer") x); | ^^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:183:26 + --> $DIR/bad-reg.rs:182:26 | LL | asm!("/* {} */", in(xer) x); | ^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:186:26 + --> $DIR/bad-reg.rs:185:26 | LL | asm!("/* {} */", out(xer) _); | ^^^^^^^^^^ error: register `cr0` conflicts with register `cr` - --> $DIR/bad-reg.rs:190:31 + --> $DIR/bad-reg.rs:189:31 | LL | asm!("", out("cr") _, out("cr0") _); | ----------- ^^^^^^^^^^^^ register `cr0` @@ -139,7 +139,7 @@ LL | asm!("", out("cr") _, out("cr0") _); | register `cr` error: register `cr1` conflicts with register `cr` - --> $DIR/bad-reg.rs:192:31 + --> $DIR/bad-reg.rs:191:31 | LL | asm!("", out("cr") _, out("cr1") _); | ----------- ^^^^^^^^^^^^ register `cr1` @@ -147,7 +147,7 @@ LL | asm!("", out("cr") _, out("cr1") _); | register `cr` error: register `cr2` conflicts with register `cr` - --> $DIR/bad-reg.rs:194:31 + --> $DIR/bad-reg.rs:193:31 | LL | asm!("", out("cr") _, out("cr2") _); | ----------- ^^^^^^^^^^^^ register `cr2` @@ -155,7 +155,7 @@ LL | asm!("", out("cr") _, out("cr2") _); | register `cr` error: register `cr3` conflicts with register `cr` - --> $DIR/bad-reg.rs:196:31 + --> $DIR/bad-reg.rs:195:31 | LL | asm!("", out("cr") _, out("cr3") _); | ----------- ^^^^^^^^^^^^ register `cr3` @@ -163,7 +163,7 @@ LL | asm!("", out("cr") _, out("cr3") _); | register `cr` error: register `cr4` conflicts with register `cr` - --> $DIR/bad-reg.rs:198:31 + --> $DIR/bad-reg.rs:197:31 | LL | asm!("", out("cr") _, out("cr4") _); | ----------- ^^^^^^^^^^^^ register `cr4` @@ -171,7 +171,7 @@ LL | asm!("", out("cr") _, out("cr4") _); | register `cr` error: register `cr5` conflicts with register `cr` - --> $DIR/bad-reg.rs:200:31 + --> $DIR/bad-reg.rs:199:31 | LL | asm!("", out("cr") _, out("cr5") _); | ----------- ^^^^^^^^^^^^ register `cr5` @@ -179,7 +179,7 @@ LL | asm!("", out("cr") _, out("cr5") _); | register `cr` error: register `cr6` conflicts with register `cr` - --> $DIR/bad-reg.rs:202:31 + --> $DIR/bad-reg.rs:201:31 | LL | asm!("", out("cr") _, out("cr6") _); | ----------- ^^^^^^^^^^^^ register `cr6` @@ -187,7 +187,7 @@ LL | asm!("", out("cr") _, out("cr6") _); | register `cr` error: register `cr7` conflicts with register `cr` - --> $DIR/bad-reg.rs:204:31 + --> $DIR/bad-reg.rs:203:31 | LL | asm!("", out("cr") _, out("cr7") _); | ----------- ^^^^^^^^^^^^ register `cr7` @@ -195,7 +195,7 @@ LL | asm!("", out("cr") _, out("cr7") _); | register `cr` error: register `vs0` conflicts with register `f0` - --> $DIR/bad-reg.rs:207:31 + --> $DIR/bad-reg.rs:206:31 | LL | asm!("", out("f0") _, out("vs0") _); | ----------- ^^^^^^^^^^^^ register `vs0` @@ -203,7 +203,7 @@ LL | asm!("", out("f0") _, out("vs0") _); | register `f0` error: register `vs1` conflicts with register `f1` - --> $DIR/bad-reg.rs:209:31 + --> $DIR/bad-reg.rs:208:31 | LL | asm!("", out("f1") _, out("vs1") _); | ----------- ^^^^^^^^^^^^ register `vs1` @@ -211,7 +211,7 @@ LL | asm!("", out("f1") _, out("vs1") _); | register `f1` error: register `vs2` conflicts with register `f2` - --> $DIR/bad-reg.rs:211:31 + --> $DIR/bad-reg.rs:210:31 | LL | asm!("", out("f2") _, out("vs2") _); | ----------- ^^^^^^^^^^^^ register `vs2` @@ -219,7 +219,7 @@ LL | asm!("", out("f2") _, out("vs2") _); | register `f2` error: register `vs3` conflicts with register `f3` - --> $DIR/bad-reg.rs:213:31 + --> $DIR/bad-reg.rs:212:31 | LL | asm!("", out("f3") _, out("vs3") _); | ----------- ^^^^^^^^^^^^ register `vs3` @@ -227,7 +227,7 @@ LL | asm!("", out("f3") _, out("vs3") _); | register `f3` error: register `vs4` conflicts with register `f4` - --> $DIR/bad-reg.rs:215:31 + --> $DIR/bad-reg.rs:214:31 | LL | asm!("", out("f4") _, out("vs4") _); | ----------- ^^^^^^^^^^^^ register `vs4` @@ -235,7 +235,7 @@ LL | asm!("", out("f4") _, out("vs4") _); | register `f4` error: register `vs5` conflicts with register `f5` - --> $DIR/bad-reg.rs:217:31 + --> $DIR/bad-reg.rs:216:31 | LL | asm!("", out("f5") _, out("vs5") _); | ----------- ^^^^^^^^^^^^ register `vs5` @@ -243,7 +243,7 @@ LL | asm!("", out("f5") _, out("vs5") _); | register `f5` error: register `vs6` conflicts with register `f6` - --> $DIR/bad-reg.rs:219:31 + --> $DIR/bad-reg.rs:218:31 | LL | asm!("", out("f6") _, out("vs6") _); | ----------- ^^^^^^^^^^^^ register `vs6` @@ -251,7 +251,7 @@ LL | asm!("", out("f6") _, out("vs6") _); | register `f6` error: register `vs7` conflicts with register `f7` - --> $DIR/bad-reg.rs:221:31 + --> $DIR/bad-reg.rs:220:31 | LL | asm!("", out("f7") _, out("vs7") _); | ----------- ^^^^^^^^^^^^ register `vs7` @@ -259,7 +259,7 @@ LL | asm!("", out("f7") _, out("vs7") _); | register `f7` error: register `vs8` conflicts with register `f8` - --> $DIR/bad-reg.rs:223:31 + --> $DIR/bad-reg.rs:222:31 | LL | asm!("", out("f8") _, out("vs8") _); | ----------- ^^^^^^^^^^^^ register `vs8` @@ -267,7 +267,7 @@ LL | asm!("", out("f8") _, out("vs8") _); | register `f8` error: register `vs9` conflicts with register `f9` - --> $DIR/bad-reg.rs:225:31 + --> $DIR/bad-reg.rs:224:31 | LL | asm!("", out("f9") _, out("vs9") _); | ----------- ^^^^^^^^^^^^ register `vs9` @@ -275,7 +275,7 @@ LL | asm!("", out("f9") _, out("vs9") _); | register `f9` error: register `vs10` conflicts with register `f10` - --> $DIR/bad-reg.rs:227:32 + --> $DIR/bad-reg.rs:226:32 | LL | asm!("", out("f10") _, out("vs10") _); | ------------ ^^^^^^^^^^^^^ register `vs10` @@ -283,7 +283,7 @@ LL | asm!("", out("f10") _, out("vs10") _); | register `f10` error: register `vs11` conflicts with register `f11` - --> $DIR/bad-reg.rs:229:32 + --> $DIR/bad-reg.rs:228:32 | LL | asm!("", out("f11") _, out("vs11") _); | ------------ ^^^^^^^^^^^^^ register `vs11` @@ -291,7 +291,7 @@ LL | asm!("", out("f11") _, out("vs11") _); | register `f11` error: register `vs12` conflicts with register `f12` - --> $DIR/bad-reg.rs:231:32 + --> $DIR/bad-reg.rs:230:32 | LL | asm!("", out("f12") _, out("vs12") _); | ------------ ^^^^^^^^^^^^^ register `vs12` @@ -299,7 +299,7 @@ LL | asm!("", out("f12") _, out("vs12") _); | register `f12` error: register `vs13` conflicts with register `f13` - --> $DIR/bad-reg.rs:233:32 + --> $DIR/bad-reg.rs:232:32 | LL | asm!("", out("f13") _, out("vs13") _); | ------------ ^^^^^^^^^^^^^ register `vs13` @@ -307,7 +307,7 @@ LL | asm!("", out("f13") _, out("vs13") _); | register `f13` error: register `vs14` conflicts with register `f14` - --> $DIR/bad-reg.rs:235:32 + --> $DIR/bad-reg.rs:234:32 | LL | asm!("", out("f14") _, out("vs14") _); | ------------ ^^^^^^^^^^^^^ register `vs14` @@ -315,7 +315,7 @@ LL | asm!("", out("f14") _, out("vs14") _); | register `f14` error: register `vs15` conflicts with register `f15` - --> $DIR/bad-reg.rs:237:32 + --> $DIR/bad-reg.rs:236:32 | LL | asm!("", out("f15") _, out("vs15") _); | ------------ ^^^^^^^^^^^^^ register `vs15` @@ -323,7 +323,7 @@ LL | asm!("", out("f15") _, out("vs15") _); | register `f15` error: register `vs16` conflicts with register `f16` - --> $DIR/bad-reg.rs:239:32 + --> $DIR/bad-reg.rs:238:32 | LL | asm!("", out("f16") _, out("vs16") _); | ------------ ^^^^^^^^^^^^^ register `vs16` @@ -331,7 +331,7 @@ LL | asm!("", out("f16") _, out("vs16") _); | register `f16` error: register `vs17` conflicts with register `f17` - --> $DIR/bad-reg.rs:241:32 + --> $DIR/bad-reg.rs:240:32 | LL | asm!("", out("f17") _, out("vs17") _); | ------------ ^^^^^^^^^^^^^ register `vs17` @@ -339,7 +339,7 @@ LL | asm!("", out("f17") _, out("vs17") _); | register `f17` error: register `vs18` conflicts with register `f18` - --> $DIR/bad-reg.rs:243:32 + --> $DIR/bad-reg.rs:242:32 | LL | asm!("", out("f18") _, out("vs18") _); | ------------ ^^^^^^^^^^^^^ register `vs18` @@ -347,7 +347,7 @@ LL | asm!("", out("f18") _, out("vs18") _); | register `f18` error: register `vs19` conflicts with register `f19` - --> $DIR/bad-reg.rs:245:32 + --> $DIR/bad-reg.rs:244:32 | LL | asm!("", out("f19") _, out("vs19") _); | ------------ ^^^^^^^^^^^^^ register `vs19` @@ -355,7 +355,7 @@ LL | asm!("", out("f19") _, out("vs19") _); | register `f19` error: register `vs20` conflicts with register `f20` - --> $DIR/bad-reg.rs:247:32 + --> $DIR/bad-reg.rs:246:32 | LL | asm!("", out("f20") _, out("vs20") _); | ------------ ^^^^^^^^^^^^^ register `vs20` @@ -363,7 +363,7 @@ LL | asm!("", out("f20") _, out("vs20") _); | register `f20` error: register `vs21` conflicts with register `f21` - --> $DIR/bad-reg.rs:249:32 + --> $DIR/bad-reg.rs:248:32 | LL | asm!("", out("f21") _, out("vs21") _); | ------------ ^^^^^^^^^^^^^ register `vs21` @@ -371,7 +371,7 @@ LL | asm!("", out("f21") _, out("vs21") _); | register `f21` error: register `vs22` conflicts with register `f22` - --> $DIR/bad-reg.rs:251:32 + --> $DIR/bad-reg.rs:250:32 | LL | asm!("", out("f22") _, out("vs22") _); | ------------ ^^^^^^^^^^^^^ register `vs22` @@ -379,7 +379,7 @@ LL | asm!("", out("f22") _, out("vs22") _); | register `f22` error: register `vs23` conflicts with register `f23` - --> $DIR/bad-reg.rs:253:32 + --> $DIR/bad-reg.rs:252:32 | LL | asm!("", out("f23") _, out("vs23") _); | ------------ ^^^^^^^^^^^^^ register `vs23` @@ -387,7 +387,7 @@ LL | asm!("", out("f23") _, out("vs23") _); | register `f23` error: register `vs24` conflicts with register `f24` - --> $DIR/bad-reg.rs:255:32 + --> $DIR/bad-reg.rs:254:32 | LL | asm!("", out("f24") _, out("vs24") _); | ------------ ^^^^^^^^^^^^^ register `vs24` @@ -395,7 +395,7 @@ LL | asm!("", out("f24") _, out("vs24") _); | register `f24` error: register `vs25` conflicts with register `f25` - --> $DIR/bad-reg.rs:257:32 + --> $DIR/bad-reg.rs:256:32 | LL | asm!("", out("f25") _, out("vs25") _); | ------------ ^^^^^^^^^^^^^ register `vs25` @@ -403,7 +403,7 @@ LL | asm!("", out("f25") _, out("vs25") _); | register `f25` error: register `vs26` conflicts with register `f26` - --> $DIR/bad-reg.rs:259:32 + --> $DIR/bad-reg.rs:258:32 | LL | asm!("", out("f26") _, out("vs26") _); | ------------ ^^^^^^^^^^^^^ register `vs26` @@ -411,7 +411,7 @@ LL | asm!("", out("f26") _, out("vs26") _); | register `f26` error: register `vs27` conflicts with register `f27` - --> $DIR/bad-reg.rs:261:32 + --> $DIR/bad-reg.rs:260:32 | LL | asm!("", out("f27") _, out("vs27") _); | ------------ ^^^^^^^^^^^^^ register `vs27` @@ -419,7 +419,7 @@ LL | asm!("", out("f27") _, out("vs27") _); | register `f27` error: register `vs28` conflicts with register `f28` - --> $DIR/bad-reg.rs:263:32 + --> $DIR/bad-reg.rs:262:32 | LL | asm!("", out("f28") _, out("vs28") _); | ------------ ^^^^^^^^^^^^^ register `vs28` @@ -427,7 +427,7 @@ LL | asm!("", out("f28") _, out("vs28") _); | register `f28` error: register `vs29` conflicts with register `f29` - --> $DIR/bad-reg.rs:265:32 + --> $DIR/bad-reg.rs:264:32 | LL | asm!("", out("f29") _, out("vs29") _); | ------------ ^^^^^^^^^^^^^ register `vs29` @@ -435,7 +435,7 @@ LL | asm!("", out("f29") _, out("vs29") _); | register `f29` error: register `vs30` conflicts with register `f30` - --> $DIR/bad-reg.rs:267:32 + --> $DIR/bad-reg.rs:266:32 | LL | asm!("", out("f30") _, out("vs30") _); | ------------ ^^^^^^^^^^^^^ register `vs30` @@ -443,7 +443,7 @@ LL | asm!("", out("f30") _, out("vs30") _); | register `f30` error: register `vs31` conflicts with register `f31` - --> $DIR/bad-reg.rs:269:32 + --> $DIR/bad-reg.rs:268:32 | LL | asm!("", out("f31") _, out("vs31") _); | ------------ ^^^^^^^^^^^^^ register `vs31` @@ -451,7 +451,7 @@ LL | asm!("", out("f31") _, out("vs31") _); | register `f31` error: register `v0` conflicts with register `vs32` - --> $DIR/bad-reg.rs:271:33 + --> $DIR/bad-reg.rs:270:33 | LL | asm!("", out("vs32") _, out("v0") _); | ------------- ^^^^^^^^^^^ register `v0` @@ -459,7 +459,7 @@ LL | asm!("", out("vs32") _, out("v0") _); | register `vs32` error: register `v1` conflicts with register `vs33` - --> $DIR/bad-reg.rs:273:33 + --> $DIR/bad-reg.rs:272:33 | LL | asm!("", out("vs33") _, out("v1") _); | ------------- ^^^^^^^^^^^ register `v1` @@ -467,7 +467,7 @@ LL | asm!("", out("vs33") _, out("v1") _); | register `vs33` error: register `v2` conflicts with register `vs34` - --> $DIR/bad-reg.rs:275:33 + --> $DIR/bad-reg.rs:274:33 | LL | asm!("", out("vs34") _, out("v2") _); | ------------- ^^^^^^^^^^^ register `v2` @@ -475,7 +475,7 @@ LL | asm!("", out("vs34") _, out("v2") _); | register `vs34` error: register `v3` conflicts with register `vs35` - --> $DIR/bad-reg.rs:277:33 + --> $DIR/bad-reg.rs:276:33 | LL | asm!("", out("vs35") _, out("v3") _); | ------------- ^^^^^^^^^^^ register `v3` @@ -483,7 +483,7 @@ LL | asm!("", out("vs35") _, out("v3") _); | register `vs35` error: register `v4` conflicts with register `vs36` - --> $DIR/bad-reg.rs:279:33 + --> $DIR/bad-reg.rs:278:33 | LL | asm!("", out("vs36") _, out("v4") _); | ------------- ^^^^^^^^^^^ register `v4` @@ -491,7 +491,7 @@ LL | asm!("", out("vs36") _, out("v4") _); | register `vs36` error: register `v5` conflicts with register `vs37` - --> $DIR/bad-reg.rs:281:33 + --> $DIR/bad-reg.rs:280:33 | LL | asm!("", out("vs37") _, out("v5") _); | ------------- ^^^^^^^^^^^ register `v5` @@ -499,7 +499,7 @@ LL | asm!("", out("vs37") _, out("v5") _); | register `vs37` error: register `v6` conflicts with register `vs38` - --> $DIR/bad-reg.rs:283:33 + --> $DIR/bad-reg.rs:282:33 | LL | asm!("", out("vs38") _, out("v6") _); | ------------- ^^^^^^^^^^^ register `v6` @@ -507,7 +507,7 @@ LL | asm!("", out("vs38") _, out("v6") _); | register `vs38` error: register `v7` conflicts with register `vs39` - --> $DIR/bad-reg.rs:285:33 + --> $DIR/bad-reg.rs:284:33 | LL | asm!("", out("vs39") _, out("v7") _); | ------------- ^^^^^^^^^^^ register `v7` @@ -515,7 +515,7 @@ LL | asm!("", out("vs39") _, out("v7") _); | register `vs39` error: register `v8` conflicts with register `vs40` - --> $DIR/bad-reg.rs:287:33 + --> $DIR/bad-reg.rs:286:33 | LL | asm!("", out("vs40") _, out("v8") _); | ------------- ^^^^^^^^^^^ register `v8` @@ -523,7 +523,7 @@ LL | asm!("", out("vs40") _, out("v8") _); | register `vs40` error: register `v9` conflicts with register `vs41` - --> $DIR/bad-reg.rs:289:33 + --> $DIR/bad-reg.rs:288:33 | LL | asm!("", out("vs41") _, out("v9") _); | ------------- ^^^^^^^^^^^ register `v9` @@ -531,7 +531,7 @@ LL | asm!("", out("vs41") _, out("v9") _); | register `vs41` error: register `v10` conflicts with register `vs42` - --> $DIR/bad-reg.rs:291:33 + --> $DIR/bad-reg.rs:290:33 | LL | asm!("", out("vs42") _, out("v10") _); | ------------- ^^^^^^^^^^^^ register `v10` @@ -539,7 +539,7 @@ LL | asm!("", out("vs42") _, out("v10") _); | register `vs42` error: register `v11` conflicts with register `vs43` - --> $DIR/bad-reg.rs:293:33 + --> $DIR/bad-reg.rs:292:33 | LL | asm!("", out("vs43") _, out("v11") _); | ------------- ^^^^^^^^^^^^ register `v11` @@ -547,7 +547,7 @@ LL | asm!("", out("vs43") _, out("v11") _); | register `vs43` error: register `v12` conflicts with register `vs44` - --> $DIR/bad-reg.rs:295:33 + --> $DIR/bad-reg.rs:294:33 | LL | asm!("", out("vs44") _, out("v12") _); | ------------- ^^^^^^^^^^^^ register `v12` @@ -555,7 +555,7 @@ LL | asm!("", out("vs44") _, out("v12") _); | register `vs44` error: register `v13` conflicts with register `vs45` - --> $DIR/bad-reg.rs:297:33 + --> $DIR/bad-reg.rs:296:33 | LL | asm!("", out("vs45") _, out("v13") _); | ------------- ^^^^^^^^^^^^ register `v13` @@ -563,7 +563,7 @@ LL | asm!("", out("vs45") _, out("v13") _); | register `vs45` error: register `v14` conflicts with register `vs46` - --> $DIR/bad-reg.rs:299:33 + --> $DIR/bad-reg.rs:298:33 | LL | asm!("", out("vs46") _, out("v14") _); | ------------- ^^^^^^^^^^^^ register `v14` @@ -571,7 +571,7 @@ LL | asm!("", out("vs46") _, out("v14") _); | register `vs46` error: register `v15` conflicts with register `vs47` - --> $DIR/bad-reg.rs:301:33 + --> $DIR/bad-reg.rs:300:33 | LL | asm!("", out("vs47") _, out("v15") _); | ------------- ^^^^^^^^^^^^ register `v15` @@ -579,7 +579,7 @@ LL | asm!("", out("vs47") _, out("v15") _); | register `vs47` error: register `v16` conflicts with register `vs48` - --> $DIR/bad-reg.rs:303:33 + --> $DIR/bad-reg.rs:302:33 | LL | asm!("", out("vs48") _, out("v16") _); | ------------- ^^^^^^^^^^^^ register `v16` @@ -587,7 +587,7 @@ LL | asm!("", out("vs48") _, out("v16") _); | register `vs48` error: register `v17` conflicts with register `vs49` - --> $DIR/bad-reg.rs:305:33 + --> $DIR/bad-reg.rs:304:33 | LL | asm!("", out("vs49") _, out("v17") _); | ------------- ^^^^^^^^^^^^ register `v17` @@ -595,7 +595,7 @@ LL | asm!("", out("vs49") _, out("v17") _); | register `vs49` error: register `v18` conflicts with register `vs50` - --> $DIR/bad-reg.rs:307:33 + --> $DIR/bad-reg.rs:306:33 | LL | asm!("", out("vs50") _, out("v18") _); | ------------- ^^^^^^^^^^^^ register `v18` @@ -603,7 +603,7 @@ LL | asm!("", out("vs50") _, out("v18") _); | register `vs50` error: register `v19` conflicts with register `vs51` - --> $DIR/bad-reg.rs:309:33 + --> $DIR/bad-reg.rs:308:33 | LL | asm!("", out("vs51") _, out("v19") _); | ------------- ^^^^^^^^^^^^ register `v19` @@ -611,7 +611,7 @@ LL | asm!("", out("vs51") _, out("v19") _); | register `vs51` error: register `v20` conflicts with register `vs52` - --> $DIR/bad-reg.rs:311:33 + --> $DIR/bad-reg.rs:310:33 | LL | asm!("", out("vs52") _, out("v20") _); | ------------- ^^^^^^^^^^^^ register `v20` @@ -619,7 +619,7 @@ LL | asm!("", out("vs52") _, out("v20") _); | register `vs52` error: register `v21` conflicts with register `vs53` - --> $DIR/bad-reg.rs:313:33 + --> $DIR/bad-reg.rs:312:33 | LL | asm!("", out("vs53") _, out("v21") _); | ------------- ^^^^^^^^^^^^ register `v21` @@ -627,7 +627,7 @@ LL | asm!("", out("vs53") _, out("v21") _); | register `vs53` error: register `v22` conflicts with register `vs54` - --> $DIR/bad-reg.rs:315:33 + --> $DIR/bad-reg.rs:314:33 | LL | asm!("", out("vs54") _, out("v22") _); | ------------- ^^^^^^^^^^^^ register `v22` @@ -635,7 +635,7 @@ LL | asm!("", out("vs54") _, out("v22") _); | register `vs54` error: register `v23` conflicts with register `vs55` - --> $DIR/bad-reg.rs:317:33 + --> $DIR/bad-reg.rs:316:33 | LL | asm!("", out("vs55") _, out("v23") _); | ------------- ^^^^^^^^^^^^ register `v23` @@ -643,7 +643,7 @@ LL | asm!("", out("vs55") _, out("v23") _); | register `vs55` error: register `v24` conflicts with register `vs56` - --> $DIR/bad-reg.rs:319:33 + --> $DIR/bad-reg.rs:318:33 | LL | asm!("", out("vs56") _, out("v24") _); | ------------- ^^^^^^^^^^^^ register `v24` @@ -651,7 +651,7 @@ LL | asm!("", out("vs56") _, out("v24") _); | register `vs56` error: register `v25` conflicts with register `vs57` - --> $DIR/bad-reg.rs:321:33 + --> $DIR/bad-reg.rs:320:33 | LL | asm!("", out("vs57") _, out("v25") _); | ------------- ^^^^^^^^^^^^ register `v25` @@ -659,7 +659,7 @@ LL | asm!("", out("vs57") _, out("v25") _); | register `vs57` error: register `v26` conflicts with register `vs58` - --> $DIR/bad-reg.rs:323:33 + --> $DIR/bad-reg.rs:322:33 | LL | asm!("", out("vs58") _, out("v26") _); | ------------- ^^^^^^^^^^^^ register `v26` @@ -667,7 +667,7 @@ LL | asm!("", out("vs58") _, out("v26") _); | register `vs58` error: register `v27` conflicts with register `vs59` - --> $DIR/bad-reg.rs:325:33 + --> $DIR/bad-reg.rs:324:33 | LL | asm!("", out("vs59") _, out("v27") _); | ------------- ^^^^^^^^^^^^ register `v27` @@ -675,7 +675,7 @@ LL | asm!("", out("vs59") _, out("v27") _); | register `vs59` error: register `v28` conflicts with register `vs60` - --> $DIR/bad-reg.rs:327:33 + --> $DIR/bad-reg.rs:326:33 | LL | asm!("", out("vs60") _, out("v28") _); | ------------- ^^^^^^^^^^^^ register `v28` @@ -683,7 +683,7 @@ LL | asm!("", out("vs60") _, out("v28") _); | register `vs60` error: register `v29` conflicts with register `vs61` - --> $DIR/bad-reg.rs:329:33 + --> $DIR/bad-reg.rs:328:33 | LL | asm!("", out("vs61") _, out("v29") _); | ------------- ^^^^^^^^^^^^ register `v29` @@ -691,7 +691,7 @@ LL | asm!("", out("vs61") _, out("v29") _); | register `vs61` error: register `v30` conflicts with register `vs62` - --> $DIR/bad-reg.rs:331:33 + --> $DIR/bad-reg.rs:330:33 | LL | asm!("", out("vs62") _, out("v30") _); | ------------- ^^^^^^^^^^^^ register `v30` @@ -699,7 +699,7 @@ LL | asm!("", out("vs62") _, out("v30") _); | register `vs62` error: register `v31` conflicts with register `vs63` - --> $DIR/bad-reg.rs:333:33 + --> $DIR/bad-reg.rs:332:33 | LL | asm!("", out("vs63") _, out("v31") _); | ------------- ^^^^^^^^^^^^ register `v31` @@ -707,133 +707,133 @@ LL | asm!("", out("vs63") _, out("v31") _); | register `vs63` error: cannot use register `r13`: r13 is a reserved register on this target - --> $DIR/bad-reg.rs:41:18 + --> $DIR/bad-reg.rs:40:18 | LL | asm!("", out("r13") _); | ^^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:54:18 + --> $DIR/bad-reg.rs:53:18 | LL | asm!("", in("v0") v32x4); // requires altivec | ^^^^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:56:18 + --> $DIR/bad-reg.rs:55:18 | LL | asm!("", out("v0") v32x4); // requires altivec | ^^^^^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:58:18 + --> $DIR/bad-reg.rs:57:18 | LL | asm!("", in("v0") v64x2); // requires vsx | ^^^^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:61:18 + --> $DIR/bad-reg.rs:60:18 | LL | asm!("", out("v0") v64x2); // requires vsx | ^^^^^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:64:18 + --> $DIR/bad-reg.rs:63:18 | LL | asm!("", in("v0") x); // FIXME: should be ok if vsx is available | ^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:67:18 + --> $DIR/bad-reg.rs:66:18 | LL | asm!("", out("v0") x); // FIXME: should be ok if vsx is available | ^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:70:26 + --> $DIR/bad-reg.rs:69:26 | LL | asm!("/* {} */", in(vreg) v32x4); // requires altivec | ^^^^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:72:26 + --> $DIR/bad-reg.rs:71:26 | LL | asm!("/* {} */", in(vreg) v64x2); // requires vsx | ^^^^^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:75:26 + --> $DIR/bad-reg.rs:74:26 | LL | asm!("/* {} */", in(vreg) x); // FIXME: should be ok if vsx is available | ^^^^^^^^^^ error: register class `vreg` requires at least one of the following target features: altivec, vsx - --> $DIR/bad-reg.rs:78:26 + --> $DIR/bad-reg.rs:77:26 | LL | asm!("/* {} */", out(vreg) _); // requires altivec | ^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:97:18 + --> $DIR/bad-reg.rs:96:18 | LL | asm!("", in("vs0") v32x4); // requires vsx | ^^^^^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:98:18 | LL | asm!("", out("vs0") v32x4); // requires vsx | ^^^^^^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:101:18 + --> $DIR/bad-reg.rs:100:18 | LL | asm!("", in("vs0") v64x2); // requires vsx | ^^^^^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:103:18 + --> $DIR/bad-reg.rs:102:18 | LL | asm!("", out("vs0") v64x2); // requires vsx | ^^^^^^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:105:18 + --> $DIR/bad-reg.rs:104:18 | LL | asm!("", in("vs0") x); // FIXME: should be ok if vsx is available | ^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:108:18 + --> $DIR/bad-reg.rs:107:18 | LL | asm!("", out("vs0") x); // FIXME: should be ok if vsx is available | ^^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:111:26 + --> $DIR/bad-reg.rs:110:26 | LL | asm!("/* {} */", in(vsreg) v32x4); // requires vsx | ^^^^^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:113:26 + --> $DIR/bad-reg.rs:112:26 | LL | asm!("/* {} */", in(vsreg) v64x2); // requires vsx | ^^^^^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:115:26 + --> $DIR/bad-reg.rs:114:26 | LL | asm!("/* {} */", in(vsreg) x); // FIXME: should be ok if vsx is available | ^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:118:26 + --> $DIR/bad-reg.rs:117:26 | LL | asm!("/* {} */", out(vsreg) _); // requires vsx | ^^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:138:27 + --> $DIR/bad-reg.rs:137:27 | LL | asm!("", in("cr") x); | ^ @@ -841,7 +841,7 @@ LL | asm!("", in("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:141:28 + --> $DIR/bad-reg.rs:140:28 | LL | asm!("", out("cr") x); | ^ @@ -849,7 +849,7 @@ LL | asm!("", out("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:144:33 + --> $DIR/bad-reg.rs:143:33 | LL | asm!("/* {} */", in(cr) x); | ^ @@ -857,7 +857,7 @@ LL | asm!("/* {} */", in(cr) x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:151:28 + --> $DIR/bad-reg.rs:150:28 | LL | asm!("", in("ctr") x); | ^ @@ -865,7 +865,7 @@ LL | asm!("", in("ctr") x); = note: register class `ctr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:154:29 + --> $DIR/bad-reg.rs:153:29 | LL | asm!("", out("ctr") x); | ^ @@ -873,7 +873,7 @@ LL | asm!("", out("ctr") x); = note: register class `ctr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:157:34 + --> $DIR/bad-reg.rs:156:34 | LL | asm!("/* {} */", in(ctr) x); | ^ @@ -881,7 +881,7 @@ LL | asm!("/* {} */", in(ctr) x); = note: register class `ctr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:164:27 + --> $DIR/bad-reg.rs:163:27 | LL | asm!("", in("lr") x); | ^ @@ -889,7 +889,7 @@ LL | asm!("", in("lr") x); = note: register class `lr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:167:28 + --> $DIR/bad-reg.rs:166:28 | LL | asm!("", out("lr") x); | ^ @@ -897,7 +897,7 @@ LL | asm!("", out("lr") x); = note: register class `lr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:170:33 + --> $DIR/bad-reg.rs:169:33 | LL | asm!("/* {} */", in(lr) x); | ^ @@ -905,7 +905,7 @@ LL | asm!("/* {} */", in(lr) x); = note: register class `lr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:177:28 + --> $DIR/bad-reg.rs:176:28 | LL | asm!("", in("xer") x); | ^ @@ -913,7 +913,7 @@ LL | asm!("", in("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:180:29 + --> $DIR/bad-reg.rs:179:29 | LL | asm!("", out("xer") x); | ^ @@ -921,7 +921,7 @@ LL | asm!("", out("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:183:34 + --> $DIR/bad-reg.rs:182:34 | LL | asm!("/* {} */", in(xer) x); | ^ diff --git a/tests/ui/asm/powerpc/bad-reg.powerpc64.stderr b/tests/ui/asm/powerpc/bad-reg.powerpc64.stderr index d5ca6e331eda..3315a00cc1ab 100644 --- a/tests/ui/asm/powerpc/bad-reg.powerpc64.stderr +++ b/tests/ui/asm/powerpc/bad-reg.powerpc64.stderr @@ -1,137 +1,137 @@ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `r2`: r2 is a system reserved register and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("r2") _); | ^^^^^^^^^^^ error: invalid register `r29`: r29 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:42:18 | LL | asm!("", out("r29") _); | ^^^^^^^^^^^^ error: invalid register `r30`: r30 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:44:18 | LL | asm!("", out("r30") _); | ^^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:47:18 + --> $DIR/bad-reg.rs:46:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `vrsave`: the vrsave register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:49:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", out("vrsave") _); | ^^^^^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:138:18 + --> $DIR/bad-reg.rs:137:18 | LL | asm!("", in("cr") x); | ^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:141:18 + --> $DIR/bad-reg.rs:140:18 | LL | asm!("", out("cr") x); | ^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:144:26 + --> $DIR/bad-reg.rs:143:26 | LL | asm!("/* {} */", in(cr) x); | ^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:147:26 + --> $DIR/bad-reg.rs:146:26 | LL | asm!("/* {} */", out(cr) _); | ^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:151:18 + --> $DIR/bad-reg.rs:150:18 | LL | asm!("", in("ctr") x); | ^^^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:154:18 + --> $DIR/bad-reg.rs:153:18 | LL | asm!("", out("ctr") x); | ^^^^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:157:26 + --> $DIR/bad-reg.rs:156:26 | LL | asm!("/* {} */", in(ctr) x); | ^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:160:26 + --> $DIR/bad-reg.rs:159:26 | LL | asm!("/* {} */", out(ctr) _); | ^^^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:164:18 + --> $DIR/bad-reg.rs:163:18 | LL | asm!("", in("lr") x); | ^^^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:167:18 + --> $DIR/bad-reg.rs:166:18 | LL | asm!("", out("lr") x); | ^^^^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:170:26 + --> $DIR/bad-reg.rs:169:26 | LL | asm!("/* {} */", in(lr) x); | ^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:173:26 + --> $DIR/bad-reg.rs:172:26 | LL | asm!("/* {} */", out(lr) _); | ^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:177:18 + --> $DIR/bad-reg.rs:176:18 | LL | asm!("", in("xer") x); | ^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:180:18 + --> $DIR/bad-reg.rs:179:18 | LL | asm!("", out("xer") x); | ^^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:183:26 + --> $DIR/bad-reg.rs:182:26 | LL | asm!("/* {} */", in(xer) x); | ^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:186:26 + --> $DIR/bad-reg.rs:185:26 | LL | asm!("/* {} */", out(xer) _); | ^^^^^^^^^^ error: register `cr0` conflicts with register `cr` - --> $DIR/bad-reg.rs:190:31 + --> $DIR/bad-reg.rs:189:31 | LL | asm!("", out("cr") _, out("cr0") _); | ----------- ^^^^^^^^^^^^ register `cr0` @@ -139,7 +139,7 @@ LL | asm!("", out("cr") _, out("cr0") _); | register `cr` error: register `cr1` conflicts with register `cr` - --> $DIR/bad-reg.rs:192:31 + --> $DIR/bad-reg.rs:191:31 | LL | asm!("", out("cr") _, out("cr1") _); | ----------- ^^^^^^^^^^^^ register `cr1` @@ -147,7 +147,7 @@ LL | asm!("", out("cr") _, out("cr1") _); | register `cr` error: register `cr2` conflicts with register `cr` - --> $DIR/bad-reg.rs:194:31 + --> $DIR/bad-reg.rs:193:31 | LL | asm!("", out("cr") _, out("cr2") _); | ----------- ^^^^^^^^^^^^ register `cr2` @@ -155,7 +155,7 @@ LL | asm!("", out("cr") _, out("cr2") _); | register `cr` error: register `cr3` conflicts with register `cr` - --> $DIR/bad-reg.rs:196:31 + --> $DIR/bad-reg.rs:195:31 | LL | asm!("", out("cr") _, out("cr3") _); | ----------- ^^^^^^^^^^^^ register `cr3` @@ -163,7 +163,7 @@ LL | asm!("", out("cr") _, out("cr3") _); | register `cr` error: register `cr4` conflicts with register `cr` - --> $DIR/bad-reg.rs:198:31 + --> $DIR/bad-reg.rs:197:31 | LL | asm!("", out("cr") _, out("cr4") _); | ----------- ^^^^^^^^^^^^ register `cr4` @@ -171,7 +171,7 @@ LL | asm!("", out("cr") _, out("cr4") _); | register `cr` error: register `cr5` conflicts with register `cr` - --> $DIR/bad-reg.rs:200:31 + --> $DIR/bad-reg.rs:199:31 | LL | asm!("", out("cr") _, out("cr5") _); | ----------- ^^^^^^^^^^^^ register `cr5` @@ -179,7 +179,7 @@ LL | asm!("", out("cr") _, out("cr5") _); | register `cr` error: register `cr6` conflicts with register `cr` - --> $DIR/bad-reg.rs:202:31 + --> $DIR/bad-reg.rs:201:31 | LL | asm!("", out("cr") _, out("cr6") _); | ----------- ^^^^^^^^^^^^ register `cr6` @@ -187,7 +187,7 @@ LL | asm!("", out("cr") _, out("cr6") _); | register `cr` error: register `cr7` conflicts with register `cr` - --> $DIR/bad-reg.rs:204:31 + --> $DIR/bad-reg.rs:203:31 | LL | asm!("", out("cr") _, out("cr7") _); | ----------- ^^^^^^^^^^^^ register `cr7` @@ -195,7 +195,7 @@ LL | asm!("", out("cr") _, out("cr7") _); | register `cr` error: register `vs0` conflicts with register `f0` - --> $DIR/bad-reg.rs:207:31 + --> $DIR/bad-reg.rs:206:31 | LL | asm!("", out("f0") _, out("vs0") _); | ----------- ^^^^^^^^^^^^ register `vs0` @@ -203,7 +203,7 @@ LL | asm!("", out("f0") _, out("vs0") _); | register `f0` error: register `vs1` conflicts with register `f1` - --> $DIR/bad-reg.rs:209:31 + --> $DIR/bad-reg.rs:208:31 | LL | asm!("", out("f1") _, out("vs1") _); | ----------- ^^^^^^^^^^^^ register `vs1` @@ -211,7 +211,7 @@ LL | asm!("", out("f1") _, out("vs1") _); | register `f1` error: register `vs2` conflicts with register `f2` - --> $DIR/bad-reg.rs:211:31 + --> $DIR/bad-reg.rs:210:31 | LL | asm!("", out("f2") _, out("vs2") _); | ----------- ^^^^^^^^^^^^ register `vs2` @@ -219,7 +219,7 @@ LL | asm!("", out("f2") _, out("vs2") _); | register `f2` error: register `vs3` conflicts with register `f3` - --> $DIR/bad-reg.rs:213:31 + --> $DIR/bad-reg.rs:212:31 | LL | asm!("", out("f3") _, out("vs3") _); | ----------- ^^^^^^^^^^^^ register `vs3` @@ -227,7 +227,7 @@ LL | asm!("", out("f3") _, out("vs3") _); | register `f3` error: register `vs4` conflicts with register `f4` - --> $DIR/bad-reg.rs:215:31 + --> $DIR/bad-reg.rs:214:31 | LL | asm!("", out("f4") _, out("vs4") _); | ----------- ^^^^^^^^^^^^ register `vs4` @@ -235,7 +235,7 @@ LL | asm!("", out("f4") _, out("vs4") _); | register `f4` error: register `vs5` conflicts with register `f5` - --> $DIR/bad-reg.rs:217:31 + --> $DIR/bad-reg.rs:216:31 | LL | asm!("", out("f5") _, out("vs5") _); | ----------- ^^^^^^^^^^^^ register `vs5` @@ -243,7 +243,7 @@ LL | asm!("", out("f5") _, out("vs5") _); | register `f5` error: register `vs6` conflicts with register `f6` - --> $DIR/bad-reg.rs:219:31 + --> $DIR/bad-reg.rs:218:31 | LL | asm!("", out("f6") _, out("vs6") _); | ----------- ^^^^^^^^^^^^ register `vs6` @@ -251,7 +251,7 @@ LL | asm!("", out("f6") _, out("vs6") _); | register `f6` error: register `vs7` conflicts with register `f7` - --> $DIR/bad-reg.rs:221:31 + --> $DIR/bad-reg.rs:220:31 | LL | asm!("", out("f7") _, out("vs7") _); | ----------- ^^^^^^^^^^^^ register `vs7` @@ -259,7 +259,7 @@ LL | asm!("", out("f7") _, out("vs7") _); | register `f7` error: register `vs8` conflicts with register `f8` - --> $DIR/bad-reg.rs:223:31 + --> $DIR/bad-reg.rs:222:31 | LL | asm!("", out("f8") _, out("vs8") _); | ----------- ^^^^^^^^^^^^ register `vs8` @@ -267,7 +267,7 @@ LL | asm!("", out("f8") _, out("vs8") _); | register `f8` error: register `vs9` conflicts with register `f9` - --> $DIR/bad-reg.rs:225:31 + --> $DIR/bad-reg.rs:224:31 | LL | asm!("", out("f9") _, out("vs9") _); | ----------- ^^^^^^^^^^^^ register `vs9` @@ -275,7 +275,7 @@ LL | asm!("", out("f9") _, out("vs9") _); | register `f9` error: register `vs10` conflicts with register `f10` - --> $DIR/bad-reg.rs:227:32 + --> $DIR/bad-reg.rs:226:32 | LL | asm!("", out("f10") _, out("vs10") _); | ------------ ^^^^^^^^^^^^^ register `vs10` @@ -283,7 +283,7 @@ LL | asm!("", out("f10") _, out("vs10") _); | register `f10` error: register `vs11` conflicts with register `f11` - --> $DIR/bad-reg.rs:229:32 + --> $DIR/bad-reg.rs:228:32 | LL | asm!("", out("f11") _, out("vs11") _); | ------------ ^^^^^^^^^^^^^ register `vs11` @@ -291,7 +291,7 @@ LL | asm!("", out("f11") _, out("vs11") _); | register `f11` error: register `vs12` conflicts with register `f12` - --> $DIR/bad-reg.rs:231:32 + --> $DIR/bad-reg.rs:230:32 | LL | asm!("", out("f12") _, out("vs12") _); | ------------ ^^^^^^^^^^^^^ register `vs12` @@ -299,7 +299,7 @@ LL | asm!("", out("f12") _, out("vs12") _); | register `f12` error: register `vs13` conflicts with register `f13` - --> $DIR/bad-reg.rs:233:32 + --> $DIR/bad-reg.rs:232:32 | LL | asm!("", out("f13") _, out("vs13") _); | ------------ ^^^^^^^^^^^^^ register `vs13` @@ -307,7 +307,7 @@ LL | asm!("", out("f13") _, out("vs13") _); | register `f13` error: register `vs14` conflicts with register `f14` - --> $DIR/bad-reg.rs:235:32 + --> $DIR/bad-reg.rs:234:32 | LL | asm!("", out("f14") _, out("vs14") _); | ------------ ^^^^^^^^^^^^^ register `vs14` @@ -315,7 +315,7 @@ LL | asm!("", out("f14") _, out("vs14") _); | register `f14` error: register `vs15` conflicts with register `f15` - --> $DIR/bad-reg.rs:237:32 + --> $DIR/bad-reg.rs:236:32 | LL | asm!("", out("f15") _, out("vs15") _); | ------------ ^^^^^^^^^^^^^ register `vs15` @@ -323,7 +323,7 @@ LL | asm!("", out("f15") _, out("vs15") _); | register `f15` error: register `vs16` conflicts with register `f16` - --> $DIR/bad-reg.rs:239:32 + --> $DIR/bad-reg.rs:238:32 | LL | asm!("", out("f16") _, out("vs16") _); | ------------ ^^^^^^^^^^^^^ register `vs16` @@ -331,7 +331,7 @@ LL | asm!("", out("f16") _, out("vs16") _); | register `f16` error: register `vs17` conflicts with register `f17` - --> $DIR/bad-reg.rs:241:32 + --> $DIR/bad-reg.rs:240:32 | LL | asm!("", out("f17") _, out("vs17") _); | ------------ ^^^^^^^^^^^^^ register `vs17` @@ -339,7 +339,7 @@ LL | asm!("", out("f17") _, out("vs17") _); | register `f17` error: register `vs18` conflicts with register `f18` - --> $DIR/bad-reg.rs:243:32 + --> $DIR/bad-reg.rs:242:32 | LL | asm!("", out("f18") _, out("vs18") _); | ------------ ^^^^^^^^^^^^^ register `vs18` @@ -347,7 +347,7 @@ LL | asm!("", out("f18") _, out("vs18") _); | register `f18` error: register `vs19` conflicts with register `f19` - --> $DIR/bad-reg.rs:245:32 + --> $DIR/bad-reg.rs:244:32 | LL | asm!("", out("f19") _, out("vs19") _); | ------------ ^^^^^^^^^^^^^ register `vs19` @@ -355,7 +355,7 @@ LL | asm!("", out("f19") _, out("vs19") _); | register `f19` error: register `vs20` conflicts with register `f20` - --> $DIR/bad-reg.rs:247:32 + --> $DIR/bad-reg.rs:246:32 | LL | asm!("", out("f20") _, out("vs20") _); | ------------ ^^^^^^^^^^^^^ register `vs20` @@ -363,7 +363,7 @@ LL | asm!("", out("f20") _, out("vs20") _); | register `f20` error: register `vs21` conflicts with register `f21` - --> $DIR/bad-reg.rs:249:32 + --> $DIR/bad-reg.rs:248:32 | LL | asm!("", out("f21") _, out("vs21") _); | ------------ ^^^^^^^^^^^^^ register `vs21` @@ -371,7 +371,7 @@ LL | asm!("", out("f21") _, out("vs21") _); | register `f21` error: register `vs22` conflicts with register `f22` - --> $DIR/bad-reg.rs:251:32 + --> $DIR/bad-reg.rs:250:32 | LL | asm!("", out("f22") _, out("vs22") _); | ------------ ^^^^^^^^^^^^^ register `vs22` @@ -379,7 +379,7 @@ LL | asm!("", out("f22") _, out("vs22") _); | register `f22` error: register `vs23` conflicts with register `f23` - --> $DIR/bad-reg.rs:253:32 + --> $DIR/bad-reg.rs:252:32 | LL | asm!("", out("f23") _, out("vs23") _); | ------------ ^^^^^^^^^^^^^ register `vs23` @@ -387,7 +387,7 @@ LL | asm!("", out("f23") _, out("vs23") _); | register `f23` error: register `vs24` conflicts with register `f24` - --> $DIR/bad-reg.rs:255:32 + --> $DIR/bad-reg.rs:254:32 | LL | asm!("", out("f24") _, out("vs24") _); | ------------ ^^^^^^^^^^^^^ register `vs24` @@ -395,7 +395,7 @@ LL | asm!("", out("f24") _, out("vs24") _); | register `f24` error: register `vs25` conflicts with register `f25` - --> $DIR/bad-reg.rs:257:32 + --> $DIR/bad-reg.rs:256:32 | LL | asm!("", out("f25") _, out("vs25") _); | ------------ ^^^^^^^^^^^^^ register `vs25` @@ -403,7 +403,7 @@ LL | asm!("", out("f25") _, out("vs25") _); | register `f25` error: register `vs26` conflicts with register `f26` - --> $DIR/bad-reg.rs:259:32 + --> $DIR/bad-reg.rs:258:32 | LL | asm!("", out("f26") _, out("vs26") _); | ------------ ^^^^^^^^^^^^^ register `vs26` @@ -411,7 +411,7 @@ LL | asm!("", out("f26") _, out("vs26") _); | register `f26` error: register `vs27` conflicts with register `f27` - --> $DIR/bad-reg.rs:261:32 + --> $DIR/bad-reg.rs:260:32 | LL | asm!("", out("f27") _, out("vs27") _); | ------------ ^^^^^^^^^^^^^ register `vs27` @@ -419,7 +419,7 @@ LL | asm!("", out("f27") _, out("vs27") _); | register `f27` error: register `vs28` conflicts with register `f28` - --> $DIR/bad-reg.rs:263:32 + --> $DIR/bad-reg.rs:262:32 | LL | asm!("", out("f28") _, out("vs28") _); | ------------ ^^^^^^^^^^^^^ register `vs28` @@ -427,7 +427,7 @@ LL | asm!("", out("f28") _, out("vs28") _); | register `f28` error: register `vs29` conflicts with register `f29` - --> $DIR/bad-reg.rs:265:32 + --> $DIR/bad-reg.rs:264:32 | LL | asm!("", out("f29") _, out("vs29") _); | ------------ ^^^^^^^^^^^^^ register `vs29` @@ -435,7 +435,7 @@ LL | asm!("", out("f29") _, out("vs29") _); | register `f29` error: register `vs30` conflicts with register `f30` - --> $DIR/bad-reg.rs:267:32 + --> $DIR/bad-reg.rs:266:32 | LL | asm!("", out("f30") _, out("vs30") _); | ------------ ^^^^^^^^^^^^^ register `vs30` @@ -443,7 +443,7 @@ LL | asm!("", out("f30") _, out("vs30") _); | register `f30` error: register `vs31` conflicts with register `f31` - --> $DIR/bad-reg.rs:269:32 + --> $DIR/bad-reg.rs:268:32 | LL | asm!("", out("f31") _, out("vs31") _); | ------------ ^^^^^^^^^^^^^ register `vs31` @@ -451,7 +451,7 @@ LL | asm!("", out("f31") _, out("vs31") _); | register `f31` error: register `v0` conflicts with register `vs32` - --> $DIR/bad-reg.rs:271:33 + --> $DIR/bad-reg.rs:270:33 | LL | asm!("", out("vs32") _, out("v0") _); | ------------- ^^^^^^^^^^^ register `v0` @@ -459,7 +459,7 @@ LL | asm!("", out("vs32") _, out("v0") _); | register `vs32` error: register `v1` conflicts with register `vs33` - --> $DIR/bad-reg.rs:273:33 + --> $DIR/bad-reg.rs:272:33 | LL | asm!("", out("vs33") _, out("v1") _); | ------------- ^^^^^^^^^^^ register `v1` @@ -467,7 +467,7 @@ LL | asm!("", out("vs33") _, out("v1") _); | register `vs33` error: register `v2` conflicts with register `vs34` - --> $DIR/bad-reg.rs:275:33 + --> $DIR/bad-reg.rs:274:33 | LL | asm!("", out("vs34") _, out("v2") _); | ------------- ^^^^^^^^^^^ register `v2` @@ -475,7 +475,7 @@ LL | asm!("", out("vs34") _, out("v2") _); | register `vs34` error: register `v3` conflicts with register `vs35` - --> $DIR/bad-reg.rs:277:33 + --> $DIR/bad-reg.rs:276:33 | LL | asm!("", out("vs35") _, out("v3") _); | ------------- ^^^^^^^^^^^ register `v3` @@ -483,7 +483,7 @@ LL | asm!("", out("vs35") _, out("v3") _); | register `vs35` error: register `v4` conflicts with register `vs36` - --> $DIR/bad-reg.rs:279:33 + --> $DIR/bad-reg.rs:278:33 | LL | asm!("", out("vs36") _, out("v4") _); | ------------- ^^^^^^^^^^^ register `v4` @@ -491,7 +491,7 @@ LL | asm!("", out("vs36") _, out("v4") _); | register `vs36` error: register `v5` conflicts with register `vs37` - --> $DIR/bad-reg.rs:281:33 + --> $DIR/bad-reg.rs:280:33 | LL | asm!("", out("vs37") _, out("v5") _); | ------------- ^^^^^^^^^^^ register `v5` @@ -499,7 +499,7 @@ LL | asm!("", out("vs37") _, out("v5") _); | register `vs37` error: register `v6` conflicts with register `vs38` - --> $DIR/bad-reg.rs:283:33 + --> $DIR/bad-reg.rs:282:33 | LL | asm!("", out("vs38") _, out("v6") _); | ------------- ^^^^^^^^^^^ register `v6` @@ -507,7 +507,7 @@ LL | asm!("", out("vs38") _, out("v6") _); | register `vs38` error: register `v7` conflicts with register `vs39` - --> $DIR/bad-reg.rs:285:33 + --> $DIR/bad-reg.rs:284:33 | LL | asm!("", out("vs39") _, out("v7") _); | ------------- ^^^^^^^^^^^ register `v7` @@ -515,7 +515,7 @@ LL | asm!("", out("vs39") _, out("v7") _); | register `vs39` error: register `v8` conflicts with register `vs40` - --> $DIR/bad-reg.rs:287:33 + --> $DIR/bad-reg.rs:286:33 | LL | asm!("", out("vs40") _, out("v8") _); | ------------- ^^^^^^^^^^^ register `v8` @@ -523,7 +523,7 @@ LL | asm!("", out("vs40") _, out("v8") _); | register `vs40` error: register `v9` conflicts with register `vs41` - --> $DIR/bad-reg.rs:289:33 + --> $DIR/bad-reg.rs:288:33 | LL | asm!("", out("vs41") _, out("v9") _); | ------------- ^^^^^^^^^^^ register `v9` @@ -531,7 +531,7 @@ LL | asm!("", out("vs41") _, out("v9") _); | register `vs41` error: register `v10` conflicts with register `vs42` - --> $DIR/bad-reg.rs:291:33 + --> $DIR/bad-reg.rs:290:33 | LL | asm!("", out("vs42") _, out("v10") _); | ------------- ^^^^^^^^^^^^ register `v10` @@ -539,7 +539,7 @@ LL | asm!("", out("vs42") _, out("v10") _); | register `vs42` error: register `v11` conflicts with register `vs43` - --> $DIR/bad-reg.rs:293:33 + --> $DIR/bad-reg.rs:292:33 | LL | asm!("", out("vs43") _, out("v11") _); | ------------- ^^^^^^^^^^^^ register `v11` @@ -547,7 +547,7 @@ LL | asm!("", out("vs43") _, out("v11") _); | register `vs43` error: register `v12` conflicts with register `vs44` - --> $DIR/bad-reg.rs:295:33 + --> $DIR/bad-reg.rs:294:33 | LL | asm!("", out("vs44") _, out("v12") _); | ------------- ^^^^^^^^^^^^ register `v12` @@ -555,7 +555,7 @@ LL | asm!("", out("vs44") _, out("v12") _); | register `vs44` error: register `v13` conflicts with register `vs45` - --> $DIR/bad-reg.rs:297:33 + --> $DIR/bad-reg.rs:296:33 | LL | asm!("", out("vs45") _, out("v13") _); | ------------- ^^^^^^^^^^^^ register `v13` @@ -563,7 +563,7 @@ LL | asm!("", out("vs45") _, out("v13") _); | register `vs45` error: register `v14` conflicts with register `vs46` - --> $DIR/bad-reg.rs:299:33 + --> $DIR/bad-reg.rs:298:33 | LL | asm!("", out("vs46") _, out("v14") _); | ------------- ^^^^^^^^^^^^ register `v14` @@ -571,7 +571,7 @@ LL | asm!("", out("vs46") _, out("v14") _); | register `vs46` error: register `v15` conflicts with register `vs47` - --> $DIR/bad-reg.rs:301:33 + --> $DIR/bad-reg.rs:300:33 | LL | asm!("", out("vs47") _, out("v15") _); | ------------- ^^^^^^^^^^^^ register `v15` @@ -579,7 +579,7 @@ LL | asm!("", out("vs47") _, out("v15") _); | register `vs47` error: register `v16` conflicts with register `vs48` - --> $DIR/bad-reg.rs:303:33 + --> $DIR/bad-reg.rs:302:33 | LL | asm!("", out("vs48") _, out("v16") _); | ------------- ^^^^^^^^^^^^ register `v16` @@ -587,7 +587,7 @@ LL | asm!("", out("vs48") _, out("v16") _); | register `vs48` error: register `v17` conflicts with register `vs49` - --> $DIR/bad-reg.rs:305:33 + --> $DIR/bad-reg.rs:304:33 | LL | asm!("", out("vs49") _, out("v17") _); | ------------- ^^^^^^^^^^^^ register `v17` @@ -595,7 +595,7 @@ LL | asm!("", out("vs49") _, out("v17") _); | register `vs49` error: register `v18` conflicts with register `vs50` - --> $DIR/bad-reg.rs:307:33 + --> $DIR/bad-reg.rs:306:33 | LL | asm!("", out("vs50") _, out("v18") _); | ------------- ^^^^^^^^^^^^ register `v18` @@ -603,7 +603,7 @@ LL | asm!("", out("vs50") _, out("v18") _); | register `vs50` error: register `v19` conflicts with register `vs51` - --> $DIR/bad-reg.rs:309:33 + --> $DIR/bad-reg.rs:308:33 | LL | asm!("", out("vs51") _, out("v19") _); | ------------- ^^^^^^^^^^^^ register `v19` @@ -611,7 +611,7 @@ LL | asm!("", out("vs51") _, out("v19") _); | register `vs51` error: register `v20` conflicts with register `vs52` - --> $DIR/bad-reg.rs:311:33 + --> $DIR/bad-reg.rs:310:33 | LL | asm!("", out("vs52") _, out("v20") _); | ------------- ^^^^^^^^^^^^ register `v20` @@ -619,7 +619,7 @@ LL | asm!("", out("vs52") _, out("v20") _); | register `vs52` error: register `v21` conflicts with register `vs53` - --> $DIR/bad-reg.rs:313:33 + --> $DIR/bad-reg.rs:312:33 | LL | asm!("", out("vs53") _, out("v21") _); | ------------- ^^^^^^^^^^^^ register `v21` @@ -627,7 +627,7 @@ LL | asm!("", out("vs53") _, out("v21") _); | register `vs53` error: register `v22` conflicts with register `vs54` - --> $DIR/bad-reg.rs:315:33 + --> $DIR/bad-reg.rs:314:33 | LL | asm!("", out("vs54") _, out("v22") _); | ------------- ^^^^^^^^^^^^ register `v22` @@ -635,7 +635,7 @@ LL | asm!("", out("vs54") _, out("v22") _); | register `vs54` error: register `v23` conflicts with register `vs55` - --> $DIR/bad-reg.rs:317:33 + --> $DIR/bad-reg.rs:316:33 | LL | asm!("", out("vs55") _, out("v23") _); | ------------- ^^^^^^^^^^^^ register `v23` @@ -643,7 +643,7 @@ LL | asm!("", out("vs55") _, out("v23") _); | register `vs55` error: register `v24` conflicts with register `vs56` - --> $DIR/bad-reg.rs:319:33 + --> $DIR/bad-reg.rs:318:33 | LL | asm!("", out("vs56") _, out("v24") _); | ------------- ^^^^^^^^^^^^ register `v24` @@ -651,7 +651,7 @@ LL | asm!("", out("vs56") _, out("v24") _); | register `vs56` error: register `v25` conflicts with register `vs57` - --> $DIR/bad-reg.rs:321:33 + --> $DIR/bad-reg.rs:320:33 | LL | asm!("", out("vs57") _, out("v25") _); | ------------- ^^^^^^^^^^^^ register `v25` @@ -659,7 +659,7 @@ LL | asm!("", out("vs57") _, out("v25") _); | register `vs57` error: register `v26` conflicts with register `vs58` - --> $DIR/bad-reg.rs:323:33 + --> $DIR/bad-reg.rs:322:33 | LL | asm!("", out("vs58") _, out("v26") _); | ------------- ^^^^^^^^^^^^ register `v26` @@ -667,7 +667,7 @@ LL | asm!("", out("vs58") _, out("v26") _); | register `vs58` error: register `v27` conflicts with register `vs59` - --> $DIR/bad-reg.rs:325:33 + --> $DIR/bad-reg.rs:324:33 | LL | asm!("", out("vs59") _, out("v27") _); | ------------- ^^^^^^^^^^^^ register `v27` @@ -675,7 +675,7 @@ LL | asm!("", out("vs59") _, out("v27") _); | register `vs59` error: register `v28` conflicts with register `vs60` - --> $DIR/bad-reg.rs:327:33 + --> $DIR/bad-reg.rs:326:33 | LL | asm!("", out("vs60") _, out("v28") _); | ------------- ^^^^^^^^^^^^ register `v28` @@ -683,7 +683,7 @@ LL | asm!("", out("vs60") _, out("v28") _); | register `vs60` error: register `v29` conflicts with register `vs61` - --> $DIR/bad-reg.rs:329:33 + --> $DIR/bad-reg.rs:328:33 | LL | asm!("", out("vs61") _, out("v29") _); | ------------- ^^^^^^^^^^^^ register `v29` @@ -691,7 +691,7 @@ LL | asm!("", out("vs61") _, out("v29") _); | register `vs61` error: register `v30` conflicts with register `vs62` - --> $DIR/bad-reg.rs:331:33 + --> $DIR/bad-reg.rs:330:33 | LL | asm!("", out("vs62") _, out("v30") _); | ------------- ^^^^^^^^^^^^ register `v30` @@ -699,7 +699,7 @@ LL | asm!("", out("vs62") _, out("v30") _); | register `vs62` error: register `v31` conflicts with register `vs63` - --> $DIR/bad-reg.rs:333:33 + --> $DIR/bad-reg.rs:332:33 | LL | asm!("", out("vs63") _, out("v31") _); | ------------- ^^^^^^^^^^^^ register `v31` @@ -707,13 +707,13 @@ LL | asm!("", out("vs63") _, out("v31") _); | register `vs63` error: cannot use register `r13`: r13 is a reserved register on this target - --> $DIR/bad-reg.rs:41:18 + --> $DIR/bad-reg.rs:40:18 | LL | asm!("", out("r13") _); | ^^^^^^^^^^^^ error: `vsx` target feature is not enabled - --> $DIR/bad-reg.rs:58:27 + --> $DIR/bad-reg.rs:57:27 | LL | asm!("", in("v0") v64x2); // requires vsx | ^^^^^ @@ -721,7 +721,7 @@ LL | asm!("", in("v0") v64x2); // requires vsx = note: this is required to use type `i64x2` with register class `vreg` error: `vsx` target feature is not enabled - --> $DIR/bad-reg.rs:61:28 + --> $DIR/bad-reg.rs:60:28 | LL | asm!("", out("v0") v64x2); // requires vsx | ^^^^^ @@ -729,7 +729,7 @@ LL | asm!("", out("v0") v64x2); // requires vsx = note: this is required to use type `i64x2` with register class `vreg` error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:64:27 + --> $DIR/bad-reg.rs:63:27 | LL | asm!("", in("v0") x); // FIXME: should be ok if vsx is available | ^ @@ -737,7 +737,7 @@ LL | asm!("", in("v0") x); // FIXME: should be ok if vsx is available = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:67:28 + --> $DIR/bad-reg.rs:66:28 | LL | asm!("", out("v0") x); // FIXME: should be ok if vsx is available | ^ @@ -745,7 +745,7 @@ LL | asm!("", out("v0") x); // FIXME: should be ok if vsx is available = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: `vsx` target feature is not enabled - --> $DIR/bad-reg.rs:72:35 + --> $DIR/bad-reg.rs:71:35 | LL | asm!("/* {} */", in(vreg) v64x2); // requires vsx | ^^^^^ @@ -753,7 +753,7 @@ LL | asm!("/* {} */", in(vreg) v64x2); // requires vsx = note: this is required to use type `i64x2` with register class `vreg` error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:75:35 + --> $DIR/bad-reg.rs:74:35 | LL | asm!("/* {} */", in(vreg) x); // FIXME: should be ok if vsx is available | ^ @@ -761,67 +761,67 @@ LL | asm!("/* {} */", in(vreg) x); // FIXME: should be ok if vsx is avai = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:97:18 + --> $DIR/bad-reg.rs:96:18 | LL | asm!("", in("vs0") v32x4); // requires vsx | ^^^^^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:98:18 | LL | asm!("", out("vs0") v32x4); // requires vsx | ^^^^^^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:101:18 + --> $DIR/bad-reg.rs:100:18 | LL | asm!("", in("vs0") v64x2); // requires vsx | ^^^^^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:103:18 + --> $DIR/bad-reg.rs:102:18 | LL | asm!("", out("vs0") v64x2); // requires vsx | ^^^^^^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:105:18 + --> $DIR/bad-reg.rs:104:18 | LL | asm!("", in("vs0") x); // FIXME: should be ok if vsx is available | ^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:108:18 + --> $DIR/bad-reg.rs:107:18 | LL | asm!("", out("vs0") x); // FIXME: should be ok if vsx is available | ^^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:111:26 + --> $DIR/bad-reg.rs:110:26 | LL | asm!("/* {} */", in(vsreg) v32x4); // requires vsx | ^^^^^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:113:26 + --> $DIR/bad-reg.rs:112:26 | LL | asm!("/* {} */", in(vsreg) v64x2); // requires vsx | ^^^^^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:115:26 + --> $DIR/bad-reg.rs:114:26 | LL | asm!("/* {} */", in(vsreg) x); // FIXME: should be ok if vsx is available | ^^^^^^^^^^^ error: register class `vsreg` requires the `vsx` target feature - --> $DIR/bad-reg.rs:118:26 + --> $DIR/bad-reg.rs:117:26 | LL | asm!("/* {} */", out(vsreg) _); // requires vsx | ^^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:138:27 + --> $DIR/bad-reg.rs:137:27 | LL | asm!("", in("cr") x); | ^ @@ -829,7 +829,7 @@ LL | asm!("", in("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:141:28 + --> $DIR/bad-reg.rs:140:28 | LL | asm!("", out("cr") x); | ^ @@ -837,7 +837,7 @@ LL | asm!("", out("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:144:33 + --> $DIR/bad-reg.rs:143:33 | LL | asm!("/* {} */", in(cr) x); | ^ @@ -845,7 +845,7 @@ LL | asm!("/* {} */", in(cr) x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:151:28 + --> $DIR/bad-reg.rs:150:28 | LL | asm!("", in("ctr") x); | ^ @@ -853,7 +853,7 @@ LL | asm!("", in("ctr") x); = note: register class `ctr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:154:29 + --> $DIR/bad-reg.rs:153:29 | LL | asm!("", out("ctr") x); | ^ @@ -861,7 +861,7 @@ LL | asm!("", out("ctr") x); = note: register class `ctr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:157:34 + --> $DIR/bad-reg.rs:156:34 | LL | asm!("/* {} */", in(ctr) x); | ^ @@ -869,7 +869,7 @@ LL | asm!("/* {} */", in(ctr) x); = note: register class `ctr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:164:27 + --> $DIR/bad-reg.rs:163:27 | LL | asm!("", in("lr") x); | ^ @@ -877,7 +877,7 @@ LL | asm!("", in("lr") x); = note: register class `lr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:167:28 + --> $DIR/bad-reg.rs:166:28 | LL | asm!("", out("lr") x); | ^ @@ -885,7 +885,7 @@ LL | asm!("", out("lr") x); = note: register class `lr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:170:33 + --> $DIR/bad-reg.rs:169:33 | LL | asm!("/* {} */", in(lr) x); | ^ @@ -893,7 +893,7 @@ LL | asm!("/* {} */", in(lr) x); = note: register class `lr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:177:28 + --> $DIR/bad-reg.rs:176:28 | LL | asm!("", in("xer") x); | ^ @@ -901,7 +901,7 @@ LL | asm!("", in("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:180:29 + --> $DIR/bad-reg.rs:179:29 | LL | asm!("", out("xer") x); | ^ @@ -909,7 +909,7 @@ LL | asm!("", out("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:183:34 + --> $DIR/bad-reg.rs:182:34 | LL | asm!("/* {} */", in(xer) x); | ^ diff --git a/tests/ui/asm/powerpc/bad-reg.powerpc64le.stderr b/tests/ui/asm/powerpc/bad-reg.powerpc64le.stderr index fb3ed07f5c65..67ad0a5d2c45 100644 --- a/tests/ui/asm/powerpc/bad-reg.powerpc64le.stderr +++ b/tests/ui/asm/powerpc/bad-reg.powerpc64le.stderr @@ -1,137 +1,137 @@ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `r2`: r2 is a system reserved register and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("r2") _); | ^^^^^^^^^^^ error: invalid register `r29`: r29 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:42:18 | LL | asm!("", out("r29") _); | ^^^^^^^^^^^^ error: invalid register `r30`: r30 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:44:18 | LL | asm!("", out("r30") _); | ^^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:47:18 + --> $DIR/bad-reg.rs:46:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `vrsave`: the vrsave register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:49:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", out("vrsave") _); | ^^^^^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:138:18 + --> $DIR/bad-reg.rs:137:18 | LL | asm!("", in("cr") x); | ^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:141:18 + --> $DIR/bad-reg.rs:140:18 | LL | asm!("", out("cr") x); | ^^^^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:144:26 + --> $DIR/bad-reg.rs:143:26 | LL | asm!("/* {} */", in(cr) x); | ^^^^^^^^ error: register class `cr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:147:26 + --> $DIR/bad-reg.rs:146:26 | LL | asm!("/* {} */", out(cr) _); | ^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:151:18 + --> $DIR/bad-reg.rs:150:18 | LL | asm!("", in("ctr") x); | ^^^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:154:18 + --> $DIR/bad-reg.rs:153:18 | LL | asm!("", out("ctr") x); | ^^^^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:157:26 + --> $DIR/bad-reg.rs:156:26 | LL | asm!("/* {} */", in(ctr) x); | ^^^^^^^^^ error: register class `ctr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:160:26 + --> $DIR/bad-reg.rs:159:26 | LL | asm!("/* {} */", out(ctr) _); | ^^^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:164:18 + --> $DIR/bad-reg.rs:163:18 | LL | asm!("", in("lr") x); | ^^^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:167:18 + --> $DIR/bad-reg.rs:166:18 | LL | asm!("", out("lr") x); | ^^^^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:170:26 + --> $DIR/bad-reg.rs:169:26 | LL | asm!("/* {} */", in(lr) x); | ^^^^^^^^ error: register class `lr` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:173:26 + --> $DIR/bad-reg.rs:172:26 | LL | asm!("/* {} */", out(lr) _); | ^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:177:18 + --> $DIR/bad-reg.rs:176:18 | LL | asm!("", in("xer") x); | ^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:180:18 + --> $DIR/bad-reg.rs:179:18 | LL | asm!("", out("xer") x); | ^^^^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:183:26 + --> $DIR/bad-reg.rs:182:26 | LL | asm!("/* {} */", in(xer) x); | ^^^^^^^^^ error: register class `xer` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:186:26 + --> $DIR/bad-reg.rs:185:26 | LL | asm!("/* {} */", out(xer) _); | ^^^^^^^^^^ error: register `cr0` conflicts with register `cr` - --> $DIR/bad-reg.rs:190:31 + --> $DIR/bad-reg.rs:189:31 | LL | asm!("", out("cr") _, out("cr0") _); | ----------- ^^^^^^^^^^^^ register `cr0` @@ -139,7 +139,7 @@ LL | asm!("", out("cr") _, out("cr0") _); | register `cr` error: register `cr1` conflicts with register `cr` - --> $DIR/bad-reg.rs:192:31 + --> $DIR/bad-reg.rs:191:31 | LL | asm!("", out("cr") _, out("cr1") _); | ----------- ^^^^^^^^^^^^ register `cr1` @@ -147,7 +147,7 @@ LL | asm!("", out("cr") _, out("cr1") _); | register `cr` error: register `cr2` conflicts with register `cr` - --> $DIR/bad-reg.rs:194:31 + --> $DIR/bad-reg.rs:193:31 | LL | asm!("", out("cr") _, out("cr2") _); | ----------- ^^^^^^^^^^^^ register `cr2` @@ -155,7 +155,7 @@ LL | asm!("", out("cr") _, out("cr2") _); | register `cr` error: register `cr3` conflicts with register `cr` - --> $DIR/bad-reg.rs:196:31 + --> $DIR/bad-reg.rs:195:31 | LL | asm!("", out("cr") _, out("cr3") _); | ----------- ^^^^^^^^^^^^ register `cr3` @@ -163,7 +163,7 @@ LL | asm!("", out("cr") _, out("cr3") _); | register `cr` error: register `cr4` conflicts with register `cr` - --> $DIR/bad-reg.rs:198:31 + --> $DIR/bad-reg.rs:197:31 | LL | asm!("", out("cr") _, out("cr4") _); | ----------- ^^^^^^^^^^^^ register `cr4` @@ -171,7 +171,7 @@ LL | asm!("", out("cr") _, out("cr4") _); | register `cr` error: register `cr5` conflicts with register `cr` - --> $DIR/bad-reg.rs:200:31 + --> $DIR/bad-reg.rs:199:31 | LL | asm!("", out("cr") _, out("cr5") _); | ----------- ^^^^^^^^^^^^ register `cr5` @@ -179,7 +179,7 @@ LL | asm!("", out("cr") _, out("cr5") _); | register `cr` error: register `cr6` conflicts with register `cr` - --> $DIR/bad-reg.rs:202:31 + --> $DIR/bad-reg.rs:201:31 | LL | asm!("", out("cr") _, out("cr6") _); | ----------- ^^^^^^^^^^^^ register `cr6` @@ -187,7 +187,7 @@ LL | asm!("", out("cr") _, out("cr6") _); | register `cr` error: register `cr7` conflicts with register `cr` - --> $DIR/bad-reg.rs:204:31 + --> $DIR/bad-reg.rs:203:31 | LL | asm!("", out("cr") _, out("cr7") _); | ----------- ^^^^^^^^^^^^ register `cr7` @@ -195,7 +195,7 @@ LL | asm!("", out("cr") _, out("cr7") _); | register `cr` error: register `vs0` conflicts with register `f0` - --> $DIR/bad-reg.rs:207:31 + --> $DIR/bad-reg.rs:206:31 | LL | asm!("", out("f0") _, out("vs0") _); | ----------- ^^^^^^^^^^^^ register `vs0` @@ -203,7 +203,7 @@ LL | asm!("", out("f0") _, out("vs0") _); | register `f0` error: register `vs1` conflicts with register `f1` - --> $DIR/bad-reg.rs:209:31 + --> $DIR/bad-reg.rs:208:31 | LL | asm!("", out("f1") _, out("vs1") _); | ----------- ^^^^^^^^^^^^ register `vs1` @@ -211,7 +211,7 @@ LL | asm!("", out("f1") _, out("vs1") _); | register `f1` error: register `vs2` conflicts with register `f2` - --> $DIR/bad-reg.rs:211:31 + --> $DIR/bad-reg.rs:210:31 | LL | asm!("", out("f2") _, out("vs2") _); | ----------- ^^^^^^^^^^^^ register `vs2` @@ -219,7 +219,7 @@ LL | asm!("", out("f2") _, out("vs2") _); | register `f2` error: register `vs3` conflicts with register `f3` - --> $DIR/bad-reg.rs:213:31 + --> $DIR/bad-reg.rs:212:31 | LL | asm!("", out("f3") _, out("vs3") _); | ----------- ^^^^^^^^^^^^ register `vs3` @@ -227,7 +227,7 @@ LL | asm!("", out("f3") _, out("vs3") _); | register `f3` error: register `vs4` conflicts with register `f4` - --> $DIR/bad-reg.rs:215:31 + --> $DIR/bad-reg.rs:214:31 | LL | asm!("", out("f4") _, out("vs4") _); | ----------- ^^^^^^^^^^^^ register `vs4` @@ -235,7 +235,7 @@ LL | asm!("", out("f4") _, out("vs4") _); | register `f4` error: register `vs5` conflicts with register `f5` - --> $DIR/bad-reg.rs:217:31 + --> $DIR/bad-reg.rs:216:31 | LL | asm!("", out("f5") _, out("vs5") _); | ----------- ^^^^^^^^^^^^ register `vs5` @@ -243,7 +243,7 @@ LL | asm!("", out("f5") _, out("vs5") _); | register `f5` error: register `vs6` conflicts with register `f6` - --> $DIR/bad-reg.rs:219:31 + --> $DIR/bad-reg.rs:218:31 | LL | asm!("", out("f6") _, out("vs6") _); | ----------- ^^^^^^^^^^^^ register `vs6` @@ -251,7 +251,7 @@ LL | asm!("", out("f6") _, out("vs6") _); | register `f6` error: register `vs7` conflicts with register `f7` - --> $DIR/bad-reg.rs:221:31 + --> $DIR/bad-reg.rs:220:31 | LL | asm!("", out("f7") _, out("vs7") _); | ----------- ^^^^^^^^^^^^ register `vs7` @@ -259,7 +259,7 @@ LL | asm!("", out("f7") _, out("vs7") _); | register `f7` error: register `vs8` conflicts with register `f8` - --> $DIR/bad-reg.rs:223:31 + --> $DIR/bad-reg.rs:222:31 | LL | asm!("", out("f8") _, out("vs8") _); | ----------- ^^^^^^^^^^^^ register `vs8` @@ -267,7 +267,7 @@ LL | asm!("", out("f8") _, out("vs8") _); | register `f8` error: register `vs9` conflicts with register `f9` - --> $DIR/bad-reg.rs:225:31 + --> $DIR/bad-reg.rs:224:31 | LL | asm!("", out("f9") _, out("vs9") _); | ----------- ^^^^^^^^^^^^ register `vs9` @@ -275,7 +275,7 @@ LL | asm!("", out("f9") _, out("vs9") _); | register `f9` error: register `vs10` conflicts with register `f10` - --> $DIR/bad-reg.rs:227:32 + --> $DIR/bad-reg.rs:226:32 | LL | asm!("", out("f10") _, out("vs10") _); | ------------ ^^^^^^^^^^^^^ register `vs10` @@ -283,7 +283,7 @@ LL | asm!("", out("f10") _, out("vs10") _); | register `f10` error: register `vs11` conflicts with register `f11` - --> $DIR/bad-reg.rs:229:32 + --> $DIR/bad-reg.rs:228:32 | LL | asm!("", out("f11") _, out("vs11") _); | ------------ ^^^^^^^^^^^^^ register `vs11` @@ -291,7 +291,7 @@ LL | asm!("", out("f11") _, out("vs11") _); | register `f11` error: register `vs12` conflicts with register `f12` - --> $DIR/bad-reg.rs:231:32 + --> $DIR/bad-reg.rs:230:32 | LL | asm!("", out("f12") _, out("vs12") _); | ------------ ^^^^^^^^^^^^^ register `vs12` @@ -299,7 +299,7 @@ LL | asm!("", out("f12") _, out("vs12") _); | register `f12` error: register `vs13` conflicts with register `f13` - --> $DIR/bad-reg.rs:233:32 + --> $DIR/bad-reg.rs:232:32 | LL | asm!("", out("f13") _, out("vs13") _); | ------------ ^^^^^^^^^^^^^ register `vs13` @@ -307,7 +307,7 @@ LL | asm!("", out("f13") _, out("vs13") _); | register `f13` error: register `vs14` conflicts with register `f14` - --> $DIR/bad-reg.rs:235:32 + --> $DIR/bad-reg.rs:234:32 | LL | asm!("", out("f14") _, out("vs14") _); | ------------ ^^^^^^^^^^^^^ register `vs14` @@ -315,7 +315,7 @@ LL | asm!("", out("f14") _, out("vs14") _); | register `f14` error: register `vs15` conflicts with register `f15` - --> $DIR/bad-reg.rs:237:32 + --> $DIR/bad-reg.rs:236:32 | LL | asm!("", out("f15") _, out("vs15") _); | ------------ ^^^^^^^^^^^^^ register `vs15` @@ -323,7 +323,7 @@ LL | asm!("", out("f15") _, out("vs15") _); | register `f15` error: register `vs16` conflicts with register `f16` - --> $DIR/bad-reg.rs:239:32 + --> $DIR/bad-reg.rs:238:32 | LL | asm!("", out("f16") _, out("vs16") _); | ------------ ^^^^^^^^^^^^^ register `vs16` @@ -331,7 +331,7 @@ LL | asm!("", out("f16") _, out("vs16") _); | register `f16` error: register `vs17` conflicts with register `f17` - --> $DIR/bad-reg.rs:241:32 + --> $DIR/bad-reg.rs:240:32 | LL | asm!("", out("f17") _, out("vs17") _); | ------------ ^^^^^^^^^^^^^ register `vs17` @@ -339,7 +339,7 @@ LL | asm!("", out("f17") _, out("vs17") _); | register `f17` error: register `vs18` conflicts with register `f18` - --> $DIR/bad-reg.rs:243:32 + --> $DIR/bad-reg.rs:242:32 | LL | asm!("", out("f18") _, out("vs18") _); | ------------ ^^^^^^^^^^^^^ register `vs18` @@ -347,7 +347,7 @@ LL | asm!("", out("f18") _, out("vs18") _); | register `f18` error: register `vs19` conflicts with register `f19` - --> $DIR/bad-reg.rs:245:32 + --> $DIR/bad-reg.rs:244:32 | LL | asm!("", out("f19") _, out("vs19") _); | ------------ ^^^^^^^^^^^^^ register `vs19` @@ -355,7 +355,7 @@ LL | asm!("", out("f19") _, out("vs19") _); | register `f19` error: register `vs20` conflicts with register `f20` - --> $DIR/bad-reg.rs:247:32 + --> $DIR/bad-reg.rs:246:32 | LL | asm!("", out("f20") _, out("vs20") _); | ------------ ^^^^^^^^^^^^^ register `vs20` @@ -363,7 +363,7 @@ LL | asm!("", out("f20") _, out("vs20") _); | register `f20` error: register `vs21` conflicts with register `f21` - --> $DIR/bad-reg.rs:249:32 + --> $DIR/bad-reg.rs:248:32 | LL | asm!("", out("f21") _, out("vs21") _); | ------------ ^^^^^^^^^^^^^ register `vs21` @@ -371,7 +371,7 @@ LL | asm!("", out("f21") _, out("vs21") _); | register `f21` error: register `vs22` conflicts with register `f22` - --> $DIR/bad-reg.rs:251:32 + --> $DIR/bad-reg.rs:250:32 | LL | asm!("", out("f22") _, out("vs22") _); | ------------ ^^^^^^^^^^^^^ register `vs22` @@ -379,7 +379,7 @@ LL | asm!("", out("f22") _, out("vs22") _); | register `f22` error: register `vs23` conflicts with register `f23` - --> $DIR/bad-reg.rs:253:32 + --> $DIR/bad-reg.rs:252:32 | LL | asm!("", out("f23") _, out("vs23") _); | ------------ ^^^^^^^^^^^^^ register `vs23` @@ -387,7 +387,7 @@ LL | asm!("", out("f23") _, out("vs23") _); | register `f23` error: register `vs24` conflicts with register `f24` - --> $DIR/bad-reg.rs:255:32 + --> $DIR/bad-reg.rs:254:32 | LL | asm!("", out("f24") _, out("vs24") _); | ------------ ^^^^^^^^^^^^^ register `vs24` @@ -395,7 +395,7 @@ LL | asm!("", out("f24") _, out("vs24") _); | register `f24` error: register `vs25` conflicts with register `f25` - --> $DIR/bad-reg.rs:257:32 + --> $DIR/bad-reg.rs:256:32 | LL | asm!("", out("f25") _, out("vs25") _); | ------------ ^^^^^^^^^^^^^ register `vs25` @@ -403,7 +403,7 @@ LL | asm!("", out("f25") _, out("vs25") _); | register `f25` error: register `vs26` conflicts with register `f26` - --> $DIR/bad-reg.rs:259:32 + --> $DIR/bad-reg.rs:258:32 | LL | asm!("", out("f26") _, out("vs26") _); | ------------ ^^^^^^^^^^^^^ register `vs26` @@ -411,7 +411,7 @@ LL | asm!("", out("f26") _, out("vs26") _); | register `f26` error: register `vs27` conflicts with register `f27` - --> $DIR/bad-reg.rs:261:32 + --> $DIR/bad-reg.rs:260:32 | LL | asm!("", out("f27") _, out("vs27") _); | ------------ ^^^^^^^^^^^^^ register `vs27` @@ -419,7 +419,7 @@ LL | asm!("", out("f27") _, out("vs27") _); | register `f27` error: register `vs28` conflicts with register `f28` - --> $DIR/bad-reg.rs:263:32 + --> $DIR/bad-reg.rs:262:32 | LL | asm!("", out("f28") _, out("vs28") _); | ------------ ^^^^^^^^^^^^^ register `vs28` @@ -427,7 +427,7 @@ LL | asm!("", out("f28") _, out("vs28") _); | register `f28` error: register `vs29` conflicts with register `f29` - --> $DIR/bad-reg.rs:265:32 + --> $DIR/bad-reg.rs:264:32 | LL | asm!("", out("f29") _, out("vs29") _); | ------------ ^^^^^^^^^^^^^ register `vs29` @@ -435,7 +435,7 @@ LL | asm!("", out("f29") _, out("vs29") _); | register `f29` error: register `vs30` conflicts with register `f30` - --> $DIR/bad-reg.rs:267:32 + --> $DIR/bad-reg.rs:266:32 | LL | asm!("", out("f30") _, out("vs30") _); | ------------ ^^^^^^^^^^^^^ register `vs30` @@ -443,7 +443,7 @@ LL | asm!("", out("f30") _, out("vs30") _); | register `f30` error: register `vs31` conflicts with register `f31` - --> $DIR/bad-reg.rs:269:32 + --> $DIR/bad-reg.rs:268:32 | LL | asm!("", out("f31") _, out("vs31") _); | ------------ ^^^^^^^^^^^^^ register `vs31` @@ -451,7 +451,7 @@ LL | asm!("", out("f31") _, out("vs31") _); | register `f31` error: register `v0` conflicts with register `vs32` - --> $DIR/bad-reg.rs:271:33 + --> $DIR/bad-reg.rs:270:33 | LL | asm!("", out("vs32") _, out("v0") _); | ------------- ^^^^^^^^^^^ register `v0` @@ -459,7 +459,7 @@ LL | asm!("", out("vs32") _, out("v0") _); | register `vs32` error: register `v1` conflicts with register `vs33` - --> $DIR/bad-reg.rs:273:33 + --> $DIR/bad-reg.rs:272:33 | LL | asm!("", out("vs33") _, out("v1") _); | ------------- ^^^^^^^^^^^ register `v1` @@ -467,7 +467,7 @@ LL | asm!("", out("vs33") _, out("v1") _); | register `vs33` error: register `v2` conflicts with register `vs34` - --> $DIR/bad-reg.rs:275:33 + --> $DIR/bad-reg.rs:274:33 | LL | asm!("", out("vs34") _, out("v2") _); | ------------- ^^^^^^^^^^^ register `v2` @@ -475,7 +475,7 @@ LL | asm!("", out("vs34") _, out("v2") _); | register `vs34` error: register `v3` conflicts with register `vs35` - --> $DIR/bad-reg.rs:277:33 + --> $DIR/bad-reg.rs:276:33 | LL | asm!("", out("vs35") _, out("v3") _); | ------------- ^^^^^^^^^^^ register `v3` @@ -483,7 +483,7 @@ LL | asm!("", out("vs35") _, out("v3") _); | register `vs35` error: register `v4` conflicts with register `vs36` - --> $DIR/bad-reg.rs:279:33 + --> $DIR/bad-reg.rs:278:33 | LL | asm!("", out("vs36") _, out("v4") _); | ------------- ^^^^^^^^^^^ register `v4` @@ -491,7 +491,7 @@ LL | asm!("", out("vs36") _, out("v4") _); | register `vs36` error: register `v5` conflicts with register `vs37` - --> $DIR/bad-reg.rs:281:33 + --> $DIR/bad-reg.rs:280:33 | LL | asm!("", out("vs37") _, out("v5") _); | ------------- ^^^^^^^^^^^ register `v5` @@ -499,7 +499,7 @@ LL | asm!("", out("vs37") _, out("v5") _); | register `vs37` error: register `v6` conflicts with register `vs38` - --> $DIR/bad-reg.rs:283:33 + --> $DIR/bad-reg.rs:282:33 | LL | asm!("", out("vs38") _, out("v6") _); | ------------- ^^^^^^^^^^^ register `v6` @@ -507,7 +507,7 @@ LL | asm!("", out("vs38") _, out("v6") _); | register `vs38` error: register `v7` conflicts with register `vs39` - --> $DIR/bad-reg.rs:285:33 + --> $DIR/bad-reg.rs:284:33 | LL | asm!("", out("vs39") _, out("v7") _); | ------------- ^^^^^^^^^^^ register `v7` @@ -515,7 +515,7 @@ LL | asm!("", out("vs39") _, out("v7") _); | register `vs39` error: register `v8` conflicts with register `vs40` - --> $DIR/bad-reg.rs:287:33 + --> $DIR/bad-reg.rs:286:33 | LL | asm!("", out("vs40") _, out("v8") _); | ------------- ^^^^^^^^^^^ register `v8` @@ -523,7 +523,7 @@ LL | asm!("", out("vs40") _, out("v8") _); | register `vs40` error: register `v9` conflicts with register `vs41` - --> $DIR/bad-reg.rs:289:33 + --> $DIR/bad-reg.rs:288:33 | LL | asm!("", out("vs41") _, out("v9") _); | ------------- ^^^^^^^^^^^ register `v9` @@ -531,7 +531,7 @@ LL | asm!("", out("vs41") _, out("v9") _); | register `vs41` error: register `v10` conflicts with register `vs42` - --> $DIR/bad-reg.rs:291:33 + --> $DIR/bad-reg.rs:290:33 | LL | asm!("", out("vs42") _, out("v10") _); | ------------- ^^^^^^^^^^^^ register `v10` @@ -539,7 +539,7 @@ LL | asm!("", out("vs42") _, out("v10") _); | register `vs42` error: register `v11` conflicts with register `vs43` - --> $DIR/bad-reg.rs:293:33 + --> $DIR/bad-reg.rs:292:33 | LL | asm!("", out("vs43") _, out("v11") _); | ------------- ^^^^^^^^^^^^ register `v11` @@ -547,7 +547,7 @@ LL | asm!("", out("vs43") _, out("v11") _); | register `vs43` error: register `v12` conflicts with register `vs44` - --> $DIR/bad-reg.rs:295:33 + --> $DIR/bad-reg.rs:294:33 | LL | asm!("", out("vs44") _, out("v12") _); | ------------- ^^^^^^^^^^^^ register `v12` @@ -555,7 +555,7 @@ LL | asm!("", out("vs44") _, out("v12") _); | register `vs44` error: register `v13` conflicts with register `vs45` - --> $DIR/bad-reg.rs:297:33 + --> $DIR/bad-reg.rs:296:33 | LL | asm!("", out("vs45") _, out("v13") _); | ------------- ^^^^^^^^^^^^ register `v13` @@ -563,7 +563,7 @@ LL | asm!("", out("vs45") _, out("v13") _); | register `vs45` error: register `v14` conflicts with register `vs46` - --> $DIR/bad-reg.rs:299:33 + --> $DIR/bad-reg.rs:298:33 | LL | asm!("", out("vs46") _, out("v14") _); | ------------- ^^^^^^^^^^^^ register `v14` @@ -571,7 +571,7 @@ LL | asm!("", out("vs46") _, out("v14") _); | register `vs46` error: register `v15` conflicts with register `vs47` - --> $DIR/bad-reg.rs:301:33 + --> $DIR/bad-reg.rs:300:33 | LL | asm!("", out("vs47") _, out("v15") _); | ------------- ^^^^^^^^^^^^ register `v15` @@ -579,7 +579,7 @@ LL | asm!("", out("vs47") _, out("v15") _); | register `vs47` error: register `v16` conflicts with register `vs48` - --> $DIR/bad-reg.rs:303:33 + --> $DIR/bad-reg.rs:302:33 | LL | asm!("", out("vs48") _, out("v16") _); | ------------- ^^^^^^^^^^^^ register `v16` @@ -587,7 +587,7 @@ LL | asm!("", out("vs48") _, out("v16") _); | register `vs48` error: register `v17` conflicts with register `vs49` - --> $DIR/bad-reg.rs:305:33 + --> $DIR/bad-reg.rs:304:33 | LL | asm!("", out("vs49") _, out("v17") _); | ------------- ^^^^^^^^^^^^ register `v17` @@ -595,7 +595,7 @@ LL | asm!("", out("vs49") _, out("v17") _); | register `vs49` error: register `v18` conflicts with register `vs50` - --> $DIR/bad-reg.rs:307:33 + --> $DIR/bad-reg.rs:306:33 | LL | asm!("", out("vs50") _, out("v18") _); | ------------- ^^^^^^^^^^^^ register `v18` @@ -603,7 +603,7 @@ LL | asm!("", out("vs50") _, out("v18") _); | register `vs50` error: register `v19` conflicts with register `vs51` - --> $DIR/bad-reg.rs:309:33 + --> $DIR/bad-reg.rs:308:33 | LL | asm!("", out("vs51") _, out("v19") _); | ------------- ^^^^^^^^^^^^ register `v19` @@ -611,7 +611,7 @@ LL | asm!("", out("vs51") _, out("v19") _); | register `vs51` error: register `v20` conflicts with register `vs52` - --> $DIR/bad-reg.rs:311:33 + --> $DIR/bad-reg.rs:310:33 | LL | asm!("", out("vs52") _, out("v20") _); | ------------- ^^^^^^^^^^^^ register `v20` @@ -619,7 +619,7 @@ LL | asm!("", out("vs52") _, out("v20") _); | register `vs52` error: register `v21` conflicts with register `vs53` - --> $DIR/bad-reg.rs:313:33 + --> $DIR/bad-reg.rs:312:33 | LL | asm!("", out("vs53") _, out("v21") _); | ------------- ^^^^^^^^^^^^ register `v21` @@ -627,7 +627,7 @@ LL | asm!("", out("vs53") _, out("v21") _); | register `vs53` error: register `v22` conflicts with register `vs54` - --> $DIR/bad-reg.rs:315:33 + --> $DIR/bad-reg.rs:314:33 | LL | asm!("", out("vs54") _, out("v22") _); | ------------- ^^^^^^^^^^^^ register `v22` @@ -635,7 +635,7 @@ LL | asm!("", out("vs54") _, out("v22") _); | register `vs54` error: register `v23` conflicts with register `vs55` - --> $DIR/bad-reg.rs:317:33 + --> $DIR/bad-reg.rs:316:33 | LL | asm!("", out("vs55") _, out("v23") _); | ------------- ^^^^^^^^^^^^ register `v23` @@ -643,7 +643,7 @@ LL | asm!("", out("vs55") _, out("v23") _); | register `vs55` error: register `v24` conflicts with register `vs56` - --> $DIR/bad-reg.rs:319:33 + --> $DIR/bad-reg.rs:318:33 | LL | asm!("", out("vs56") _, out("v24") _); | ------------- ^^^^^^^^^^^^ register `v24` @@ -651,7 +651,7 @@ LL | asm!("", out("vs56") _, out("v24") _); | register `vs56` error: register `v25` conflicts with register `vs57` - --> $DIR/bad-reg.rs:321:33 + --> $DIR/bad-reg.rs:320:33 | LL | asm!("", out("vs57") _, out("v25") _); | ------------- ^^^^^^^^^^^^ register `v25` @@ -659,7 +659,7 @@ LL | asm!("", out("vs57") _, out("v25") _); | register `vs57` error: register `v26` conflicts with register `vs58` - --> $DIR/bad-reg.rs:323:33 + --> $DIR/bad-reg.rs:322:33 | LL | asm!("", out("vs58") _, out("v26") _); | ------------- ^^^^^^^^^^^^ register `v26` @@ -667,7 +667,7 @@ LL | asm!("", out("vs58") _, out("v26") _); | register `vs58` error: register `v27` conflicts with register `vs59` - --> $DIR/bad-reg.rs:325:33 + --> $DIR/bad-reg.rs:324:33 | LL | asm!("", out("vs59") _, out("v27") _); | ------------- ^^^^^^^^^^^^ register `v27` @@ -675,7 +675,7 @@ LL | asm!("", out("vs59") _, out("v27") _); | register `vs59` error: register `v28` conflicts with register `vs60` - --> $DIR/bad-reg.rs:327:33 + --> $DIR/bad-reg.rs:326:33 | LL | asm!("", out("vs60") _, out("v28") _); | ------------- ^^^^^^^^^^^^ register `v28` @@ -683,7 +683,7 @@ LL | asm!("", out("vs60") _, out("v28") _); | register `vs60` error: register `v29` conflicts with register `vs61` - --> $DIR/bad-reg.rs:329:33 + --> $DIR/bad-reg.rs:328:33 | LL | asm!("", out("vs61") _, out("v29") _); | ------------- ^^^^^^^^^^^^ register `v29` @@ -691,7 +691,7 @@ LL | asm!("", out("vs61") _, out("v29") _); | register `vs61` error: register `v30` conflicts with register `vs62` - --> $DIR/bad-reg.rs:331:33 + --> $DIR/bad-reg.rs:330:33 | LL | asm!("", out("vs62") _, out("v30") _); | ------------- ^^^^^^^^^^^^ register `v30` @@ -699,7 +699,7 @@ LL | asm!("", out("vs62") _, out("v30") _); | register `vs62` error: register `v31` conflicts with register `vs63` - --> $DIR/bad-reg.rs:333:33 + --> $DIR/bad-reg.rs:332:33 | LL | asm!("", out("vs63") _, out("v31") _); | ------------- ^^^^^^^^^^^^ register `v31` @@ -707,13 +707,13 @@ LL | asm!("", out("vs63") _, out("v31") _); | register `vs63` error: cannot use register `r13`: r13 is a reserved register on this target - --> $DIR/bad-reg.rs:41:18 + --> $DIR/bad-reg.rs:40:18 | LL | asm!("", out("r13") _); | ^^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:64:27 + --> $DIR/bad-reg.rs:63:27 | LL | asm!("", in("v0") x); // FIXME: should be ok if vsx is available | ^ @@ -721,7 +721,7 @@ LL | asm!("", in("v0") x); // FIXME: should be ok if vsx is available = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:67:28 + --> $DIR/bad-reg.rs:66:28 | LL | asm!("", out("v0") x); // FIXME: should be ok if vsx is available | ^ @@ -729,7 +729,7 @@ LL | asm!("", out("v0") x); // FIXME: should be ok if vsx is available = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:75:35 + --> $DIR/bad-reg.rs:74:35 | LL | asm!("/* {} */", in(vreg) x); // FIXME: should be ok if vsx is available | ^ @@ -737,7 +737,7 @@ LL | asm!("/* {} */", in(vreg) x); // FIXME: should be ok if vsx is avai = note: register class `vreg` supports these types: i8x16, i16x8, i32x4, f32x4, f32, f64, i64x2, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:105:28 + --> $DIR/bad-reg.rs:104:28 | LL | asm!("", in("vs0") x); // FIXME: should be ok if vsx is available | ^ @@ -745,7 +745,7 @@ LL | asm!("", in("vs0") x); // FIXME: should be ok if vsx is available = note: register class `vsreg` supports these types: f32, f64, i8x16, i16x8, i32x4, i64x2, f32x4, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:108:29 + --> $DIR/bad-reg.rs:107:29 | LL | asm!("", out("vs0") x); // FIXME: should be ok if vsx is available | ^ @@ -753,7 +753,7 @@ LL | asm!("", out("vs0") x); // FIXME: should be ok if vsx is available = note: register class `vsreg` supports these types: f32, f64, i8x16, i16x8, i32x4, i64x2, f32x4, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:115:36 + --> $DIR/bad-reg.rs:114:36 | LL | asm!("/* {} */", in(vsreg) x); // FIXME: should be ok if vsx is available | ^ @@ -761,7 +761,7 @@ LL | asm!("/* {} */", in(vsreg) x); // FIXME: should be ok if vsx is ava = note: register class `vsreg` supports these types: f32, f64, i8x16, i16x8, i32x4, i64x2, f32x4, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:138:27 + --> $DIR/bad-reg.rs:137:27 | LL | asm!("", in("cr") x); | ^ @@ -769,7 +769,7 @@ LL | asm!("", in("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:141:28 + --> $DIR/bad-reg.rs:140:28 | LL | asm!("", out("cr") x); | ^ @@ -777,7 +777,7 @@ LL | asm!("", out("cr") x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:144:33 + --> $DIR/bad-reg.rs:143:33 | LL | asm!("/* {} */", in(cr) x); | ^ @@ -785,7 +785,7 @@ LL | asm!("/* {} */", in(cr) x); = note: register class `cr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:151:28 + --> $DIR/bad-reg.rs:150:28 | LL | asm!("", in("ctr") x); | ^ @@ -793,7 +793,7 @@ LL | asm!("", in("ctr") x); = note: register class `ctr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:154:29 + --> $DIR/bad-reg.rs:153:29 | LL | asm!("", out("ctr") x); | ^ @@ -801,7 +801,7 @@ LL | asm!("", out("ctr") x); = note: register class `ctr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:157:34 + --> $DIR/bad-reg.rs:156:34 | LL | asm!("/* {} */", in(ctr) x); | ^ @@ -809,7 +809,7 @@ LL | asm!("/* {} */", in(ctr) x); = note: register class `ctr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:164:27 + --> $DIR/bad-reg.rs:163:27 | LL | asm!("", in("lr") x); | ^ @@ -817,7 +817,7 @@ LL | asm!("", in("lr") x); = note: register class `lr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:167:28 + --> $DIR/bad-reg.rs:166:28 | LL | asm!("", out("lr") x); | ^ @@ -825,7 +825,7 @@ LL | asm!("", out("lr") x); = note: register class `lr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:170:33 + --> $DIR/bad-reg.rs:169:33 | LL | asm!("/* {} */", in(lr) x); | ^ @@ -833,7 +833,7 @@ LL | asm!("/* {} */", in(lr) x); = note: register class `lr` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:177:28 + --> $DIR/bad-reg.rs:176:28 | LL | asm!("", in("xer") x); | ^ @@ -841,7 +841,7 @@ LL | asm!("", in("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:180:29 + --> $DIR/bad-reg.rs:179:29 | LL | asm!("", out("xer") x); | ^ @@ -849,7 +849,7 @@ LL | asm!("", out("xer") x); = note: register class `xer` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:183:34 + --> $DIR/bad-reg.rs:182:34 | LL | asm!("/* {} */", in(xer) x); | ^ diff --git a/tests/ui/asm/powerpc/bad-reg.rs b/tests/ui/asm/powerpc/bad-reg.rs index d133dae3da62..c09001f27ffe 100644 --- a/tests/ui/asm/powerpc/bad-reg.rs +++ b/tests/ui/asm/powerpc/bad-reg.rs @@ -8,7 +8,6 @@ //@[powerpc64le] needs-llvm-components: powerpc //@[aix64] compile-flags: --target powerpc64-ibm-aix //@[aix64] needs-llvm-components: powerpc -//@ needs-asm-support //@ ignore-backends: gcc // ignore-tidy-linelength diff --git a/tests/ui/asm/riscv/bad-reg.riscv32e.stderr b/tests/ui/asm/riscv/bad-reg.riscv32e.stderr index 3999f9cf0761..27c8e958e536 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32e.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32e.stderr @@ -1,185 +1,185 @@ error: invalid register `s1`: s1 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("s1") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:40:18 + --> $DIR/bad-reg.rs:39:18 | LL | asm!("", out("gp") _); | ^^^^^^^^^^^ error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:42:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:44:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:95:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:98:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:101:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:104:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: cannot use register `x16`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:47:18 + --> $DIR/bad-reg.rs:46:18 | LL | asm!("", out("x16") _); | ^^^^^^^^^^^^ error: cannot use register `x17`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:49:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", out("x17") _); | ^^^^^^^^^^^^ error: cannot use register `x18`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:51:18 + --> $DIR/bad-reg.rs:50:18 | LL | asm!("", out("x18") _); | ^^^^^^^^^^^^ error: cannot use register `x19`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:53:18 + --> $DIR/bad-reg.rs:52:18 | LL | asm!("", out("x19") _); | ^^^^^^^^^^^^ error: cannot use register `x20`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:55:18 + --> $DIR/bad-reg.rs:54:18 | LL | asm!("", out("x20") _); | ^^^^^^^^^^^^ error: cannot use register `x21`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:57:18 + --> $DIR/bad-reg.rs:56:18 | LL | asm!("", out("x21") _); | ^^^^^^^^^^^^ error: cannot use register `x22`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:59:18 + --> $DIR/bad-reg.rs:58:18 | LL | asm!("", out("x22") _); | ^^^^^^^^^^^^ error: cannot use register `x23`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:61:18 + --> $DIR/bad-reg.rs:60:18 | LL | asm!("", out("x23") _); | ^^^^^^^^^^^^ error: cannot use register `x24`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:63:18 + --> $DIR/bad-reg.rs:62:18 | LL | asm!("", out("x24") _); | ^^^^^^^^^^^^ error: cannot use register `x25`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:65:18 + --> $DIR/bad-reg.rs:64:18 | LL | asm!("", out("x25") _); | ^^^^^^^^^^^^ error: cannot use register `x26`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:67:18 + --> $DIR/bad-reg.rs:66:18 | LL | asm!("", out("x26") _); | ^^^^^^^^^^^^ error: cannot use register `x27`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:69:18 + --> $DIR/bad-reg.rs:68:18 | LL | asm!("", out("x27") _); | ^^^^^^^^^^^^ error: cannot use register `x28`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:71:18 + --> $DIR/bad-reg.rs:70:18 | LL | asm!("", out("x28") _); | ^^^^^^^^^^^^ error: cannot use register `x29`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:73:18 + --> $DIR/bad-reg.rs:72:18 | LL | asm!("", out("x29") _); | ^^^^^^^^^^^^ error: cannot use register `x30`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:75:18 + --> $DIR/bad-reg.rs:74:18 | LL | asm!("", out("x30") _); | ^^^^^^^^^^^^ error: cannot use register `x31`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:77:18 + --> $DIR/bad-reg.rs:76:18 | LL | asm!("", out("x31") _); | ^^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:81:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:83:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:85:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:88:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:95:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -187,7 +187,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:98:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -195,7 +195,7 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:101:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ diff --git a/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr b/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr index 0e458d2a47e2..4ff03d819e60 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr @@ -1,65 +1,65 @@ error: invalid register `s1`: s1 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("s1") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:40:18 + --> $DIR/bad-reg.rs:39:18 | LL | asm!("", out("gp") _); | ^^^^^^^^^^^ error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:42:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:44:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:95:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:98:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:101:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:104:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:95:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -67,7 +67,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:98:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -75,7 +75,7 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:101:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ diff --git a/tests/ui/asm/riscv/bad-reg.riscv32i.stderr b/tests/ui/asm/riscv/bad-reg.riscv32i.stderr index 18af1957d087..fbe63eb0563c 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32i.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32i.stderr @@ -1,89 +1,89 @@ error: invalid register `s1`: s1 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("s1") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:40:18 + --> $DIR/bad-reg.rs:39:18 | LL | asm!("", out("gp") _); | ^^^^^^^^^^^ error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:42:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:44:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:95:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:98:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:101:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:104:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:81:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:83:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:85:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:88:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:95:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -91,7 +91,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:98:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -99,7 +99,7 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:101:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ diff --git a/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr b/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr index d3b74d1bd0dc..57664cfe893b 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr @@ -1,65 +1,65 @@ error: invalid register `s1`: s1 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("s1") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:40:18 + --> $DIR/bad-reg.rs:39:18 | LL | asm!("", out("gp") _); | ^^^^^^^^^^^ error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:42:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:44:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:95:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:98:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:101:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:104:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: `d` target feature is not enabled - --> $DIR/bad-reg.rs:85:35 + --> $DIR/bad-reg.rs:84:35 | LL | asm!("/* {} */", in(freg) d); | ^ @@ -67,7 +67,7 @@ LL | asm!("/* {} */", in(freg) d); = note: this is required to use type `f64` with register class `freg` error: `d` target feature is not enabled - --> $DIR/bad-reg.rs:88:36 + --> $DIR/bad-reg.rs:87:36 | LL | asm!("/* {} */", out(freg) d); | ^ @@ -75,7 +75,7 @@ LL | asm!("/* {} */", out(freg) d); = note: this is required to use type `f64` with register class `freg` error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:95:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -83,7 +83,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:98:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -91,7 +91,7 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:101:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ diff --git a/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr b/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr index 0e458d2a47e2..4ff03d819e60 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr @@ -1,65 +1,65 @@ error: invalid register `s1`: s1 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("s1") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:40:18 + --> $DIR/bad-reg.rs:39:18 | LL | asm!("", out("gp") _); | ^^^^^^^^^^^ error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:42:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:44:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:95:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:98:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:101:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:104:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:95:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -67,7 +67,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:98:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -75,7 +75,7 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:101:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ diff --git a/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr b/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr index 18af1957d087..fbe63eb0563c 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr @@ -1,89 +1,89 @@ error: invalid register `s1`: s1 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("s1") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:40:18 + --> $DIR/bad-reg.rs:39:18 | LL | asm!("", out("gp") _); | ^^^^^^^^^^^ error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:42:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:44:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:95:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:98:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:101:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:104:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:81:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:83:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:85:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:88:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:95:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -91,7 +91,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:98:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -99,7 +99,7 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:101:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ diff --git a/tests/ui/asm/riscv/bad-reg.rs b/tests/ui/asm/riscv/bad-reg.rs index e2ff311851ba..0acf94c71994 100644 --- a/tests/ui/asm/riscv/bad-reg.rs +++ b/tests/ui/asm/riscv/bad-reg.rs @@ -1,5 +1,4 @@ //@ add-core-stubs -//@ needs-asm-support //@ revisions: riscv32i riscv32imafc riscv32gc riscv32e riscv64imac riscv64gc //@[riscv32i] compile-flags: --target riscv32i-unknown-none-elf //@[riscv32i] needs-llvm-components: riscv diff --git a/tests/ui/asm/s390x/bad-reg.rs b/tests/ui/asm/s390x/bad-reg.rs index 2915e874593e..274f4cd887c1 100644 --- a/tests/ui/asm/s390x/bad-reg.rs +++ b/tests/ui/asm/s390x/bad-reg.rs @@ -1,5 +1,4 @@ //@ add-core-stubs -//@ needs-asm-support //@ revisions: s390x s390x_vector s390x_vector_stable //@[s390x] compile-flags: --target s390x-unknown-linux-gnu -C target-feature=-vector //@[s390x] needs-llvm-components: systemz diff --git a/tests/ui/asm/s390x/bad-reg.s390x.stderr b/tests/ui/asm/s390x/bad-reg.s390x.stderr index c89d43defa39..238419b376b7 100644 --- a/tests/ui/asm/s390x/bad-reg.s390x.stderr +++ b/tests/ui/asm/s390x/bad-reg.s390x.stderr @@ -1,149 +1,149 @@ error: invalid register `r11`: The frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:31:18 | LL | asm!("", out("r11") _); | ^^^^^^^^^^^^ error: invalid register `r15`: The stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("r15") _); | ^^^^^^^^^^^^ error: invalid register `c0`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("c0") _); | ^^^^^^^^^^^ error: invalid register `c1`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("c1") _); | ^^^^^^^^^^^ error: invalid register `c2`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:40:18 + --> $DIR/bad-reg.rs:39:18 | LL | asm!("", out("c2") _); | ^^^^^^^^^^^ error: invalid register `c3`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:42:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("c3") _); | ^^^^^^^^^^^ error: invalid register `c4`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:44:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("c4") _); | ^^^^^^^^^^^ error: invalid register `c5`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:46:18 + --> $DIR/bad-reg.rs:45:18 | LL | asm!("", out("c5") _); | ^^^^^^^^^^^ error: invalid register `c6`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:48:18 + --> $DIR/bad-reg.rs:47:18 | LL | asm!("", out("c6") _); | ^^^^^^^^^^^ error: invalid register `c7`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:50:18 + --> $DIR/bad-reg.rs:49:18 | LL | asm!("", out("c7") _); | ^^^^^^^^^^^ error: invalid register `c8`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:52:18 + --> $DIR/bad-reg.rs:51:18 | LL | asm!("", out("c8") _); | ^^^^^^^^^^^ error: invalid register `c9`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:54:18 + --> $DIR/bad-reg.rs:53:18 | LL | asm!("", out("c9") _); | ^^^^^^^^^^^ error: invalid register `c10`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:56:18 + --> $DIR/bad-reg.rs:55:18 | LL | asm!("", out("c10") _); | ^^^^^^^^^^^^ error: invalid register `c11`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:58:18 + --> $DIR/bad-reg.rs:57:18 | LL | asm!("", out("c11") _); | ^^^^^^^^^^^^ error: invalid register `c12`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:60:18 + --> $DIR/bad-reg.rs:59:18 | LL | asm!("", out("c12") _); | ^^^^^^^^^^^^ error: invalid register `c13`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:62:18 + --> $DIR/bad-reg.rs:61:18 | LL | asm!("", out("c13") _); | ^^^^^^^^^^^^ error: invalid register `c14`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:64:18 + --> $DIR/bad-reg.rs:63:18 | LL | asm!("", out("c14") _); | ^^^^^^^^^^^^ error: invalid register `c15`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:66:18 + --> $DIR/bad-reg.rs:65:18 | LL | asm!("", out("c15") _); | ^^^^^^^^^^^^ error: invalid register `a0`: a0 and a1 are reserved for system use and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:68:18 + --> $DIR/bad-reg.rs:67:18 | LL | asm!("", out("a0") _); | ^^^^^^^^^^^ error: invalid register `a1`: a0 and a1 are reserved for system use and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:70:18 + --> $DIR/bad-reg.rs:69:18 | LL | asm!("", out("a1") _); | ^^^^^^^^^^^ error: register class `areg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:121:18 + --> $DIR/bad-reg.rs:120:18 | LL | asm!("", in("a2") x); | ^^^^^^^^^^ error: register class `areg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:124:18 + --> $DIR/bad-reg.rs:123:18 | LL | asm!("", out("a2") x); | ^^^^^^^^^^^ error: register class `areg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:127:26 + --> $DIR/bad-reg.rs:126:26 | LL | asm!("/* {} */", in(areg) x); | ^^^^^^^^^^ error: register class `areg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:130:26 + --> $DIR/bad-reg.rs:129:26 | LL | asm!("/* {} */", out(areg) _); | ^^^^^^^^^^^ error: register `f0` conflicts with register `v0` - --> $DIR/bad-reg.rs:135:31 + --> $DIR/bad-reg.rs:134:31 | LL | asm!("", out("v0") _, out("f0") _); | ----------- ^^^^^^^^^^^ register `f0` @@ -151,7 +151,7 @@ LL | asm!("", out("v0") _, out("f0") _); | register `v0` error: register `f1` conflicts with register `v1` - --> $DIR/bad-reg.rs:137:31 + --> $DIR/bad-reg.rs:136:31 | LL | asm!("", out("v1") _, out("f1") _); | ----------- ^^^^^^^^^^^ register `f1` @@ -159,7 +159,7 @@ LL | asm!("", out("v1") _, out("f1") _); | register `v1` error: register `f2` conflicts with register `v2` - --> $DIR/bad-reg.rs:139:31 + --> $DIR/bad-reg.rs:138:31 | LL | asm!("", out("v2") _, out("f2") _); | ----------- ^^^^^^^^^^^ register `f2` @@ -167,7 +167,7 @@ LL | asm!("", out("v2") _, out("f2") _); | register `v2` error: register `f3` conflicts with register `v3` - --> $DIR/bad-reg.rs:141:31 + --> $DIR/bad-reg.rs:140:31 | LL | asm!("", out("v3") _, out("f3") _); | ----------- ^^^^^^^^^^^ register `f3` @@ -175,7 +175,7 @@ LL | asm!("", out("v3") _, out("f3") _); | register `v3` error: register `f4` conflicts with register `v4` - --> $DIR/bad-reg.rs:143:31 + --> $DIR/bad-reg.rs:142:31 | LL | asm!("", out("v4") _, out("f4") _); | ----------- ^^^^^^^^^^^ register `f4` @@ -183,7 +183,7 @@ LL | asm!("", out("v4") _, out("f4") _); | register `v4` error: register `f5` conflicts with register `v5` - --> $DIR/bad-reg.rs:145:31 + --> $DIR/bad-reg.rs:144:31 | LL | asm!("", out("v5") _, out("f5") _); | ----------- ^^^^^^^^^^^ register `f5` @@ -191,7 +191,7 @@ LL | asm!("", out("v5") _, out("f5") _); | register `v5` error: register `f6` conflicts with register `v6` - --> $DIR/bad-reg.rs:147:31 + --> $DIR/bad-reg.rs:146:31 | LL | asm!("", out("v6") _, out("f6") _); | ----------- ^^^^^^^^^^^ register `f6` @@ -199,7 +199,7 @@ LL | asm!("", out("v6") _, out("f6") _); | register `v6` error: register `f7` conflicts with register `v7` - --> $DIR/bad-reg.rs:149:31 + --> $DIR/bad-reg.rs:148:31 | LL | asm!("", out("v7") _, out("f7") _); | ----------- ^^^^^^^^^^^ register `f7` @@ -207,7 +207,7 @@ LL | asm!("", out("v7") _, out("f7") _); | register `v7` error: register `f8` conflicts with register `v8` - --> $DIR/bad-reg.rs:151:31 + --> $DIR/bad-reg.rs:150:31 | LL | asm!("", out("v8") _, out("f8") _); | ----------- ^^^^^^^^^^^ register `f8` @@ -215,7 +215,7 @@ LL | asm!("", out("v8") _, out("f8") _); | register `v8` error: register `f9` conflicts with register `v9` - --> $DIR/bad-reg.rs:153:31 + --> $DIR/bad-reg.rs:152:31 | LL | asm!("", out("v9") _, out("f9") _); | ----------- ^^^^^^^^^^^ register `f9` @@ -223,7 +223,7 @@ LL | asm!("", out("v9") _, out("f9") _); | register `v9` error: register `f10` conflicts with register `v10` - --> $DIR/bad-reg.rs:155:32 + --> $DIR/bad-reg.rs:154:32 | LL | asm!("", out("v10") _, out("f10") _); | ------------ ^^^^^^^^^^^^ register `f10` @@ -231,7 +231,7 @@ LL | asm!("", out("v10") _, out("f10") _); | register `v10` error: register `f11` conflicts with register `v11` - --> $DIR/bad-reg.rs:157:32 + --> $DIR/bad-reg.rs:156:32 | LL | asm!("", out("v11") _, out("f11") _); | ------------ ^^^^^^^^^^^^ register `f11` @@ -239,7 +239,7 @@ LL | asm!("", out("v11") _, out("f11") _); | register `v11` error: register `f12` conflicts with register `v12` - --> $DIR/bad-reg.rs:159:32 + --> $DIR/bad-reg.rs:158:32 | LL | asm!("", out("v12") _, out("f12") _); | ------------ ^^^^^^^^^^^^ register `f12` @@ -247,7 +247,7 @@ LL | asm!("", out("v12") _, out("f12") _); | register `v12` error: register `f13` conflicts with register `v13` - --> $DIR/bad-reg.rs:161:32 + --> $DIR/bad-reg.rs:160:32 | LL | asm!("", out("v13") _, out("f13") _); | ------------ ^^^^^^^^^^^^ register `f13` @@ -255,7 +255,7 @@ LL | asm!("", out("v13") _, out("f13") _); | register `v13` error: register `f14` conflicts with register `v14` - --> $DIR/bad-reg.rs:163:32 + --> $DIR/bad-reg.rs:162:32 | LL | asm!("", out("v14") _, out("f14") _); | ------------ ^^^^^^^^^^^^ register `f14` @@ -263,7 +263,7 @@ LL | asm!("", out("v14") _, out("f14") _); | register `v14` error: register `f15` conflicts with register `v15` - --> $DIR/bad-reg.rs:165:32 + --> $DIR/bad-reg.rs:164:32 | LL | asm!("", out("v15") _, out("f15") _); | ------------ ^^^^^^^^^^^^ register `f15` @@ -271,73 +271,73 @@ LL | asm!("", out("v15") _, out("f15") _); | register `v15` error: invalid register `f16`: unknown register - --> $DIR/bad-reg.rs:168:32 + --> $DIR/bad-reg.rs:167:32 | LL | asm!("", out("v16") _, out("f16") _); | ^^^^^^^^^^^^ error: register class `vreg` requires the `vector` target feature - --> $DIR/bad-reg.rs:75:18 + --> $DIR/bad-reg.rs:74:18 | LL | asm!("", in("v0") v); // requires vector & asm_experimental_reg | ^^^^^^^^^^ error: register class `vreg` requires the `vector` target feature - --> $DIR/bad-reg.rs:79:18 + --> $DIR/bad-reg.rs:78:18 | LL | asm!("", out("v0") v); // requires vector & asm_experimental_reg | ^^^^^^^^^^^ error: register class `vreg` requires the `vector` target feature - --> $DIR/bad-reg.rs:83:18 + --> $DIR/bad-reg.rs:82:18 | LL | asm!("", in("v0") x); // requires vector & asm_experimental_reg | ^^^^^^^^^^ error: register class `vreg` requires the `vector` target feature - --> $DIR/bad-reg.rs:87:18 + --> $DIR/bad-reg.rs:86:18 | LL | asm!("", out("v0") x); // requires vector & asm_experimental_reg | ^^^^^^^^^^^ error: register class `vreg` requires the `vector` target feature - --> $DIR/bad-reg.rs:91:18 + --> $DIR/bad-reg.rs:90:18 | LL | asm!("", in("v0") b); | ^^^^^^^^^^ error: register class `vreg` requires the `vector` target feature - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:95:18 | LL | asm!("", out("v0") b); | ^^^^^^^^^^^ error: register class `vreg` requires the `vector` target feature - --> $DIR/bad-reg.rs:101:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) v); // requires vector & asm_experimental_reg | ^^^^^^^^^^ error: register class `vreg` requires the `vector` target feature - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:104:26 | LL | asm!("/* {} */", in(vreg) x); // requires vector & asm_experimental_reg | ^^^^^^^^^^ error: register class `vreg` requires the `vector` target feature - --> $DIR/bad-reg.rs:109:26 + --> $DIR/bad-reg.rs:108:26 | LL | asm!("/* {} */", in(vreg) b); | ^^^^^^^^^^ error: register class `vreg` requires the `vector` target feature - --> $DIR/bad-reg.rs:114:26 + --> $DIR/bad-reg.rs:113:26 | LL | asm!("/* {} */", out(vreg) _); // requires vector & asm_experimental_reg | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:121:27 + --> $DIR/bad-reg.rs:120:27 | LL | asm!("", in("a2") x); | ^ @@ -345,7 +345,7 @@ LL | asm!("", in("a2") x); = note: register class `areg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:124:28 + --> $DIR/bad-reg.rs:123:28 | LL | asm!("", out("a2") x); | ^ @@ -353,7 +353,7 @@ LL | asm!("", out("a2") x); = note: register class `areg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:127:35 + --> $DIR/bad-reg.rs:126:35 | LL | asm!("/* {} */", in(areg) x); | ^ diff --git a/tests/ui/asm/s390x/bad-reg.s390x_vector.stderr b/tests/ui/asm/s390x/bad-reg.s390x_vector.stderr index 3890e1442ccc..897f872ae72a 100644 --- a/tests/ui/asm/s390x/bad-reg.s390x_vector.stderr +++ b/tests/ui/asm/s390x/bad-reg.s390x_vector.stderr @@ -1,149 +1,149 @@ error: invalid register `r11`: The frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:31:18 | LL | asm!("", out("r11") _); | ^^^^^^^^^^^^ error: invalid register `r15`: The stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("r15") _); | ^^^^^^^^^^^^ error: invalid register `c0`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("c0") _); | ^^^^^^^^^^^ error: invalid register `c1`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("c1") _); | ^^^^^^^^^^^ error: invalid register `c2`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:40:18 + --> $DIR/bad-reg.rs:39:18 | LL | asm!("", out("c2") _); | ^^^^^^^^^^^ error: invalid register `c3`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:42:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("c3") _); | ^^^^^^^^^^^ error: invalid register `c4`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:44:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("c4") _); | ^^^^^^^^^^^ error: invalid register `c5`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:46:18 + --> $DIR/bad-reg.rs:45:18 | LL | asm!("", out("c5") _); | ^^^^^^^^^^^ error: invalid register `c6`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:48:18 + --> $DIR/bad-reg.rs:47:18 | LL | asm!("", out("c6") _); | ^^^^^^^^^^^ error: invalid register `c7`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:50:18 + --> $DIR/bad-reg.rs:49:18 | LL | asm!("", out("c7") _); | ^^^^^^^^^^^ error: invalid register `c8`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:52:18 + --> $DIR/bad-reg.rs:51:18 | LL | asm!("", out("c8") _); | ^^^^^^^^^^^ error: invalid register `c9`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:54:18 + --> $DIR/bad-reg.rs:53:18 | LL | asm!("", out("c9") _); | ^^^^^^^^^^^ error: invalid register `c10`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:56:18 + --> $DIR/bad-reg.rs:55:18 | LL | asm!("", out("c10") _); | ^^^^^^^^^^^^ error: invalid register `c11`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:58:18 + --> $DIR/bad-reg.rs:57:18 | LL | asm!("", out("c11") _); | ^^^^^^^^^^^^ error: invalid register `c12`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:60:18 + --> $DIR/bad-reg.rs:59:18 | LL | asm!("", out("c12") _); | ^^^^^^^^^^^^ error: invalid register `c13`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:62:18 + --> $DIR/bad-reg.rs:61:18 | LL | asm!("", out("c13") _); | ^^^^^^^^^^^^ error: invalid register `c14`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:64:18 + --> $DIR/bad-reg.rs:63:18 | LL | asm!("", out("c14") _); | ^^^^^^^^^^^^ error: invalid register `c15`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:66:18 + --> $DIR/bad-reg.rs:65:18 | LL | asm!("", out("c15") _); | ^^^^^^^^^^^^ error: invalid register `a0`: a0 and a1 are reserved for system use and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:68:18 + --> $DIR/bad-reg.rs:67:18 | LL | asm!("", out("a0") _); | ^^^^^^^^^^^ error: invalid register `a1`: a0 and a1 are reserved for system use and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:70:18 + --> $DIR/bad-reg.rs:69:18 | LL | asm!("", out("a1") _); | ^^^^^^^^^^^ error: register class `areg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:121:18 + --> $DIR/bad-reg.rs:120:18 | LL | asm!("", in("a2") x); | ^^^^^^^^^^ error: register class `areg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:124:18 + --> $DIR/bad-reg.rs:123:18 | LL | asm!("", out("a2") x); | ^^^^^^^^^^^ error: register class `areg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:127:26 + --> $DIR/bad-reg.rs:126:26 | LL | asm!("/* {} */", in(areg) x); | ^^^^^^^^^^ error: register class `areg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:130:26 + --> $DIR/bad-reg.rs:129:26 | LL | asm!("/* {} */", out(areg) _); | ^^^^^^^^^^^ error: register `f0` conflicts with register `v0` - --> $DIR/bad-reg.rs:135:31 + --> $DIR/bad-reg.rs:134:31 | LL | asm!("", out("v0") _, out("f0") _); | ----------- ^^^^^^^^^^^ register `f0` @@ -151,7 +151,7 @@ LL | asm!("", out("v0") _, out("f0") _); | register `v0` error: register `f1` conflicts with register `v1` - --> $DIR/bad-reg.rs:137:31 + --> $DIR/bad-reg.rs:136:31 | LL | asm!("", out("v1") _, out("f1") _); | ----------- ^^^^^^^^^^^ register `f1` @@ -159,7 +159,7 @@ LL | asm!("", out("v1") _, out("f1") _); | register `v1` error: register `f2` conflicts with register `v2` - --> $DIR/bad-reg.rs:139:31 + --> $DIR/bad-reg.rs:138:31 | LL | asm!("", out("v2") _, out("f2") _); | ----------- ^^^^^^^^^^^ register `f2` @@ -167,7 +167,7 @@ LL | asm!("", out("v2") _, out("f2") _); | register `v2` error: register `f3` conflicts with register `v3` - --> $DIR/bad-reg.rs:141:31 + --> $DIR/bad-reg.rs:140:31 | LL | asm!("", out("v3") _, out("f3") _); | ----------- ^^^^^^^^^^^ register `f3` @@ -175,7 +175,7 @@ LL | asm!("", out("v3") _, out("f3") _); | register `v3` error: register `f4` conflicts with register `v4` - --> $DIR/bad-reg.rs:143:31 + --> $DIR/bad-reg.rs:142:31 | LL | asm!("", out("v4") _, out("f4") _); | ----------- ^^^^^^^^^^^ register `f4` @@ -183,7 +183,7 @@ LL | asm!("", out("v4") _, out("f4") _); | register `v4` error: register `f5` conflicts with register `v5` - --> $DIR/bad-reg.rs:145:31 + --> $DIR/bad-reg.rs:144:31 | LL | asm!("", out("v5") _, out("f5") _); | ----------- ^^^^^^^^^^^ register `f5` @@ -191,7 +191,7 @@ LL | asm!("", out("v5") _, out("f5") _); | register `v5` error: register `f6` conflicts with register `v6` - --> $DIR/bad-reg.rs:147:31 + --> $DIR/bad-reg.rs:146:31 | LL | asm!("", out("v6") _, out("f6") _); | ----------- ^^^^^^^^^^^ register `f6` @@ -199,7 +199,7 @@ LL | asm!("", out("v6") _, out("f6") _); | register `v6` error: register `f7` conflicts with register `v7` - --> $DIR/bad-reg.rs:149:31 + --> $DIR/bad-reg.rs:148:31 | LL | asm!("", out("v7") _, out("f7") _); | ----------- ^^^^^^^^^^^ register `f7` @@ -207,7 +207,7 @@ LL | asm!("", out("v7") _, out("f7") _); | register `v7` error: register `f8` conflicts with register `v8` - --> $DIR/bad-reg.rs:151:31 + --> $DIR/bad-reg.rs:150:31 | LL | asm!("", out("v8") _, out("f8") _); | ----------- ^^^^^^^^^^^ register `f8` @@ -215,7 +215,7 @@ LL | asm!("", out("v8") _, out("f8") _); | register `v8` error: register `f9` conflicts with register `v9` - --> $DIR/bad-reg.rs:153:31 + --> $DIR/bad-reg.rs:152:31 | LL | asm!("", out("v9") _, out("f9") _); | ----------- ^^^^^^^^^^^ register `f9` @@ -223,7 +223,7 @@ LL | asm!("", out("v9") _, out("f9") _); | register `v9` error: register `f10` conflicts with register `v10` - --> $DIR/bad-reg.rs:155:32 + --> $DIR/bad-reg.rs:154:32 | LL | asm!("", out("v10") _, out("f10") _); | ------------ ^^^^^^^^^^^^ register `f10` @@ -231,7 +231,7 @@ LL | asm!("", out("v10") _, out("f10") _); | register `v10` error: register `f11` conflicts with register `v11` - --> $DIR/bad-reg.rs:157:32 + --> $DIR/bad-reg.rs:156:32 | LL | asm!("", out("v11") _, out("f11") _); | ------------ ^^^^^^^^^^^^ register `f11` @@ -239,7 +239,7 @@ LL | asm!("", out("v11") _, out("f11") _); | register `v11` error: register `f12` conflicts with register `v12` - --> $DIR/bad-reg.rs:159:32 + --> $DIR/bad-reg.rs:158:32 | LL | asm!("", out("v12") _, out("f12") _); | ------------ ^^^^^^^^^^^^ register `f12` @@ -247,7 +247,7 @@ LL | asm!("", out("v12") _, out("f12") _); | register `v12` error: register `f13` conflicts with register `v13` - --> $DIR/bad-reg.rs:161:32 + --> $DIR/bad-reg.rs:160:32 | LL | asm!("", out("v13") _, out("f13") _); | ------------ ^^^^^^^^^^^^ register `f13` @@ -255,7 +255,7 @@ LL | asm!("", out("v13") _, out("f13") _); | register `v13` error: register `f14` conflicts with register `v14` - --> $DIR/bad-reg.rs:163:32 + --> $DIR/bad-reg.rs:162:32 | LL | asm!("", out("v14") _, out("f14") _); | ------------ ^^^^^^^^^^^^ register `f14` @@ -263,7 +263,7 @@ LL | asm!("", out("v14") _, out("f14") _); | register `v14` error: register `f15` conflicts with register `v15` - --> $DIR/bad-reg.rs:165:32 + --> $DIR/bad-reg.rs:164:32 | LL | asm!("", out("v15") _, out("f15") _); | ------------ ^^^^^^^^^^^^ register `f15` @@ -271,13 +271,13 @@ LL | asm!("", out("v15") _, out("f15") _); | register `v15` error: invalid register `f16`: unknown register - --> $DIR/bad-reg.rs:168:32 + --> $DIR/bad-reg.rs:167:32 | LL | asm!("", out("v16") _, out("f16") _); | ^^^^^^^^^^^^ error: type `u8` cannot be used with this register class - --> $DIR/bad-reg.rs:91:27 + --> $DIR/bad-reg.rs:90:27 | LL | asm!("", in("v0") b); | ^ @@ -285,7 +285,7 @@ LL | asm!("", in("v0") b); = note: register class `vreg` supports these types: i32, f32, i64, f64, i128, f128, i8x16, i16x8, i32x4, i64x2, f32x4, f64x2 error: type `u8` cannot be used with this register class - --> $DIR/bad-reg.rs:96:28 + --> $DIR/bad-reg.rs:95:28 | LL | asm!("", out("v0") b); | ^ @@ -293,7 +293,7 @@ LL | asm!("", out("v0") b); = note: register class `vreg` supports these types: i32, f32, i64, f64, i128, f128, i8x16, i16x8, i32x4, i64x2, f32x4, f64x2 error: type `u8` cannot be used with this register class - --> $DIR/bad-reg.rs:109:35 + --> $DIR/bad-reg.rs:108:35 | LL | asm!("/* {} */", in(vreg) b); | ^ @@ -301,7 +301,7 @@ LL | asm!("/* {} */", in(vreg) b); = note: register class `vreg` supports these types: i32, f32, i64, f64, i128, f128, i8x16, i16x8, i32x4, i64x2, f32x4, f64x2 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:121:27 + --> $DIR/bad-reg.rs:120:27 | LL | asm!("", in("a2") x); | ^ @@ -309,7 +309,7 @@ LL | asm!("", in("a2") x); = note: register class `areg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:124:28 + --> $DIR/bad-reg.rs:123:28 | LL | asm!("", out("a2") x); | ^ @@ -317,7 +317,7 @@ LL | asm!("", out("a2") x); = note: register class `areg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:127:35 + --> $DIR/bad-reg.rs:126:35 | LL | asm!("/* {} */", in(areg) x); | ^ diff --git a/tests/ui/asm/s390x/bad-reg.s390x_vector_stable.stderr b/tests/ui/asm/s390x/bad-reg.s390x_vector_stable.stderr index 20d129ebb387..e2b3eeef4e92 100644 --- a/tests/ui/asm/s390x/bad-reg.s390x_vector_stable.stderr +++ b/tests/ui/asm/s390x/bad-reg.s390x_vector_stable.stderr @@ -1,125 +1,125 @@ error: invalid register `r11`: The frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:31:18 | LL | asm!("", out("r11") _); | ^^^^^^^^^^^^ error: invalid register `r15`: The stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("r15") _); | ^^^^^^^^^^^^ error: invalid register `c0`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("c0") _); | ^^^^^^^^^^^ error: invalid register `c1`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("c1") _); | ^^^^^^^^^^^ error: invalid register `c2`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:40:18 + --> $DIR/bad-reg.rs:39:18 | LL | asm!("", out("c2") _); | ^^^^^^^^^^^ error: invalid register `c3`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:42:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("c3") _); | ^^^^^^^^^^^ error: invalid register `c4`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:44:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("c4") _); | ^^^^^^^^^^^ error: invalid register `c5`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:46:18 + --> $DIR/bad-reg.rs:45:18 | LL | asm!("", out("c5") _); | ^^^^^^^^^^^ error: invalid register `c6`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:48:18 + --> $DIR/bad-reg.rs:47:18 | LL | asm!("", out("c6") _); | ^^^^^^^^^^^ error: invalid register `c7`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:50:18 + --> $DIR/bad-reg.rs:49:18 | LL | asm!("", out("c7") _); | ^^^^^^^^^^^ error: invalid register `c8`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:52:18 + --> $DIR/bad-reg.rs:51:18 | LL | asm!("", out("c8") _); | ^^^^^^^^^^^ error: invalid register `c9`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:54:18 + --> $DIR/bad-reg.rs:53:18 | LL | asm!("", out("c9") _); | ^^^^^^^^^^^ error: invalid register `c10`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:56:18 + --> $DIR/bad-reg.rs:55:18 | LL | asm!("", out("c10") _); | ^^^^^^^^^^^^ error: invalid register `c11`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:58:18 + --> $DIR/bad-reg.rs:57:18 | LL | asm!("", out("c11") _); | ^^^^^^^^^^^^ error: invalid register `c12`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:60:18 + --> $DIR/bad-reg.rs:59:18 | LL | asm!("", out("c12") _); | ^^^^^^^^^^^^ error: invalid register `c13`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:62:18 + --> $DIR/bad-reg.rs:61:18 | LL | asm!("", out("c13") _); | ^^^^^^^^^^^^ error: invalid register `c14`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:64:18 + --> $DIR/bad-reg.rs:63:18 | LL | asm!("", out("c14") _); | ^^^^^^^^^^^^ error: invalid register `c15`: control registers are reserved by the kernel and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:66:18 + --> $DIR/bad-reg.rs:65:18 | LL | asm!("", out("c15") _); | ^^^^^^^^^^^^ error: invalid register `a0`: a0 and a1 are reserved for system use and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:68:18 + --> $DIR/bad-reg.rs:67:18 | LL | asm!("", out("a0") _); | ^^^^^^^^^^^ error: invalid register `a1`: a0 and a1 are reserved for system use and cannot be used as operands for inline asm - --> $DIR/bad-reg.rs:70:18 + --> $DIR/bad-reg.rs:69:18 | LL | asm!("", out("a1") _); | ^^^^^^^^^^^ error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:75:18 + --> $DIR/bad-reg.rs:74:18 | LL | asm!("", in("v0") v); // requires vector & asm_experimental_reg | ^^^^^^^^^^ @@ -129,7 +129,7 @@ LL | asm!("", in("v0") v); // requires vector & asm_experimental_reg = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:79:18 + --> $DIR/bad-reg.rs:78:18 | LL | asm!("", out("v0") v); // requires vector & asm_experimental_reg | ^^^^^^^^^^^ @@ -139,7 +139,7 @@ LL | asm!("", out("v0") v); // requires vector & asm_experimental_reg = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:83:18 + --> $DIR/bad-reg.rs:82:18 | LL | asm!("", in("v0") x); // requires vector & asm_experimental_reg | ^^^^^^^^^^ @@ -149,7 +149,7 @@ LL | asm!("", in("v0") x); // requires vector & asm_experimental_reg = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:87:18 + --> $DIR/bad-reg.rs:86:18 | LL | asm!("", out("v0") x); // requires vector & asm_experimental_reg | ^^^^^^^^^^^ @@ -159,7 +159,7 @@ LL | asm!("", out("v0") x); // requires vector & asm_experimental_reg = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:91:18 + --> $DIR/bad-reg.rs:90:18 | LL | asm!("", in("v0") b); | ^^^^^^^^^^ @@ -169,7 +169,7 @@ LL | asm!("", in("v0") b); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:95:18 | LL | asm!("", out("v0") b); | ^^^^^^^^^^^ @@ -179,7 +179,7 @@ LL | asm!("", out("v0") b); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:101:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) v); // requires vector & asm_experimental_reg | ^^^^^^^^^^ @@ -189,7 +189,7 @@ LL | asm!("/* {} */", in(vreg) v); // requires vector & asm_experimental = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:104:26 | LL | asm!("/* {} */", in(vreg) x); // requires vector & asm_experimental_reg | ^^^^^^^^^^ @@ -199,7 +199,7 @@ LL | asm!("/* {} */", in(vreg) x); // requires vector & asm_experimental = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:109:26 + --> $DIR/bad-reg.rs:108:26 | LL | asm!("/* {} */", in(vreg) b); | ^^^^^^^^^^ @@ -209,7 +209,7 @@ LL | asm!("/* {} */", in(vreg) b); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:114:26 + --> $DIR/bad-reg.rs:113:26 | LL | asm!("/* {} */", out(vreg) _); // requires vector & asm_experimental_reg | ^^^^^^^^^^^ @@ -219,31 +219,31 @@ LL | asm!("/* {} */", out(vreg) _); // requires vector & asm_experimenta = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: register class `areg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:121:18 + --> $DIR/bad-reg.rs:120:18 | LL | asm!("", in("a2") x); | ^^^^^^^^^^ error: register class `areg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:124:18 + --> $DIR/bad-reg.rs:123:18 | LL | asm!("", out("a2") x); | ^^^^^^^^^^^ error: register class `areg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:127:26 + --> $DIR/bad-reg.rs:126:26 | LL | asm!("/* {} */", in(areg) x); | ^^^^^^^^^^ error: register class `areg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:130:26 + --> $DIR/bad-reg.rs:129:26 | LL | asm!("/* {} */", out(areg) _); | ^^^^^^^^^^^ error: register `f0` conflicts with register `v0` - --> $DIR/bad-reg.rs:135:31 + --> $DIR/bad-reg.rs:134:31 | LL | asm!("", out("v0") _, out("f0") _); | ----------- ^^^^^^^^^^^ register `f0` @@ -251,7 +251,7 @@ LL | asm!("", out("v0") _, out("f0") _); | register `v0` error: register `f1` conflicts with register `v1` - --> $DIR/bad-reg.rs:137:31 + --> $DIR/bad-reg.rs:136:31 | LL | asm!("", out("v1") _, out("f1") _); | ----------- ^^^^^^^^^^^ register `f1` @@ -259,7 +259,7 @@ LL | asm!("", out("v1") _, out("f1") _); | register `v1` error: register `f2` conflicts with register `v2` - --> $DIR/bad-reg.rs:139:31 + --> $DIR/bad-reg.rs:138:31 | LL | asm!("", out("v2") _, out("f2") _); | ----------- ^^^^^^^^^^^ register `f2` @@ -267,7 +267,7 @@ LL | asm!("", out("v2") _, out("f2") _); | register `v2` error: register `f3` conflicts with register `v3` - --> $DIR/bad-reg.rs:141:31 + --> $DIR/bad-reg.rs:140:31 | LL | asm!("", out("v3") _, out("f3") _); | ----------- ^^^^^^^^^^^ register `f3` @@ -275,7 +275,7 @@ LL | asm!("", out("v3") _, out("f3") _); | register `v3` error: register `f4` conflicts with register `v4` - --> $DIR/bad-reg.rs:143:31 + --> $DIR/bad-reg.rs:142:31 | LL | asm!("", out("v4") _, out("f4") _); | ----------- ^^^^^^^^^^^ register `f4` @@ -283,7 +283,7 @@ LL | asm!("", out("v4") _, out("f4") _); | register `v4` error: register `f5` conflicts with register `v5` - --> $DIR/bad-reg.rs:145:31 + --> $DIR/bad-reg.rs:144:31 | LL | asm!("", out("v5") _, out("f5") _); | ----------- ^^^^^^^^^^^ register `f5` @@ -291,7 +291,7 @@ LL | asm!("", out("v5") _, out("f5") _); | register `v5` error: register `f6` conflicts with register `v6` - --> $DIR/bad-reg.rs:147:31 + --> $DIR/bad-reg.rs:146:31 | LL | asm!("", out("v6") _, out("f6") _); | ----------- ^^^^^^^^^^^ register `f6` @@ -299,7 +299,7 @@ LL | asm!("", out("v6") _, out("f6") _); | register `v6` error: register `f7` conflicts with register `v7` - --> $DIR/bad-reg.rs:149:31 + --> $DIR/bad-reg.rs:148:31 | LL | asm!("", out("v7") _, out("f7") _); | ----------- ^^^^^^^^^^^ register `f7` @@ -307,7 +307,7 @@ LL | asm!("", out("v7") _, out("f7") _); | register `v7` error: register `f8` conflicts with register `v8` - --> $DIR/bad-reg.rs:151:31 + --> $DIR/bad-reg.rs:150:31 | LL | asm!("", out("v8") _, out("f8") _); | ----------- ^^^^^^^^^^^ register `f8` @@ -315,7 +315,7 @@ LL | asm!("", out("v8") _, out("f8") _); | register `v8` error: register `f9` conflicts with register `v9` - --> $DIR/bad-reg.rs:153:31 + --> $DIR/bad-reg.rs:152:31 | LL | asm!("", out("v9") _, out("f9") _); | ----------- ^^^^^^^^^^^ register `f9` @@ -323,7 +323,7 @@ LL | asm!("", out("v9") _, out("f9") _); | register `v9` error: register `f10` conflicts with register `v10` - --> $DIR/bad-reg.rs:155:32 + --> $DIR/bad-reg.rs:154:32 | LL | asm!("", out("v10") _, out("f10") _); | ------------ ^^^^^^^^^^^^ register `f10` @@ -331,7 +331,7 @@ LL | asm!("", out("v10") _, out("f10") _); | register `v10` error: register `f11` conflicts with register `v11` - --> $DIR/bad-reg.rs:157:32 + --> $DIR/bad-reg.rs:156:32 | LL | asm!("", out("v11") _, out("f11") _); | ------------ ^^^^^^^^^^^^ register `f11` @@ -339,7 +339,7 @@ LL | asm!("", out("v11") _, out("f11") _); | register `v11` error: register `f12` conflicts with register `v12` - --> $DIR/bad-reg.rs:159:32 + --> $DIR/bad-reg.rs:158:32 | LL | asm!("", out("v12") _, out("f12") _); | ------------ ^^^^^^^^^^^^ register `f12` @@ -347,7 +347,7 @@ LL | asm!("", out("v12") _, out("f12") _); | register `v12` error: register `f13` conflicts with register `v13` - --> $DIR/bad-reg.rs:161:32 + --> $DIR/bad-reg.rs:160:32 | LL | asm!("", out("v13") _, out("f13") _); | ------------ ^^^^^^^^^^^^ register `f13` @@ -355,7 +355,7 @@ LL | asm!("", out("v13") _, out("f13") _); | register `v13` error: register `f14` conflicts with register `v14` - --> $DIR/bad-reg.rs:163:32 + --> $DIR/bad-reg.rs:162:32 | LL | asm!("", out("v14") _, out("f14") _); | ------------ ^^^^^^^^^^^^ register `f14` @@ -363,7 +363,7 @@ LL | asm!("", out("v14") _, out("f14") _); | register `v14` error: register `f15` conflicts with register `v15` - --> $DIR/bad-reg.rs:165:32 + --> $DIR/bad-reg.rs:164:32 | LL | asm!("", out("v15") _, out("f15") _); | ------------ ^^^^^^^^^^^^ register `f15` @@ -371,13 +371,13 @@ LL | asm!("", out("v15") _, out("f15") _); | register `v15` error: invalid register `f16`: unknown register - --> $DIR/bad-reg.rs:168:32 + --> $DIR/bad-reg.rs:167:32 | LL | asm!("", out("v16") _, out("f16") _); | ^^^^^^^^^^^^ error[E0658]: type `i64x2` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:75:27 + --> $DIR/bad-reg.rs:74:27 | LL | asm!("", in("v0") v); // requires vector & asm_experimental_reg | ^ @@ -387,7 +387,7 @@ LL | asm!("", in("v0") v); // requires vector & asm_experimental_reg = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `i64x2` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:79:28 + --> $DIR/bad-reg.rs:78:28 | LL | asm!("", out("v0") v); // requires vector & asm_experimental_reg | ^ @@ -397,7 +397,7 @@ LL | asm!("", out("v0") v); // requires vector & asm_experimental_reg = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `i32` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:83:27 + --> $DIR/bad-reg.rs:82:27 | LL | asm!("", in("v0") x); // requires vector & asm_experimental_reg | ^ @@ -407,7 +407,7 @@ LL | asm!("", in("v0") x); // requires vector & asm_experimental_reg = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `i32` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:87:28 + --> $DIR/bad-reg.rs:86:28 | LL | asm!("", out("v0") x); // requires vector & asm_experimental_reg | ^ @@ -417,7 +417,7 @@ LL | asm!("", out("v0") x); // requires vector & asm_experimental_reg = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: type `u8` cannot be used with this register class - --> $DIR/bad-reg.rs:91:27 + --> $DIR/bad-reg.rs:90:27 | LL | asm!("", in("v0") b); | ^ @@ -425,7 +425,7 @@ LL | asm!("", in("v0") b); = note: register class `vreg` supports these types: error: type `u8` cannot be used with this register class - --> $DIR/bad-reg.rs:96:28 + --> $DIR/bad-reg.rs:95:28 | LL | asm!("", out("v0") b); | ^ @@ -433,7 +433,7 @@ LL | asm!("", out("v0") b); = note: register class `vreg` supports these types: error[E0658]: type `i64x2` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:101:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) v); // requires vector & asm_experimental_reg | ^ @@ -443,7 +443,7 @@ LL | asm!("/* {} */", in(vreg) v); // requires vector & asm_experimental = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `i32` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:105:35 + --> $DIR/bad-reg.rs:104:35 | LL | asm!("/* {} */", in(vreg) x); // requires vector & asm_experimental_reg | ^ @@ -453,7 +453,7 @@ LL | asm!("/* {} */", in(vreg) x); // requires vector & asm_experimental = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: type `u8` cannot be used with this register class - --> $DIR/bad-reg.rs:109:35 + --> $DIR/bad-reg.rs:108:35 | LL | asm!("/* {} */", in(vreg) b); | ^ @@ -461,7 +461,7 @@ LL | asm!("/* {} */", in(vreg) b); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:121:27 + --> $DIR/bad-reg.rs:120:27 | LL | asm!("", in("a2") x); | ^ @@ -469,7 +469,7 @@ LL | asm!("", in("a2") x); = note: register class `areg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:124:28 + --> $DIR/bad-reg.rs:123:28 | LL | asm!("", out("a2") x); | ^ @@ -477,7 +477,7 @@ LL | asm!("", out("a2") x); = note: register class `areg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:127:35 + --> $DIR/bad-reg.rs:126:35 | LL | asm!("/* {} */", in(areg) x); | ^ diff --git a/tests/ui/asm/sparc/bad-reg.rs b/tests/ui/asm/sparc/bad-reg.rs index 8b6fdc422120..82ac1ebb7afa 100644 --- a/tests/ui/asm/sparc/bad-reg.rs +++ b/tests/ui/asm/sparc/bad-reg.rs @@ -6,7 +6,6 @@ //@[sparcv8plus] needs-llvm-components: sparc //@[sparc64] compile-flags: --target sparc64-unknown-linux-gnu //@[sparc64] needs-llvm-components: sparc -//@ needs-asm-support //@ ignore-backends: gcc #![crate_type = "rlib"] diff --git a/tests/ui/asm/sparc/bad-reg.sparc.stderr b/tests/ui/asm/sparc/bad-reg.sparc.stderr index 84e56871ecc2..e0580ad3232f 100644 --- a/tests/ui/asm/sparc/bad-reg.sparc.stderr +++ b/tests/ui/asm/sparc/bad-reg.sparc.stderr @@ -1,77 +1,77 @@ error: invalid register `g0`: g0 is always zero and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:23:18 + --> $DIR/bad-reg.rs:22:18 | LL | asm!("", out("g0") _); | ^^^^^^^^^^^ error: invalid register `g1`: reserved by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:25:18 | LL | asm!("", out("g1") _); | ^^^^^^^^^^^ error: invalid register `g6`: reserved for system and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("g6") _); | ^^^^^^^^^^^ error: invalid register `g7`: reserved for system and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("g7") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `i7`: the return address register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 + --> $DIR/bad-reg.rs:40:18 | LL | asm!("", out("i7") _); | ^^^^^^^^^^^ error: register class `yreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:47:18 + --> $DIR/bad-reg.rs:46:18 | LL | asm!("", in("y") x); | ^^^^^^^^^ error: register class `yreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:50:18 + --> $DIR/bad-reg.rs:49:18 | LL | asm!("", out("y") x); | ^^^^^^^^^^ error: register class `yreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:53:26 + --> $DIR/bad-reg.rs:52:26 | LL | asm!("/* {} */", in(yreg) x); | ^^^^^^^^^^ error: register class `yreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:56:26 + --> $DIR/bad-reg.rs:55:26 | LL | asm!("/* {} */", out(yreg) _); | ^^^^^^^^^^^ error: cannot use register `r5`: g5 is reserved for system on SPARC32 - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("g5") _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:47:26 + --> $DIR/bad-reg.rs:46:26 | LL | asm!("", in("y") x); | ^ @@ -79,7 +79,7 @@ LL | asm!("", in("y") x); = note: register class `yreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:50:27 + --> $DIR/bad-reg.rs:49:27 | LL | asm!("", out("y") x); | ^ @@ -87,7 +87,7 @@ LL | asm!("", out("y") x); = note: register class `yreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:53:35 + --> $DIR/bad-reg.rs:52:35 | LL | asm!("/* {} */", in(yreg) x); | ^ diff --git a/tests/ui/asm/sparc/bad-reg.sparc64.stderr b/tests/ui/asm/sparc/bad-reg.sparc64.stderr index 4822eeab00c1..bdeb8c328db6 100644 --- a/tests/ui/asm/sparc/bad-reg.sparc64.stderr +++ b/tests/ui/asm/sparc/bad-reg.sparc64.stderr @@ -1,71 +1,71 @@ error: invalid register `g0`: g0 is always zero and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:23:18 + --> $DIR/bad-reg.rs:22:18 | LL | asm!("", out("g0") _); | ^^^^^^^^^^^ error: invalid register `g1`: reserved by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:25:18 | LL | asm!("", out("g1") _); | ^^^^^^^^^^^ error: invalid register `g6`: reserved for system and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("g6") _); | ^^^^^^^^^^^ error: invalid register `g7`: reserved for system and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("g7") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `i7`: the return address register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 + --> $DIR/bad-reg.rs:40:18 | LL | asm!("", out("i7") _); | ^^^^^^^^^^^ error: register class `yreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:47:18 + --> $DIR/bad-reg.rs:46:18 | LL | asm!("", in("y") x); | ^^^^^^^^^ error: register class `yreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:50:18 + --> $DIR/bad-reg.rs:49:18 | LL | asm!("", out("y") x); | ^^^^^^^^^^ error: register class `yreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:53:26 + --> $DIR/bad-reg.rs:52:26 | LL | asm!("/* {} */", in(yreg) x); | ^^^^^^^^^^ error: register class `yreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:56:26 + --> $DIR/bad-reg.rs:55:26 | LL | asm!("/* {} */", out(yreg) _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:47:26 + --> $DIR/bad-reg.rs:46:26 | LL | asm!("", in("y") x); | ^ @@ -73,7 +73,7 @@ LL | asm!("", in("y") x); = note: register class `yreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:50:27 + --> $DIR/bad-reg.rs:49:27 | LL | asm!("", out("y") x); | ^ @@ -81,7 +81,7 @@ LL | asm!("", out("y") x); = note: register class `yreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:53:35 + --> $DIR/bad-reg.rs:52:35 | LL | asm!("/* {} */", in(yreg) x); | ^ diff --git a/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr b/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr index 84e56871ecc2..e0580ad3232f 100644 --- a/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr +++ b/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr @@ -1,77 +1,77 @@ error: invalid register `g0`: g0 is always zero and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:23:18 + --> $DIR/bad-reg.rs:22:18 | LL | asm!("", out("g0") _); | ^^^^^^^^^^^ error: invalid register `g1`: reserved by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:25:18 | LL | asm!("", out("g1") _); | ^^^^^^^^^^^ error: invalid register `g6`: reserved for system and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("g6") _); | ^^^^^^^^^^^ error: invalid register `g7`: reserved for system and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("g7") _); | ^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("fp") _); | ^^^^^^^^^^^ error: invalid register `i7`: the return address register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 + --> $DIR/bad-reg.rs:40:18 | LL | asm!("", out("i7") _); | ^^^^^^^^^^^ error: register class `yreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:47:18 + --> $DIR/bad-reg.rs:46:18 | LL | asm!("", in("y") x); | ^^^^^^^^^ error: register class `yreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:50:18 + --> $DIR/bad-reg.rs:49:18 | LL | asm!("", out("y") x); | ^^^^^^^^^^ error: register class `yreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:53:26 + --> $DIR/bad-reg.rs:52:26 | LL | asm!("/* {} */", in(yreg) x); | ^^^^^^^^^^ error: register class `yreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:56:26 + --> $DIR/bad-reg.rs:55:26 | LL | asm!("/* {} */", out(yreg) _); | ^^^^^^^^^^^ error: cannot use register `r5`: g5 is reserved for system on SPARC32 - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("g5") _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:47:26 + --> $DIR/bad-reg.rs:46:26 | LL | asm!("", in("y") x); | ^ @@ -79,7 +79,7 @@ LL | asm!("", in("y") x); = note: register class `yreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:50:27 + --> $DIR/bad-reg.rs:49:27 | LL | asm!("", out("y") x); | ^ @@ -87,7 +87,7 @@ LL | asm!("", out("y") x); = note: register class `yreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:53:35 + --> $DIR/bad-reg.rs:52:35 | LL | asm!("/* {} */", in(yreg) x); | ^ diff --git a/tests/ui/feature-gates/feature-gate-asm_experimental_reg.rs b/tests/ui/feature-gates/feature-gate-asm_experimental_reg.rs index ecb01e816ec1..15d6d4731c8f 100644 --- a/tests/ui/feature-gates/feature-gate-asm_experimental_reg.rs +++ b/tests/ui/feature-gates/feature-gate-asm_experimental_reg.rs @@ -1,5 +1,4 @@ //@ add-core-stubs -//@ needs-asm-support //@ compile-flags: --target s390x-unknown-linux-gnu //@ needs-llvm-components: systemz //@ ignore-backends: gcc diff --git a/tests/ui/feature-gates/feature-gate-asm_experimental_reg.stderr b/tests/ui/feature-gates/feature-gate-asm_experimental_reg.stderr index 6bdb4213f319..6f2ea0ddaec1 100644 --- a/tests/ui/feature-gates/feature-gate-asm_experimental_reg.stderr +++ b/tests/ui/feature-gates/feature-gate-asm_experimental_reg.stderr @@ -1,5 +1,5 @@ error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/feature-gate-asm_experimental_reg.rs:15:14 + --> $DIR/feature-gate-asm_experimental_reg.rs:14:14 | LL | asm!("", in("v0") 0); | ^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | asm!("", in("v0") 0); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `i32` cannot be used with this register class in stable - --> $DIR/feature-gate-asm_experimental_reg.rs:15:23 + --> $DIR/feature-gate-asm_experimental_reg.rs:14:23 | LL | asm!("", in("v0") 0); | ^ From 468150578ff2e9f9eb9d6a7cadd16f76c98b8285 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 16 Oct 2025 10:58:31 -0500 Subject: [PATCH 031/170] refactor(parser): Push higher level content --- src/tools/rust-analyzer/crates/parser/src/lexed_str.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index edc3f406a67e..c2d26b5dacf2 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -38,8 +38,7 @@ impl<'a> LexedStr<'a> { let _p = tracing::info_span!("LexedStr::new").entered(); let mut conv = Converter::new(edition, text); if let Some(shebang_len) = rustc_lexer::strip_shebang(text) { - conv.res.push(SHEBANG, conv.offset); - conv.offset = shebang_len; + conv.push(SHEBANG, shebang_len, Vec::new()); }; // Re-create the tokenizer from scratch every token because `GuardedStrPrefix` is one token in the lexer From 223e141085c6e866752763fe54809cb9bdd40aa0 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 16 Oct 2025 11:06:41 -0500 Subject: [PATCH 032/170] test(parser): Show current frontmatter behavior --- .../test_data/lexer/ok/frontmatter.rast | 29 ++++++++++++++++++ .../parser/test_data/lexer/ok/frontmatter.rs | 8 +++++ .../lexer/ok/shebang_frontmatter.rast | 30 +++++++++++++++++++ .../test_data/lexer/ok/shebang_frontmatter.rs | 9 ++++++ 4 files changed, 76 insertions(+) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/frontmatter.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/frontmatter.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/shebang_frontmatter.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/shebang_frontmatter.rs diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/frontmatter.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/frontmatter.rast new file mode 100644 index 000000000000..94fbd3ebefe6 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/frontmatter.rast @@ -0,0 +1,29 @@ +WHITESPACE "\n" +MINUS "-" +MINUS "-" +MINUS "-" +WHITESPACE "\n" +L_BRACK "[" +IDENT "dependencies" +R_BRACK "]" +WHITESPACE "\n" +IDENT "clap" +WHITESPACE " " +EQ "=" +WHITESPACE " " +STRING "\"4\"" +WHITESPACE "\n" +MINUS "-" +MINUS "-" +MINUS "-" +WHITESPACE "\n\n" +FN_KW "fn" +WHITESPACE " " +IDENT "main" +L_PAREN "(" +R_PAREN ")" +WHITESPACE " " +L_CURLY "{" +WHITESPACE "\n" +R_CURLY "}" +WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/frontmatter.rs b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/frontmatter.rs new file mode 100644 index 000000000000..be7bf74fdba2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/frontmatter.rs @@ -0,0 +1,8 @@ + +--- +[dependencies] +clap = "4" +--- + +fn main() { +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/shebang_frontmatter.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/shebang_frontmatter.rast new file mode 100644 index 000000000000..8b1344a1b830 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/shebang_frontmatter.rast @@ -0,0 +1,30 @@ +SHEBANG "#!/usr/bin/env cargo" +WHITESPACE "\n\n" +MINUS "-" +MINUS "-" +MINUS "-" +WHITESPACE "\n" +L_BRACK "[" +IDENT "dependencies" +R_BRACK "]" +WHITESPACE "\n" +IDENT "clap" +WHITESPACE " " +EQ "=" +WHITESPACE " " +STRING "\"4\"" +WHITESPACE "\n" +MINUS "-" +MINUS "-" +MINUS "-" +WHITESPACE "\n\n" +FN_KW "fn" +WHITESPACE " " +IDENT "main" +L_PAREN "(" +R_PAREN ")" +WHITESPACE " " +L_CURLY "{" +WHITESPACE "\n" +R_CURLY "}" +WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/shebang_frontmatter.rs b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/shebang_frontmatter.rs new file mode 100644 index 000000000000..090b7713feb3 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/shebang_frontmatter.rs @@ -0,0 +1,9 @@ +#!/usr/bin/env cargo + +--- +[dependencies] +clap = "4" +--- + +fn main() { +} From 77d9b8ec15e6a5c7cd80af2c4954cd8e2f3b890d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 16 Oct 2025 11:01:12 -0500 Subject: [PATCH 033/170] feat(parser): Don't error on frontmatter --- src/tools/rust-analyzer/Cargo.lock | 1 + .../rust-analyzer/crates/parser/Cargo.toml | 1 + .../crates/parser/src/frontmatter.rs | 348 ++++++++++++++++++ .../crates/parser/src/lexed_str.rs | 12 +- .../rust-analyzer/crates/parser/src/lib.rs | 1 + .../test_data/lexer/ok/frontmatter.rast | 19 +- .../lexer/ok/shebang_frontmatter.rast | 21 +- .../lexer/ok/single_line_comments.rast | 3 +- .../parser/err/0002_duplicate_shebang.rast | 3 +- src/tools/rust-analyzer/xtask/src/tidy.rs | 2 +- 10 files changed, 367 insertions(+), 44 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/src/frontmatter.rs diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 55e5bdc138d9..027d38df2cdf 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1579,6 +1579,7 @@ dependencies = [ "rustc-literal-escaper 0.0.4", "stdx", "tracing", + "winnow", ] [[package]] diff --git a/src/tools/rust-analyzer/crates/parser/Cargo.toml b/src/tools/rust-analyzer/crates/parser/Cargo.toml index c7da654de6d9..8384d5bec21a 100644 --- a/src/tools/rust-analyzer/crates/parser/Cargo.toml +++ b/src/tools/rust-analyzer/crates/parser/Cargo.toml @@ -19,6 +19,7 @@ rustc-literal-escaper.workspace = true tracing = { workspace = true, optional = true } edition.workspace = true +winnow = { version = "0.7.13", default-features = false } [dev-dependencies] expect-test = "1.5.1" diff --git a/src/tools/rust-analyzer/crates/parser/src/frontmatter.rs b/src/tools/rust-analyzer/crates/parser/src/frontmatter.rs new file mode 100644 index 000000000000..2747db4327c5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/frontmatter.rs @@ -0,0 +1,348 @@ +// Copied from https://github.com/rust-lang/cargo/blob/367fd9f213750cd40317803dd0a5a3ce3f0c676d/src/cargo/util/frontmatter.rs +#![expect(dead_code)] // avoid editing +#![expect(unreachable_pub)] // avoid editing +#![expect(clippy::useless_format)] // avoid editing + +type Span = std::ops::Range; + +#[derive(Debug)] +pub struct ScriptSource<'s> { + /// The full file + raw: &'s str, + /// The `#!/usr/bin/env cargo` line, if present + shebang: Option, + /// The code fence opener (`---`) + open: Option, + /// Trailing text after `ScriptSource::open` that identifies the meaning of + /// `ScriptSource::frontmatter` + info: Option, + /// The lines between `ScriptSource::open` and `ScriptSource::close` + frontmatter: Option, + /// The code fence closer (`---`) + close: Option, + /// All content after the frontmatter and shebang + content: Span, +} + +impl<'s> ScriptSource<'s> { + pub fn parse(raw: &'s str) -> Result { + use winnow::stream::FindSlice as _; + use winnow::stream::Location as _; + use winnow::stream::Offset as _; + use winnow::stream::Stream as _; + + let content_end = raw.len(); + let mut source = Self { + raw, + shebang: None, + open: None, + info: None, + frontmatter: None, + close: None, + content: 0..content_end, + }; + + let mut input = winnow::stream::LocatingSlice::new(raw); + + if let Some(shebang_end) = strip_shebang(input.as_ref()) { + let shebang_start = input.current_token_start(); + let _ = input.next_slice(shebang_end); + let shebang_end = input.current_token_start(); + source.shebang = Some(shebang_start..shebang_end); + source.content = shebang_end..content_end; + } + + // Whitespace may precede a frontmatter but must end with a newline + if let Some(nl_end) = strip_ws_lines(input.as_ref()) { + let _ = input.next_slice(nl_end); + } + + // Opens with a line that starts with 3 or more `-` followed by an optional identifier + const FENCE_CHAR: char = '-'; + let fence_length = input + .as_ref() + .char_indices() + .find_map(|(i, c)| (c != FENCE_CHAR).then_some(i)) + .unwrap_or_else(|| input.eof_offset()); + let open_start = input.current_token_start(); + let fence_pattern = input.next_slice(fence_length); + let open_end = input.current_token_start(); + match fence_length { + 0 => { + return Ok(source); + } + 1 | 2 => { + // either not a frontmatter or invalid frontmatter opening + return Err(FrontmatterError::new( + format!( + "found {fence_length} `{FENCE_CHAR}` in rust frontmatter, expected at least 3" + ), + raw.len()..raw.len(), + ).push_visible_span(open_start..open_end)); + } + _ => {} + } + source.open = Some(open_start..open_end); + let Some(info_nl) = input.find_slice("\n") else { + return Err(FrontmatterError::new( + format!("unclosed frontmatter; expected `{fence_pattern}`"), + raw.len()..raw.len(), + ) + .push_visible_span(open_start..open_end)); + }; + let info = input.next_slice(info_nl.start); + let info = info.strip_suffix('\r').unwrap_or(info); // already excludes `\n` + let info = info.trim_matches(is_horizontal_whitespace); + if !info.is_empty() { + let info_start = info.offset_from(&raw); + let info_end = info_start + info.len(); + source.info = Some(info_start..info_end); + } + + // Ends with a line that starts with a matching number of `-` only followed by whitespace + let nl_fence_pattern = format!("\n{fence_pattern}"); + let Some(frontmatter_nl) = input.find_slice(nl_fence_pattern.as_str()) else { + for len in (2..(nl_fence_pattern.len() - 1)).rev() { + let Some(frontmatter_nl) = input.find_slice(&nl_fence_pattern[0..len]) else { + continue; + }; + let _ = input.next_slice(frontmatter_nl.start + 1); + let close_start = input.current_token_start(); + let _ = input.next_slice(len); + let close_end = input.current_token_start(); + let fewer_dashes = fence_length - len; + return Err(FrontmatterError::new( + format!( + "closing code fence has {fewer_dashes} less `-` than the opening fence" + ), + close_start..close_end, + ) + .push_visible_span(open_start..open_end)); + } + return Err(FrontmatterError::new( + format!("unclosed frontmatter; expected `{fence_pattern}`"), + raw.len()..raw.len(), + ) + .push_visible_span(open_start..open_end)); + }; + let frontmatter_start = input.current_token_start() + 1; // skip nl from infostring + let _ = input.next_slice(frontmatter_nl.start + 1); + let frontmatter_end = input.current_token_start(); + source.frontmatter = Some(frontmatter_start..frontmatter_end); + let close_start = input.current_token_start(); + let _ = input.next_slice(fence_length); + let close_end = input.current_token_start(); + source.close = Some(close_start..close_end); + + let nl = input.find_slice("\n"); + let after_closing_fence = + input.next_slice(nl.map(|span| span.end).unwrap_or_else(|| input.eof_offset())); + let content_start = input.current_token_start(); + let extra_dashes = after_closing_fence.chars().take_while(|b| *b == FENCE_CHAR).count(); + if 0 < extra_dashes { + let extra_start = close_end; + let extra_end = extra_start + extra_dashes; + return Err(FrontmatterError::new( + format!("closing code fence has {extra_dashes} more `-` than the opening fence"), + extra_start..extra_end, + ) + .push_visible_span(open_start..open_end)); + } else { + let after_closing_fence = strip_newline(after_closing_fence); + let after_closing_fence = after_closing_fence.trim_matches(is_horizontal_whitespace); + if !after_closing_fence.is_empty() { + // extra characters beyond the original fence pattern + let after_start = after_closing_fence.offset_from(&raw); + let after_end = after_start + after_closing_fence.len(); + return Err(FrontmatterError::new( + format!("unexpected characters after frontmatter close"), + after_start..after_end, + ) + .push_visible_span(open_start..open_end)); + } + } + + source.content = content_start..content_end; + + if let Some(nl_end) = strip_ws_lines(input.as_ref()) { + let _ = input.next_slice(nl_end); + } + let fence_length = input + .as_ref() + .char_indices() + .find_map(|(i, c)| (c != FENCE_CHAR).then_some(i)) + .unwrap_or_else(|| input.eof_offset()); + if 0 < fence_length { + let fence_start = input.current_token_start(); + let fence_end = fence_start + fence_length; + return Err(FrontmatterError::new( + format!("only one frontmatter is supported"), + fence_start..fence_end, + ) + .push_visible_span(open_start..open_end) + .push_visible_span(close_start..close_end)); + } + + Ok(source) + } + + pub fn shebang(&self) -> Option<&'s str> { + self.shebang.clone().map(|span| &self.raw[span]) + } + + pub fn shebang_span(&self) -> Option { + self.shebang.clone() + } + + pub fn open_span(&self) -> Option { + self.open.clone() + } + + pub fn info(&self) -> Option<&'s str> { + self.info.clone().map(|span| &self.raw[span]) + } + + pub fn info_span(&self) -> Option { + self.info.clone() + } + + pub fn frontmatter(&self) -> Option<&'s str> { + self.frontmatter.clone().map(|span| &self.raw[span]) + } + + pub fn frontmatter_span(&self) -> Option { + self.frontmatter.clone() + } + + pub fn close_span(&self) -> Option { + self.close.clone() + } + + pub fn content(&self) -> &'s str { + &self.raw[self.content.clone()] + } + + pub fn content_span(&self) -> Span { + self.content.clone() + } +} + +/// Returns the index after the shebang line, if present +pub fn strip_shebang(input: &str) -> Option { + // See rust-lang/rust's compiler/rustc_lexer/src/lib.rs's `strip_shebang` + // Shebang must start with `#!` literally, without any preceding whitespace. + // For simplicity we consider any line starting with `#!` a shebang, + // regardless of restrictions put on shebangs by specific platforms. + if let Some(rest) = input.strip_prefix("#!") { + // Ok, this is a shebang but if the next non-whitespace token is `[`, + // then it may be valid Rust code, so consider it Rust code. + // + // NOTE: rustc considers line and block comments to be whitespace but to avoid + // any more awareness of Rust grammar, we are excluding it. + if !rest.trim_start().starts_with('[') { + // No other choice than to consider this a shebang. + let newline_end = input.find('\n').map(|pos| pos + 1).unwrap_or(input.len()); + return Some(newline_end); + } + } + None +} + +/// Returns the index after any lines with only whitespace, if present +pub fn strip_ws_lines(input: &str) -> Option { + let ws_end = input.find(|c| !is_whitespace(c)).unwrap_or(input.len()); + if ws_end == 0 { + return None; + } + + let nl_start = input[0..ws_end].rfind('\n')?; + let nl_end = nl_start + 1; + Some(nl_end) +} + +/// 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 { + // This is Pattern_White_Space. + // + // Note that this set is stable (ie, it doesn't change with different + // Unicode versions), so it's ok to just hard-code the values. + + matches!( + c, + // End-of-line characters + | '\u{000A}' // line feed (\n) + | '\u{000B}' // vertical tab + | '\u{000C}' // form feed + | '\u{000D}' // carriage return (\r) + | '\u{0085}' // next line (from latin1) + | '\u{2028}' // LINE SEPARATOR + | '\u{2029}' // PARAGRAPH SEPARATOR + + // `Default_Ignorable_Code_Point` characters + | '\u{200E}' // LEFT-TO-RIGHT MARK + | '\u{200F}' // RIGHT-TO-LEFT MARK + + // Horizontal space characters + | '\u{0009}' // tab (\t) + | '\u{0020}' // space + ) +} + +/// True if `c` is considered horizontal whitespace according to Rust language definition. +fn is_horizontal_whitespace(c: char) -> bool { + // This is Pattern_White_Space. + // + // Note that this set is stable (ie, it doesn't change with different + // Unicode versions), so it's ok to just hard-code the values. + + matches!( + c, + // Horizontal space characters + '\u{0009}' // tab (\t) + | '\u{0020}' // space + ) +} + +fn strip_newline(text: &str) -> &str { + text.strip_suffix("\r\n").or_else(|| text.strip_suffix('\n')).unwrap_or(text) +} + +#[derive(Debug)] +pub struct FrontmatterError { + message: String, + primary_span: Span, + visible_spans: Vec, +} + +impl FrontmatterError { + pub fn new(message: impl Into, span: Span) -> Self { + Self { message: message.into(), primary_span: span, visible_spans: Vec::new() } + } + + pub fn push_visible_span(mut self, span: Span) -> Self { + self.visible_spans.push(span); + self + } + + pub fn message(&self) -> &str { + self.message.as_str() + } + + pub fn primary_span(&self) -> Span { + self.primary_span.clone() + } + + pub fn visible_spans(&self) -> &[Span] { + &self.visible_spans + } +} + +impl std::fmt::Display for FrontmatterError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.message.fmt(fmt) + } +} + +impl std::error::Error for FrontmatterError {} diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index c2d26b5dacf2..7c78ba8faf5f 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -37,9 +37,17 @@ impl<'a> LexedStr<'a> { pub fn new(edition: Edition, text: &'a str) -> LexedStr<'a> { let _p = tracing::info_span!("LexedStr::new").entered(); let mut conv = Converter::new(edition, text); - if let Some(shebang_len) = rustc_lexer::strip_shebang(text) { + if let Ok(script) = crate::frontmatter::ScriptSource::parse(text) { + if let Some(shebang) = script.shebang_span() { + conv.push(SHEBANG, shebang.end - shebang.start, Vec::new()); + } + if script.frontmatter().is_some() { + conv.push(FRONTMATTER, script.content_span().start - conv.offset, Vec::new()); + } + } else if let Some(shebang_len) = rustc_lexer::strip_shebang(text) { + // Leave error reporting to `rustc_lexer` conv.push(SHEBANG, shebang_len, Vec::new()); - }; + } // Re-create the tokenizer from scratch every token because `GuardedStrPrefix` is one token in the lexer // but we want to split it to two in edition <2024. diff --git a/src/tools/rust-analyzer/crates/parser/src/lib.rs b/src/tools/rust-analyzer/crates/parser/src/lib.rs index 7963f00bb25c..53444ef52cff 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lib.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lib.rs @@ -26,6 +26,7 @@ extern crate ra_ap_rustc_lexer as rustc_lexer; extern crate rustc_lexer; mod event; +mod frontmatter; mod grammar; mod input; mod lexed_str; diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/frontmatter.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/frontmatter.rast index 94fbd3ebefe6..2c7d3cdb1227 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/frontmatter.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/frontmatter.rast @@ -1,22 +1,5 @@ +FRONTMATTER "\n---\n[dependencies]\nclap = \"4\"\n---\n" WHITESPACE "\n" -MINUS "-" -MINUS "-" -MINUS "-" -WHITESPACE "\n" -L_BRACK "[" -IDENT "dependencies" -R_BRACK "]" -WHITESPACE "\n" -IDENT "clap" -WHITESPACE " " -EQ "=" -WHITESPACE " " -STRING "\"4\"" -WHITESPACE "\n" -MINUS "-" -MINUS "-" -MINUS "-" -WHITESPACE "\n\n" FN_KW "fn" WHITESPACE " " IDENT "main" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/shebang_frontmatter.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/shebang_frontmatter.rast index 8b1344a1b830..fb4787f4001f 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/shebang_frontmatter.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/shebang_frontmatter.rast @@ -1,23 +1,6 @@ -SHEBANG "#!/usr/bin/env cargo" -WHITESPACE "\n\n" -MINUS "-" -MINUS "-" -MINUS "-" +SHEBANG "#!/usr/bin/env cargo\n" +FRONTMATTER "\n---\n[dependencies]\nclap = \"4\"\n---\n" WHITESPACE "\n" -L_BRACK "[" -IDENT "dependencies" -R_BRACK "]" -WHITESPACE "\n" -IDENT "clap" -WHITESPACE " " -EQ "=" -WHITESPACE " " -STRING "\"4\"" -WHITESPACE "\n" -MINUS "-" -MINUS "-" -MINUS "-" -WHITESPACE "\n\n" FN_KW "fn" WHITESPACE " " IDENT "main" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/single_line_comments.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/single_line_comments.rast index a7681e9f5086..c4e531b449f7 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/single_line_comments.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/single_line_comments.rast @@ -1,5 +1,4 @@ -SHEBANG "#!/usr/bin/env bash" -WHITESPACE "\n" +SHEBANG "#!/usr/bin/env bash\n" COMMENT "// hello" WHITESPACE "\n" COMMENT "//! World" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast index 3159a15a3b1c..7ee1ecfbb159 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast @@ -1,6 +1,5 @@ SOURCE_FILE - SHEBANG "#!/use/bin/env rusti" - WHITESPACE "\n" + SHEBANG "#!/use/bin/env rusti\n" ATTR POUND "#" BANG "!" diff --git a/src/tools/rust-analyzer/xtask/src/tidy.rs b/src/tools/rust-analyzer/xtask/src/tidy.rs index 0462835f0675..40997eb93d35 100644 --- a/src/tools/rust-analyzer/xtask/src/tidy.rs +++ b/src/tools/rust-analyzer/xtask/src/tidy.rs @@ -259,7 +259,7 @@ impl TidyDocs { } fn is_exclude_file(d: &Path) -> bool { - let file_names = ["tests.rs", "famous_defs_fixture.rs"]; + let file_names = ["tests.rs", "famous_defs_fixture.rs", "frontmatter.rs"]; d.file_name() .unwrap_or_default() From db6734e22f2b4d7370ca6d588bb4b097abd3cfec Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 16 Oct 2025 19:34:14 +0300 Subject: [PATCH 034/170] Improve fixture support Support more features beside highlighting, and support items from minicore. --- src/tools/rust-analyzer/Cargo.lock | 4 + .../crates/ide-completion/Cargo.toml | 1 + .../crates/ide-completion/src/completions.rs | 5 + .../src/completions/ra_fixture.rs | 113 ++++ .../crates/ide-completion/src/config.rs | 5 +- .../crates/ide-completion/src/context.rs | 3 + .../ide-completion/src/context/tests.rs | 2 +- .../crates/ide-completion/src/item.rs | 3 +- .../crates/ide-completion/src/lib.rs | 3 +- .../crates/ide-completion/src/tests.rs | 3 +- .../ide-completion/src/tests/flyimport.rs | 3 +- .../rust-analyzer/crates/ide-db/Cargo.toml | 9 +- .../rust-analyzer/crates/ide-db/src/lib.rs | 26 + .../crates/ide-db/src/ra_fixture.rs | 532 ++++++++++++++++++ .../crates/ide-db/src/range_mapper.rs | 65 +++ .../crates/ide-db/src/source_change.rs | 3 +- .../crates/ide-db/src/text_edit.rs | 5 +- .../crates/ide-diagnostics/src/tests.rs | 6 +- src/tools/rust-analyzer/crates/ide/Cargo.toml | 1 + .../crates/ide/src/annotations.rs | 30 +- .../crates/ide/src/call_hierarchy.rs | 39 +- .../crates/ide/src/goto_declaration.rs | 20 +- .../crates/ide/src/goto_definition.rs | 146 +++-- .../rust-analyzer/crates/ide/src/hover.rs | 61 +- .../crates/ide/src/hover/render.rs | 22 +- .../crates/ide/src/hover/tests.rs | 5 +- .../crates/ide/src/inlay_hints.rs | 48 +- .../crates/ide/src/inlay_hints/adjustment.rs | 2 +- .../crates/ide/src/inlay_hints/bind_pat.rs | 2 +- .../ide/src/inlay_hints/binding_mode.rs | 2 +- .../crates/ide/src/inlay_hints/bounds.rs | 2 +- .../crates/ide/src/inlay_hints/chaining.rs | 4 +- .../ide/src/inlay_hints/closing_brace.rs | 2 +- .../ide/src/inlay_hints/closure_captures.rs | 2 +- .../crates/ide/src/inlay_hints/closure_ret.rs | 2 +- .../ide/src/inlay_hints/discriminant.rs | 4 +- .../ide/src/inlay_hints/extern_block.rs | 8 +- .../ide/src/inlay_hints/generic_param.rs | 2 +- .../ide/src/inlay_hints/implicit_drop.rs | 4 +- .../ide/src/inlay_hints/implicit_static.rs | 2 +- .../ide/src/inlay_hints/implied_dyn_trait.rs | 2 +- .../crates/ide/src/inlay_hints/lifetime.rs | 8 +- .../crates/ide/src/inlay_hints/param_name.rs | 2 +- .../crates/ide/src/inlay_hints/ra_fixture.rs | 32 ++ .../ide/src/inlay_hints/range_exclusive.rs | 2 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 109 ++-- .../rust-analyzer/crates/ide/src/markup.rs | 4 + .../crates/ide/src/navigation_target.rs | 39 ++ .../crates/ide/src/references.rs | 48 +- .../rust-analyzer/crates/ide/src/runnables.rs | 6 +- .../crates/ide/src/static_index.rs | 4 +- .../crates/ide/src/syntax_highlighting.rs | 25 +- .../ide/src/syntax_highlighting/html.rs | 6 +- .../ide/src/syntax_highlighting/inject.rs | 164 +++--- .../ide/src/syntax_highlighting/injector.rs | 77 --- .../test_data/highlight_injection.html | 21 +- .../test_data/highlight_injection_2.html | 61 ++ .../ide/src/syntax_highlighting/tests.rs | 39 +- .../rust-analyzer/crates/macros/src/lib.rs | 39 ++ .../rust-analyzer/src/cli/analysis_stats.rs | 28 +- .../crates/rust-analyzer/src/config.rs | 53 +- .../crates/rust-analyzer/src/global_state.rs | 25 +- .../rust-analyzer/src/handlers/request.rs | 68 ++- .../src/integrated_benchmarks.rs | 5 +- .../crates/rust-analyzer/src/lsp/to_proto.rs | 27 +- .../crates/rust-analyzer/src/main_loop.rs | 7 + .../crates/syntax/src/ast/token_ext.rs | 4 + .../crates/test-fixture/src/lib.rs | 26 +- .../crates/test-utils/src/fixture.rs | 32 +- 69 files changed, 1652 insertions(+), 512 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-completion/src/completions/ra_fixture.rs create mode 100644 src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs create mode 100644 src/tools/rust-analyzer/crates/ide-db/src/range_mapper.rs create mode 100644 src/tools/rust-analyzer/crates/ide/src/inlay_hints/ra_fixture.rs delete mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/injector.rs create mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 55e5bdc138d9..539f8cf1b933 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -930,6 +930,7 @@ dependencies = [ "ide-diagnostics", "ide-ssr", "itertools", + "macros", "nohash-hasher", "oorandom", "profile", @@ -976,6 +977,7 @@ dependencies = [ "hir", "ide-db", "itertools", + "macros", "smallvec", "stdx", "syntax", @@ -1000,6 +1002,7 @@ dependencies = [ "indexmap", "itertools", "line-index 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "macros", "memchr", "nohash-hasher", "parser", @@ -1009,6 +1012,7 @@ dependencies = [ "rustc-hash 2.1.1", "salsa", "salsa-macros", + "smallvec", "span", "stdx", "syntax", diff --git a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml index 9bad21fc8e90..277d5dfa495c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml @@ -28,6 +28,7 @@ syntax.workspace = true # completions crate should depend only on the top-level `hir` package. if you need # something from some `hir-xxx` subpackage, reexport the API via `hir`. hir.workspace = true +macros.workspace = true [dev-dependencies] expect-test = "1.5.1" 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 b822f53d7b7b..ed58e862d437 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -16,6 +16,7 @@ pub(crate) mod lifetime; pub(crate) mod mod_; pub(crate) mod pattern; pub(crate) mod postfix; +pub(crate) mod ra_fixture; pub(crate) mod record; pub(crate) mod snippet; pub(crate) mod r#type; @@ -74,6 +75,10 @@ impl Completions { self.buf.push(item) } + fn add_many(&mut self, items: impl IntoIterator) { + self.buf.extend(items) + } + fn add_opt(&mut self, item: Option) { if let Some(item) = item { self.buf.push(item) 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 new file mode 100644 index 000000000000..b44c90757f68 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/ra_fixture.rs @@ -0,0 +1,113 @@ +//! Injected completions for `#[rust_analyzer::rust_fixture]`. + +use hir::FilePositionWrapper; +use ide_db::{ + impl_empty_upmap_from_ra_fixture, + ra_fixture::{RaFixtureAnalysis, UpmapFromRaFixture}, +}; +use syntax::ast; + +use crate::{ + CompletionItemKind, CompletionItemRefMode, CompletionRelevance, completions::Completions, + context::CompletionContext, item::CompletionItemLabel, +}; + +pub(crate) fn complete_ra_fixture( + acc: &mut Completions, + ctx: &CompletionContext<'_>, + original: &ast::String, + expanded: &ast::String, +) -> Option<()> { + let analysis = RaFixtureAnalysis::analyze_ra_fixture( + &ctx.sema, + original.clone(), + expanded, + ctx.config.minicore, + &mut |_| {}, + )?; + let (virtual_file_id, virtual_offset) = analysis.map_offset_down(ctx.position.offset)?; + let completions = hir::attach_db_allow_change(&analysis.db, || { + crate::completions( + &analysis.db, + ctx.config, + FilePositionWrapper { file_id: virtual_file_id, offset: virtual_offset }, + ctx.trigger_character, + ) + })?; + let completions = + completions.upmap_from_ra_fixture(&analysis, virtual_file_id, ctx.position.file_id).ok()?; + acc.add_many(completions); + Some(()) +} + +impl_empty_upmap_from_ra_fixture!( + CompletionItemLabel, + CompletionItemKind, + CompletionRelevance, + CompletionItemRefMode, +); + +#[cfg(test)] +mod tests { + use expect_test::expect; + + use crate::tests::check; + + #[test] + fn it_works() { + check( + r##" +fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {} + +fn foo() { + fixture(r#" +fn complete_me() {} + +fn baz() { + let foo_bar_baz = 123; + f$0 +} + "#); +} + "##, + expect![[r#" + fn baz() fn() + fn complete_me() fn() + lc foo_bar_baz i32 + 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-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index b7367cb62f09..5623257a2792 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -6,13 +6,13 @@ use hir::FindPathConfig; use ide_db::{ - SnippetCap, + MiniCore, SnippetCap, imports::{import_assets::ImportPathConfig, insert_use::InsertUseConfig}, }; use crate::{CompletionFieldsToResolve, snippet::Snippet}; -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug)] pub struct CompletionConfig<'a> { pub enable_postfix_completions: bool, pub enable_imports_on_the_fly: bool, @@ -35,6 +35,7 @@ pub struct CompletionConfig<'a> { pub fields_to_resolve: CompletionFieldsToResolve, pub exclude_flyimport: Vec<(String, AutoImportExclusionType)>, pub exclude_traits: &'a [String], + pub minicore: MiniCore<'a>, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 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 4032329ac658..fc2cc3b796ec 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -440,6 +440,7 @@ pub(crate) struct CompletionContext<'a> { pub(crate) config: &'a CompletionConfig<'a>, pub(crate) position: FilePosition, + pub(crate) trigger_character: Option, /// The token before the cursor, in the original file. pub(crate) original_token: SyntaxToken, /// The token before the cursor, in the macro-expanded file. @@ -703,6 +704,7 @@ impl<'db> CompletionContext<'db> { db: &'db RootDatabase, position @ FilePosition { file_id, offset }: FilePosition, config: &'db CompletionConfig<'db>, + trigger_character: Option, ) -> Option<(CompletionContext<'db>, CompletionAnalysis<'db>)> { let _p = tracing::info_span!("CompletionContext::new").entered(); let sema = Semantics::new(db); @@ -871,6 +873,7 @@ impl<'db> CompletionContext<'db> { db, config, position, + trigger_character, original_token, token, krate, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index e798f3b23af4..51d28bd4ff98 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -10,7 +10,7 @@ fn check_expected_type_and_name(#[rust_analyzer::rust_fixture] ra_fixture: &str, let (db, pos) = position(ra_fixture); let config = TEST_CONFIG; let (completion_context, _analysis) = - hir::attach_db(&db, || CompletionContext::new(&db, pos, &config).unwrap()); + hir::attach_db(&db, || CompletionContext::new(&db, pos, &config, None).unwrap()); let ty = completion_context .expected_type 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 5fb9dc93c93d..303c71230d60 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -9,6 +9,7 @@ use ide_db::{ imports::import_assets::LocatedImport, }; use itertools::Itertools; +use macros::UpmapFromRaFixture; use smallvec::SmallVec; use stdx::{format_to, impl_from, never}; use syntax::{Edition, SmolStr, TextRange, TextSize, format_smolstr}; @@ -23,7 +24,7 @@ use crate::{ /// /// It is basically a POD with various properties. To construct a [`CompletionItem`], /// use [`Builder::new`] method and the [`Builder`] struct. -#[derive(Clone)] +#[derive(Clone, UpmapFromRaFixture)] #[non_exhaustive] pub struct CompletionItem { /// Label in the completion pop up which identifies completion. 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 a70a1138d2f4..8a0aaf3f0cc2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -187,7 +187,7 @@ pub fn completions( position: FilePosition, trigger_character: Option, ) -> Option> { - let (ctx, analysis) = &CompletionContext::new(db, position, config)?; + let (ctx, analysis) = &CompletionContext::new(db, position, config, trigger_character)?; let mut completions = Completions::default(); // prevent `(` from triggering unwanted completion noise @@ -241,6 +241,7 @@ pub fn completions( completions::extern_abi::complete_extern_abi(acc, ctx, expanded); completions::format_string::format_string(acc, ctx, original, expanded); completions::env_vars::complete_cargo_env_vars(acc, ctx, original, expanded); + completions::ra_fixture::complete_ra_fixture(acc, ctx, original, expanded); } CompletionAnalysis::UnexpandedAttrTT { colon_prefix, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index ec9cd9fdf378..b32a89545726 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -29,7 +29,7 @@ use expect_test::Expect; use hir::db::HirDatabase; use hir::{PrefixKind, setup_tracing}; use ide_db::{ - FilePosition, RootDatabase, SnippetCap, + FilePosition, MiniCore, RootDatabase, SnippetCap, imports::insert_use::{ImportGranularity, InsertUseConfig}, }; use itertools::Itertools; @@ -90,6 +90,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig { exclude_traits: &[], enable_auto_await: true, enable_auto_iter: true, + minicore: MiniCore::default(), }; pub(crate) fn completion_list(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 2d3ebad9340c..0cd42089b487 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 @@ -16,7 +16,8 @@ fn check_with_config( expect: Expect, ) { let (db, position) = crate::tests::position(ra_fixture); - let (ctx, analysis) = crate::context::CompletionContext::new(&db, position, &config).unwrap(); + let (ctx, analysis) = + crate::context::CompletionContext::new(&db, position, &config, None).unwrap(); let mut acc = crate::completions::Completions::default(); hir::attach_db(ctx.db, || { diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index e065adb0f0ba..b7148160182c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml @@ -30,6 +30,7 @@ query-group.workspace = true triomphe.workspace = true nohash-hasher.workspace = true bitflags.workspace = true +smallvec.workspace = true # local deps base-db.workspace = true @@ -42,15 +43,15 @@ vfs.workspace = true # ide should depend only on the top-level `hir` package. if you need # something from some `hir-xxx` subpackage, reexport the API via `hir`. hir.workspace = true +macros.workspace = true + +test-utils.workspace = true +test-fixture.workspace = true line-index.workspace = true [dev-dependencies] expect-test = "1.5.1" -# local deps -test-utils.workspace = true -test-fixture.workspace = true - [lints] workspace = true 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 44bccd86d870..7efa97be5573 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -2,6 +2,8 @@ //! //! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. +extern crate self as ide_db; + mod apply_change; pub mod active_parameter; @@ -14,6 +16,8 @@ pub mod items_locator; pub mod label; pub mod path_transform; pub mod prime_caches; +pub mod ra_fixture; +pub mod range_mapper; pub mod rename; pub mod rust_doc; pub mod search; @@ -364,3 +368,25 @@ pub enum Severity { WeakWarning, Allow, } + +#[derive(Debug, Clone, Copy)] +pub struct MiniCore<'a>(&'a str); + +impl<'a> MiniCore<'a> { + #[inline] + pub fn new(minicore: &'a str) -> Self { + Self(minicore) + } + + #[inline] + pub const fn default() -> Self { + Self(test_utils::MiniCore::RAW_SOURCE) + } +} + +impl<'a> Default for MiniCore<'a> { + #[inline] + fn default() -> Self { + Self::default() + } +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs b/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs new file mode 100644 index 000000000000..1f056a835bc6 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs @@ -0,0 +1,532 @@ +//! Working with the fixtures in r-a tests, and providing IDE services for them. + +use std::hash::{BuildHasher, Hash}; + +use hir::{CfgExpr, FilePositionWrapper, FileRangeWrapper, Semantics}; +use smallvec::SmallVec; +use span::{TextRange, TextSize}; +use syntax::{ + AstToken, SmolStr, + ast::{self, IsString}, +}; + +use crate::{ + MiniCore, RootDatabase, SymbolKind, active_parameter::ActiveParameter, + documentation::Documentation, range_mapper::RangeMapper, search::ReferenceCategory, +}; + +pub use span::FileId; + +impl RootDatabase { + fn from_ra_fixture( + text: &str, + minicore: MiniCore<'_>, + ) -> Result<(RootDatabase, Vec<(FileId, usize)>, Vec), ()> { + // We don't want a mistake in the fixture to crash r-a, so we wrap this in `catch_unwind()`. + std::panic::catch_unwind(|| { + let mut db = RootDatabase::default(); + let fixture = test_fixture::ChangeFixture::parse_with_proc_macros( + &db, + text, + minicore.0, + Vec::new(), + ); + db.apply_change(fixture.change); + let files = fixture + .files + .into_iter() + .zip(fixture.file_lines) + .map(|(file_id, range)| (file_id.file_id(&db), range)) + .collect(); + (db, files, fixture.sysroot_files) + }) + .map_err(|error| { + tracing::error!( + "cannot crate the crate graph: {}\nCrate graph:\n{}\n", + if let Some(&s) = error.downcast_ref::<&'static str>() { + s + } else if let Some(s) = error.downcast_ref::() { + s.as_str() + } else { + "Box" + }, + text, + ); + }) + } +} + +pub struct RaFixtureAnalysis { + pub db: RootDatabase, + tmp_file_ids: Vec<(FileId, usize)>, + line_offsets: Vec, + virtual_file_id_to_line: Vec, + mapper: RangeMapper, + literal: ast::String, + // `minicore` etc.. + sysroot_files: Vec, + combined_len: TextSize, +} + +impl RaFixtureAnalysis { + pub fn analyze_ra_fixture( + sema: &Semantics<'_, RootDatabase>, + literal: ast::String, + expanded: &ast::String, + minicore: MiniCore<'_>, + on_cursor: &mut dyn FnMut(TextRange), + ) -> Option { + if !literal.is_raw() { + return None; + } + + let active_parameter = ActiveParameter::at_token(sema, expanded.syntax().clone())?; + let has_rust_fixture_attr = active_parameter.attrs().is_some_and(|attrs| { + attrs.filter_map(|attr| attr.as_simple_path()).any(|path| { + path.segments() + .zip(["rust_analyzer", "rust_fixture"]) + .all(|(seg, name)| seg.name_ref().map_or(false, |nr| nr.text() == name)) + }) + }); + if !has_rust_fixture_attr { + return None; + } + let value = literal.value().ok()?; + + let mut mapper = RangeMapper::default(); + + // This is used for the `Injector`, to resolve precise location in the string literal, + // which will then be used to resolve precise location in the enclosing file. + let mut offset_with_indent = TextSize::new(0); + // This is used to resolve the location relative to the virtual file into a location + // relative to the indentation-trimmed file which will then (by the `Injector`) used + // to resolve to a location in the actual file. + // Besides indentation, we also skip `$0` cursors for this, since they are not included + // in the virtual files. + let mut offset_without_indent = TextSize::new(0); + + let mut text = &*value; + if let Some(t) = text.strip_prefix('\n') { + offset_with_indent += TextSize::of("\n"); + text = t; + } + // This stores the offsets of each line, **after we remove indentation**. + let mut line_offsets = Vec::new(); + for mut line in text.split_inclusive('\n') { + line_offsets.push(offset_without_indent); + + if line.starts_with("@@") { + // Introducing `//` into a fixture inside fixture causes all sorts of problems, + // so for testing purposes we escape it as `@@` and replace it here. + mapper.add("//", TextRange::at(offset_with_indent, TextSize::of("@@"))); + line = &line["@@".len()..]; + offset_with_indent += TextSize::of("@@"); + offset_without_indent += TextSize::of("@@"); + } + + // Remove indentation to simplify the mapping with fixture (which de-indents). + // Removing indentation shouldn't affect highlighting. + let mut unindented_line = line.trim_start(); + if unindented_line.is_empty() { + // The whole line was whitespaces, but we need the newline. + unindented_line = "\n"; + } + offset_with_indent += TextSize::of(line) - TextSize::of(unindented_line); + + let marker = "$0"; + match unindented_line.find(marker) { + Some(marker_pos) => { + let (before_marker, after_marker) = unindented_line.split_at(marker_pos); + let after_marker = &after_marker[marker.len()..]; + + mapper.add( + before_marker, + TextRange::at(offset_with_indent, TextSize::of(before_marker)), + ); + offset_with_indent += TextSize::of(before_marker); + offset_without_indent += TextSize::of(before_marker); + + if let Some(marker_range) = literal + .map_range_up(TextRange::at(offset_with_indent, TextSize::of(marker))) + { + on_cursor(marker_range); + } + offset_with_indent += TextSize::of(marker); + + mapper.add( + after_marker, + TextRange::at(offset_with_indent, TextSize::of(after_marker)), + ); + offset_with_indent += TextSize::of(after_marker); + offset_without_indent += TextSize::of(after_marker); + } + None => { + mapper.add( + unindented_line, + TextRange::at(offset_with_indent, TextSize::of(unindented_line)), + ); + offset_with_indent += TextSize::of(unindented_line); + offset_without_indent += TextSize::of(unindented_line); + } + } + } + + let combined = mapper.take_text(); + let combined_len = TextSize::of(&combined); + let (analysis, tmp_file_ids, sysroot_files) = + RootDatabase::from_ra_fixture(&combined, minicore).ok()?; + + // We use a `Vec` because we know the `FileId`s will always be close. + let mut virtual_file_id_to_line = Vec::new(); + for &(file_id, line) in &tmp_file_ids { + virtual_file_id_to_line.resize(file_id.index() as usize + 1, usize::MAX); + virtual_file_id_to_line[file_id.index() as usize] = line; + } + + Some(RaFixtureAnalysis { + db: analysis, + tmp_file_ids, + line_offsets, + virtual_file_id_to_line, + mapper, + literal, + sysroot_files, + combined_len, + }) + } + + pub fn files(&self) -> impl Iterator { + self.tmp_file_ids.iter().map(|(file, _)| *file) + } + + /// This returns `None` for minicore or other sysroot files. + fn virtual_file_id_to_line(&self, file_id: FileId) -> Option { + if self.is_sysroot_file(file_id) { + None + } else { + Some(self.virtual_file_id_to_line[file_id.index() as usize]) + } + } + + pub fn map_offset_down(&self, offset: TextSize) -> Option<(FileId, TextSize)> { + let inside_literal_range = self.literal.map_offset_down(offset)?; + let combined_offset = self.mapper.map_offset_down(inside_literal_range)?; + // There is usually a small number of files, so a linear search is smaller and faster. + let (_, &(file_id, file_line)) = + self.tmp_file_ids.iter().enumerate().find(|&(idx, &(_, file_line))| { + let file_start = self.line_offsets[file_line]; + let file_end = self + .tmp_file_ids + .get(idx + 1) + .map(|&(_, next_file_line)| self.line_offsets[next_file_line]) + .unwrap_or_else(|| self.combined_len); + TextRange::new(file_start, file_end).contains(combined_offset) + })?; + let file_line_offset = self.line_offsets[file_line]; + let file_offset = combined_offset - file_line_offset; + Some((file_id, file_offset)) + } + + pub fn map_range_down(&self, range: TextRange) -> Option<(FileId, TextRange)> { + let (start_file_id, start_offset) = self.map_offset_down(range.start())?; + let (end_file_id, end_offset) = self.map_offset_down(range.end())?; + if start_file_id != end_file_id { + None + } else { + Some((start_file_id, TextRange::new(start_offset, end_offset))) + } + } + + pub fn map_range_up( + &self, + virtual_file: FileId, + range: TextRange, + ) -> impl Iterator { + // This could be `None` if the file is empty. + self.virtual_file_id_to_line(virtual_file) + .and_then(|line| self.line_offsets.get(line)) + .into_iter() + .flat_map(move |&tmp_file_offset| { + // Resolve the offset relative to the virtual file to an offset relative to the combined indentation-trimmed file + let range = range + tmp_file_offset; + // Then resolve that to an offset relative to the real file. + self.mapper.map_range_up(range) + }) + // And finally resolve the offset relative to the literal to relative to the file. + .filter_map(|range| self.literal.map_range_up(range)) + } + + pub fn map_offset_up(&self, virtual_file: FileId, offset: TextSize) -> Option { + self.map_range_up(virtual_file, TextRange::empty(offset)).next().map(|range| range.start()) + } + + pub fn is_sysroot_file(&self, file_id: FileId) -> bool { + self.sysroot_files.contains(&file_id) + } +} + +pub trait UpmapFromRaFixture: Sized { + fn upmap_from_ra_fixture( + self, + analysis: &RaFixtureAnalysis, + virtual_file_id: FileId, + real_file_id: FileId, + ) -> Result; +} + +trait IsEmpty { + fn is_empty(&self) -> bool; +} + +impl IsEmpty for Vec { + fn is_empty(&self) -> bool { + self.is_empty() + } +} + +impl IsEmpty for SmallVec<[T; N]> { + fn is_empty(&self) -> bool { + self.is_empty() + } +} + +#[allow(clippy::disallowed_types)] +impl IsEmpty for std::collections::HashMap { + fn is_empty(&self) -> bool { + self.is_empty() + } +} + +fn upmap_collection( + collection: Collection, + analysis: &RaFixtureAnalysis, + virtual_file_id: FileId, + real_file_id: FileId, +) -> Result +where + T: UpmapFromRaFixture, + Collection: IntoIterator + FromIterator + IsEmpty, +{ + if collection.is_empty() { + // The collection was already empty, don't mark it as failing just because of that. + return Ok(collection); + } + let result = collection + .into_iter() + .filter_map(|item| item.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id).ok()) + .collect::(); + if result.is_empty() { + // The collection was emptied by the upmapping - all items errored, therefore mark it as erroring as well. + Err(()) + } else { + Ok(result) + } +} + +impl UpmapFromRaFixture for Option { + fn upmap_from_ra_fixture( + self, + analysis: &RaFixtureAnalysis, + virtual_file_id: FileId, + real_file_id: FileId, + ) -> Result { + Ok(match self { + Some(it) => Some(it.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?), + None => None, + }) + } +} + +impl UpmapFromRaFixture for Vec { + fn upmap_from_ra_fixture( + self, + analysis: &RaFixtureAnalysis, + virtual_file_id: FileId, + real_file_id: FileId, + ) -> Result { + upmap_collection(self, analysis, virtual_file_id, real_file_id) + } +} + +impl UpmapFromRaFixture for SmallVec<[T; N]> { + fn upmap_from_ra_fixture( + self, + analysis: &RaFixtureAnalysis, + virtual_file_id: FileId, + real_file_id: FileId, + ) -> Result { + upmap_collection(self, analysis, virtual_file_id, real_file_id) + } +} + +#[allow(clippy::disallowed_types)] +impl + UpmapFromRaFixture for std::collections::HashMap +{ + fn upmap_from_ra_fixture( + self, + analysis: &RaFixtureAnalysis, + virtual_file_id: FileId, + real_file_id: FileId, + ) -> Result { + upmap_collection(self, analysis, virtual_file_id, real_file_id) + } +} + +// A map of `FileId`s is treated as associating the ranges in the values with the keys. +#[allow(clippy::disallowed_types)] +impl UpmapFromRaFixture + for std::collections::HashMap +{ + fn upmap_from_ra_fixture( + self, + analysis: &RaFixtureAnalysis, + _virtual_file_id: FileId, + real_file_id: FileId, + ) -> Result { + if self.is_empty() { + return Ok(self); + } + let result = self + .into_iter() + .filter_map(|(virtual_file_id, value)| { + Some(( + real_file_id, + value.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id).ok()?, + )) + }) + .collect::>(); + if result.is_empty() { Err(()) } else { Ok(result) } + } +} + +macro_rules! impl_tuple { + () => {}; // Base case. + ( $first:ident, $( $rest:ident, )* ) => { + impl< + $first: UpmapFromRaFixture, + $( $rest: UpmapFromRaFixture, )* + > UpmapFromRaFixture for ( $first, $( $rest, )* ) { + fn upmap_from_ra_fixture( + self, + analysis: &RaFixtureAnalysis, + virtual_file_id: FileId, + real_file_id: FileId, + ) -> Result { + #[allow(non_snake_case)] + let ( $first, $($rest,)* ) = self; + Ok(( + $first.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?, + $( $rest.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?, )* + )) + } + } + + impl_tuple!( $($rest,)* ); + }; +} +impl_tuple!(A, B, C, D, E,); + +impl UpmapFromRaFixture for TextSize { + fn upmap_from_ra_fixture( + self, + analysis: &RaFixtureAnalysis, + virtual_file_id: FileId, + _real_file_id: FileId, + ) -> Result { + analysis.map_offset_up(virtual_file_id, self).ok_or(()) + } +} + +impl UpmapFromRaFixture for TextRange { + fn upmap_from_ra_fixture( + self, + analysis: &RaFixtureAnalysis, + virtual_file_id: FileId, + _real_file_id: FileId, + ) -> Result { + analysis.map_range_up(virtual_file_id, self).next().ok_or(()) + } +} + +// Deliberately do not implement that, as it's easy to get things misbehave and be treated with the wrong FileId: +// +// impl UpmapFromRaFixture for FileId { +// fn upmap_from_ra_fixture( +// self, +// _analysis: &RaFixtureAnalysis, +// _virtual_file_id: FileId, +// real_file_id: FileId, +// ) -> Result { +// Ok(real_file_id) +// } +// } + +impl UpmapFromRaFixture for FilePositionWrapper { + fn upmap_from_ra_fixture( + self, + analysis: &RaFixtureAnalysis, + _virtual_file_id: FileId, + real_file_id: FileId, + ) -> Result { + Ok(FilePositionWrapper { + file_id: real_file_id, + offset: self.offset.upmap_from_ra_fixture(analysis, self.file_id, real_file_id)?, + }) + } +} + +impl UpmapFromRaFixture for FileRangeWrapper { + fn upmap_from_ra_fixture( + self, + analysis: &RaFixtureAnalysis, + _virtual_file_id: FileId, + real_file_id: FileId, + ) -> Result { + Ok(FileRangeWrapper { + file_id: real_file_id, + range: self.range.upmap_from_ra_fixture(analysis, self.file_id, real_file_id)?, + }) + } +} + +#[macro_export] +macro_rules! impl_empty_upmap_from_ra_fixture { + ( $( $ty:ty ),* $(,)? ) => { + $( + impl $crate::ra_fixture::UpmapFromRaFixture for $ty { + fn upmap_from_ra_fixture( + self, + _analysis: &$crate::ra_fixture::RaFixtureAnalysis, + _virtual_file_id: $crate::ra_fixture::FileId, + _real_file_id: $crate::ra_fixture::FileId, + ) -> Result { + Ok(self) + } + } + )* + }; +} + +impl_empty_upmap_from_ra_fixture!( + bool, + i8, + i16, + i32, + i64, + i128, + u8, + u16, + u32, + u64, + u128, + f32, + f64, + &str, + String, + SmolStr, + Documentation, + SymbolKind, + CfgExpr, + ReferenceCategory, +); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/range_mapper.rs b/src/tools/rust-analyzer/crates/ide-db/src/range_mapper.rs new file mode 100644 index 000000000000..ef84888b83b4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-db/src/range_mapper.rs @@ -0,0 +1,65 @@ +//! Maps between ranges in documents. + +use std::cmp::Ordering; + +use stdx::equal_range_by; +use syntax::{TextRange, TextSize}; + +#[derive(Default)] +pub struct RangeMapper { + buf: String, + ranges: Vec<(TextRange, Option)>, +} + +impl RangeMapper { + pub fn add(&mut self, text: &str, source_range: TextRange) { + let len = TextSize::of(text); + assert_eq!(len, source_range.len()); + self.add_impl(text, Some(source_range.start())); + } + + pub fn add_unmapped(&mut self, text: &str) { + self.add_impl(text, None); + } + + fn add_impl(&mut self, text: &str, source: Option) { + let len = TextSize::of(text); + let target_range = TextRange::at(TextSize::of(&self.buf), len); + self.ranges.push((target_range, source.map(|it| TextRange::at(it, len)))); + self.buf.push_str(text); + } + + pub fn take_text(&mut self) -> String { + std::mem::take(&mut self.buf) + } + + pub fn map_range_up(&self, range: TextRange) -> impl Iterator + '_ { + equal_range_by(&self.ranges, |&(r, _)| { + if range.is_empty() && r.contains(range.start()) { + Ordering::Equal + } else { + TextRange::ordering(r, range) + } + }) + .filter_map(move |i| { + let (target_range, source_range) = self.ranges[i]; + let intersection = target_range.intersect(range).unwrap(); + let source_range = source_range?; + Some(intersection - target_range.start() + source_range.start()) + }) + } + + pub fn map_offset_down(&self, offset: TextSize) -> Option { + // Using a binary search here is a bit complicated because of the `None` entries. + // But the number of lines in fixtures is usually low. + let (target_range, source_range) = + self.ranges.iter().find_map(|&(target_range, source_range)| { + let source_range = source_range?; + if !source_range.contains(offset) { + return None; + } + Some((target_range, source_range)) + })?; + Some(offset - source_range.start() + target_range.start()) + } +} 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 16c0d8d97a7d..57072bb5ba36 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 @@ -10,6 +10,7 @@ use crate::text_edit::{TextEdit, TextEditBuilder}; use crate::{SnippetCap, assists::Command, syntax_helpers::tree_diff::diff}; use base_db::AnchoredPathBuf; use itertools::Itertools; +use macros::UpmapFromRaFixture; use nohash_hasher::IntMap; use rustc_hash::FxHashMap; use span::FileId; @@ -20,7 +21,7 @@ use syntax::{ }; /// An annotation ID associated with an indel, to describe changes. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, UpmapFromRaFixture)] pub struct ChangeAnnotationId(u32); impl fmt::Display for ChangeAnnotationId { 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 6e9bd7bdcc21..d2a73710d58b 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 @@ -5,6 +5,7 @@ //! rust-analyzer. use itertools::Itertools; +use macros::UpmapFromRaFixture; pub use span::{TextRange, TextSize}; use std::cmp::max; @@ -13,14 +14,14 @@ use crate::source_change::ChangeAnnotationId; /// `InsertDelete` -- a single "atomic" change to text /// /// Must not overlap with other `InDel`s -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, UpmapFromRaFixture)] pub struct Indel { pub insert: String, /// Refers to offsets in the original text pub delete: TextRange, } -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, UpmapFromRaFixture)] pub struct TextEdit { /// Invariant: disjoint and sorted by `delete`. indels: Vec, 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 37af05e0d1bb..3dc155efe96b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -311,7 +311,7 @@ fn minicore_smoke_test() { } fn check(minicore: MiniCore) { - let source = minicore.source_code(); + let source = minicore.source_code(MiniCore::RAW_SOURCE); let mut config = DiagnosticsConfig::test_sample(); // This should be ignored since we conditionally remove code which creates single item use with braces config.disabled.insert("unused_braces".to_owned()); @@ -321,7 +321,7 @@ fn minicore_smoke_test() { } // Checks that there is no diagnostic in minicore for each flag. - for flag in MiniCore::available_flags() { + for flag in MiniCore::available_flags(MiniCore::RAW_SOURCE) { if flag == "clone" { // Clone without copy has `moved-out-of-ref`, so ignoring. // FIXME: Maybe we should merge copy and clone in a single flag? @@ -332,5 +332,5 @@ fn minicore_smoke_test() { } // And one time for all flags, to check codes which are behind multiple flags + prevent name collisions eprintln!("Checking all minicore flags"); - check(MiniCore::from_flags(MiniCore::available_flags())) + check(MiniCore::from_flags(MiniCore::available_flags(MiniCore::RAW_SOURCE))) } diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml index 06d2776ebe87..08ffd391c02d 100644 --- a/src/tools/rust-analyzer/crates/ide/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml @@ -42,6 +42,7 @@ span.workspace = true # ide should depend only on the top-level `hir` package. if you need # something from some `hir-xxx` subpackage, reexport the API via `hir`. hir.workspace = true +macros.workspace = true [target.'cfg(not(any(target_arch = "wasm32", target_os = "emscripten")))'.dependencies] toolchain.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index dec1889926da..36c44044bb5d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -1,6 +1,6 @@ use hir::{HasSource, InFile, InRealFile, Semantics}; use ide_db::{ - FileId, FilePosition, FileRange, FxIndexSet, RootDatabase, defs::Definition, + FileId, FilePosition, FileRange, FxIndexSet, MiniCore, RootDatabase, defs::Definition, helpers::visit_file_defs, }; use itertools::Itertools; @@ -11,7 +11,7 @@ use crate::{ annotations::fn_references::find_all_methods, goto_implementation::goto_implementation, navigation_target, - references::find_all_refs, + references::{FindAllRefsConfig, find_all_refs}, runnables::{Runnable, runnables}, }; @@ -36,7 +36,7 @@ pub enum AnnotationKind { HasReferences { pos: FilePosition, data: Option> }, } -pub struct AnnotationConfig { +pub struct AnnotationConfig<'a> { pub binary_target: bool, pub annotate_runnables: bool, pub annotate_impls: bool, @@ -44,6 +44,7 @@ pub struct AnnotationConfig { pub annotate_method_references: bool, pub annotate_enum_variant_references: bool, pub location: AnnotationLocation, + pub minicore: MiniCore<'a>, } pub enum AnnotationLocation { @@ -53,7 +54,7 @@ pub enum AnnotationLocation { pub(crate) fn annotations( db: &RootDatabase, - config: &AnnotationConfig, + config: &AnnotationConfig<'_>, file_id: FileId, ) -> Vec { let mut annotations = FxIndexSet::default(); @@ -196,13 +197,22 @@ pub(crate) fn annotations( .collect() } -pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation { +pub(crate) fn resolve_annotation( + db: &RootDatabase, + config: &AnnotationConfig<'_>, + mut annotation: Annotation, +) -> Annotation { match annotation.kind { AnnotationKind::HasImpls { pos, ref mut data } => { *data = goto_implementation(db, pos).map(|range| range.info); } AnnotationKind::HasReferences { pos, ref mut data } => { - *data = find_all_refs(&Semantics::new(db), pos, None).map(|result| { + *data = find_all_refs( + &Semantics::new(db), + pos, + &FindAllRefsConfig { search_scope: None, minicore: config.minicore }, + ) + .map(|result| { result .into_iter() .flat_map(|res| res.references) @@ -228,12 +238,13 @@ fn should_skip_runnable(kind: &RunnableKind, binary_target: bool) -> bool { #[cfg(test)] mod tests { use expect_test::{Expect, expect}; + use ide_db::MiniCore; use crate::{Annotation, AnnotationConfig, fixture}; use super::AnnotationLocation; - const DEFAULT_CONFIG: AnnotationConfig = AnnotationConfig { + const DEFAULT_CONFIG: AnnotationConfig<'_> = AnnotationConfig { binary_target: true, annotate_runnables: true, annotate_impls: true, @@ -241,12 +252,13 @@ mod tests { annotate_method_references: true, annotate_enum_variant_references: true, location: AnnotationLocation::AboveName, + minicore: MiniCore::default(), }; fn check_with_config( #[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect, - config: &AnnotationConfig, + config: &AnnotationConfig<'_>, ) { let (analysis, file_id) = fixture::file(ra_fixture); @@ -254,7 +266,7 @@ mod tests { .annotations(config, file_id) .unwrap() .into_iter() - .map(|annotation| analysis.resolve_annotation(annotation).unwrap()) + .map(|annotation| analysis.resolve_annotation(&DEFAULT_CONFIG, annotation).unwrap()) .collect(); expect.assert_debug_eq(&annotations); diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs index f42cead3501d..aded911a8db1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -4,14 +4,16 @@ use std::iter; use hir::Semantics; use ide_db::{ - FileRange, FxIndexMap, RootDatabase, + FileRange, FxIndexMap, MiniCore, RootDatabase, defs::{Definition, NameClass, NameRefClass}, helpers::pick_best_token, search::FileReference, }; use syntax::{AstNode, SyntaxKind::IDENT, ast}; -use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav, goto_definition}; +use crate::{ + FilePosition, GotoDefinitionConfig, NavigationTarget, RangeInfo, TryToNav, goto_definition, +}; #[derive(Debug, Clone)] pub struct CallItem { @@ -19,22 +21,28 @@ pub struct CallItem { pub ranges: Vec, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct CallHierarchyConfig { +#[derive(Debug, Clone, Copy)] +pub struct CallHierarchyConfig<'a> { /// Whether to exclude tests from the call hierarchy pub exclude_tests: bool, + pub minicore: MiniCore<'a>, } pub(crate) fn call_hierarchy( db: &RootDatabase, position: FilePosition, + config: &CallHierarchyConfig<'_>, ) -> Option>> { - goto_definition::goto_definition(db, position) + goto_definition::goto_definition( + db, + position, + &GotoDefinitionConfig { minicore: config.minicore }, + ) } pub(crate) fn incoming_calls( db: &RootDatabase, - CallHierarchyConfig { exclude_tests }: CallHierarchyConfig, + config: &CallHierarchyConfig<'_>, FilePosition { file_id, offset }: FilePosition, ) -> Option> { let sema = &Semantics::new(db); @@ -71,7 +79,7 @@ pub(crate) fn incoming_calls( }); if let Some((def, nav)) = def_nav { - if exclude_tests && def.is_test(db) { + if config.exclude_tests && def.is_test(db) { continue; } @@ -89,7 +97,7 @@ pub(crate) fn incoming_calls( pub(crate) fn outgoing_calls( db: &RootDatabase, - CallHierarchyConfig { exclude_tests }: CallHierarchyConfig, + config: &CallHierarchyConfig<'_>, FilePosition { file_id, offset }: FilePosition, ) -> Option> { let sema = Semantics::new(db); @@ -119,7 +127,7 @@ pub(crate) fn outgoing_calls( let callable = sema.type_of_expr(&expr)?.original.as_callable(db)?; match callable.kind() { hir::CallableKind::Function(it) => { - if exclude_tests && it.is_test(db) { + if config.exclude_tests && it.is_test(db) { return None; } it.try_to_nav(&sema) @@ -132,7 +140,7 @@ pub(crate) fn outgoing_calls( } ast::CallableExpr::MethodCall(expr) => { let function = sema.resolve_method_call(&expr)?; - if exclude_tests && function.is_test(db) { + if config.exclude_tests && function.is_test(db) { return None; } function @@ -166,7 +174,7 @@ impl CallLocations { #[cfg(test)] mod tests { use expect_test::{Expect, expect}; - use ide_db::FilePosition; + use ide_db::{FilePosition, MiniCore}; use itertools::Itertools; use crate::fixture; @@ -189,21 +197,20 @@ mod tests { ) } + let config = crate::CallHierarchyConfig { exclude_tests, minicore: MiniCore::default() }; let (analysis, pos) = fixture::position(ra_fixture); - let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info; + let mut navs = analysis.call_hierarchy(pos, &config).unwrap().unwrap().info; assert_eq!(navs.len(), 1); let nav = navs.pop().unwrap(); expected_nav.assert_eq(&nav.debug_render()); - let config = crate::CallHierarchyConfig { exclude_tests }; - let item_pos = FilePosition { file_id: nav.file_id, offset: nav.focus_or_full_range().start() }; - let incoming_calls = analysis.incoming_calls(config, item_pos).unwrap().unwrap(); + let incoming_calls = analysis.incoming_calls(&config, item_pos).unwrap().unwrap(); expected_incoming.assert_eq(&incoming_calls.into_iter().map(debug_render).join("\n")); - let outgoing_calls = analysis.outgoing_calls(config, item_pos).unwrap().unwrap(); + let outgoing_calls = analysis.outgoing_calls(&config, item_pos).unwrap().unwrap(); expected_outgoing.assert_eq(&outgoing_calls.into_iter().map(debug_render).join("\n")); } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs index 686dbe241293..375ce94bf644 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs @@ -6,8 +6,8 @@ use ide_db::{ use syntax::{AstNode, SyntaxKind::*, T, ast, match_ast}; use crate::{ - FilePosition, NavigationTarget, RangeInfo, goto_definition::goto_definition, - navigation_target::TryToNav, + FilePosition, GotoDefinitionConfig, NavigationTarget, RangeInfo, + goto_definition::goto_definition, navigation_target::TryToNav, }; // Feature: Go to Declaration @@ -21,6 +21,7 @@ use crate::{ pub(crate) fn goto_declaration( db: &RootDatabase, position @ FilePosition { file_id, offset }: FilePosition, + config: &GotoDefinitionConfig<'_>, ) -> Option>> { let sema = Semantics::new(db); let file = sema.parse_guess_edition(file_id).syntax().clone(); @@ -69,20 +70,27 @@ pub(crate) fn goto_declaration( .flatten() .collect(); - if info.is_empty() { goto_definition(db, position) } else { Some(RangeInfo::new(range, info)) } + if info.is_empty() { + goto_definition(db, position, config) + } else { + Some(RangeInfo::new(range, info)) + } } #[cfg(test)] mod tests { - use ide_db::FileRange; + use ide_db::{FileRange, MiniCore}; use itertools::Itertools; - use crate::fixture; + use crate::{GotoDefinitionConfig, fixture}; + + const TEST_CONFIG: GotoDefinitionConfig<'_> = + GotoDefinitionConfig { minicore: MiniCore::default() }; fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let (analysis, position, expected) = fixture::annotations(ra_fixture); let navs = analysis - .goto_declaration(position) + .goto_declaration(position, &TEST_CONFIG) .unwrap() .expect("no declaration or definition found") .info; 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 2dcb13d9e7aa..e335989ab2b0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -1,5 +1,6 @@ use std::{iter, mem::discriminant}; +use crate::Analysis; use crate::{ FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult, doc_links::token_as_doc_comment, @@ -8,6 +9,7 @@ use crate::{ use hir::{ AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, Semantics, sym, }; +use ide_db::{MiniCore, ra_fixture::UpmapFromRaFixture}; use ide_db::{ RootDatabase, SymbolKind, base_db::{AnchoredPath, SourceDatabase}, @@ -25,6 +27,11 @@ use syntax::{ match_ast, }; +#[derive(Debug)] +pub struct GotoDefinitionConfig<'a> { + pub minicore: MiniCore<'a>, +} + // Feature: Go to Definition // // Navigates to the definition of an identifier. @@ -39,6 +46,7 @@ use syntax::{ pub(crate) fn goto_definition( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, + config: &GotoDefinitionConfig<'_>, ) -> Option>> { let sema = &Semantics::new(db); let file = sema.parse_guess_edition(file_id).syntax().clone(); @@ -83,52 +91,64 @@ pub(crate) fn goto_definition( return Some(RangeInfo::new(original_token.text_range(), navs)); } - let navs = sema - .descend_into_macros_no_opaque(original_token.clone(), false) - .into_iter() - .filter_map(|token| { - if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &token.value) { - return Some(navs); - } + let tokens = sema.descend_into_macros_no_opaque(original_token.clone(), false); + let mut navs = Vec::new(); + for token in tokens { + if let Some(n) = find_definition_for_known_blanket_dual_impls(sema, &token.value) { + navs.extend(n); + continue; + } - let parent = token.value.parent()?; + if let Some(token) = ast::String::cast(token.value.clone()) + && let Some(original_token) = ast::String::cast(original_token.clone()) + && let Some((analysis, fixture_analysis)) = + Analysis::from_ra_fixture(sema, original_token, &token, config.minicore) + && let Some((virtual_file_id, file_offset)) = fixture_analysis.map_offset_down(offset) + { + return hir::attach_db_allow_change(&analysis.db, || { + goto_definition( + &analysis.db, + FilePosition { file_id: virtual_file_id, offset: file_offset }, + config, + ) + }) + .and_then(|navs| { + navs.upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id).ok() + }); + } - let token_file_id = token.file_id; - if let Some(token) = ast::String::cast(token.value.clone()) - && let Some(x) = - try_lookup_include_path(sema, InFile::new(token_file_id, token), file_id) - { - return Some(vec![x]); - } + let parent = token.value.parent()?; - if ast::TokenTree::can_cast(parent.kind()) - && let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.value) - { - return Some(vec![x]); - } + let token_file_id = token.file_id; + if let Some(token) = ast::String::cast(token.value.clone()) + && let Some(x) = + try_lookup_include_path(sema, InFile::new(token_file_id, token), file_id) + { + navs.push(x); + continue; + } - Some( - IdentClass::classify_node(sema, &parent)? - .definitions() + if ast::TokenTree::can_cast(parent.kind()) + && let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.value) + { + navs.push(x); + continue; + } + + let Some(ident_class) = IdentClass::classify_node(sema, &parent) else { continue }; + navs.extend(ident_class.definitions().into_iter().flat_map(|(def, _)| { + if let Definition::ExternCrateDecl(crate_def) = def { + return crate_def + .resolved_crate(db) + .map(|it| it.root_module().to_nav(sema.db)) .into_iter() - .flat_map(|(def, _)| { - if let Definition::ExternCrateDecl(crate_def) = def { - return crate_def - .resolved_crate(db) - .map(|it| it.root_module().to_nav(sema.db)) - .into_iter() - .flatten() - .collect(); - } - try_filter_trait_item_definition(sema, &def) - .unwrap_or_else(|| def_to_nav(sema, def)) - }) - .collect(), - ) - }) - .flatten() - .unique() - .collect::>(); + .flatten() + .collect(); + } + try_filter_trait_item_definition(sema, &def).unwrap_or_else(|| def_to_nav(sema, def)) + })); + } + let navs = navs.into_iter().unique().collect(); Some(RangeInfo::new(original_token.text_range(), navs)) } @@ -584,15 +604,22 @@ fn expr_to_nav( #[cfg(test)] mod tests { - use crate::fixture; - use ide_db::FileRange; + use crate::{GotoDefinitionConfig, fixture}; + use ide_db::{FileRange, MiniCore}; use itertools::Itertools; use syntax::SmolStr; + const TEST_CONFIG: GotoDefinitionConfig<'_> = + GotoDefinitionConfig { minicore: MiniCore::default() }; + #[track_caller] fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let (analysis, position, expected) = fixture::annotations(ra_fixture); - let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info; + let navs = analysis + .goto_definition(position, &TEST_CONFIG) + .unwrap() + .expect("no definition found") + .info; let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start()); let navs = navs @@ -611,14 +638,22 @@ mod tests { fn check_unresolved(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let (analysis, position) = fixture::position(ra_fixture); - let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info; + let navs = analysis + .goto_definition(position, &TEST_CONFIG) + .unwrap() + .expect("no definition found") + .info; assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}") } fn check_name(expected_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) { let (analysis, position, _) = fixture::annotations(ra_fixture); - let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info; + let navs = analysis + .goto_definition(position, &TEST_CONFIG) + .unwrap() + .expect("no definition found") + .info; assert!(navs.len() < 2, "expected single navigation target but encountered {}", navs.len()); let Some(target) = navs.into_iter().next() else { panic!("expected single navigation target but encountered none"); @@ -3961,4 +3996,23 @@ mod prim_str {} "#, ); } + + #[test] + fn ra_fixture() { + check( + r##" +fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {} + +fn foo() { + fixture(r#" +fn foo() {} +// ^^^ +fn bar() { + f$0oo(); +} + "#) +} + "##, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index c4fb6d1a5b4b..e1d18b0c4116 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -11,29 +11,32 @@ use hir::{ db::DefDatabase, }; use ide_db::{ - FileRange, FxIndexSet, Ranker, RootDatabase, + FileRange, FxIndexSet, MiniCore, Ranker, RootDatabase, defs::{Definition, IdentClass, NameRefClass, OperatorClass}, famous_defs::FamousDefs, helpers::pick_best_token, + ra_fixture::UpmapFromRaFixture, }; use itertools::{Itertools, multizip}; -use span::Edition; +use macros::UpmapFromRaFixture; +use span::{Edition, TextRange}; use syntax::{ - AstNode, + AstNode, AstToken, SyntaxKind::{self, *}, SyntaxNode, T, ast, }; use crate::{ - FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav, + Analysis, FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav, doc_links::token_as_doc_comment, markdown_remove::remove_markdown, markup::Markup, navigation_target::UpmappingResult, runnables::{runnable_fn, runnable_mod}, }; -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct HoverConfig { + +#[derive(Clone, Debug)] +pub struct HoverConfig<'a> { pub links_in_hover: bool, pub memory_layout: Option, pub documentation: bool, @@ -44,6 +47,7 @@ pub struct HoverConfig { pub max_enum_variants_count: Option, pub max_subst_ty_len: SubstTyLen, pub show_drop_glue: bool, + pub minicore: MiniCore<'a>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -75,7 +79,7 @@ pub enum HoverDocFormat { PlainText, } -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, UpmapFromRaFixture)] pub enum HoverAction { Runnable(Runnable), Implementation(FilePosition), @@ -108,14 +112,14 @@ impl HoverAction { } } -#[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, Eq, PartialEq, Hash, UpmapFromRaFixture)] pub struct HoverGotoTypeData { pub mod_path: String, pub nav: NavigationTarget, } /// Contains the results when hovering over an item -#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)] +#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, UpmapFromRaFixture)] pub struct HoverResult { pub markup: Markup, pub actions: Vec, @@ -130,7 +134,7 @@ pub struct HoverResult { pub(crate) fn hover( db: &RootDatabase, frange @ FileRange { file_id, range }: FileRange, - config: &HoverConfig, + config: &HoverConfig<'_>, ) -> Option> { let sema = &hir::Semantics::new(db); let file = sema.parse_guess_edition(file_id).syntax().clone(); @@ -161,7 +165,7 @@ fn hover_offset( sema: &Semantics<'_, RootDatabase>, FilePosition { file_id, offset }: FilePosition, file: SyntaxNode, - config: &HoverConfig, + config: &HoverConfig<'_>, edition: Edition, display_target: DisplayTarget, ) -> Option> { @@ -219,6 +223,21 @@ fn hover_offset( return Some(RangeInfo::new(range, res)); } + if let Some(literal) = ast::String::cast(original_token.clone()) + && let Some((analysis, fixture_analysis)) = + Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore) + { + let (virtual_file_id, virtual_offset) = fixture_analysis.map_offset_down(offset)?; + return analysis + .hover( + config, + FileRange { file_id: virtual_file_id, range: TextRange::empty(virtual_offset) }, + ) + .ok()?? + .upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id) + .ok(); + } + // prefer descending the same token kind in attribute expansions, in normal macros text // equivalency is more important let mut descended = sema.descend_into_macros(original_token.clone()); @@ -383,9 +402,9 @@ fn hover_offset( fn hover_ranged( sema: &Semantics<'_, RootDatabase>, - FileRange { range, .. }: FileRange, + FileRange { file_id, range }: FileRange, file: SyntaxNode, - config: &HoverConfig, + config: &HoverConfig<'_>, edition: Edition, display_target: DisplayTarget, ) -> Option> { @@ -404,6 +423,20 @@ fn hover_ranged( { render::deref_expr(sema, config, prefix_expr, edition, display_target) } + Either::Left(ast::Expr::Literal(literal)) => { + if let Some(literal) = ast::String::cast(literal.token()) + && let Some((analysis, fixture_analysis)) = + Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore) + { + let (virtual_file_id, virtual_range) = fixture_analysis.map_range_down(range)?; + return analysis + .hover(config, FileRange { file_id: virtual_file_id, range: virtual_range }) + .ok()?? + .upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id) + .ok(); + } + None + } _ => None, }; let res = @@ -426,7 +459,7 @@ pub(crate) fn hover_for_definition( scope_node: &SyntaxNode, macro_arm: Option, render_extras: bool, - config: &HoverConfig, + config: &HoverConfig<'_>, edition: Edition, display_target: DisplayTarget, ) -> HoverResult { diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index f29ccc985c18..a1eff3aaee78 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -35,7 +35,7 @@ use crate::{ pub(super) fn type_info_of( sema: &Semantics<'_, RootDatabase>, - _config: &HoverConfig, + _config: &HoverConfig<'_>, expr_or_pat: &Either, edition: Edition, display_target: DisplayTarget, @@ -49,7 +49,7 @@ pub(super) fn type_info_of( pub(super) fn closure_expr( sema: &Semantics<'_, RootDatabase>, - config: &HoverConfig, + config: &HoverConfig<'_>, c: ast::ClosureExpr, edition: Edition, display_target: DisplayTarget, @@ -60,7 +60,7 @@ pub(super) fn closure_expr( pub(super) fn try_expr( sema: &Semantics<'_, RootDatabase>, - _config: &HoverConfig, + _config: &HoverConfig<'_>, try_expr: &ast::TryExpr, edition: Edition, display_target: DisplayTarget, @@ -155,7 +155,7 @@ pub(super) fn try_expr( pub(super) fn deref_expr( sema: &Semantics<'_, RootDatabase>, - _config: &HoverConfig, + _config: &HoverConfig<'_>, deref_expr: &ast::PrefixExpr, edition: Edition, display_target: DisplayTarget, @@ -219,7 +219,7 @@ pub(super) fn deref_expr( pub(super) fn underscore( sema: &Semantics<'_, RootDatabase>, - config: &HoverConfig, + config: &HoverConfig<'_>, token: &SyntaxToken, edition: Edition, display_target: DisplayTarget, @@ -263,7 +263,7 @@ pub(super) fn underscore( pub(super) fn keyword( sema: &Semantics<'_, RootDatabase>, - config: &HoverConfig, + config: &HoverConfig<'_>, token: &SyntaxToken, edition: Edition, display_target: DisplayTarget, @@ -290,7 +290,7 @@ pub(super) fn keyword( /// i.e. `let S {a, ..} = S {a: 1, b: 2}` pub(super) fn struct_rest_pat( sema: &Semantics<'_, RootDatabase>, - _config: &HoverConfig, + _config: &HoverConfig<'_>, pattern: &ast::RecordPat, edition: Edition, display_target: DisplayTarget, @@ -371,7 +371,7 @@ pub(super) fn process_markup( def: Definition, markup: &Markup, markup_range_map: Option, - config: &HoverConfig, + config: &HoverConfig<'_>, ) -> Markup { let markup = markup.as_str(); let markup = if config.links_in_hover { @@ -481,7 +481,7 @@ pub(super) fn definition( macro_arm: Option, render_extras: bool, subst_types: Option<&Vec<(Symbol, Type<'_>)>>, - config: &HoverConfig, + config: &HoverConfig<'_>, edition: Edition, display_target: DisplayTarget, ) -> (Markup, Option) { @@ -979,7 +979,7 @@ fn render_notable_trait( fn type_info( sema: &Semantics<'_, RootDatabase>, - config: &HoverConfig, + config: &HoverConfig<'_>, ty: TypeInfo<'_>, edition: Edition, display_target: DisplayTarget, @@ -1038,7 +1038,7 @@ fn type_info( fn closure_ty( sema: &Semantics<'_, RootDatabase>, - config: &HoverConfig, + config: &HoverConfig<'_>, TypeInfo { original, adjusted }: &TypeInfo<'_>, edition: Edition, display_target: DisplayTarget, 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 df1800616803..91fb4d0a6715 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -1,5 +1,5 @@ use expect_test::{Expect, expect}; -use ide_db::{FileRange, base_db::SourceDatabase}; +use ide_db::{FileRange, MiniCore, base_db::SourceDatabase}; use syntax::TextRange; use crate::{ @@ -8,7 +8,7 @@ use crate::{ use hir::setup_tracing; -const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { +const HOVER_BASE_CONFIG: HoverConfig<'_> = HoverConfig { links_in_hover: false, memory_layout: Some(MemoryLayoutHoverConfig { size: Some(MemoryLayoutHoverRenderKind::Both), @@ -25,6 +25,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { max_enum_variants_count: Some(5), max_subst_ty_len: super::SubstTyLen::Unlimited, show_drop_glue: true, + minicore: MiniCore::default(), }; fn check_hover_no_result(#[rust_analyzer::rust_fixture] ra_fixture: &str) { 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 f7b09b43813d..21550d5e6665 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -8,9 +8,12 @@ use hir::{ ClosureStyle, DisplayTarget, EditionedFileId, HasVisibility, HirDisplay, HirDisplayError, HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym, }; -use ide_db::{FileRange, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder}; +use ide_db::{ + FileRange, MiniCore, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder, +}; use ide_db::{FxHashSet, text_edit::TextEdit}; use itertools::Itertools; +use macros::UpmapFromRaFixture; use smallvec::{SmallVec, smallvec}; use stdx::never; use syntax::{ @@ -37,6 +40,7 @@ mod implicit_static; mod implied_dyn_trait; mod lifetime; mod param_name; +mod ra_fixture; mod range_exclusive; // Feature: Inlay Hints @@ -80,7 +84,7 @@ pub(crate) fn inlay_hints( db: &RootDatabase, file_id: FileId, range_limit: Option, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, ) -> Vec { let _p = tracing::info_span!("inlay_hints").entered(); let sema = Semantics::new(db); @@ -132,7 +136,7 @@ pub(crate) fn inlay_hints_resolve( file_id: FileId, resolve_range: TextRange, hash: u64, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, hasher: impl Fn(&InlayHint) -> u64, ) -> Option { let _p = tracing::info_span!("inlay_hints_resolve").entered(); @@ -208,7 +212,7 @@ fn hints( hints: &mut Vec, ctx: &mut InlayHintCtx, famous_defs @ FamousDefs(sema, _krate): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, file_id: EditionedFileId, display_target: DisplayTarget, node: SyntaxNode, @@ -239,6 +243,7 @@ fn hints( closure_ret::hints(hints, famous_defs, config, display_target, it) }, ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, it), + ast::Expr::Literal(it) => ra_fixture::hints(hints, famous_defs.0, file_id, config, it), _ => Some(()), } }, @@ -294,8 +299,8 @@ fn hints( }; } -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct InlayHintsConfig { +#[derive(Clone, Debug)] +pub struct InlayHintsConfig<'a> { pub render_colons: bool, pub type_hints: bool, pub sized_bound: bool, @@ -321,9 +326,10 @@ pub struct InlayHintsConfig { pub max_length: Option, pub closing_brace_hints_min_lines: Option, pub fields_to_resolve: InlayFieldsToResolve, + pub minicore: MiniCore<'a>, } -impl InlayHintsConfig { +impl InlayHintsConfig<'_> { fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> LazyProperty { if self.fields_to_resolve.resolve_text_edits { LazyProperty::Lazy @@ -466,7 +472,7 @@ pub enum InlayHintPosition { After, } -#[derive(Debug)] +#[derive(Debug, UpmapFromRaFixture)] pub struct InlayHint { /// The text range this inlay hint applies to. pub range: TextRange, @@ -485,9 +491,10 @@ pub struct InlayHint { } /// A type signaling that a value is either computed, or is available for computation. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default, UpmapFromRaFixture)] pub enum LazyProperty { Computed(T), + #[default] Lazy, } @@ -537,7 +544,7 @@ pub enum InlayTooltip { Markdown(String), } -#[derive(Default, Hash)] +#[derive(Default, Hash, UpmapFromRaFixture)] pub struct InlayHintLabel { pub parts: SmallVec<[InlayHintLabelPart; 1]>, } @@ -623,6 +630,7 @@ impl fmt::Debug for InlayHintLabel { } } +#[derive(UpmapFromRaFixture)] pub struct InlayHintLabelPart { pub text: String, /// Source location represented by this label part. The client will use this to fetch the part's @@ -724,7 +732,7 @@ impl InlayHintLabelBuilder<'_> { fn label_of_ty( famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, ty: &hir::Type<'_>, display_target: DisplayTarget, ) -> Option { @@ -734,7 +742,7 @@ fn label_of_ty( mut max_length: Option, ty: &hir::Type<'_>, label_builder: &mut InlayHintLabelBuilder<'_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, display_target: DisplayTarget, ) -> Result<(), HirDisplayError> { hir::attach_db(sema.db, || { @@ -829,7 +837,7 @@ fn hint_iterator<'db>( fn ty_to_text_edit( sema: &Semantics<'_, RootDatabase>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, node_for_hint: &SyntaxNode, ty: &hir::Type<'_>, offset_to_insert_ty: TextSize, @@ -860,6 +868,7 @@ mod tests { use expect_test::Expect; use hir::ClosureStyle; + use ide_db::MiniCore; use itertools::Itertools; use test_utils::extract_annotations; @@ -869,7 +878,7 @@ mod tests { use super::{ClosureReturnTypeHints, GenericParameterHints, InlayFieldsToResolve}; - pub(super) const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig { + pub(super) const DISABLED_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig { discriminant_hints: DiscriminantHints::Never, render_colons: false, type_hints: false, @@ -899,8 +908,9 @@ mod tests { fields_to_resolve: InlayFieldsToResolve::empty(), implicit_drop_hints: false, range_exclusive_hints: false, + minicore: MiniCore::default(), }; - pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig { + pub(super) const TEST_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig { type_hints: true, parameter_hints: true, chaining_hints: true, @@ -917,7 +927,7 @@ mod tests { #[track_caller] pub(super) fn check_with_config( - config: InlayHintsConfig, + config: InlayHintsConfig<'_>, #[rust_analyzer::rust_fixture] ra_fixture: &str, ) { let (analysis, file_id) = fixture::file(ra_fixture); @@ -936,7 +946,7 @@ mod tests { #[track_caller] pub(super) fn check_expect( - config: InlayHintsConfig, + config: InlayHintsConfig<'_>, #[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect, ) { @@ -951,7 +961,7 @@ mod tests { /// expect test. #[track_caller] pub(super) fn check_edit( - config: InlayHintsConfig, + config: InlayHintsConfig<'_>, #[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect, ) { @@ -974,7 +984,7 @@ mod tests { #[track_caller] pub(super) fn check_no_edit( - config: InlayHintsConfig, + config: InlayHintsConfig<'_>, #[rust_analyzer::rust_fixture] ra_fixture: &str, ) { let (analysis, file_id) = fixture::file(ra_fixture); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 7231a3194d09..ebb0d5752501 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -23,7 +23,7 @@ use crate::{ pub(super) fn hints( acc: &mut Vec, FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, display_target: DisplayTarget, expr: &ast::Expr, ) -> Option<()> { 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 121b16b97e87..de207c7821da 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 @@ -20,7 +20,7 @@ use crate::{ pub(super) fn hints( acc: &mut Vec, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, display_target: DisplayTarget, pat: &ast::IdentPat, ) -> Option<()> { 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 169ab92342ba..e8d305afb3b9 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 @@ -15,7 +15,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla pub(super) fn hints( acc: &mut Vec, FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, pat: &ast::Pat, ) -> Option<()> { if !config.binding_mode_hints { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs index 4abd67b91f5e..c9fbdf3ae754 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs @@ -13,7 +13,7 @@ use crate::{ pub(super) fn hints( acc: &mut Vec, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, params: ast::GenericParamList, ) -> Option<()> { if !config.sized_bound { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index a8bb652fda22..cf3149c9461b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -13,7 +13,7 @@ use super::label_of_ty; pub(super) fn hints( acc: &mut Vec, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, display_target: DisplayTarget, expr: &ast::Expr, ) -> Option<()> { @@ -93,7 +93,7 @@ mod tests { #[track_caller] pub(super) fn check_expect_clear_loc( - config: InlayHintsConfig, + config: InlayHintsConfig<'_>, #[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect, ) { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index 9d246eda57e0..ab3ce5b05b01 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -19,7 +19,7 @@ use crate::{ pub(super) fn hints( acc: &mut Vec, sema: &Semantics<'_, RootDatabase>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, display_target: DisplayTarget, InRealFile { file_id, value: node }: InRealFile, ) -> Option<()> { 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 3186a566d2bc..f8d4ddc6eb57 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 @@ -13,7 +13,7 @@ use crate::{ pub(super) fn hints( acc: &mut Vec, FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, closure: ast::ClosureExpr, ) -> Option<()> { if !config.closure_capture_hints { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs index fef1cb83c119..7765dc4f087c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs @@ -13,7 +13,7 @@ use crate::{ pub(super) fn hints( acc: &mut Vec, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, display_target: DisplayTarget, closure: ast::ClosureExpr, ) -> Option<()> { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs index a2a702835a79..5b9267126f8a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs @@ -17,7 +17,7 @@ use crate::{ pub(super) fn enum_hints( acc: &mut Vec, FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, enum_: ast::Enum, ) -> Option<()> { if let DiscriminantHints::Never = config.discriminant_hints { @@ -41,7 +41,7 @@ pub(super) fn enum_hints( fn variant_hints( acc: &mut Vec, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, sema: &Semantics<'_, RootDatabase>, enum_: &ast::Enum, variant: &ast::Variant, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs index 491018a4dda8..8dd6c4db4c04 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs @@ -7,7 +7,7 @@ use crate::{InlayHint, InlayHintsConfig}; pub(super) fn extern_block_hints( acc: &mut Vec, FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, extern_block: ast::ExternBlock, ) -> Option<()> { if extern_block.unsafe_token().is_some() { @@ -33,7 +33,7 @@ pub(super) fn extern_block_hints( pub(super) fn fn_hints( acc: &mut Vec, FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, fn_: &ast::Fn, extern_block: &ast::ExternBlock, ) -> Option<()> { @@ -51,7 +51,7 @@ pub(super) fn fn_hints( pub(super) fn static_hints( acc: &mut Vec, FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, static_: &ast::Static, extern_block: &ast::ExternBlock, ) -> Option<()> { @@ -67,7 +67,7 @@ pub(super) fn static_hints( } fn item_hint( - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, extern_block: &ast::ExternBlock, token: SyntaxToken, ) -> InlayHint { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs index 1fddb6fbe01d..27d14f7a73cd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs @@ -16,7 +16,7 @@ use super::param_name::is_argument_similar_to_param_name; pub(crate) fn hints( acc: &mut Vec, FamousDefs(sema, krate): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, node: AnyHasGenericArgs, ) -> Option<()> { let GenericParameterHints { type_hints, lifetime_hints, const_hints } = diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index 1e272fe3ba82..951a672d4b79 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -23,7 +23,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla pub(super) fn hints( acc: &mut Vec, FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, display_target: hir::DisplayTarget, node: &ast::Fn, ) -> Option<()> { @@ -147,7 +147,7 @@ mod tests { inlay_hints::tests::{DISABLED_CONFIG, check_with_config}, }; - const ONLY_DROP_CONFIG: InlayHintsConfig = + const ONLY_DROP_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig { implicit_drop_hints: true, ..DISABLED_CONFIG }; #[test] diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs index bddce904dfde..0492991790c8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs @@ -15,7 +15,7 @@ use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeE pub(super) fn hints( acc: &mut Vec, FamousDefs(_sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, statik_or_const: Either, ) -> Option<()> { if config.lifetime_elision_hints != LifetimeElisionHints::Always { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs index 0da1785234ae..562eb1e00213 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs @@ -11,7 +11,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla pub(super) fn hints( acc: &mut Vec, FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, path: Either, ) -> Option<()> { let parent = path.syntax().parent()?; diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs index a89c53e00b3b..4982b60f1dc8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs @@ -21,7 +21,7 @@ pub(super) fn fn_hints( acc: &mut Vec, ctx: &mut InlayHintCtx, fd: &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, func: ast::Fn, ) -> Option<()> { if config.lifetime_elision_hints == LifetimeElisionHints::Never { @@ -70,7 +70,7 @@ pub(super) fn fn_ptr_hints( acc: &mut Vec, ctx: &mut InlayHintCtx, fd: &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, func: ast::FnPtrType, ) -> Option<()> { if config.lifetime_elision_hints == LifetimeElisionHints::Never { @@ -135,7 +135,7 @@ pub(super) fn fn_path_hints( acc: &mut Vec, ctx: &mut InlayHintCtx, fd: &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, func: &ast::PathType, ) -> Option<()> { if config.lifetime_elision_hints == LifetimeElisionHints::Never { @@ -196,7 +196,7 @@ fn hints_( acc: &mut Vec, ctx: &mut InlayHintCtx, FamousDefs(_, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, params: impl Iterator, ast::Type)>, generic_param_list: Option, ret_type: Option, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index 754707784055..3e555e88303d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -18,7 +18,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla pub(super) fn hints( acc: &mut Vec, FamousDefs(sema, krate): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, file_id: EditionedFileId, expr: ast::Expr, ) -> Option<()> { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/ra_fixture.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/ra_fixture.rs new file mode 100644 index 000000000000..bee18416424c --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/ra_fixture.rs @@ -0,0 +1,32 @@ +//! Injected inlay hints for `#[rust_analyzer::rust_fixture]`. + +use hir::{EditionedFileId, Semantics}; +use ide_db::{RootDatabase, impl_empty_upmap_from_ra_fixture, ra_fixture::UpmapFromRaFixture}; +use syntax::{AstToken, ast}; + +use crate::{Analysis, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip}; + +pub(super) fn hints( + acc: &mut Vec, + sema: &Semantics<'_, RootDatabase>, + file_id: EditionedFileId, + config: &InlayHintsConfig<'_>, + literal: ast::Literal, +) -> Option<()> { + let file_id = file_id.file_id(sema.db); + let literal = ast::String::cast(literal.token())?; + let (analysis, fixture_analysis) = + Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore)?; + for virtual_file_id in fixture_analysis.files() { + acc.extend( + analysis + .inlay_hints(config, virtual_file_id, None) + .ok()? + .upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id) + .ok()?, + ); + } + Some(()) +} + +impl_empty_upmap_from_ra_fixture!(InlayHintPosition, InlayKind, InlayTooltip); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs index 47bd6d737f82..a446908e736b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs @@ -11,7 +11,7 @@ use crate::{InlayHint, InlayHintsConfig}; pub(super) fn hints( acc: &mut Vec, FamousDefs(_sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, range: impl ast::RangeItem, ) -> Option<()> { (config.range_exclusive_hints && range.end().is_some()) diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index f7d21c947950..857252832ffe 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -62,7 +62,7 @@ use std::panic::{AssertUnwindSafe, UnwindSafe}; use cfg::CfgOptions; use fetch_crates::CrateInfo; -use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, db::HirDatabase, sym}; +use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym}; use ide_db::{ FxHashMap, FxIndexSet, LineIndexDatabase, base_db::{ @@ -71,7 +71,9 @@ use ide_db::{ }, prime_caches, symbol_index, }; -use syntax::SourceFile; +use ide_db::{MiniCore, ra_fixture::RaFixtureAnalysis}; +use macros::UpmapFromRaFixture; +use syntax::{SourceFile, ast}; use triomphe::Arc; use view_memory_layout::{RecursiveMemoryLayout, view_memory_layout}; @@ -83,6 +85,7 @@ pub use crate::{ expand_macro::ExpandedMacro, file_structure::{FileStructureConfig, StructureNode, StructureNodeKind}, folding_ranges::{Fold, FoldKind}, + goto_definition::GotoDefinitionConfig, highlight_related::{HighlightRelatedConfig, HighlightedRange}, hover::{ HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult, @@ -102,7 +105,7 @@ pub use crate::{ }, move_item::Direction, navigation_target::{NavigationTarget, TryToNav, UpmappingResult}, - references::ReferenceSearchResult, + references::{FindAllRefsConfig, ReferenceSearchResult}, rename::RenameError, runnables::{Runnable, RunnableKind, TestId, UpdateTest}, signature_help::SignatureHelp, @@ -144,7 +147,7 @@ pub use syntax::{TextRange, TextSize}; pub type Cancellable = Result; /// Info associated with a text range. -#[derive(Debug)] +#[derive(Debug, UpmapFromRaFixture)] pub struct RangeInfo { pub range: TextRange, pub info: T, @@ -274,6 +277,28 @@ impl Analysis { (host.analysis(), file_id) } + pub(crate) fn from_ra_fixture( + sema: &Semantics<'_, RootDatabase>, + literal: ast::String, + expanded: &ast::String, + minicore: MiniCore<'_>, + ) -> Option<(Analysis, RaFixtureAnalysis)> { + Self::from_ra_fixture_with_on_cursor(sema, literal, expanded, minicore, &mut |_| {}) + } + + /// Like [`Analysis::from_ra_fixture()`], but also calls `on_cursor` with the cursor position. + pub(crate) fn from_ra_fixture_with_on_cursor( + sema: &Semantics<'_, RootDatabase>, + literal: ast::String, + expanded: &ast::String, + minicore: MiniCore<'_>, + on_cursor: &mut dyn FnMut(TextRange), + ) -> Option<(Analysis, RaFixtureAnalysis)> { + let analysis = + RaFixtureAnalysis::analyze_ra_fixture(sema, literal, expanded, minicore, on_cursor)?; + Some((Analysis { db: analysis.db.clone() }, analysis)) + } + /// Debug info about the current state of the analysis. pub fn status(&self, file_id: Option) -> Cancellable { self.with_db(|db| status::status(db, file_id)) @@ -446,7 +471,7 @@ impl Analysis { /// Returns a list of the places in the file where type hints can be displayed. pub fn inlay_hints( &self, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, file_id: FileId, range: Option, ) -> Cancellable> { @@ -454,7 +479,7 @@ impl Analysis { } pub fn inlay_hints_resolve( &self, - config: &InlayHintsConfig, + config: &InlayHintsConfig<'_>, file_id: FileId, resolve_range: TextRange, hash: u64, @@ -495,16 +520,18 @@ impl Analysis { pub fn goto_definition( &self, position: FilePosition, + config: &GotoDefinitionConfig<'_>, ) -> Cancellable>>> { - self.with_db(|db| goto_definition::goto_definition(db, position)) + self.with_db(|db| goto_definition::goto_definition(db, position, config)) } /// Returns the declaration from the symbol at `position`. pub fn goto_declaration( &self, position: FilePosition, + config: &GotoDefinitionConfig<'_>, ) -> Cancellable>>> { - self.with_db(|db| goto_declaration::goto_declaration(db, position)) + self.with_db(|db| goto_declaration::goto_declaration(db, position, config)) } /// Returns the impls from the symbol at `position`. @@ -526,19 +553,16 @@ impl Analysis { pub fn find_all_refs( &self, position: FilePosition, - search_scope: Option, + config: &FindAllRefsConfig<'_>, ) -> Cancellable>> { - let search_scope = AssertUnwindSafe(search_scope); - self.with_db(|db| { - let _ = &search_scope; - references::find_all_refs(&Semantics::new(db), position, search_scope.0) - }) + let config = AssertUnwindSafe(config); + self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, &config)) } /// Returns a short text describing element at position. pub fn hover( &self, - config: &HoverConfig, + config: &HoverConfig<'_>, range: FileRange, ) -> Cancellable>> { self.with_db(|db| hover::hover(db, range, config)) @@ -576,14 +600,15 @@ impl Analysis { pub fn call_hierarchy( &self, position: FilePosition, + config: &CallHierarchyConfig<'_>, ) -> Cancellable>>> { - self.with_db(|db| call_hierarchy::call_hierarchy(db, position)) + self.with_db(|db| call_hierarchy::call_hierarchy(db, position, config)) } /// Computes incoming calls for the given file position. pub fn incoming_calls( &self, - config: CallHierarchyConfig, + config: &CallHierarchyConfig<'_>, position: FilePosition, ) -> Cancellable>> { self.with_db(|db| call_hierarchy::incoming_calls(db, config, position)) @@ -592,7 +617,7 @@ impl Analysis { /// Computes outgoing calls for the given file position. pub fn outgoing_calls( &self, - config: CallHierarchyConfig, + config: &CallHierarchyConfig<'_>, position: FilePosition, ) -> Cancellable>> { self.with_db(|db| call_hierarchy::outgoing_calls(db, config, position)) @@ -675,28 +700,22 @@ impl Analysis { /// Computes syntax highlighting for the given file pub fn highlight( &self, - highlight_config: HighlightConfig, + highlight_config: HighlightConfig<'_>, file_id: FileId, ) -> Cancellable> { - // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database - // highlighting instead sets up the attach hook where neceesary for the trait solver - Cancelled::catch(|| { - syntax_highlighting::highlight(&self.db, highlight_config, file_id, None) - }) + self.with_db(|db| syntax_highlighting::highlight(db, &highlight_config, file_id, None)) } /// Computes syntax highlighting for the given file range. pub fn highlight_range( &self, - highlight_config: HighlightConfig, + highlight_config: HighlightConfig<'_>, frange: FileRange, ) -> Cancellable> { - // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database - // highlighting instead sets up the attach hook where neceesary for the trait solver - Cancelled::catch(|| { + self.with_db(|db| { syntax_highlighting::highlight( - &self.db, - highlight_config, + db, + &highlight_config, frange.file_id, Some(frange.range), ) @@ -706,22 +725,18 @@ impl Analysis { /// Computes syntax highlighting for the given file. pub fn highlight_as_html_with_config( &self, - config: HighlightConfig, + config: HighlightConfig<'_>, file_id: FileId, rainbow: bool, ) -> Cancellable { - // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database - // highlighting instead sets up the attach hook where neceesary for the trait solver - Cancelled::catch(|| { - syntax_highlighting::highlight_as_html_with_config(&self.db, config, file_id, rainbow) + self.with_db(|db| { + syntax_highlighting::highlight_as_html_with_config(db, &config, file_id, rainbow) }) } /// Computes syntax highlighting for the given file. pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancellable { - // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database - // highlighting instead sets up the attach hook where neceesary for the trait solver - Cancelled::catch(|| syntax_highlighting::highlight_as_html(&self.db, file_id, rainbow)) + self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow)) } /// Computes completions at the given position. @@ -853,14 +868,18 @@ impl Analysis { pub fn annotations( &self, - config: &AnnotationConfig, + config: &AnnotationConfig<'_>, file_id: FileId, ) -> Cancellable> { self.with_db(|db| annotations::annotations(db, config, file_id)) } - pub fn resolve_annotation(&self, annotation: Annotation) -> Cancellable { - self.with_db(|db| annotations::resolve_annotation(db, annotation)) + pub fn resolve_annotation( + &self, + config: &AnnotationConfig<'_>, + annotation: Annotation, + ) -> Cancellable { + self.with_db(|db| annotations::resolve_annotation(db, config, annotation)) } pub fn move_item( @@ -899,12 +918,8 @@ impl Analysis { where F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, { - hir::attach_db(&self.db, || { - // the trait solver code may invoke `as_view` outside of queries, - // so technically we might run into a panic in salsa if the downcaster has not yet been registered. - HirDatabase::zalsa_register_downcaster(&self.db); - Cancelled::catch(|| f(&self.db)) - }) + // We use `attach_db_allow_change()` and not `attach_db()` because fixture injection can change the database. + hir::attach_db_allow_change(&self.db, || Cancelled::catch(|| f(&self.db))) } } diff --git a/src/tools/rust-analyzer/crates/ide/src/markup.rs b/src/tools/rust-analyzer/crates/ide/src/markup.rs index 750d12542605..3eb9986c120f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/markup.rs +++ b/src/tools/rust-analyzer/crates/ide/src/markup.rs @@ -5,6 +5,8 @@ //! what is used by LSP, so let's keep it simple. use std::fmt; +use ide_db::impl_empty_upmap_from_ra_fixture; + #[derive(Clone, Default, Debug, Hash, PartialEq, Eq)] pub struct Markup { text: String, @@ -39,3 +41,5 @@ impl Markup { format!("```text\n{contents}\n```").into() } } + +impl_empty_upmap_from_ra_fixture!(Markup); 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 db1298385b11..40580080c089 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -14,6 +14,7 @@ use ide_db::{ defs::{Definition, find_std_module}, documentation::{Documentation, HasDocs}, famous_defs::FamousDefs, + ra_fixture::UpmapFromRaFixture, }; use span::Edition; use stdx::never; @@ -78,6 +79,44 @@ impl fmt::Debug for NavigationTarget { } } +impl UpmapFromRaFixture for NavigationTarget { + fn upmap_from_ra_fixture( + self, + analysis: &ide_db::ra_fixture::RaFixtureAnalysis, + _virtual_file_id: FileId, + real_file_id: FileId, + ) -> Result { + let virtual_file_id = self.file_id; + Ok(NavigationTarget { + file_id: real_file_id, + full_range: self.full_range.upmap_from_ra_fixture( + analysis, + virtual_file_id, + real_file_id, + )?, + focus_range: self.focus_range.upmap_from_ra_fixture( + analysis, + virtual_file_id, + real_file_id, + )?, + name: self.name.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?, + kind: self.kind.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?, + container_name: self.container_name.upmap_from_ra_fixture( + analysis, + virtual_file_id, + real_file_id, + )?, + description: self.description.upmap_from_ra_fixture( + analysis, + virtual_file_id, + real_file_id, + )?, + docs: self.docs.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?, + alias: self.alias.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?, + }) + } +} + pub(crate) trait ToNav { fn to_nav(&self, db: &RootDatabase) -> UpmappingResult; } diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 0189939eac31..a53a19299727 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -19,14 +19,17 @@ use hir::{PathResolution, Semantics}; use ide_db::{ - FileId, RootDatabase, + FileId, MiniCore, RootDatabase, defs::{Definition, NameClass, NameRefClass}, helpers::pick_best_token, + ra_fixture::UpmapFromRaFixture, search::{ReferenceCategory, SearchScope, UsageSearchResult}, }; use itertools::Itertools; +use macros::UpmapFromRaFixture; use nohash_hasher::IntMap; use span::Edition; +use syntax::AstToken; use syntax::{ AstNode, SyntaxKind::*, @@ -35,10 +38,12 @@ use syntax::{ match_ast, }; -use crate::{FilePosition, HighlightedRange, NavigationTarget, TryToNav, highlight_related}; +use crate::{ + Analysis, FilePosition, HighlightedRange, NavigationTarget, TryToNav, highlight_related, +}; /// Result of a reference search operation. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, UpmapFromRaFixture)] pub struct ReferenceSearchResult { /// Information about the declaration site of the searched item. /// For ADTs (structs/enums), this points to the type definition. @@ -54,7 +59,7 @@ pub struct ReferenceSearchResult { } /// Information about the declaration site of a searched item. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, UpmapFromRaFixture)] pub struct Declaration { /// Navigation information to jump to the declaration pub nav: NavigationTarget, @@ -82,6 +87,12 @@ pub struct Declaration { // // ![Find All References](https://user-images.githubusercontent.com/48062697/113020670-b7c34f00-917a-11eb-8003-370ac5f2b3cb.gif) +#[derive(Debug)] +pub struct FindAllRefsConfig<'a> { + pub search_scope: Option, + pub minicore: MiniCore<'a>, +} + /// Find all references to the item at the given position. /// /// # Arguments @@ -110,14 +121,14 @@ pub struct Declaration { pub(crate) fn find_all_refs( sema: &Semantics<'_, RootDatabase>, position: FilePosition, - search_scope: Option, + config: &FindAllRefsConfig<'_>, ) -> Option> { let _p = tracing::info_span!("find_all_refs").entered(); let syntax = sema.parse_guess_edition(position.file_id).syntax().clone(); let make_searcher = |literal_search: bool| { move |def: Definition| { let mut usages = - def.usages(sema).set_scope(search_scope.as_ref()).include_self_refs().all(); + def.usages(sema).set_scope(config.search_scope.as_ref()).include_self_refs().all(); if literal_search { retain_adt_literal_usages(&mut usages, def, sema); } @@ -165,6 +176,20 @@ pub(crate) fn find_all_refs( return Some(vec![res]); } + if let Some(token) = syntax.token_at_offset(position.offset).left_biased() + && let Some(token) = ast::String::cast(token.clone()) + && let Some((analysis, fixture_analysis)) = + Analysis::from_ra_fixture(sema, token.clone(), &token, config.minicore) + && let Some((virtual_file_id, file_offset)) = + fixture_analysis.map_offset_down(position.offset) + { + return analysis + .find_all_refs(FilePosition { file_id: virtual_file_id, offset: file_offset }, config) + .ok()?? + .upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, position.file_id) + .ok(); + } + match name_for_constructor_search(&syntax, position) { Some(name) => { let def = match NameClass::classify(sema, &name)? { @@ -433,10 +458,10 @@ fn handle_control_flow_keywords( mod tests { use expect_test::{Expect, expect}; use hir::EditionedFileId; - use ide_db::{FileId, RootDatabase}; + use ide_db::{FileId, MiniCore, RootDatabase}; use stdx::format_to; - use crate::{SearchScope, fixture}; + use crate::{SearchScope, fixture, references::FindAllRefsConfig}; #[test] fn exclude_tests() { @@ -1513,8 +1538,11 @@ fn main() { expect: Expect, ) { let (analysis, pos) = fixture::position(ra_fixture); - let refs = - analysis.find_all_refs(pos, search_scope.map(|it| it(&analysis.db))).unwrap().unwrap(); + let config = FindAllRefsConfig { + search_scope: search_scope.map(|it| it(&analysis.db)), + minicore: MiniCore::default(), + }; + let refs = analysis.find_all_refs(pos, &config).unwrap().unwrap(); let mut actual = String::new(); for mut refs in refs { diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index cc1bbfbe20d6..494701d97def 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -8,6 +8,7 @@ use hir::{ sym, }; use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; +use ide_db::impl_empty_upmap_from_ra_fixture; use ide_db::{ FilePosition, FxHashMap, FxIndexMap, FxIndexSet, RootDatabase, SymbolKind, base_db::RootQueryDb, @@ -17,6 +18,7 @@ use ide_db::{ search::{FileReferenceNode, SearchScope}, }; use itertools::Itertools; +use macros::UpmapFromRaFixture; use smallvec::SmallVec; use span::{Edition, TextSize}; use stdx::format_to; @@ -28,7 +30,7 @@ use syntax::{ use crate::{FileId, NavigationTarget, ToNav, TryToNav, references}; -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, UpmapFromRaFixture)] pub struct Runnable { pub use_name_in_title: bool, pub nav: NavigationTarget, @@ -37,6 +39,8 @@ pub struct Runnable { pub update_test: UpdateTest, } +impl_empty_upmap_from_ra_fixture!(RunnableKind, UpdateTest); + #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum TestId { Name(SmolStr), diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 453d6f537a8b..e261928c413f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -4,7 +4,7 @@ use arrayvec::ArrayVec; use hir::{Crate, Module, Semantics, db::HirDatabase}; use ide_db::{ - FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, + FileId, FileRange, FxHashMap, FxHashSet, MiniCore, RootDatabase, base_db::{RootQueryDb, SourceDatabase, VfsPath}, defs::{Definition, IdentClass}, documentation::Documentation, @@ -184,6 +184,7 @@ impl StaticIndex<'_> { closing_brace_hints_min_lines: Some(25), fields_to_resolve: InlayFieldsToResolve::empty(), range_exclusive_hints: false, + minicore: MiniCore::default(), }, file_id, None, @@ -215,6 +216,7 @@ impl StaticIndex<'_> { max_enum_variants_count: Some(5), max_subst_ty_len: SubstTyLen::Unlimited, show_drop_glue: true, + minicore: MiniCore::default(), }; let tokens = tokens.filter(|token| { matches!( diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index 0da9ee097ac3..66895cb0b053 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -1,7 +1,6 @@ pub(crate) mod tags; mod highlights; -mod injector; mod escape; mod format; @@ -16,7 +15,7 @@ use std::ops::ControlFlow; use either::Either; use hir::{DefWithBody, EditionedFileId, InFile, InRealFile, MacroKind, Name, Semantics}; -use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind}; +use ide_db::{FxHashMap, FxHashSet, MiniCore, Ranker, RootDatabase, SymbolKind}; use syntax::{ AstNode, AstToken, NodeOrToken, SyntaxKind::*, @@ -44,8 +43,8 @@ pub struct HlRange { pub binding_hash: Option, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct HighlightConfig { +#[derive(Copy, Clone, Debug)] +pub struct HighlightConfig<'a> { /// Whether to highlight strings pub strings: bool, /// Whether to highlight comments @@ -64,6 +63,7 @@ pub struct HighlightConfig { pub macro_bang: bool, /// Whether to highlight unresolved things be their syntax pub syntactic_name_ref_highlighting: bool, + pub minicore: MiniCore<'a>, } // Feature: Semantic Syntax Highlighting @@ -191,7 +191,7 @@ pub struct HighlightConfig { // ![Semantic Syntax Highlighting](https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png) pub(crate) fn highlight( db: &RootDatabase, - config: HighlightConfig, + config: &HighlightConfig<'_>, file_id: FileId, range_to_highlight: Option, ) -> Vec { @@ -226,7 +226,7 @@ pub(crate) fn highlight( fn traverse( hl: &mut Highlights, sema: &Semantics<'_, RootDatabase>, - config: HighlightConfig, + config: &HighlightConfig<'_>, InRealFile { file_id, value: root }: InRealFile<&SyntaxNode>, krate: Option, range_to_highlight: TextRange, @@ -426,12 +426,9 @@ fn traverse( let edition = descended_element.file_id.edition(sema.db); let (unsafe_ops, bindings_shadow_count) = match current_body { Some(current_body) => { - let (ops, bindings) = per_body_cache.entry(current_body).or_insert_with(|| { - ( - hir::attach_db(sema.db, || sema.get_unsafe_ops(current_body)), - Default::default(), - ) - }); + let (ops, bindings) = per_body_cache + .entry(current_body) + .or_insert_with(|| (sema.get_unsafe_ops(current_body), Default::default())); (&*ops, Some(bindings)) } None => (&empty, None), @@ -494,7 +491,7 @@ fn traverse( fn string_injections( hl: &mut Highlights, sema: &Semantics<'_, RootDatabase>, - config: HighlightConfig, + config: &HighlightConfig<'_>, file_id: EditionedFileId, krate: Option, token: SyntaxToken, @@ -591,7 +588,7 @@ fn descend_token( }) } -fn filter_by_config(highlight: &mut Highlight, config: HighlightConfig) -> bool { +fn filter_by_config(highlight: &mut Highlight, config: &HighlightConfig<'_>) -> bool { match &mut highlight.tag { HlTag::StringLiteral if !config.strings => return false, HlTag::Comment if !config.comments => return false, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs index 358ac9b4ef35..75e46b8ebfde 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs @@ -1,6 +1,7 @@ //! Renders a bit of code as HTML. use hir::{EditionedFileId, Semantics}; +use ide_db::MiniCore; use oorandom::Rand32; use stdx::format_to; use syntax::AstNode; @@ -12,7 +13,7 @@ use crate::{ pub(crate) fn highlight_as_html_with_config( db: &RootDatabase, - config: HighlightConfig, + config: &HighlightConfig<'_>, file_id: FileId, rainbow: bool, ) -> String { @@ -60,7 +61,7 @@ pub(crate) fn highlight_as_html_with_config( pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { highlight_as_html_with_config( db, - HighlightConfig { + &HighlightConfig { strings: true, comments: true, punctuation: true, @@ -70,6 +71,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo inject_doc_comment: true, macro_bang: true, syntactic_name_ref_highlighting: false, + minicore: MiniCore::default(), }, file_id, rainbow, 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 efc77823a2a4..7955f5ac0de9 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 @@ -4,9 +4,9 @@ use std::mem; use either::Either; use hir::{EditionedFileId, HirFileId, InFile, Semantics, sym}; +use ide_db::range_mapper::RangeMapper; use ide_db::{ - SymbolKind, active_parameter::ActiveParameter, defs::Definition, - documentation::docs_with_rangemap, rust_doc::is_rust_fence, + SymbolKind, defs::Definition, documentation::docs_with_rangemap, rust_doc::is_rust_fence, }; use syntax::{ AstToken, NodeOrToken, SyntaxNode, TextRange, TextSize, @@ -16,85 +16,56 @@ use syntax::{ use crate::{ Analysis, HlMod, HlRange, HlTag, RootDatabase, doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def}, - syntax_highlighting::{HighlightConfig, highlights::Highlights, injector::Injector}, + syntax_highlighting::{HighlightConfig, highlights::Highlights}, }; pub(super) fn ra_fixture( hl: &mut Highlights, sema: &Semantics<'_, RootDatabase>, - config: HighlightConfig, + config: &HighlightConfig<'_>, literal: &ast::String, expanded: &ast::String, ) -> Option<()> { - let active_parameter = - hir::attach_db(sema.db, || ActiveParameter::at_token(sema, expanded.syntax().clone()))?; - let has_rust_fixture_attr = active_parameter.attrs().is_some_and(|attrs| { - attrs.filter_map(|attr| attr.as_simple_path()).any(|path| { - path.segments() - .zip(["rust_analyzer", "rust_fixture"]) - .all(|(seg, name)| seg.name_ref().map_or(false, |nr| nr.text() == name)) - }) - }); - if !has_rust_fixture_attr { - return None; - } - let value = literal.value().ok()?; + let (analysis, fixture_analysis) = Analysis::from_ra_fixture_with_on_cursor( + sema, + literal.clone(), + expanded, + config.minicore, + &mut |range| { + hl.add(HlRange { + range, + highlight: HlTag::Keyword | HlMod::Injected, + binding_hash: None, + }); + }, + )?; if let Some(range) = literal.open_quote_text_range() { hl.add(HlRange { range, highlight: HlTag::StringLiteral.into(), binding_hash: None }) } - let mut inj = Injector::default(); - - let mut text = &*value; - let mut offset: TextSize = 0.into(); - - while !text.is_empty() { - let marker = "$0"; - let idx = text.find(marker).unwrap_or(text.len()); - let (chunk, next) = text.split_at(idx); - inj.add(chunk, TextRange::at(offset, TextSize::of(chunk))); - - text = next; - offset += TextSize::of(chunk); - - if let Some(next) = text.strip_prefix(marker) { - if let Some(range) = literal.map_range_up(TextRange::at(offset, TextSize::of(marker))) { - hl.add(HlRange { - range, - highlight: HlTag::Keyword | HlMod::Injected, - binding_hash: None, - }); - } - - text = next; - - let marker_len = TextSize::of(marker); - offset += marker_len; - } - } - - let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text()); - - for mut hl_range in analysis - .highlight( - HighlightConfig { - syntactic_name_ref_highlighting: false, - comments: true, - punctuation: true, - operator: true, - strings: true, - specialize_punctuation: config.specialize_punctuation, - specialize_operator: config.operator, - inject_doc_comment: config.inject_doc_comment, - macro_bang: config.macro_bang, - }, - tmp_file_id, - ) - .unwrap() - { - for range in inj.map_range_up(hl_range.range) { - if let Some(range) = literal.map_range_up(range) { + for tmp_file_id in fixture_analysis.files() { + for mut hl_range in analysis + .highlight( + HighlightConfig { + syntactic_name_ref_highlighting: false, + comments: true, + punctuation: true, + operator: true, + strings: true, + specialize_punctuation: config.specialize_punctuation, + specialize_operator: config.operator, + inject_doc_comment: config.inject_doc_comment, + macro_bang: config.macro_bang, + // What if there is a fixture inside a fixture? It's fixtures all the way down. + // (In fact, we have a fixture inside a fixture in our test suite!) + minicore: config.minicore, + }, + tmp_file_id, + ) + .unwrap() + { + for range in fixture_analysis.map_range_up(tmp_file_id, hl_range.range) { hl_range.range = range; hl_range.highlight |= HlMod::Injected; hl.add(hl_range); @@ -116,7 +87,7 @@ const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"]; pub(super) fn doc_comment( hl: &mut Highlights, sema: &Semantics<'_, RootDatabase>, - config: HighlightConfig, + config: &HighlightConfig<'_>, src_file_id: EditionedFileId, node: &SyntaxNode, ) { @@ -128,39 +99,37 @@ pub(super) fn doc_comment( // Extract intra-doc links and emit highlights for them. if let Some((docs, doc_mapping)) = docs_with_rangemap(sema.db, &attributes) { - hir::attach_db(sema.db, || { - extract_definitions_from_docs(&docs) - .into_iter() - .filter_map(|(range, link, ns)| { - doc_mapping - .map(range) - .filter(|(mapping, _)| mapping.file_id == src_file_id) - .and_then(|(InFile { value: mapped_range, .. }, attr_id)| { - Some(mapped_range).zip(resolve_doc_path_for_def( - sema.db, - def, - &link, - ns, - attr_id.is_inner_attr(), - )) - }) - }) - .for_each(|(range, def)| { - hl.add(HlRange { - range, - highlight: module_def_to_hl_tag(def) - | HlMod::Documentation - | HlMod::Injected - | HlMod::IntraDocLink, - binding_hash: None, + extract_definitions_from_docs(&docs) + .into_iter() + .filter_map(|(range, link, ns)| { + doc_mapping + .map(range) + .filter(|(mapping, _)| mapping.file_id == src_file_id) + .and_then(|(InFile { value: mapped_range, .. }, attr_id)| { + Some(mapped_range).zip(resolve_doc_path_for_def( + sema.db, + def, + &link, + ns, + attr_id.is_inner_attr(), + )) }) + }) + .for_each(|(range, def)| { + hl.add(HlRange { + range, + highlight: module_def_to_hl_tag(def) + | HlMod::Documentation + | HlMod::Injected + | HlMod::IntraDocLink, + binding_hash: None, }) - }); + }) } // Extract doc-test sources from the docs and calculate highlighting for them. - let mut inj = Injector::default(); + let mut inj = RangeMapper::default(); inj.add_unmapped("fn doctest() {\n"); let attrs_source_map = attributes.source_map(sema.db); @@ -249,7 +218,7 @@ pub(super) fn doc_comment( if let Ok(ranges) = analysis.with_db(|db| { super::highlight( db, - HighlightConfig { + &HighlightConfig { syntactic_name_ref_highlighting: true, comments: true, punctuation: true, @@ -259,6 +228,7 @@ pub(super) fn doc_comment( specialize_operator: config.operator, inject_doc_comment: config.inject_doc_comment, macro_bang: config.macro_bang, + minicore: config.minicore, }, tmp_file_id, None, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/injector.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/injector.rs deleted file mode 100644 index c30f79732496..000000000000 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/injector.rs +++ /dev/null @@ -1,77 +0,0 @@ -//! Extracts a subsequence of a text document, remembering the mapping of ranges -//! between original and extracted texts. -use std::ops::{self, Sub}; - -use stdx::equal_range_by; -use syntax::{TextRange, TextSize}; - -#[derive(Default)] -pub(super) struct Injector { - buf: String, - ranges: Vec<(TextRange, Option>)>, -} - -impl Injector { - pub(super) fn add(&mut self, text: &str, source_range: TextRange) { - let len = TextSize::of(text); - assert_eq!(len, source_range.len()); - self.add_impl(text, Some(source_range.start())); - } - - pub(super) fn add_unmapped(&mut self, text: &str) { - self.add_impl(text, None); - } - - fn add_impl(&mut self, text: &str, source: Option) { - let len = TextSize::of(text); - let target_range = TextRange::at(TextSize::of(&self.buf), len); - self.ranges.push((target_range, source.map(|it| Delta::new(target_range.start(), it)))); - self.buf.push_str(text); - } - - pub(super) fn take_text(&mut self) -> String { - std::mem::take(&mut self.buf) - } - - pub(super) fn map_range_up(&self, range: TextRange) -> impl Iterator + '_ { - equal_range_by(&self.ranges, |&(r, _)| TextRange::ordering(r, range)).filter_map(move |i| { - let (target_range, delta) = self.ranges[i]; - let intersection = target_range.intersect(range).unwrap(); - Some(intersection + delta?) - }) - } -} - -#[derive(Clone, Copy)] -enum Delta { - Add(T), - Sub(T), -} - -impl Delta { - fn new(from: T, to: T) -> Delta - where - T: Ord + Sub, - { - if to >= from { Delta::Add(to - from) } else { Delta::Sub(from - to) } - } -} - -impl ops::Add> for TextSize { - type Output = TextSize; - - fn add(self, rhs: Delta) -> TextSize { - match rhs { - Delta::Add(it) => self + it, - Delta::Sub(it) => self - it, - } - } -} - -impl ops::Add> for TextRange { - type Output = TextRange; - - fn add(self, rhs: Delta) -> TextRange { - TextRange::at(self.start() + rhs, self.len()) - } -} diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index 3b468ab6dba6..579c6ceadcb8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html @@ -43,18 +43,19 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {}
 
 fn main() {
-    fixture(r#"
-trait Foo {
-    fn foo() {
-        println!("2 + 2 = {}", 4);
-    }
+    fixture(r#"
+@@- minicore: sized
+trait Foo: Sized {
+    fn foo() {
+        println!("2 + 2 = {}", 4);
+    }
 }"#
     );
-    fixture(r"
-fn foo() {
-    foo($0{
-        92
-    }$0)
+    fixture(r"
+fn foo() {
+    foo($0{
+        92
+    }$0)
 }"
     );
 }
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html new file mode 100644 index 000000000000..fc2d9a387016 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html @@ -0,0 +1,61 @@ + + +
fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {}
+
+fn main() {
+    fixture(r#"
+@@- /main.rs crate:main deps:other_crate
+fn test() {
+    let x = other_crate::foo::S::thing();
+    x;
+} //^ i128
+
+@@- /lib.rs crate:other_crate
+pub mod foo {
+    pub struct S;
+    impl S {
+        pub fn thing() -> i128 { 0 }
+    }
+}
+    "#);
+}
\ No newline at end of file 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 8198701d6843..4e84127c29f8 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 @@ -1,13 +1,13 @@ use std::time::Instant; use expect_test::{ExpectFile, expect_file}; -use ide_db::SymbolKind; +use ide_db::{MiniCore, SymbolKind}; use span::Edition; use test_utils::{AssertLinear, bench, bench_fixture, skip_slow_tests}; use crate::{FileRange, HighlightConfig, HlTag, TextRange, fixture}; -const HL_CONFIG: HighlightConfig = HighlightConfig { +const HL_CONFIG: HighlightConfig<'_> = HighlightConfig { strings: true, comments: true, punctuation: true, @@ -17,6 +17,7 @@ const HL_CONFIG: HighlightConfig = HighlightConfig { inject_doc_comment: true, macro_bang: true, syntactic_name_ref_highlighting: false, + minicore: MiniCore::default(), }; #[test] @@ -1016,6 +1017,35 @@ impl t for foo { ) } +#[test] +fn test_injection_2() { + check_highlighting( + r##" +fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {} + +fn main() { + fixture(r#" +@@- /main.rs crate:main deps:other_crate +fn test() { + let x = other_crate::foo::S::thing(); + x; +} //^ i128 + +@@- /lib.rs crate:other_crate +pub mod foo { + pub struct S; + impl S { + pub fn thing() -> i128 { 0 } + } +} + "#); +} +"##, + expect_file!["./test_data/highlight_injection_2.html"], + false, + ); +} + #[test] fn test_injection() { check_highlighting( @@ -1024,7 +1054,8 @@ fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {} fn main() { fixture(r#" -trait Foo { +@@- minicore: sized +trait Foo: Sized { fn foo() { println!("2 + 2 = {}", 4); } @@ -1223,7 +1254,7 @@ fn foo(x: &fn(&dyn Trait)) {} /// Note that the `snapshot` file is overwritten by the rendered HTML. fn check_highlighting_with_config( #[rust_analyzer::rust_fixture] ra_fixture: &str, - config: HighlightConfig, + config: HighlightConfig<'_>, expect: ExpectFile, rainbow: bool, ) { diff --git a/src/tools/rust-analyzer/crates/macros/src/lib.rs b/src/tools/rust-analyzer/crates/macros/src/lib.rs index 8bafcf498c51..3f90ecc8f902 100644 --- a/src/tools/rust-analyzer/crates/macros/src/lib.rs +++ b/src/tools/rust-analyzer/crates/macros/src/lib.rs @@ -162,3 +162,42 @@ fn has_ignore_attr(attrs: &[syn::Attribute], name: &'static str, meta: &'static ignored } + +decl_derive!( + [UpmapFromRaFixture] => upmap_from_ra_fixture +); + +fn upmap_from_ra_fixture(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + if let syn::Data::Union(_) = s.ast().data { + panic!("cannot derive on union") + } + + s.add_bounds(synstructure::AddBounds::Generics); + s.bind_with(|_| synstructure::BindStyle::Move); + let body = s.each_variant(|vi| { + let bindings = vi.bindings(); + vi.construct(|_, index| { + let bind = &bindings[index]; + + quote! { + ::ide_db::ra_fixture::UpmapFromRaFixture::upmap_from_ra_fixture( + #bind, __analysis, __virtual_file_id, __real_file_id, + )? + } + }) + }); + + s.bound_impl( + quote!(::ide_db::ra_fixture::UpmapFromRaFixture), + quote! { + fn upmap_from_ra_fixture( + self, + __analysis: &::ide_db::ra_fixture::RaFixtureAnalysis, + __virtual_file_id: ::ide_db::ra_fixture::FileId, + __real_file_id: ::ide_db::ra_fixture::FileId, + ) -> Result { + Ok(match self { #body }) + } + }, + ) +} 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 2a9ef981291e..717bd230a21e 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 @@ -25,7 +25,7 @@ use ide::{ InlayHintsConfig, LineCol, RootDatabase, }; use ide_db::{ - EditionedFileId, LineIndexDatabase, SnippetCap, + EditionedFileId, LineIndexDatabase, MiniCore, SnippetCap, base_db::{SourceDatabase, salsa::Database}, }; use itertools::Itertools; @@ -1194,6 +1194,7 @@ impl flags::AnalysisStats { closing_brace_hints_min_lines: Some(20), fields_to_resolve: InlayFieldsToResolve::empty(), range_exclusive_hints: true, + minicore: MiniCore::default(), }, analysis.editioned_file_id_to_vfs(file_id), None, @@ -1203,26 +1204,25 @@ impl flags::AnalysisStats { bar.finish_and_clear(); let mut bar = create_bar(); + let annotation_config = AnnotationConfig { + binary_target: true, + annotate_runnables: true, + annotate_impls: true, + annotate_references: false, + annotate_method_references: false, + annotate_enum_variant_references: false, + location: ide::AnnotationLocation::AboveName, + minicore: MiniCore::default(), + }; for &file_id in file_ids { let msg = format!("annotations: {}", vfs.file_path(file_id.file_id(db))); bar.set_message(move || msg.clone()); analysis - .annotations( - &AnnotationConfig { - binary_target: true, - annotate_runnables: true, - annotate_impls: true, - annotate_references: false, - annotate_method_references: false, - annotate_enum_variant_references: false, - location: ide::AnnotationLocation::AboveName, - }, - analysis.editioned_file_id_to_vfs(file_id), - ) + .annotations(&annotation_config, analysis.editioned_file_id_to_vfs(file_id)) .unwrap() .into_iter() .for_each(|annotation| { - _ = analysis.resolve_annotation(annotation); + _ = analysis.resolve_annotation(&annotation_config, annotation); }); bar.inc(1); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 96b65838ae42..652c2e32ffa6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -8,14 +8,14 @@ use std::{env, fmt, iter, ops::Not, sync::OnceLock}; use cfg::{CfgAtom, CfgDiff}; use hir::Symbol; use ide::{ - AssistConfig, CallHierarchyConfig, CallableSnippets, CompletionConfig, - CompletionFieldsToResolve, DiagnosticsConfig, GenericParameterHints, HighlightConfig, - HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve, InlayHintsConfig, - JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope, - SourceRootId, + AnnotationConfig, AssistConfig, CallHierarchyConfig, CallableSnippets, CompletionConfig, + CompletionFieldsToResolve, DiagnosticsConfig, GenericParameterHints, GotoDefinitionConfig, + HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve, + InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, + Snippet, SnippetScope, SourceRootId, }; use ide_db::{ - SnippetCap, + MiniCore, SnippetCap, assists::ExprFillDefaultMode, imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, }; @@ -1454,6 +1454,23 @@ impl LensConfig { pub fn references(&self) -> bool { self.method_refs || self.refs_adt || self.refs_trait || self.enum_variant_refs } + + pub fn into_annotation_config<'a>( + self, + binary_target: bool, + minicore: MiniCore<'a>, + ) -> AnnotationConfig<'a> { + AnnotationConfig { + binary_target, + annotate_runnables: self.runnable(), + annotate_impls: self.implementations, + annotate_references: self.refs_adt, + annotate_method_references: self.method_refs, + annotate_enum_variant_references: self.enum_variant_refs, + location: self.location.into(), + minicore, + } + } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -1688,11 +1705,15 @@ impl Config { } } - pub fn call_hierarchy(&self) -> CallHierarchyConfig { - CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned() } + pub fn call_hierarchy<'a>(&self, minicore: MiniCore<'a>) -> CallHierarchyConfig<'a> { + CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned(), minicore } } - pub fn completion(&self, source_root: Option) -> CompletionConfig<'_> { + pub fn completion<'a>( + &'a self, + source_root: Option, + minicore: MiniCore<'a>, + ) -> CompletionConfig<'a> { let client_capability_fields = self.completion_resolve_support_properties(); CompletionConfig { enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(), @@ -1746,6 +1767,7 @@ impl Config { }) .collect(), exclude_traits: self.completion_excludeTraits(source_root), + minicore, } } @@ -1820,7 +1842,7 @@ impl Config { } } - pub fn hover(&self) -> HoverConfig { + pub fn hover<'a>(&self, minicore: MiniCore<'a>) -> HoverConfig<'a> { let mem_kind = |kind| match kind { MemoryLayoutHoverRenderKindDef::Both => MemoryLayoutHoverRenderKind::Both, MemoryLayoutHoverRenderKindDef::Decimal => MemoryLayoutHoverRenderKind::Decimal, @@ -1853,10 +1875,15 @@ impl Config { None => ide::SubstTyLen::Unlimited, }, show_drop_glue: *self.hover_dropGlue_enable(), + minicore, } } - pub fn inlay_hints(&self) -> InlayHintsConfig { + pub fn goto_definition<'a>(&self, minicore: MiniCore<'a>) -> GotoDefinitionConfig<'a> { + GotoDefinitionConfig { minicore } + } + + pub fn inlay_hints<'a>(&self, minicore: MiniCore<'a>) -> InlayHintsConfig<'a> { let client_capability_fields = self.inlay_hint_resolve_support_properties(); InlayHintsConfig { @@ -1938,6 +1965,7 @@ impl Config { ), implicit_drop_hints: self.inlayHints_implicitDrops_enable().to_owned(), range_exclusive_hints: self.inlayHints_rangeExclusiveHints_enable().to_owned(), + minicore, } } @@ -1975,7 +2003,7 @@ impl Config { self.semanticHighlighting_nonStandardTokens().to_owned() } - pub fn highlighting_config(&self) -> HighlightConfig { + pub fn highlighting_config<'a>(&self, minicore: MiniCore<'a>) -> HighlightConfig<'a> { HighlightConfig { strings: self.semanticHighlighting_strings_enable().to_owned(), comments: self.semanticHighlighting_comments_enable().to_owned(), @@ -1990,6 +2018,7 @@ impl Config { .to_owned(), inject_doc_comment: self.semanticHighlighting_doc_comment_inject_enable().to_owned(), syntactic_name_ref_highlighting: false, + minicore, } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index ce6644f725ca..f557dd5cb092 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -13,7 +13,10 @@ use cargo_metadata::PackageId; use crossbeam_channel::{Receiver, Sender, unbounded}; use hir::ChangeWithProcMacros; use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId}; -use ide_db::base_db::{Crate, ProcMacroPaths, SourceDatabase}; +use ide_db::{ + MiniCore, + base_db::{Crate, ProcMacroPaths, SourceDatabase}, +}; use itertools::Itertools; use load_cargo::SourceRootConfig; use lsp_types::{SemanticTokens, Url}; @@ -188,6 +191,14 @@ pub(crate) struct GlobalState { /// This is marked true if we failed to load a crate root file at crate graph creation, /// which will usually end up causing a bunch of incorrect diagnostics on startup. pub(crate) incomplete_crate_graph: bool, + + pub(crate) minicore: MiniCoreRustAnalyzerInternalOnly, +} + +// FIXME: This should move to the VFS once the rewrite is done. +#[derive(Debug, Clone, Default)] +pub(crate) struct MiniCoreRustAnalyzerInternalOnly { + pub(crate) minicore_text: Option, } /// An immutable snapshot of the world's state at a point in time. @@ -204,6 +215,7 @@ pub(crate) struct GlobalStateSnapshot { // FIXME: Can we derive this from somewhere else? pub(crate) proc_macros_loaded: bool, pub(crate) flycheck: Arc<[FlycheckHandle]>, + minicore: MiniCoreRustAnalyzerInternalOnly, } impl std::panic::UnwindSafe for GlobalStateSnapshot {} @@ -304,6 +316,8 @@ impl GlobalState { deferred_task_queue: task_queue, incomplete_crate_graph: false, + + minicore: MiniCoreRustAnalyzerInternalOnly::default(), }; // Apply any required database inputs from the config. this.update_configuration(config); @@ -550,6 +564,7 @@ impl GlobalState { workspaces: Arc::clone(&self.workspaces), analysis: self.analysis_host.analysis(), vfs: Arc::clone(&self.vfs), + minicore: self.minicore.clone(), check_fixes: Arc::clone(&self.diagnostics.check_fixes), mem_docs: self.mem_docs.clone(), semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache), @@ -838,6 +853,14 @@ impl GlobalStateSnapshot { pub(crate) fn file_exists(&self, file_id: FileId) -> bool { self.vfs.read().0.exists(file_id) } + + #[inline] + pub(crate) fn minicore(&self) -> MiniCore<'_> { + match &self.minicore.minicore_text { + Some(minicore) => MiniCore::new(minicore), + None => MiniCore::default(), + } + } } pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 6cb28aecf748..55d092f30f6b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -7,8 +7,8 @@ use anyhow::Context; use base64::{Engine, prelude::BASE64_STANDARD}; use ide::{ - AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve, - FilePosition, FileRange, FileStructureConfig, HoverAction, HoverGotoTypeData, + AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve, FilePosition, + FileRange, FileStructureConfig, FindAllRefsConfig, HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, }; @@ -811,7 +811,8 @@ pub(crate) fn handle_goto_definition( let _p = tracing::info_span!("handle_goto_definition").entered(); let position = try_default!(from_proto::file_position(&snap, params.text_document_position_params)?); - let nav_info = match snap.analysis.goto_definition(position)? { + let config = snap.config.goto_definition(snap.minicore()); + let nav_info = match snap.analysis.goto_definition(position, &config)? { None => return Ok(None), Some(it) => it, }; @@ -829,7 +830,8 @@ pub(crate) fn handle_goto_declaration( &snap, params.text_document_position_params.clone() )?); - let nav_info = match snap.analysis.goto_declaration(position)? { + let config = snap.config.goto_definition(snap.minicore()); + let nav_info = match snap.analysis.goto_declaration(position, &config)? { None => return handle_goto_definition(snap, params), Some(it) => it, }; @@ -1106,7 +1108,7 @@ pub(crate) fn handle_completion( context.and_then(|ctx| ctx.trigger_character).and_then(|s| s.chars().next()); let source_root = snap.analysis.source_root_id(position.file_id)?; - let completion_config = &snap.config.completion(Some(source_root)); + let completion_config = &snap.config.completion(Some(source_root), snap.minicore()); // FIXME: We should fix up the position when retrying the cancelled request instead position.offset = position.offset.min(line_index.index.len()); let items = match snap.analysis.completions( @@ -1160,7 +1162,8 @@ pub(crate) fn handle_completion_resolve( }; let source_root = snap.analysis.source_root_id(file_id)?; - let mut forced_resolve_completions_config = snap.config.completion(Some(source_root)); + let mut forced_resolve_completions_config = + snap.config.completion(Some(source_root), snap.minicore()); forced_resolve_completions_config.fields_to_resolve = CompletionFieldsToResolve::empty(); let position = FilePosition { file_id, offset }; @@ -1274,7 +1277,7 @@ pub(crate) fn handle_hover( }; let file_range = try_default!(from_proto::file_range(&snap, ¶ms.text_document, range)?); - let hover = snap.config.hover(); + let hover = snap.config.hover(snap.minicore()); let info = match snap.analysis.hover(&hover, file_range)? { None => return Ok(None), Some(info) => info, @@ -1360,7 +1363,11 @@ pub(crate) fn handle_references( let exclude_imports = snap.config.find_all_refs_exclude_imports(); let exclude_tests = snap.config.find_all_refs_exclude_tests(); - let Some(refs) = snap.analysis.find_all_refs(position, None)? else { + let Some(refs) = snap.analysis.find_all_refs( + position, + &FindAllRefsConfig { search_scope: None, minicore: snap.minicore() }, + )? + else { return Ok(None); }; @@ -1615,8 +1622,8 @@ pub(crate) fn handle_code_lens( let target_spec = TargetSpec::for_file(&snap, file_id)?; let annotations = snap.analysis.annotations( - &AnnotationConfig { - binary_target: target_spec + &lens_config.into_annotation_config( + target_spec .map(|spec| { matches!( spec.target_kind(), @@ -1624,13 +1631,8 @@ pub(crate) fn handle_code_lens( ) }) .unwrap_or(false), - annotate_runnables: lens_config.runnable(), - annotate_impls: lens_config.implementations, - annotate_references: lens_config.refs_adt, - annotate_method_references: lens_config.method_refs, - annotate_enum_variant_references: lens_config.enum_variant_refs, - location: lens_config.location.into(), - }, + snap.minicore(), + ), file_id, )?; @@ -1653,7 +1655,8 @@ pub(crate) fn handle_code_lens_resolve( let Some(annotation) = from_proto::annotation(&snap, code_lens.range, resolve)? else { return Ok(code_lens); }; - let annotation = snap.analysis.resolve_annotation(annotation)?; + let config = snap.config.lens().into_annotation_config(false, snap.minicore()); + let annotation = snap.analysis.resolve_annotation(&config, annotation)?; let mut acc = Vec::new(); to_proto::code_lens(&mut acc, &snap, annotation)?; @@ -1736,7 +1739,7 @@ pub(crate) fn handle_inlay_hints( range.end().min(line_index.index.len()), ); - let inlay_hints_config = snap.config.inlay_hints(); + let inlay_hints_config = snap.config.inlay_hints(snap.minicore()); Ok(Some( snap.analysis .inlay_hints(&inlay_hints_config, file_id, Some(range))? @@ -1777,7 +1780,7 @@ pub(crate) fn handle_inlay_hints_resolve( let line_index = snap.file_line_index(file_id)?; let range = from_proto::text_range(&line_index, resolve_data.resolve_range)?; - let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(); + let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(snap.minicore()); forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty(); let resolve_hints = snap.analysis.inlay_hints_resolve( &forced_resolve_inlay_hints_config, @@ -1816,7 +1819,8 @@ pub(crate) fn handle_call_hierarchy_prepare( let position = try_default!(from_proto::file_position(&snap, params.text_document_position_params)?); - let nav_info = match snap.analysis.call_hierarchy(position)? { + let config = snap.config.call_hierarchy(snap.minicore()); + let nav_info = match snap.analysis.call_hierarchy(position, &config)? { None => return Ok(None), Some(it) => it, }; @@ -1842,8 +1846,8 @@ pub(crate) fn handle_call_hierarchy_incoming( let frange = try_default!(from_proto::file_range(&snap, &doc, item.selection_range)?); let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; - let config = snap.config.call_hierarchy(); - let call_items = match snap.analysis.incoming_calls(config, fpos)? { + let config = snap.config.call_hierarchy(snap.minicore()); + let call_items = match snap.analysis.incoming_calls(&config, fpos)? { None => return Ok(None), Some(it) => it, }; @@ -1881,8 +1885,8 @@ pub(crate) fn handle_call_hierarchy_outgoing( let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; let line_index = snap.file_line_index(fpos.file_id)?; - let config = snap.config.call_hierarchy(); - let call_items = match snap.analysis.outgoing_calls(config, fpos)? { + let config = snap.config.call_hierarchy(snap.minicore()); + let call_items = match snap.analysis.outgoing_calls(&config, fpos)? { None => return Ok(None), Some(it) => it, }; @@ -1916,7 +1920,7 @@ pub(crate) fn handle_semantic_tokens_full( let text = snap.analysis.file_text(file_id)?; let line_index = snap.file_line_index(file_id)?; - let mut highlight_config = snap.config.highlighting_config(); + let mut highlight_config = snap.config.highlighting_config(snap.minicore()); // Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet. highlight_config.syntactic_name_ref_highlighting = snap.workspaces.is_empty() || !snap.proc_macros_loaded; @@ -1946,7 +1950,7 @@ pub(crate) fn handle_semantic_tokens_full_delta( let text = snap.analysis.file_text(file_id)?; let line_index = snap.file_line_index(file_id)?; - let mut highlight_config = snap.config.highlighting_config(); + let mut highlight_config = snap.config.highlighting_config(snap.minicore()); // Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet. highlight_config.syntactic_name_ref_highlighting = snap.workspaces.is_empty() || !snap.proc_macros_loaded; @@ -1988,7 +1992,7 @@ pub(crate) fn handle_semantic_tokens_range( let text = snap.analysis.file_text(frange.file_id)?; let line_index = snap.file_line_index(frange.file_id)?; - let mut highlight_config = snap.config.highlighting_config(); + let mut highlight_config = snap.config.highlighting_config(snap.minicore()); // Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet. highlight_config.syntactic_name_ref_highlighting = snap.workspaces.is_empty() || !snap.proc_macros_loaded; @@ -2156,7 +2160,13 @@ fn show_ref_command_link( ) -> Option { if snap.config.hover_actions().references && snap.config.client_commands().show_reference - && let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None) + && let Some(ref_search_res) = snap + .analysis + .find_all_refs( + *position, + &FindAllRefsConfig { search_scope: None, minicore: snap.minicore() }, + ) + .unwrap_or(None) { let uri = to_proto::url(snap, position.file_id); let line_index = snap.file_line_index(position.file_id).ok()?; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index 84b7888258f8..38ee9cbe7fc8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -16,7 +16,7 @@ use ide::{ FilePosition, TextSize, }; use ide_db::{ - SnippetCap, + MiniCore, SnippetCap, imports::insert_use::{ImportGranularity, InsertUseConfig}, }; use project_model::CargoConfig; @@ -186,6 +186,7 @@ fn integrated_completion_benchmark() { exclude_traits: &[], enable_auto_await: true, enable_auto_iter: true, + minicore: MiniCore::default(), }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -240,6 +241,7 @@ fn integrated_completion_benchmark() { exclude_traits: &[], enable_auto_await: true, enable_auto_iter: true, + minicore: MiniCore::default(), }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -292,6 +294,7 @@ fn integrated_completion_benchmark() { exclude_traits: &[], enable_auto_await: true, enable_auto_iter: true, + minicore: MiniCore::default(), }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; 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 d51ddb86d197..cd384ca713ec 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 @@ -16,7 +16,9 @@ use ide::{ SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, UpdateTest, }; -use ide_db::{FxHasher, assists, rust_doc::format_docs, source_change::ChangeAnnotationId}; +use ide_db::{ + FxHasher, MiniCore, assists, rust_doc::format_docs, source_change::ChangeAnnotationId, +}; use itertools::Itertools; use paths::{Utf8Component, Utf8Prefix}; use semver::VersionReq; @@ -270,7 +272,7 @@ pub(crate) fn completion_items( ); } - if let Some(limit) = config.completion(None).limit { + if let Some(limit) = config.completion(None, MiniCore::default()).limit { res.sort_by(|item1, item2| item1.sort_text.cmp(&item2.sort_text)); res.truncate(limit); } @@ -400,16 +402,17 @@ fn completion_item( set_score(&mut lsp_item, max_relevance, item.relevance); - let imports = - if config.completion(None).enable_imports_on_the_fly && !item.import_to_add.is_empty() { - item.import_to_add - .clone() - .into_iter() - .map(|import_path| lsp_ext::CompletionImport { full_import_path: import_path }) - .collect() - } else { - Vec::new() - }; + let imports = if config.completion(None, MiniCore::default()).enable_imports_on_the_fly + && !item.import_to_add.is_empty() + { + item.import_to_add + .clone() + .into_iter() + .map(|import_path| lsp_ext::CompletionImport { full_import_path: import_path }) + .collect() + } else { + Vec::new() + }; let (ref_resolve_data, resolve_data) = if something_to_resolve || !imports.is_empty() { let ref_resolve_data = if ref_match.is_some() { let ref_resolve_data = lsp_ext::CompletionResolveData { 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 3e80e8b7bdfb..c0947b2a291e 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 @@ -847,6 +847,13 @@ impl GlobalState { self.debounce_workspace_fetch(); let vfs = &mut self.vfs.write().0; for (path, contents) in files { + if matches!(path.name_and_extension(), Some(("minicore", Some("rs")))) { + // Not a lot of bad can happen from mistakenly identifying `minicore`, so proceed with that. + self.minicore.minicore_text = contents + .as_ref() + .and_then(|contents| String::from_utf8(contents.clone()).ok()); + } + let path = VfsPath::from(path); // if the file is in mem docs, it's managed by the client via notifications // so only set it if its not in there diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index d9223e8216da..e1a9f3ac0341 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -201,6 +201,10 @@ pub trait IsString: AstToken { None } } + fn map_offset_down(&self, offset: TextSize) -> Option { + let contents_range = self.text_range_between_quotes()?; + offset.checked_sub(contents_range.start()) + } } impl IsString for ast::String { diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index a4549794db33..aefe81f83e29 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -24,7 +24,7 @@ use paths::AbsPathBuf; use span::{Edition, FileId, Span}; use stdx::itertools::Itertools; use test_utils::{ - CURSOR_MARKER, ESCAPED_CURSOR_MARKER, Fixture, FixtureWithProjectMeta, RangeOrOffset, + CURSOR_MARKER, ESCAPED_CURSOR_MARKER, Fixture, FixtureWithProjectMeta, MiniCore, RangeOrOffset, extract_range_or_offset, }; use triomphe::Arc; @@ -69,7 +69,12 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { proc_macros: Vec<(String, ProcMacro)>, ) -> Self { let mut db = Self::default(); - let fixture = ChangeFixture::parse_with_proc_macros(&db, ra_fixture, proc_macros); + let fixture = ChangeFixture::parse_with_proc_macros( + &db, + ra_fixture, + MiniCore::RAW_SOURCE, + proc_macros, + ); fixture.change.apply(&mut db); assert!(fixture.file_position.is_none()); db @@ -112,8 +117,10 @@ impl WithFixture for DB pub struct ChangeFixture { pub file_position: Option<(EditionedFileId, RangeOrOffset)>, + pub file_lines: Vec, pub files: Vec, pub change: ChangeWithProcMacros, + pub sysroot_files: Vec, } const SOURCE_ROOT_PREFIX: &str = "/"; @@ -123,12 +130,13 @@ impl ChangeFixture { db: &dyn salsa::Database, #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> ChangeFixture { - Self::parse_with_proc_macros(db, ra_fixture, Vec::new()) + Self::parse_with_proc_macros(db, ra_fixture, MiniCore::RAW_SOURCE, Vec::new()) } pub fn parse_with_proc_macros( db: &dyn salsa::Database, #[rust_analyzer::rust_fixture] ra_fixture: &str, + minicore_raw: &str, mut proc_macro_defs: Vec<(String, ProcMacro)>, ) -> ChangeFixture { let FixtureWithProjectMeta { @@ -149,6 +157,8 @@ impl ChangeFixture { let mut source_change = FileChange::default(); let mut files = Vec::new(); + let mut sysroot_files = Vec::new(); + let mut file_lines = Vec::new(); let mut crate_graph = CrateGraphBuilder::default(); let mut crates = FxIndexMap::default(); let mut crate_deps = Vec::new(); @@ -173,6 +183,8 @@ impl ChangeFixture { let proc_macro_cwd = Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())); for entry in fixture { + file_lines.push(entry.line); + let mut range_or_offset = None; let text = if entry.text.contains(CURSOR_MARKER) { if entry.text.contains(ESCAPED_CURSOR_MARKER) { @@ -259,7 +271,9 @@ impl ChangeFixture { fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_owned())); roots.push(SourceRoot::new_library(fs)); - source_change.change_file(core_file, Some(mini_core.source_code())); + sysroot_files.push(core_file); + + source_change.change_file(core_file, Some(mini_core.source_code(minicore_raw))); let core_crate = crate_graph.add_crate_root( core_file, @@ -348,6 +362,8 @@ impl ChangeFixture { ); roots.push(SourceRoot::new_library(fs)); + sysroot_files.push(proc_lib_file); + source_change.change_file(proc_lib_file, Some(source)); let all_crates = crate_graph.iter().collect::>(); @@ -396,7 +412,7 @@ impl ChangeFixture { change.source_change.set_roots(roots); change.source_change.set_crate_graph(crate_graph); - ChangeFixture { file_position, files, change } + ChangeFixture { file_position, file_lines, files, change, sysroot_files } } } diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs index c024089a016f..559894ee6205 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs @@ -132,13 +132,17 @@ pub struct Fixture { pub library: bool, /// Actual file contents. All meta comments are stripped. pub text: String, + /// The line number in the original fixture of the beginning of this fixture. + pub line: usize, } +#[derive(Debug)] pub struct MiniCore { activated_flags: Vec, valid_flags: Vec, } +#[derive(Debug)] pub struct FixtureWithProjectMeta { pub fixture: Vec, pub mini_core: Option, @@ -184,40 +188,49 @@ impl FixtureWithProjectMeta { let mut mini_core = None; let mut res: Vec = Vec::new(); let mut proc_macro_names = vec![]; + let mut first_row = 0; if let Some(meta) = fixture.strip_prefix("//- toolchain:") { + first_row += 1; let (meta, remain) = meta.split_once('\n').unwrap(); toolchain = Some(meta.trim().to_owned()); fixture = remain; } if let Some(meta) = fixture.strip_prefix("//- target_data_layout:") { + first_row += 1; let (meta, remain) = meta.split_once('\n').unwrap(); meta.trim().clone_into(&mut target_data_layout); fixture = remain; } if let Some(meta) = fixture.strip_prefix("//- target_arch:") { + first_row += 1; let (meta, remain) = meta.split_once('\n').unwrap(); meta.trim().clone_into(&mut target_arch); fixture = remain; } if let Some(meta) = fixture.strip_prefix("//- proc_macros:") { + first_row += 1; let (meta, remain) = meta.split_once('\n').unwrap(); proc_macro_names = meta.split(',').map(|it| it.trim().to_owned()).collect(); fixture = remain; } if let Some(meta) = fixture.strip_prefix("//- minicore:") { + first_row += 1; let (meta, remain) = meta.split_once('\n').unwrap(); mini_core = Some(MiniCore::parse(meta)); fixture = remain; } - let default = if fixture.contains("//-") { None } else { Some("//- /main.rs") }; + let default = + if fixture.contains("//- /") { None } else { Some((first_row - 1, "//- /main.rs")) }; - for (ix, line) in default.into_iter().chain(fixture.split_inclusive('\n')).enumerate() { + for (ix, line) in + default.into_iter().chain((first_row..).zip(fixture.split_inclusive('\n'))) + { if line.contains("//-") { assert!( line.starts_with("//-"), @@ -228,7 +241,7 @@ impl FixtureWithProjectMeta { } if let Some(line) = line.strip_prefix("//-") { - let meta = Self::parse_meta_line(line); + let meta = Self::parse_meta_line(line, (ix + 1).try_into().unwrap()); res.push(meta); } else { if matches!(line.strip_prefix("// "), Some(l) if l.trim().starts_with('/')) { @@ -252,7 +265,7 @@ impl FixtureWithProjectMeta { } //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo - fn parse_meta_line(meta: &str) -> Fixture { + fn parse_meta_line(meta: &str, line: usize) -> Fixture { let meta = meta.trim(); let mut components = meta.split_ascii_whitespace(); @@ -317,6 +330,7 @@ impl FixtureWithProjectMeta { Fixture { path, text: String::new(), + line, krate, deps, extern_prelude, @@ -330,7 +344,7 @@ impl FixtureWithProjectMeta { } impl MiniCore { - const RAW_SOURCE: &'static str = include_str!("./minicore.rs"); + pub const RAW_SOURCE: &'static str = include_str!("./minicore.rs"); fn has_flag(&self, flag: &str) -> bool { self.activated_flags.iter().any(|it| it == flag) @@ -363,8 +377,8 @@ impl MiniCore { res } - pub fn available_flags() -> impl Iterator { - let lines = MiniCore::RAW_SOURCE.split_inclusive('\n'); + pub fn available_flags(raw_source: &str) -> impl Iterator { + let lines = raw_source.split_inclusive('\n'); lines .map_while(|x| x.strip_prefix("//!")) .skip_while(|line| !line.contains("Available flags:")) @@ -375,9 +389,9 @@ impl MiniCore { /// Strips parts of minicore.rs which are flagged by inactive flags. /// /// This is probably over-engineered to support flags dependencies. - pub fn source_code(mut self) -> String { + pub fn source_code(mut self, raw_source: &str) -> String { let mut buf = String::new(); - let mut lines = MiniCore::RAW_SOURCE.split_inclusive('\n'); + let mut lines = raw_source.split_inclusive('\n'); let mut implications = Vec::new(); From 9a2db55596ce6c2c08bfde8fb69cc4672cd85992 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 17 Oct 2025 18:40:07 +0800 Subject: [PATCH 035/170] Support underscore suffix parameter hide inlayHints Using suffix underscores to avoid keywords is one of the common skill, and inlayHints hiding should support it Example --- **Before this PR**: ```rust fn far(loop_: u32) {} fn faz(r#loop: u32) {} let loop_level = 0; far(loop_level); //^^^^^^^^^^ loop_ faz(loop_level); ``` **After this PR**: ```rust fn far(loop_: u32) {} fn faz(r#loop: u32) {} let loop_level = 0; far(loop_level); faz(loop_level); ``` --- .../crates/ide/src/inlay_hints/param_name.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index 754707784055..d6271ce5adeb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -124,12 +124,12 @@ fn should_hide_param_name_hint( // hide when: // - the parameter name is a suffix of the function's name // - the argument is a qualified constructing or call expression where the qualifier is an ADT - // - exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix - // of argument with _ splitting it off + // - exact argument<->parameter match(ignoring leading and trailing underscore) or + // parameter is a prefix/suffix of argument with _ splitting it off // - param starts with `ra_fixture` // - param is a well known name in a unary function - let param_name = param_name.trim_start_matches('_'); + let param_name = param_name.trim_matches('_'); if param_name.is_empty() { return true; } @@ -540,6 +540,8 @@ fn enum_matches_param_name(completion_kind: CompletionKind) {} fn foo(param: u32) {} fn bar(param_eter: u32) {} fn baz(a_d_e: u32) {} +fn far(loop_: u32) {} +fn faz(r#loop: u32) {} enum CompletionKind { Keyword, @@ -590,6 +592,9 @@ fn main() { let param_eter2 = 0; bar(param_eter2); //^^^^^^^^^^^ param_eter + let loop_level = 0; + far(loop_level); + faz(loop_level); non_ident_pat((0, 0)); From 3c6ae00204456dd08dc09e707f8e8e070e5b5694 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 2 Oct 2025 22:14:11 +0000 Subject: [PATCH 036/170] Make diagnostics clearer for binop-related errors in foreign crates Fixes redundant language and bad grammar. --- .../rustc_hir_typeck/src/method/suggest.rs | 52 +++++-------------- tests/ui/array-slice-vec/vec-res-add.stderr | 4 +- .../autoderef-box-no-add.stderr | 8 +-- .../binary-op-not-allowed-issue-125631.stderr | 26 +++++----- tests/ui/binop/binop-bitxor-str.stderr | 4 +- tests/ui/error-codes/E0067.stderr | 4 +- .../box-arithmetic-14915.stderr | 4 +- tests/ui/pattern/pattern-tyvar-2.stderr | 4 +- .../ui/typeck/assign-non-lval-derefmut.stderr | 8 +-- tests/ui/typeck/minus-string.stderr | 4 +- 10 files changed, 47 insertions(+), 71 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 44602e628994..0954a768c11f 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2985,46 +2985,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } foreign_preds.sort_by_key(|pred: &&ty::TraitPredicate<'_>| pred.trait_ref.to_string()); - let foreign_def_ids = foreign_preds - .iter() - .filter_map(|pred| match pred.self_ty().kind() { - ty::Adt(def, _) => Some(def.did()), - _ => None, - }) - .collect::>(); - let mut foreign_spans: MultiSpan = foreign_def_ids - .iter() - .filter_map(|def_id| { - let span = self.tcx.def_span(*def_id); - if span.is_dummy() { None } else { Some(span) } - }) - .collect::>() - .into(); - for pred in &foreign_preds { - if let ty::Adt(def, _) = pred.self_ty().kind() { - foreign_spans.push_span_label( - self.tcx.def_span(def.did()), - format!("not implement `{}`", pred.trait_ref.print_trait_sugared()), - ); + + for pred in foreign_preds { + let ty = pred.self_ty(); + let ty::Adt(def, _) = ty.kind() else { continue }; + let span = self.tcx.def_span(def.did()); + if span.is_dummy() { + continue; } - } - if foreign_spans.primary_span().is_some() { - let msg = if let [foreign_pred] = foreign_preds.as_slice() { - format!( - "the foreign item type `{}` doesn't implement `{}`", - foreign_pred.self_ty(), - foreign_pred.trait_ref.print_trait_sugared() - ) - } else { - format!( - "the foreign item type{} {} implement required trait{} for this \ - operation to be valid", - pluralize!(foreign_def_ids.len()), - if foreign_def_ids.len() > 1 { "don't" } else { "doesn't" }, - pluralize!(foreign_preds.len()), - ) - }; - err.span_note(foreign_spans, msg); + let mut mspan: MultiSpan = span.into(); + mspan.push_span_label(span, format!("`{ty}` is defined in another crate")); + err.span_note( + mspan, + format!("`{ty}` does not implement `{}`", pred.trait_ref.print_trait_sugared()), + ); } let preds: Vec<_> = errors diff --git a/tests/ui/array-slice-vec/vec-res-add.stderr b/tests/ui/array-slice-vec/vec-res-add.stderr index 34fd69426a8b..4e13dbd0366d 100644 --- a/tests/ui/array-slice-vec/vec-res-add.stderr +++ b/tests/ui/array-slice-vec/vec-res-add.stderr @@ -6,10 +6,10 @@ LL | let k = i + j; | | | Vec | -note: the foreign item type `Vec` doesn't implement `Add` +note: `Vec` does not implement `Add` --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL | - = note: not implement `Add` + = note: `Vec` is defined in another crate error: aborting due to 1 previous error diff --git a/tests/ui/autoref-autoderef/autoderef-box-no-add.stderr b/tests/ui/autoref-autoderef/autoderef-box-no-add.stderr index 20ef3352831a..d861d103bde4 100644 --- a/tests/ui/autoref-autoderef/autoderef-box-no-add.stderr +++ b/tests/ui/autoref-autoderef/autoderef-box-no-add.stderr @@ -6,11 +6,11 @@ LL | let z: isize = a.x + b.y; | | | Box | -note: the foreign item type `Box` doesn't implement `Add` +note: `Box` does not implement `Add` --> $SRC_DIR/alloc/src/boxed.rs:LL:COL ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL | - = note: not implement `Add` + = note: `Box` is defined in another crate error[E0369]: cannot add `Box` to `Box` --> $DIR/autoderef-box-no-add.rs:31:33 @@ -20,11 +20,11 @@ LL | let answer: isize = forty.a + two.a; | | | Box | -note: the foreign item type `Box` doesn't implement `Add` +note: `Box` does not implement `Add` --> $SRC_DIR/alloc/src/boxed.rs:LL:COL ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL | - = note: not implement `Add` + = note: `Box` is defined in another crate error: aborting due to 2 previous errors diff --git a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr index 1cf75bbc1a57..d95876659c7a 100644 --- a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr +++ b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr @@ -11,10 +11,10 @@ note: an implementation of `PartialEq` might be missing for `T1` | LL | struct T1; | ^^^^^^^^^ must implement `PartialEq` -note: the foreign item type `std::io::Error` doesn't implement `PartialEq` +note: `std::io::Error` does not implement `PartialEq` --> $SRC_DIR/std/src/io/error.rs:LL:COL | - = note: not implement `PartialEq` + = note: `std::io::Error` is defined in another crate help: consider annotating `T1` with `#[derive(PartialEq)]` | LL + #[derive(PartialEq)] @@ -29,13 +29,14 @@ LL | (Error::new(ErrorKind::Other, "2"), thread::current()) LL | == (Error::new(ErrorKind::Other, "2"), thread::current()); | ^^ ------------------------------------------------------ (std::io::Error, Thread) | -note: the foreign item types don't implement required traits for this operation to be valid - --> $SRC_DIR/std/src/io/error.rs:LL:COL - | - = note: not implement `PartialEq` +note: `Thread` does not implement `PartialEq` --> $SRC_DIR/std/src/thread/mod.rs:LL:COL | - = note: not implement `PartialEq` + = note: `Thread` is defined in another crate +note: `std::io::Error` does not implement `PartialEq` + --> $SRC_DIR/std/src/io/error.rs:LL:COL + | + = note: `std::io::Error` is defined in another crate error[E0369]: binary operation `==` cannot be applied to type `(std::io::Error, Thread, T1, T2)` --> $DIR/binary-op-not-allowed-issue-125631.rs:14:9 @@ -52,13 +53,14 @@ LL | struct T1; | ^^^^^^^^^ must implement `PartialEq` LL | struct T2; | ^^^^^^^^^ must implement `PartialEq` -note: the foreign item types don't implement required traits for this operation to be valid - --> $SRC_DIR/std/src/io/error.rs:LL:COL - | - = note: not implement `PartialEq` +note: `Thread` does not implement `PartialEq` --> $SRC_DIR/std/src/thread/mod.rs:LL:COL | - = note: not implement `PartialEq` + = note: `Thread` is defined in another crate +note: `std::io::Error` does not implement `PartialEq` + --> $SRC_DIR/std/src/io/error.rs:LL:COL + | + = note: `std::io::Error` is defined in another crate help: consider annotating `T1` with `#[derive(PartialEq)]` | LL + #[derive(PartialEq)] diff --git a/tests/ui/binop/binop-bitxor-str.stderr b/tests/ui/binop/binop-bitxor-str.stderr index d4bb0c17bfa7..014a41f9e3ac 100644 --- a/tests/ui/binop/binop-bitxor-str.stderr +++ b/tests/ui/binop/binop-bitxor-str.stderr @@ -6,10 +6,10 @@ LL | fn main() { let x = "a".to_string() ^ "b".to_string(); } | | | String | -note: the foreign item type `String` doesn't implement `BitXor` +note: `String` does not implement `BitXor` --> $SRC_DIR/alloc/src/string.rs:LL:COL | - = note: not implement `BitXor` + = note: `String` is defined in another crate error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0067.stderr b/tests/ui/error-codes/E0067.stderr index 71b720805442..d350805da834 100644 --- a/tests/ui/error-codes/E0067.stderr +++ b/tests/ui/error-codes/E0067.stderr @@ -6,11 +6,11 @@ LL | LinkedList::new() += 1; | | | cannot use `+=` on type `LinkedList<_>` | -note: the foreign item type `LinkedList<_>` doesn't implement `AddAssign<{integer}>` +note: `LinkedList<_>` does not implement `AddAssign<{integer}>` --> $SRC_DIR/alloc/src/collections/linked_list.rs:LL:COL ::: $SRC_DIR/alloc/src/collections/linked_list.rs:LL:COL | - = note: not implement `AddAssign<{integer}>` + = note: `LinkedList<_>` is defined in another crate error[E0067]: invalid left-hand side of assignment --> $DIR/E0067.rs:4:23 diff --git a/tests/ui/operator-recovery/box-arithmetic-14915.stderr b/tests/ui/operator-recovery/box-arithmetic-14915.stderr index 1dd80472bb87..5ff73fce2ff8 100644 --- a/tests/ui/operator-recovery/box-arithmetic-14915.stderr +++ b/tests/ui/operator-recovery/box-arithmetic-14915.stderr @@ -6,11 +6,11 @@ LL | println!("{}", x + 1); | | | Box | -note: the foreign item type `Box` doesn't implement `Add<{integer}>` +note: `Box` does not implement `Add<{integer}>` --> $SRC_DIR/alloc/src/boxed.rs:LL:COL ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL | - = note: not implement `Add<{integer}>` + = note: `Box` is defined in another crate error: aborting due to 1 previous error diff --git a/tests/ui/pattern/pattern-tyvar-2.stderr b/tests/ui/pattern/pattern-tyvar-2.stderr index be52fa8b2394..6676d5129872 100644 --- a/tests/ui/pattern/pattern-tyvar-2.stderr +++ b/tests/ui/pattern/pattern-tyvar-2.stderr @@ -6,10 +6,10 @@ LL | fn foo(t: Bar) -> isize { match t { Bar::T1(_, Some(x)) => { return x * 3; | | | Vec | -note: the foreign item type `Vec` doesn't implement `Mul<{integer}>` +note: `Vec` does not implement `Mul<{integer}>` --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL | - = note: not implement `Mul<{integer}>` + = note: `Vec` is defined in another crate error: aborting due to 1 previous error diff --git a/tests/ui/typeck/assign-non-lval-derefmut.stderr b/tests/ui/typeck/assign-non-lval-derefmut.stderr index f57b5abe2eed..a0ba0eae1e44 100644 --- a/tests/ui/typeck/assign-non-lval-derefmut.stderr +++ b/tests/ui/typeck/assign-non-lval-derefmut.stderr @@ -19,10 +19,10 @@ LL | x.lock().unwrap() += 1; | | | cannot use `+=` on type `std::sync::MutexGuard<'_, usize>` | -note: the foreign item type `std::sync::MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>` +note: `std::sync::MutexGuard<'_, usize>` does not implement `AddAssign<{integer}>` --> $SRC_DIR/std/src/sync/poison/mutex.rs:LL:COL | - = note: not implement `AddAssign<{integer}>` + = note: `std::sync::MutexGuard<'_, usize>` is defined in another crate help: `+=` can be used on `usize` if you dereference the left-hand side | LL | *x.lock().unwrap() += 1; @@ -51,10 +51,10 @@ LL | y += 1; | | | cannot use `+=` on type `std::sync::MutexGuard<'_, usize>` | -note: the foreign item type `std::sync::MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>` +note: `std::sync::MutexGuard<'_, usize>` does not implement `AddAssign<{integer}>` --> $SRC_DIR/std/src/sync/poison/mutex.rs:LL:COL | - = note: not implement `AddAssign<{integer}>` + = note: `std::sync::MutexGuard<'_, usize>` is defined in another crate help: `+=` can be used on `usize` if you dereference the left-hand side | LL | *y += 1; diff --git a/tests/ui/typeck/minus-string.stderr b/tests/ui/typeck/minus-string.stderr index d2ebcd01ff9c..fee723baff19 100644 --- a/tests/ui/typeck/minus-string.stderr +++ b/tests/ui/typeck/minus-string.stderr @@ -4,10 +4,10 @@ error[E0600]: cannot apply unary operator `-` to type `String` LL | -"foo".to_string(); | ^^^^^^^^^^^^^^^^^^ cannot apply unary operator `-` | -note: the foreign item type `String` doesn't implement `Neg` +note: `String` does not implement `Neg` --> $SRC_DIR/alloc/src/string.rs:LL:COL | - = note: not implement `Neg` + = note: `String` is defined in another crate error: aborting due to 1 previous error From d3263775c414d967ddd2a09eb95940a87beafa74 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 18 Oct 2025 10:21:35 +0800 Subject: [PATCH 037/170] Migrate `generate_single_field_struct_from` assist to use `SyntaxEditor` --- .../generate_single_field_struct_from.rs | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 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 6c302a2a6fbd..a1ec76336566 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 @@ -5,9 +5,10 @@ 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, }; +use syntax::syntax_editor::{Element, Position}; use syntax::{ TokenText, - ast::{self, AstNode, HasAttrs, HasGenericParams, HasName, edit, edit_in_place::Indent}, + ast::{self, AstNode, HasAttrs, HasGenericParams, HasName, edit::AstNodeEdit}, }; use crate::{ @@ -111,9 +112,8 @@ pub(crate) fn generate_single_field_struct_from( false, false, ) - .clone_for_update(); + .indent(1.into()); - fn_.indent(1.into()); let cfg_attrs = strukt .attrs() .filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")); @@ -129,16 +129,25 @@ pub(crate) fn generate_single_field_struct_from( make::ty("From"), ty.clone(), None, - ty_where_clause.map(|wc| edit::AstNodeEdit::reset_indent(&wc)), + ty_where_clause.map(|wc| wc.reset_indent()), None, ) .clone_for_update(); impl_.get_or_create_assoc_item_list().add_item(fn_.into()); + let impl_ = impl_.indent(indent); - impl_.reindent_to(indent); + let mut edit = builder.make_editor(strukt.syntax()); - builder.insert(strukt.syntax().text_range().end(), format!("\n\n{indent}{impl_}")); + edit.insert_all( + Position::after(strukt.syntax()), + vec![ + make::tokens::whitespace(&format!("\n\n{indent}")).syntax_element(), + impl_.syntax().syntax_element(), + ], + ); + + builder.add_file_edits(ctx.vfs_file_id(), edit); }, ) } From aeae085dc3ac100c75dc4fc0d2bfc35cf664e67c Mon Sep 17 00:00:00 2001 From: Dawid Lachowicz Date: Fri, 11 Jul 2025 15:34:40 +0100 Subject: [PATCH 038/170] Add contract variable declarations Contract variables can be declared in the `requires` clause and can be referenced both in `requires` and `ensures`, subject to usual borrow checking rules. This allows any setup common to both the `requires` and `ensures` clauses to only be done once. --- compiler/rustc_ast/src/ast.rs | 3 ++ compiler/rustc_ast_lowering/src/block.rs | 2 +- compiler/rustc_ast_lowering/src/contract.rs | 30 +++++++++--- .../rustc_builtin_macros/src/contracts.rs | 35 ++----------- compiler/rustc_parse/src/parser/expr.rs | 24 +++++++++ compiler/rustc_parse/src/parser/generics.rs | 49 ++++++++++++++----- ...racts-disabled-side-effect-declarations.rs | 17 +++++++ ...s-disabled-side-effect-declarations.stderr | 11 +++++ .../contracts-disabled-side-effect-ensures.rs | 2 +- .../declared-vars-referring-to-params.rs | 19 +++++++ .../declared-vars-referring-to-params.stderr | 11 +++++ .../declared-vars-used-in-ensures.rs | 17 +++++++ .../declared-vars-used-in-ensures.stderr | 11 +++++ ...lared-vars-used-in-requires-and-ensures.rs | 19 +++++++ ...d-vars-used-in-requires-and-ensures.stderr | 11 +++++ .../contract-ast-extensions-nest.rs | 4 +- .../contract-ast-extensions-tail.rs | 4 +- ...g-ensures-is-not-inherited-when-nesting.rs | 2 +- ...-requires-is-not-inherited-when-nesting.rs | 2 +- .../internal-feature-gating.rs | 4 +- .../internal-feature-gating.stderr | 4 +- .../internal_machinery/lowering/basics.rs | 24 +++++++++ .../internal_machinery/lowering/basics.stderr | 11 +++++ .../requires-bool-expr-with-semicolon.rs | 18 +++++++ .../requires-bool-expr-with-semicolon.stderr | 18 +++++++ .../contracts/requires-no-final-expression.rs | 18 +++++++ .../requires-no-final-expression.stderr | 18 +++++++ 27 files changed, 326 insertions(+), 62 deletions(-) create mode 100644 tests/ui/contracts/contracts-disabled-side-effect-declarations.rs create mode 100644 tests/ui/contracts/contracts-disabled-side-effect-declarations.stderr create mode 100644 tests/ui/contracts/declared-vars-referring-to-params.rs create mode 100644 tests/ui/contracts/declared-vars-referring-to-params.stderr create mode 100644 tests/ui/contracts/declared-vars-used-in-ensures.rs create mode 100644 tests/ui/contracts/declared-vars-used-in-ensures.stderr create mode 100644 tests/ui/contracts/declared-vars-used-in-requires-and-ensures.rs create mode 100644 tests/ui/contracts/declared-vars-used-in-requires-and-ensures.stderr create mode 100644 tests/ui/contracts/internal_machinery/lowering/basics.rs create mode 100644 tests/ui/contracts/internal_machinery/lowering/basics.stderr create mode 100644 tests/ui/contracts/requires-bool-expr-with-semicolon.rs create mode 100644 tests/ui/contracts/requires-bool-expr-with-semicolon.stderr create mode 100644 tests/ui/contracts/requires-no-final-expression.rs create mode 100644 tests/ui/contracts/requires-no-final-expression.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 0adf4f898ab6..e5f20fc77078 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3700,6 +3700,9 @@ pub struct TraitImplHeader { #[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)] pub struct FnContract { + /// Declarations of variables accessible both in the `requires` and + /// `ensures` clauses. + pub declarations: ThinVec, pub requires: Option>, pub ensures: Option>, } diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index f1e810a8b9ea..5bfe63008516 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -27,7 +27,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::Block { hir_id, stmts, expr, rules, span: self.lower_span(b.span), targeted_by_break } } - fn lower_stmts( + pub(super) fn lower_stmts( &mut self, mut ast_stmts: &[Stmt], ) -> (&'hir [hir::Stmt<'hir>], Option<&'hir hir::Expr<'hir>>) { diff --git a/compiler/rustc_ast_lowering/src/contract.rs b/compiler/rustc_ast_lowering/src/contract.rs index 2f1c3d66d4e7..bafc49607f41 100644 --- a/compiler/rustc_ast_lowering/src/contract.rs +++ b/compiler/rustc_ast_lowering/src/contract.rs @@ -18,6 +18,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>, contract: &rustc_ast::FnContract, ) -> rustc_hir::Expr<'hir> { + // The order in which things are lowered is important! I.e to + // refer to variables in contract_decls from postcond/precond, + // we must lower it first! + let contract_decls = self.lower_stmts(&contract.declarations).0; + match (&contract.requires, &contract.ensures) { (Some(req), Some(ens)) => { // Lower the fn contract, which turns: @@ -27,6 +32,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // into: // // let __postcond = if contract_checks { + // CONTRACT_DECLARATIONS; // contract_check_requires(PRECOND); // Some(|ret_val| POSTCOND) // } else { @@ -45,8 +51,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let precond = self.lower_precond(req); let postcond_checker = self.lower_postcond_checker(ens); - let contract_check = - self.lower_contract_check_with_postcond(Some(precond), postcond_checker); + let contract_check = self.lower_contract_check_with_postcond( + contract_decls, + Some(precond), + postcond_checker, + ); let wrapped_body = self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span); @@ -68,15 +77,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // let ret = { body }; // // if contract_checks { + // CONTRACT_DECLARATIONS; // contract_check_ensures(__postcond, ret) // } else { // ret // } // } - let postcond_checker = self.lower_postcond_checker(ens); let contract_check = - self.lower_contract_check_with_postcond(None, postcond_checker); + self.lower_contract_check_with_postcond(contract_decls, None, postcond_checker); let wrapped_body = self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span); @@ -91,12 +100,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // // { // if contracts_checks { + // CONTRACT_DECLARATIONS; // contract_requires(PRECOND); // } // body // } let precond = self.lower_precond(req); - let precond_check = self.lower_contract_check_just_precond(precond); + let precond_check = self.lower_contract_check_just_precond(contract_decls, precond); let body = self.arena.alloc(body(self)); @@ -145,9 +155,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_contract_check_just_precond( &mut self, + contract_decls: &'hir [rustc_hir::Stmt<'hir>], precond: rustc_hir::Stmt<'hir>, ) -> rustc_hir::Stmt<'hir> { - let stmts = self.arena.alloc_from_iter([precond].into_iter()); + let stmts = self + .arena + .alloc_from_iter(contract_decls.into_iter().map(|d| *d).chain([precond].into_iter())); let then_block_stmts = self.block_all(precond.span, stmts, None); let then_block = self.arena.alloc(self.expr_block(&then_block_stmts)); @@ -164,10 +177,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_contract_check_with_postcond( &mut self, + contract_decls: &'hir [rustc_hir::Stmt<'hir>], precond: Option>, postcond_checker: &'hir rustc_hir::Expr<'hir>, ) -> &'hir rustc_hir::Expr<'hir> { - let stmts = self.arena.alloc_from_iter(precond.into_iter()); + let stmts = self + .arena + .alloc_from_iter(contract_decls.into_iter().map(|d| *d).chain(precond.into_iter())); let span = match precond { Some(precond) => precond.span, None => postcond_checker.span, diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs index 118efd15412e..52ddfbaa99f9 100644 --- a/compiler/rustc_builtin_macros/src/contracts.rs +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -17,7 +17,7 @@ impl AttrProcMacro for ExpandRequires { annotation: TokenStream, annotated: TokenStream, ) -> Result { - expand_requires_tts(ecx, span, annotation, annotated) + expand_contract_clause_tts(ecx, span, annotation, annotated, kw::ContractRequires) } } @@ -29,7 +29,7 @@ impl AttrProcMacro for ExpandEnsures { annotation: TokenStream, annotated: TokenStream, ) -> Result { - expand_ensures_tts(ecx, span, annotation, annotated) + expand_contract_clause_tts(ecx, span, annotation, annotated, kw::ContractEnsures) } } @@ -130,42 +130,17 @@ fn expand_contract_clause( Ok(new_tts) } -fn expand_requires_tts( +fn expand_contract_clause_tts( ecx: &mut ExtCtxt<'_>, attr_span: Span, annotation: TokenStream, annotated: TokenStream, + clause_keyword: rustc_span::Symbol, ) -> Result { let feature_span = ecx.with_def_site_ctxt(attr_span); expand_contract_clause(ecx, attr_span, annotated, |new_tts| { new_tts.push_tree(TokenTree::Token( - token::Token::from_ast_ident(Ident::new(kw::ContractRequires, feature_span)), - Spacing::Joint, - )); - new_tts.push_tree(TokenTree::Token( - token::Token::new(token::TokenKind::OrOr, attr_span), - Spacing::Alone, - )); - new_tts.push_tree(TokenTree::Delimited( - DelimSpan::from_single(attr_span), - DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), - token::Delimiter::Brace, - annotation, - )); - Ok(()) - }) -} - -fn expand_ensures_tts( - ecx: &mut ExtCtxt<'_>, - attr_span: Span, - annotation: TokenStream, - annotated: TokenStream, -) -> Result { - let feature_span = ecx.with_def_site_ctxt(attr_span); - expand_contract_clause(ecx, attr_span, annotated, |new_tts| { - new_tts.push_tree(TokenTree::Token( - token::Token::from_ast_ident(Ident::new(kw::ContractEnsures, feature_span)), + token::Token::from_ast_ident(Ident::new(clause_keyword, feature_span)), Spacing::Joint, )); new_tts.push_tree(TokenTree::Delimited( diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 57bd0d500d5a..e7c89f079622 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -4036,6 +4036,30 @@ impl<'a> Parser<'a> { self.mk_expr(span, ExprKind::Err(guar)) } + pub(crate) fn mk_unit_expr(&self, span: Span) -> Box { + self.mk_expr(span, ExprKind::Tup(Default::default())) + } + + pub(crate) fn mk_closure_expr(&self, span: Span, body: Box) -> Box { + self.mk_expr( + span, + ast::ExprKind::Closure(Box::new(ast::Closure { + binder: rustc_ast::ClosureBinder::NotPresent, + constness: rustc_ast::Const::No, + movability: rustc_ast::Movability::Movable, + capture_clause: rustc_ast::CaptureBy::Ref, + coroutine_kind: None, + fn_decl: Box::new(rustc_ast::FnDecl { + inputs: Default::default(), + output: rustc_ast::FnRetTy::Default(span), + }), + fn_arg_span: span, + fn_decl_span: span, + body, + })), + ) + } + /// Create expression span ensuring the span of the parent node /// is larger than the span of lhs and rhs, including the attributes. fn mk_expr_sp(&self, lhs: &Box, lhs_span: Span, op_span: Span, rhs_span: Span) -> Span { diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index eb684c3a62f9..2c76c0fe3583 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -312,25 +312,48 @@ impl<'a> Parser<'a> { /// Parses an experimental fn contract /// (`contract_requires(WWW) contract_ensures(ZZZ)`) pub(super) fn parse_contract(&mut self) -> PResult<'a, Option>> { - let requires = if self.eat_keyword_noexpect(exp!(ContractRequires).kw) { - self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span); - let precond = self.parse_expr()?; - Some(precond) + let (declarations, requires) = self.parse_contract_requires()?; + let ensures = self.parse_contract_ensures()?; + + if requires.is_none() && ensures.is_none() { + Ok(None) } else { - None - }; - let ensures = if self.eat_keyword_noexpect(exp!(ContractEnsures).kw) { + Ok(Some(Box::new(ast::FnContract { declarations, requires, ensures }))) + } + } + + fn parse_contract_requires( + &mut self, + ) -> PResult<'a, (ThinVec, Option>)> { + Ok(if self.eat_keyword_noexpect(exp!(ContractRequires).kw) { + self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span); + let mut decls_and_precond = self.parse_block()?; + + let precond = match decls_and_precond.stmts.pop() { + Some(precond) => match precond.kind { + rustc_ast::StmtKind::Expr(expr) => expr, + // Insert dummy node that will be rejected by typechecker to + // avoid reinventing an error + _ => self.mk_unit_expr(decls_and_precond.span), + }, + None => self.mk_unit_expr(decls_and_precond.span), + }; + let precond = self.mk_closure_expr(precond.span, precond); + let decls = decls_and_precond.stmts; + (decls, Some(precond)) + } else { + (Default::default(), None) + }) + } + + fn parse_contract_ensures(&mut self) -> PResult<'a, Option>> { + Ok(if self.eat_keyword_noexpect(exp!(ContractEnsures).kw) { self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span); let postcond = self.parse_expr()?; Some(postcond) } else { None - }; - if requires.is_none() && ensures.is_none() { - Ok(None) - } else { - Ok(Some(Box::new(ast::FnContract { requires, ensures }))) - } + }) } /// Parses an optional where-clause. diff --git a/tests/ui/contracts/contracts-disabled-side-effect-declarations.rs b/tests/ui/contracts/contracts-disabled-side-effect-declarations.rs new file mode 100644 index 000000000000..fc07729e9132 --- /dev/null +++ b/tests/ui/contracts/contracts-disabled-side-effect-declarations.rs @@ -0,0 +1,17 @@ +//@ run-pass +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +extern crate core; +use core::contracts::requires; + +#[requires(*x = 0; true)] +fn buggy_add(x: &mut u32, y: u32) { + *x = *x + y; +} + +fn main() { + let mut x = 10; + buggy_add(&mut x, 100); + assert_eq!(x, 110); +} diff --git a/tests/ui/contracts/contracts-disabled-side-effect-declarations.stderr b/tests/ui/contracts/contracts-disabled-side-effect-declarations.stderr new file mode 100644 index 000000000000..4c8a12538433 --- /dev/null +++ b/tests/ui/contracts/contracts-disabled-side-effect-declarations.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-disabled-side-effect-declarations.rs:2:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-disabled-side-effect-ensures.rs b/tests/ui/contracts/contracts-disabled-side-effect-ensures.rs index d234acb8268a..a3a77b0de9a2 100644 --- a/tests/ui/contracts/contracts-disabled-side-effect-ensures.rs +++ b/tests/ui/contracts/contracts-disabled-side-effect-ensures.rs @@ -5,7 +5,7 @@ extern crate core; use core::contracts::ensures; -#[ensures({*x = 0; |_ret| true})] +#[ensures(*x = 0; |_ret| true)] fn buggy_add(x: &mut u32, y: u32) { *x = *x + y; } diff --git a/tests/ui/contracts/declared-vars-referring-to-params.rs b/tests/ui/contracts/declared-vars-referring-to-params.rs new file mode 100644 index 000000000000..52885da048e2 --- /dev/null +++ b/tests/ui/contracts/declared-vars-referring-to-params.rs @@ -0,0 +1,19 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +extern crate core; +use core::contracts::{ensures, requires}; + +// checks that variable declarations are lowered properly, with the ability to +// access function parameters +#[requires(let y = 2 * x; true)] +#[ensures(move |ret| { *ret == y })] +fn foo(x: u32) -> u32 { + x * 2 +} + +fn main() { + foo(1); +} diff --git a/tests/ui/contracts/declared-vars-referring-to-params.stderr b/tests/ui/contracts/declared-vars-referring-to-params.stderr new file mode 100644 index 000000000000..0ad9064e8606 --- /dev/null +++ b/tests/ui/contracts/declared-vars-referring-to-params.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/declared-vars-referring-to-params.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/declared-vars-used-in-ensures.rs b/tests/ui/contracts/declared-vars-used-in-ensures.rs new file mode 100644 index 000000000000..9703709e2b8e --- /dev/null +++ b/tests/ui/contracts/declared-vars-used-in-ensures.rs @@ -0,0 +1,17 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +extern crate core; +use core::contracts::{ensures, requires}; + +#[requires(let y = 1; true)] +#[ensures(move |_ret| { y == 1 })] +fn foo(x: u32) -> u32 { + x * 2 +} + +fn main() { + foo(1); +} diff --git a/tests/ui/contracts/declared-vars-used-in-ensures.stderr b/tests/ui/contracts/declared-vars-used-in-ensures.stderr new file mode 100644 index 000000000000..000a1b239932 --- /dev/null +++ b/tests/ui/contracts/declared-vars-used-in-ensures.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/declared-vars-used-in-ensures.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/declared-vars-used-in-requires-and-ensures.rs b/tests/ui/contracts/declared-vars-used-in-requires-and-ensures.rs new file mode 100644 index 000000000000..e066a95314a9 --- /dev/null +++ b/tests/ui/contracts/declared-vars-used-in-requires-and-ensures.rs @@ -0,0 +1,19 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +extern crate core; +use core::contracts::{ensures, requires}; + +// checks that variable declarations are lowered properly, with the ability to +// refer to them *both* in requires and ensures +#[requires(let y = 2 * x; y > 0)] +#[ensures(move |ret| { *ret == y })] +fn foo(x: u32) -> u32 { + x * 2 +} + +fn main() { + foo(1); +} diff --git a/tests/ui/contracts/declared-vars-used-in-requires-and-ensures.stderr b/tests/ui/contracts/declared-vars-used-in-requires-and-ensures.stderr new file mode 100644 index 000000000000..52b163553fba --- /dev/null +++ b/tests/ui/contracts/declared-vars-used-in-requires-and-ensures.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/declared-vars-used-in-requires-and-ensures.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs index 4da0480f8bc1..faf0d80adaf3 100644 --- a/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs +++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs @@ -19,8 +19,8 @@ #![feature(contracts_internals)] fn nest(x: Baz) -> i32 - contract_requires(|| x.baz > 0) - contract_ensures(|ret| *ret > 100) + contract_requires { x.baz > 0 } + contract_ensures { |ret| *ret > 100 } { loop { return x.baz + 50; diff --git a/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs index f3cf5ce082c0..8095bdd2978b 100644 --- a/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs +++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs @@ -19,8 +19,8 @@ #![feature(contracts_internals)] fn tail(x: Baz) -> i32 - contract_requires(|| x.baz > 0) - contract_ensures(|ret| *ret > 100) + contract_requires { x.baz > 0 } + contract_ensures { |ret| *ret > 100 } { x.baz + 50 } diff --git a/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs index 960ccaed3588..6dd1fae5615a 100644 --- a/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs +++ b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs @@ -3,7 +3,7 @@ #![feature(contracts_internals)] fn outer() -> i32 - contract_ensures(|ret| *ret > 0) + contract_ensures { |ret| *ret > 0 } { let inner_closure = || -> i32 { 0 }; inner_closure(); diff --git a/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs index bee703de16a0..42c665694e7a 100644 --- a/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs +++ b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs @@ -5,7 +5,7 @@ struct Outer { outer: std::cell::Cell } fn outer(x: Outer) - contract_requires(|| x.outer.get() > 0) + contract_requires { x.outer.get() > 0 } { let inner_closure = || { }; x.outer.set(0); diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs index 48bd376594f2..9d3765bc228d 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs @@ -11,8 +11,8 @@ fn main() { //~^ ERROR use of unstable library feature `contracts_internals` // ast extensions are guarded by contracts_internals feature gate - fn identity_1() -> i32 contract_requires(|| true) { 10 } + fn identity_1() -> i32 contract_requires { true } { 10 } //~^ ERROR contract internal machinery is for internal use only - fn identity_2() -> i32 contract_ensures(|_| true) { 10 } + fn identity_2() -> i32 contract_ensures { |_| true } { 10 } //~^ ERROR contract internal machinery is for internal use only } diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr index c1318fc15a78..0010fca2f96c 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr @@ -1,7 +1,7 @@ error[E0658]: contract internal machinery is for internal use only --> $DIR/internal-feature-gating.rs:14:28 | -LL | fn identity_1() -> i32 contract_requires(|| true) { 10 } +LL | fn identity_1() -> i32 contract_requires { true } { 10 } | ^^^^^^^^^^^^^^^^^ | = note: see issue #128044 for more information @@ -11,7 +11,7 @@ LL | fn identity_1() -> i32 contract_requires(|| true) { 10 } error[E0658]: contract internal machinery is for internal use only --> $DIR/internal-feature-gating.rs:16:28 | -LL | fn identity_2() -> i32 contract_ensures(|_| true) { 10 } +LL | fn identity_2() -> i32 contract_ensures { |_| true } { 10 } | ^^^^^^^^^^^^^^^^ | = note: see issue #128044 for more information diff --git a/tests/ui/contracts/internal_machinery/lowering/basics.rs b/tests/ui/contracts/internal_machinery/lowering/basics.rs new file mode 100644 index 000000000000..9160517a7932 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/lowering/basics.rs @@ -0,0 +1,24 @@ +//@ run-pass +#![feature(contracts, cfg_contract_checks, contracts_internals, core_intrinsics)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +extern crate core; + +// we check here if the "lowered" program behaves as expected before +// implementing the actual lowering in the compiler + +fn foo(x: u32) -> u32 { + let post = { + let y = 2 * x; + // call contract_check_requires here to avoid borrow checker issues + // with variables declared in contract requires + core::intrinsics::contract_check_requires(|| y > 0); + Some(core::contracts::build_check_ensures(move |ret| *ret == y)) + }; + + core::intrinsics::contract_check_ensures(post, { 2 * x }) +} + +fn main() { + foo(1); +} diff --git a/tests/ui/contracts/internal_machinery/lowering/basics.stderr b/tests/ui/contracts/internal_machinery/lowering/basics.stderr new file mode 100644 index 000000000000..118229694a90 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/lowering/basics.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/basics.rs:2:12 + | +LL | #![feature(contracts, cfg_contract_checks, contracts_internals, core_intrinsics)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/requires-bool-expr-with-semicolon.rs b/tests/ui/contracts/requires-bool-expr-with-semicolon.rs new file mode 100644 index 000000000000..d0b3ed661ed6 --- /dev/null +++ b/tests/ui/contracts/requires-bool-expr-with-semicolon.rs @@ -0,0 +1,18 @@ +//@ dont-require-annotations: NOTE +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +extern crate core; +use core::contracts::requires; + +#[requires(true;)] +//~^ ERROR mismatched types [E0308] +//~| NOTE expected `bool`, found `()` +fn foo(x: u32) -> u32 { + x * 2 +} + +fn main() { + foo(1); +} diff --git a/tests/ui/contracts/requires-bool-expr-with-semicolon.stderr b/tests/ui/contracts/requires-bool-expr-with-semicolon.stderr new file mode 100644 index 000000000000..fd38fa4edcf5 --- /dev/null +++ b/tests/ui/contracts/requires-bool-expr-with-semicolon.stderr @@ -0,0 +1,18 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/requires-bool-expr-with-semicolon.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/requires-bool-expr-with-semicolon.rs:9:1 + | +LL | #[requires(true;)] + | ^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/contracts/requires-no-final-expression.rs b/tests/ui/contracts/requires-no-final-expression.rs new file mode 100644 index 000000000000..474b29d63ba6 --- /dev/null +++ b/tests/ui/contracts/requires-no-final-expression.rs @@ -0,0 +1,18 @@ +//@ dont-require-annotations: NOTE +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +extern crate core; +use core::contracts::requires; + +#[requires(let y = 1;)] +//~^ ERROR mismatched types [E0308] +//~| NOTE expected `bool`, found `()` +fn foo(x: u32) -> u32 { + x * 2 +} + +fn main() { + foo(1); +} diff --git a/tests/ui/contracts/requires-no-final-expression.stderr b/tests/ui/contracts/requires-no-final-expression.stderr new file mode 100644 index 000000000000..0db164857394 --- /dev/null +++ b/tests/ui/contracts/requires-no-final-expression.stderr @@ -0,0 +1,18 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/requires-no-final-expression.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/requires-no-final-expression.rs:9:1 + | +LL | #[requires(let y = 1;)] + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. From 0aa39c42330f00ca14d2655d038a441c6957eb0f Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sun, 19 Oct 2025 18:31:26 +0900 Subject: [PATCH 039/170] fix: Run `cargo metadata` on sysroot with cwd=sysroot --- src/tools/rust-analyzer/crates/project-model/src/sysroot.rs | 3 +-- src/tools/rust-analyzer/crates/project-model/src/tests.rs | 1 - src/tools/rust-analyzer/crates/project-model/src/workspace.rs | 4 ---- .../rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs | 1 - 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index c0a5009afba3..272cf7dada8a 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -210,7 +210,6 @@ impl Sysroot { &self, sysroot_source_config: &RustSourceWorkspaceConfig, no_deps: bool, - current_dir: &AbsPath, target_dir: &Utf8Path, progress: &dyn Fn(String), ) -> Option { @@ -224,7 +223,7 @@ impl Sysroot { if fs::metadata(&library_manifest).is_ok() { match self.load_library_via_cargo( &library_manifest, - current_dir, + src_root, target_dir, cargo_config, no_deps, diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index 987d381fac63..a79c8640fa6b 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -241,7 +241,6 @@ fn smoke_test_real_sysroot_cargo() { let loaded_sysroot = sysroot.load_workspace( &RustSourceWorkspaceConfig::default_cargo(), false, - &cwd, &Utf8PathBuf::default(), &|_| (), ); diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 0649ce9eeb9d..957f336ee419 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -383,7 +383,6 @@ impl ProjectWorkspace { toolchain.clone(), )), config.no_deps, - workspace_dir, &target_dir, progress, ) @@ -487,7 +486,6 @@ impl ProjectWorkspace { sysroot.load_workspace( &RustSourceWorkspaceConfig::Json(*sysroot_project), config.no_deps, - project_root, &target_dir, progress, ) @@ -499,7 +497,6 @@ impl ProjectWorkspace { toolchain.clone(), )), config.no_deps, - project_root, &target_dir, progress, ) @@ -561,7 +558,6 @@ impl ProjectWorkspace { toolchain.clone(), )), config.no_deps, - dir, &target_dir, &|_| (), ); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index 609ebf2b514f..20567149bb4b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -78,7 +78,6 @@ impl Tester { let loaded_sysroot = sysroot.load_workspace( &RustSourceWorkspaceConfig::default_cargo(), false, - &path, &Utf8PathBuf::default(), &|_| (), ); From 4182a95e05157c6c735be7f87cd8a0c1a23c9148 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 20 Oct 2025 01:55:52 +0900 Subject: [PATCH 040/170] fix: Report metadata errors for sysroot --- .../crates/project-model/src/sysroot.rs | 28 ++++++++++++++----- .../crates/project-model/src/tests.rs | 2 +- .../crates/project-model/src/workspace.rs | 4 +-- .../crates/rust-analyzer/src/reload.rs | 10 +++++++ 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index 272cf7dada8a..5cc399bfe76d 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -30,7 +30,7 @@ pub struct Sysroot { #[derive(Debug, Clone, Eq, PartialEq)] pub enum RustLibSrcWorkspace { - Workspace(CargoWorkspace), + Workspace { ws: CargoWorkspace, metadata_err: Option }, Json(ProjectJson), Stitched(stitched::Stitched), Empty, @@ -39,7 +39,9 @@ pub enum RustLibSrcWorkspace { impl fmt::Display for RustLibSrcWorkspace { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - RustLibSrcWorkspace::Workspace(ws) => write!(f, "workspace {}", ws.workspace_root()), + RustLibSrcWorkspace::Workspace { ws, .. } => { + write!(f, "workspace {}", ws.workspace_root()) + } RustLibSrcWorkspace::Json(json) => write!(f, "json {}", json.manifest_or_root()), RustLibSrcWorkspace::Stitched(stitched) => { write!(f, "stitched with {} crates", stitched.crates.len()) @@ -74,7 +76,7 @@ impl Sysroot { pub fn is_rust_lib_src_empty(&self) -> bool { match &self.workspace { - RustLibSrcWorkspace::Workspace(ws) => ws.packages().next().is_none(), + RustLibSrcWorkspace::Workspace { ws, .. } => ws.packages().next().is_none(), RustLibSrcWorkspace::Json(project_json) => project_json.n_crates() == 0, RustLibSrcWorkspace::Stitched(stitched) => stitched.crates.is_empty(), RustLibSrcWorkspace::Empty => true, @@ -85,9 +87,16 @@ impl Sysroot { self.error.as_deref() } + pub fn metadata_error(&self) -> Option<&str> { + match &self.workspace { + RustLibSrcWorkspace::Workspace { metadata_err, .. } => metadata_err.as_deref(), + _ => None, + } + } + pub fn num_packages(&self) -> usize { match &self.workspace { - RustLibSrcWorkspace::Workspace(ws) => ws.packages().count(), + RustLibSrcWorkspace::Workspace { ws, .. } => ws.packages().count(), RustLibSrcWorkspace::Json(project_json) => project_json.n_crates(), RustLibSrcWorkspace::Stitched(stitched) => stitched.crates.len(), RustLibSrcWorkspace::Empty => 0, @@ -293,7 +302,9 @@ impl Sysroot { && let Some(src_root) = &self.rust_lib_src_root { let has_core = match &self.workspace { - RustLibSrcWorkspace::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"), + RustLibSrcWorkspace::Workspace { ws: workspace, .. } => { + workspace.packages().any(|p| workspace[p].name == "core") + } RustLibSrcWorkspace::Json(project_json) => project_json .crates() .filter_map(|(_, krate)| krate.display_name.clone()) @@ -332,7 +343,7 @@ impl Sysroot { // Make sure we never attempt to write to the sysroot let locked = true; - let (mut res, _) = + let (mut res, err) = FetchMetadata::new(library_manifest, current_dir, &cargo_config, self, no_deps) .exec(target_dir, locked, progress)?; @@ -387,7 +398,10 @@ impl Sysroot { let cargo_workspace = CargoWorkspace::new(res, library_manifest.clone(), Default::default(), true); - Ok(RustLibSrcWorkspace::Workspace(cargo_workspace)) + Ok(RustLibSrcWorkspace::Workspace { + ws: cargo_workspace, + metadata_err: err.map(|e| format!("{e:#}")), + }) } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index a79c8640fa6b..711cdd11b9a8 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -248,7 +248,7 @@ fn smoke_test_real_sysroot_cargo() { sysroot.set_workspace(loaded_sysroot); } assert!( - matches!(sysroot.workspace(), RustLibSrcWorkspace::Workspace(_)), + matches!(sysroot.workspace(), RustLibSrcWorkspace::Workspace { .. }), "got {}", sysroot.workspace() ); diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 957f336ee419..22b84791aee9 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -743,7 +743,7 @@ impl ProjectWorkspace { pub fn to_roots(&self) -> Vec { let mk_sysroot = || { let mut r = match self.sysroot.workspace() { - RustLibSrcWorkspace::Workspace(ws) => ws + RustLibSrcWorkspace::Workspace { ws, .. } => ws .packages() .filter_map(|pkg| { if ws[pkg].is_local { @@ -1731,7 +1731,7 @@ fn sysroot_to_crate_graph( ) -> (SysrootPublicDeps, Option) { let _p = tracing::info_span!("sysroot_to_crate_graph").entered(); match sysroot.workspace() { - RustLibSrcWorkspace::Workspace(cargo) => { + RustLibSrcWorkspace::Workspace { ws: cargo, .. } => { let (sysroot_cg, sysroot_pm) = cargo_to_crate_graph( load, None, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index ca15e6a98e03..1475f02447d2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -222,6 +222,16 @@ impl GlobalState { message.push_str(err); message.push_str("\n\n"); } + if let Some(err) = ws.sysroot.metadata_error() { + status.health |= lsp_ext::Health::Warning; + format_to!( + message, + "Failed to read Cargo metadata with dependencies for sysroot of `{}`: ", + ws.manifest_or_root() + ); + message.push_str(err); + message.push_str("\n\n"); + } if let ProjectWorkspaceKind::Cargo { rustc: Err(Some(err)), .. } = &ws.kind { status.health |= lsp_ext::Health::Warning; format_to!( From 6232ba8d08d200fd9fc862076d4991a599fdc0da Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 17 Oct 2025 05:14:02 +0300 Subject: [PATCH 041/170] Migrate variance to the new solver --- .../crates/hir-ty/src/chalk_db.rs | 48 +- .../crates/hir-ty/src/chalk_ext.rs | 21 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 9 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 1 - .../crates/hir-ty/src/next_solver/def_id.rs | 4 +- .../crates/hir-ty/src/next_solver/interner.rs | 52 ++- .../crates/hir-ty/src/next_solver/mapping.rs | 36 +- .../crates/hir-ty/src/variance.rs | 442 +++++++----------- src/tools/rust-analyzer/crates/hir/src/lib.rs | 37 +- 9 files changed, 227 insertions(+), 423 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index 3d06b5210670..a6b859b37210 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -1,55 +1,9 @@ //! The implementation of `RustIrDatabase` for Chalk, which provides information //! about the code that Chalk needs. -use hir_def::{CallableDefId, GenericDefId}; -use crate::{Interner, db::HirDatabase, mapping::from_chalk}; +use crate::Interner; pub(crate) type AssocTypeId = chalk_ir::AssocTypeId; pub(crate) type TraitId = chalk_ir::TraitId; pub(crate) type AdtId = chalk_ir::AdtId; pub(crate) type ImplId = chalk_ir::ImplId; -pub(crate) type Variances = chalk_ir::Variances; - -impl chalk_ir::UnificationDatabase for &dyn HirDatabase { - fn fn_def_variance( - &self, - fn_def_id: chalk_ir::FnDefId, - ) -> chalk_ir::Variances { - HirDatabase::fn_def_variance(*self, from_chalk(*self, fn_def_id)) - } - - fn adt_variance(&self, adt_id: chalk_ir::AdtId) -> chalk_ir::Variances { - HirDatabase::adt_variance(*self, adt_id.0) - } -} - -pub(crate) fn fn_def_variance_query( - db: &dyn HirDatabase, - callable_def: CallableDefId, -) -> Variances { - Variances::from_iter( - Interner, - db.variances_of(GenericDefId::from_callable(db, callable_def)) - .as_deref() - .unwrap_or_default() - .iter() - .map(|v| match v { - crate::variance::Variance::Covariant => chalk_ir::Variance::Covariant, - crate::variance::Variance::Invariant => chalk_ir::Variance::Invariant, - crate::variance::Variance::Contravariant => chalk_ir::Variance::Contravariant, - crate::variance::Variance::Bivariant => chalk_ir::Variance::Invariant, - }), - ) -} - -pub(crate) fn adt_variance_query(db: &dyn HirDatabase, adt_id: hir_def::AdtId) -> Variances { - Variances::from_iter( - Interner, - db.variances_of(adt_id.into()).as_deref().unwrap_or_default().iter().map(|v| match v { - crate::variance::Variance::Covariant => chalk_ir::Variance::Covariant, - crate::variance::Variance::Invariant => chalk_ir::Variance::Invariant, - crate::variance::Variance::Contravariant => chalk_ir::Variance::Contravariant, - crate::variance::Variance::Bivariant => chalk_ir::Variance::Invariant, - }), - ) -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index a315f699ddaa..4ea563d46e6e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -3,8 +3,8 @@ use hir_def::{ItemContainerId, Lookup, TraitId}; use crate::{ - Binders, DynTy, Interner, ProjectionTy, Substitution, TraitRef, Ty, db::HirDatabase, - from_assoc_type_id, from_chalk_trait_id, generics::generics, to_chalk_trait_id, + Interner, ProjectionTy, Substitution, TraitRef, Ty, db::HirDatabase, from_assoc_type_id, + from_chalk_trait_id, generics::generics, to_chalk_trait_id, }; pub(crate) trait ProjectionTyExt { @@ -35,23 +35,6 @@ impl ProjectionTyExt for ProjectionTy { } } -pub(crate) trait DynTyExt { - fn principal(&self) -> Option>>; -} - -impl DynTyExt for DynTy { - fn principal(&self) -> Option>> { - self.bounds.as_ref().filter_map(|bounds| { - bounds.interned().first().and_then(|b| { - b.as_ref().filter_map(|b| match b { - crate::WhereClause::Implemented(trait_ref) => Some(trait_ref), - _ => None, - }) - }) - }) - } -} - pub(crate) trait TraitRefExt { fn hir_trait_id(&self) -> TraitId; } 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 7ad76f35b1f2..a4c19eea162e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -17,7 +17,6 @@ use triomphe::Arc; use crate::{ Binders, ImplTraitId, ImplTraits, InferenceResult, TraitEnvironment, Ty, TyDefId, ValueTyDefId, - chalk_db, consteval::ConstEvalError, dyn_compatibility::DynCompatibilityViolation, layout::{Layout, LayoutError}, @@ -308,19 +307,13 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::interned] fn intern_coroutine(&self, id: InternedCoroutine) -> InternedCoroutineId; - #[salsa::invoke(chalk_db::fn_def_variance_query)] - fn fn_def_variance(&self, fn_def_id: CallableDefId) -> chalk_db::Variances; - - #[salsa::invoke(chalk_db::adt_variance_query)] - fn adt_variance(&self, adt_id: AdtId) -> chalk_db::Variances; - #[salsa::invoke(crate::variance::variances_of)] #[salsa::cycle( // cycle_fn = crate::variance::variances_of_cycle_fn, // cycle_initial = crate::variance::variances_of_cycle_initial, cycle_result = crate::variance::variances_of_cycle_initial, )] - fn variances_of(&self, def: GenericDefId) -> Option>; + fn variances_of(&self, def: GenericDefId) -> crate::next_solver::VariancesOf<'_>; // next trait solver 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 7277617bce8a..77585177c1b5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -117,7 +117,6 @@ pub use utils::{ TargetFeatureIsSafeInTarget, Unsafety, all_super_traits, direct_super_traits, is_fn_unsafe_to_call, target_feature_is_safe_in_target, }; -pub use variance::Variance; use chalk_ir::{BoundVar, DebruijnIndex, Safety, Scalar}; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index 8525d4bc96e6..928e1321e738 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -155,7 +155,7 @@ impl From for SolverDefId { } impl TryFrom for GenericDefId { - type Error = SolverDefId; + type Error = (); fn try_from(value: SolverDefId) -> Result { Ok(match value { @@ -170,7 +170,7 @@ impl TryFrom for GenericDefId { | SolverDefId::InternedCoroutineId(_) | SolverDefId::InternedOpaqueTyId(_) | SolverDefId::EnumVariantId(_) - | SolverDefId::Ctor(_) => return Err(value), + | SolverDefId::Ctor(_) => return Err(()), }) } } 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 cfa8b5b8a7f7..7be891106df3 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 @@ -83,7 +83,7 @@ macro_rules! _interned_vec_nolifetime_salsa { ($name:ident, $ty:ty) => { interned_vec_nolifetime_salsa!($name, $ty, nofold); - impl<'db> rustc_type_ir::TypeFoldable> for $name { + impl<'db> rustc_type_ir::TypeFoldable> for $name<'db> { fn try_fold_with>>( self, folder: &mut F, @@ -104,7 +104,7 @@ macro_rules! _interned_vec_nolifetime_salsa { } } - impl<'db> rustc_type_ir::TypeVisitable> for $name { + impl<'db> rustc_type_ir::TypeVisitable> for $name<'db> { fn visit_with>>( &self, visitor: &mut V, @@ -117,14 +117,14 @@ macro_rules! _interned_vec_nolifetime_salsa { } }; ($name:ident, $ty:ty, nofold) => { - #[salsa::interned(no_lifetime, constructor = new_, debug)] + #[salsa::interned(constructor = new_, debug)] pub struct $name { #[returns(ref)] inner_: smallvec::SmallVec<[$ty; 2]>, } - impl $name { - pub fn new_from_iter<'db>( + impl<'db> $name<'db> { + pub fn new_from_iter( interner: DbInterner<'db>, data: impl IntoIterator, ) -> Self { @@ -140,7 +140,7 @@ macro_rules! _interned_vec_nolifetime_salsa { } } - impl rustc_type_ir::inherent::SliceLike for $name { + impl<'db> rustc_type_ir::inherent::SliceLike for $name<'db> { type Item = $ty; type IntoIter = as IntoIterator>::IntoIter; @@ -154,7 +154,7 @@ macro_rules! _interned_vec_nolifetime_salsa { } } - impl IntoIterator for $name { + impl<'db> IntoIterator for $name<'db> { type Item = $ty; type IntoIter = ::IntoIter; @@ -163,7 +163,7 @@ macro_rules! _interned_vec_nolifetime_salsa { } } - impl Default for $name { + impl<'db> Default for $name<'db> { fn default() -> Self { $name::new_from_iter(DbInterner::conjure(), []) } @@ -887,7 +887,7 @@ macro_rules! as_lang_item { impl<'db> rustc_type_ir::Interner for DbInterner<'db> { type DefId = SolverDefId; type LocalDefId = SolverDefId; - type LocalDefIds = SolverDefIds; + type LocalDefIds = SolverDefIds<'db>; type TraitId = TraitIdWrapper; type ForeignId = TypeAliasIdWrapper; type FunctionId = CallableIdWrapper; @@ -904,7 +904,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { type Term = Term<'db>; - type BoundVarKinds = BoundVarKinds; + type BoundVarKinds = BoundVarKinds<'db>; type BoundVarKind = BoundVarKind; type PredefinedOpaques = PredefinedOpaques<'db>; @@ -977,7 +977,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { type GenericsOf = Generics; - type VariancesOf = VariancesOf; + type VariancesOf = VariancesOf<'db>; type AdtDef = AdtDef; @@ -1045,10 +1045,9 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf { let generic_def = match def_id { - SolverDefId::FunctionId(def_id) => def_id.into(), - SolverDefId::AdtId(def_id) => def_id.into(), - SolverDefId::Ctor(Ctor::Struct(def_id)) => def_id.into(), - SolverDefId::Ctor(Ctor::Enum(def_id)) => def_id.loc(self.db).parent.into(), + SolverDefId::Ctor(Ctor::Enum(def_id)) | SolverDefId::EnumVariantId(def_id) => { + def_id.loc(self.db).parent.into() + } SolverDefId::InternedOpaqueTyId(_def_id) => { // FIXME(next-solver): track variances // @@ -1059,17 +1058,20 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { (0..self.generics_of(def_id).count()).map(|_| Variance::Invariant), ); } - _ => return VariancesOf::new_from_iter(self, []), + SolverDefId::Ctor(Ctor::Struct(def_id)) => def_id.into(), + SolverDefId::AdtId(def_id) => def_id.into(), + SolverDefId::FunctionId(def_id) => def_id.into(), + SolverDefId::ConstId(_) + | SolverDefId::StaticId(_) + | SolverDefId::TraitId(_) + | SolverDefId::TypeAliasId(_) + | SolverDefId::ImplId(_) + | SolverDefId::InternedClosureId(_) + | SolverDefId::InternedCoroutineId(_) => { + return VariancesOf::new_from_iter(self, []); + } }; - VariancesOf::new_from_iter( - self, - self.db() - .variances_of(generic_def) - .as_deref() - .unwrap_or_default() - .iter() - .map(|v| v.to_nextsolver(self)), - ) + self.db.variances_of(generic_def) } fn type_of(self, def_id: Self::DefId) -> EarlyBinder { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 1a5982cc00d3..adbc6094a221 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -605,8 +605,8 @@ impl<'db, T: NextSolverToChalk<'db, U>, U: HasInterner> } } -impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKinds { +impl<'db> ChalkToNextSolver<'db, BoundVarKinds<'db>> for chalk_ir::VariableKinds { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKinds<'db> { BoundVarKinds::new_from_iter( interner, self.iter(Interner).map(|v| v.to_nextsolver(interner)), @@ -614,7 +614,7 @@ impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds NextSolverToChalk<'db, chalk_ir::VariableKinds> for BoundVarKinds { +impl<'db> NextSolverToChalk<'db, chalk_ir::VariableKinds> for BoundVarKinds<'db> { fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::VariableKinds { chalk_ir::VariableKinds::from_iter(Interner, self.iter().map(|v| v.to_chalk(interner))) } @@ -763,36 +763,6 @@ impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutabil } } -impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for crate::Variance { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::Variance { - match self { - crate::Variance::Covariant => rustc_type_ir::Variance::Covariant, - crate::Variance::Invariant => rustc_type_ir::Variance::Invariant, - crate::Variance::Contravariant => rustc_type_ir::Variance::Contravariant, - crate::Variance::Bivariant => rustc_type_ir::Variance::Bivariant, - } - } -} - -impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for chalk_ir::Variance { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::Variance { - match self { - chalk_ir::Variance::Covariant => rustc_type_ir::Variance::Covariant, - chalk_ir::Variance::Invariant => rustc_type_ir::Variance::Invariant, - chalk_ir::Variance::Contravariant => rustc_type_ir::Variance::Contravariant, - } - } -} - -impl<'db> ChalkToNextSolver<'db, VariancesOf> for chalk_ir::Variances { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> VariancesOf { - VariancesOf::new_from_iter( - interner, - self.as_slice(Interner).iter().map(|v| v.to_nextsolver(interner)), - ) - } -} - impl<'db> ChalkToNextSolver<'db, Goal, Predicate<'db>>> for chalk_ir::InEnvironment> { 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 0ff110106ebe..46898ddeec12 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -13,43 +13,45 @@ //! by the next salsa version. If not, we will likely have to adapt and go with the rustc approach //! while installing firewall per item queries to prevent invalidation issues. -use crate::db::HirDatabase; -use crate::generics::{Generics, generics}; -use crate::next_solver::DbInterner; -use crate::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk}; -use crate::{ - AliasTy, Const, ConstScalar, DynTyExt, GenericArg, GenericArgData, Interner, Lifetime, - LifetimeData, Ty, TyKind, +use hir_def::{AdtId, GenericDefId, GenericParamId, VariantId, signatures::StructFlags}; +use rustc_ast_ir::Mutability; +use rustc_type_ir::{ + Variance, + inherent::{AdtDef, IntoKind, SliceLike}, }; -use chalk_ir::Mutability; -use hir_def::signatures::StructFlags; -use hir_def::{AdtId, GenericDefId, GenericParamId, VariantId}; -use std::fmt; -use std::ops::Not; use stdx::never; -use triomphe::Arc; -pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option> { +use crate::{ + db::HirDatabase, + generics::{Generics, generics}, + next_solver::{ + Const, ConstKind, DbInterner, ExistentialPredicate, GenericArg, GenericArgs, Region, + RegionKind, Term, Ty, TyKind, VariancesOf, + }, +}; + +pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> VariancesOf<'_> { tracing::debug!("variances_of(def={:?})", def); + let interner = DbInterner::new_with(db, None, None); match def { GenericDefId::FunctionId(_) => (), GenericDefId::AdtId(adt) => { if let AdtId::StructId(id) = adt { let flags = &db.struct_signature(id).flags; if flags.contains(StructFlags::IS_UNSAFE_CELL) { - return Some(Arc::from_iter(vec![Variance::Invariant; 1])); + return VariancesOf::new_from_iter(interner, [Variance::Invariant]); } else if flags.contains(StructFlags::IS_PHANTOM_DATA) { - return Some(Arc::from_iter(vec![Variance::Covariant; 1])); + return VariancesOf::new_from_iter(interner, [Variance::Covariant]); } } } - _ => return None, + _ => return VariancesOf::new_from_iter(interner, []), } let generics = generics(db, def); let count = generics.len(); if count == 0 { - return None; + return VariancesOf::new_from_iter(interner, []); } let mut variances = Context { generics, variances: vec![Variance::Bivariant; count], db }.solve(); @@ -69,7 +71,7 @@ pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option Option Variance { + // Greatest lower bound of the variance lattice as defined in The Paper: + // + // * + // - + + // o + match (v1, v2) { + (Variance::Invariant, _) | (_, Variance::Invariant) => Variance::Invariant, + + (Variance::Covariant, Variance::Contravariant) => Variance::Invariant, + (Variance::Contravariant, Variance::Covariant) => Variance::Invariant, + + (Variance::Covariant, Variance::Covariant) => Variance::Covariant, + + (Variance::Contravariant, Variance::Contravariant) => Variance::Contravariant, + + (x, Variance::Bivariant) | (Variance::Bivariant, x) => x, + } +} + pub(crate) fn variances_of_cycle_initial( db: &dyn HirDatabase, def: GenericDefId, -) -> Option> { +) -> VariancesOf<'_> { + let interner = DbInterner::new_with(db, None, None); let generics = generics(db, def); let count = generics.len(); - if count == 0 { - return None; - } // FIXME(next-solver): Returns `Invariance` and not `Bivariance` here, see the comment in the main query. - Some(Arc::from(vec![Variance::Invariant; count])) -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum Variance { - Covariant, // T <: T iff A <: B -- e.g., function return type - Invariant, // T <: T iff B == A -- e.g., type of mutable cell - Contravariant, // T <: T iff B <: A -- e.g., function param type - Bivariant, // T <: T -- e.g., unused type parameter -} - -impl fmt::Display for Variance { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Variance::Covariant => write!(f, "covariant"), - Variance::Invariant => write!(f, "invariant"), - Variance::Contravariant => write!(f, "contravariant"), - Variance::Bivariant => write!(f, "bivariant"), - } - } -} - -impl Variance { - /// `a.xform(b)` combines the variance of a context with the - /// variance of a type with the following meaning. If we are in a - /// context with variance `a`, and we encounter a type argument in - /// a position with variance `b`, then `a.xform(b)` is the new - /// variance with which the argument appears. - /// - /// Example 1: - /// ```ignore (illustrative) - /// *mut Vec - /// ``` - /// Here, the "ambient" variance starts as covariant. `*mut T` is - /// invariant with respect to `T`, so the variance in which the - /// `Vec` appears is `Covariant.xform(Invariant)`, which - /// yields `Invariant`. Now, the type `Vec` is covariant with - /// respect to its type argument `T`, and hence the variance of - /// the `i32` here is `Invariant.xform(Covariant)`, which results - /// (again) in `Invariant`. - /// - /// Example 2: - /// ```ignore (illustrative) - /// fn(*const Vec, *mut Vec` appears is - /// `Contravariant.xform(Covariant)` or `Contravariant`. The same - /// is true for its `i32` argument. In the `*mut T` case, the - /// variance of `Vec` is `Contravariant.xform(Invariant)`, - /// and hence the outermost type is `Invariant` with respect to - /// `Vec` (and its `i32` argument). - /// - /// Source: Figure 1 of "Taming the Wildcards: - /// Combining Definition- and Use-Site Variance" published in PLDI'11. - fn xform(self, v: Variance) -> Variance { - match (self, v) { - // Figure 1, column 1. - (Variance::Covariant, Variance::Covariant) => Variance::Covariant, - (Variance::Covariant, Variance::Contravariant) => Variance::Contravariant, - (Variance::Covariant, Variance::Invariant) => Variance::Invariant, - (Variance::Covariant, Variance::Bivariant) => Variance::Bivariant, - - // Figure 1, column 2. - (Variance::Contravariant, Variance::Covariant) => Variance::Contravariant, - (Variance::Contravariant, Variance::Contravariant) => Variance::Covariant, - (Variance::Contravariant, Variance::Invariant) => Variance::Invariant, - (Variance::Contravariant, Variance::Bivariant) => Variance::Bivariant, - - // Figure 1, column 3. - (Variance::Invariant, _) => Variance::Invariant, - - // Figure 1, column 4. - (Variance::Bivariant, _) => Variance::Bivariant, - } - } - - fn glb(self, v: Variance) -> Variance { - // Greatest lower bound of the variance lattice as - // defined in The Paper: - // - // * - // - + - // o - match (self, v) { - (Variance::Invariant, _) | (_, Variance::Invariant) => Variance::Invariant, - - (Variance::Covariant, Variance::Contravariant) => Variance::Invariant, - (Variance::Contravariant, Variance::Covariant) => Variance::Invariant, - - (Variance::Covariant, Variance::Covariant) => Variance::Covariant, - - (Variance::Contravariant, Variance::Contravariant) => Variance::Contravariant, - - (x, Variance::Bivariant) | (Variance::Bivariant, x) => x, - } - } - - pub fn invariant(self) -> Self { - self.xform(Variance::Invariant) - } - - pub fn covariant(self) -> Self { - self.xform(Variance::Covariant) - } - - pub fn contravariant(self) -> Self { - self.xform(Variance::Contravariant) - } + VariancesOf::new_from_iter(interner, std::iter::repeat_n(Variance::Invariant, count)) } struct Context<'db> { @@ -213,17 +121,16 @@ struct Context<'db> { variances: Vec, } -impl Context<'_> { +impl<'db> Context<'db> { fn solve(mut self) -> Vec { tracing::debug!("solve(generics={:?})", self.generics); match self.generics.def() { GenericDefId::AdtId(adt) => { let db = self.db; let mut add_constraints_from_variant = |variant| { - let subst = self.generics.placeholder_subst(db); - for (_, field) in db.field_types(variant).iter() { + for (_, field) in db.field_types_ns(variant).iter() { self.add_constraints_from_ty( - &field.clone().substitute(Interner, &subst), + field.instantiate_identity(), Variance::Covariant, ); } @@ -239,16 +146,9 @@ impl Context<'_> { } } GenericDefId::FunctionId(f) => { - let subst = self.generics.placeholder_subst(self.db); - let interner = DbInterner::new_with(self.db, None, None); - let args: crate::next_solver::GenericArgs<'_> = subst.to_nextsolver(interner); - let sig = self - .db - .callable_item_signature(f.into()) - .instantiate(interner, args) - .skip_binder() - .to_chalk(interner); - self.add_constraints_from_sig(sig.params_and_return.iter(), Variance::Covariant); + let sig = + self.db.callable_item_signature(f.into()).instantiate_identity().skip_binder(); + self.add_constraints_from_sig(sig.inputs_and_output.iter(), Variance::Covariant); } _ => {} } @@ -276,122 +176,102 @@ impl Context<'_> { /// Adds constraints appropriate for an instance of `ty` appearing /// in a context with the generics defined in `generics` and /// ambient variance `variance` - fn add_constraints_from_ty(&mut self, ty: &Ty, variance: Variance) { + fn add_constraints_from_ty(&mut self, ty: Ty<'db>, variance: Variance) { tracing::debug!("add_constraints_from_ty(ty={:?}, variance={:?})", ty, variance); - match ty.kind(Interner) { - TyKind::Scalar(_) | TyKind::Never | TyKind::Str | TyKind::Foreign(..) => { + match ty.kind() { + TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Char + | TyKind::Bool + | TyKind::Never + | TyKind::Str + | TyKind::Foreign(..) => { // leaf type -- noop } - TyKind::FnDef(..) | TyKind::Coroutine(..) | TyKind::Closure(..) => { + TyKind::FnDef(..) + | TyKind::Coroutine(..) + | TyKind::CoroutineClosure(..) + | TyKind::Closure(..) => { never!("Unexpected unnameable type in variance computation: {:?}", ty); } - TyKind::Ref(mutbl, lifetime, ty) => { + TyKind::Ref(lifetime, ty, mutbl) => { self.add_constraints_from_region(lifetime, variance); - self.add_constraints_from_mt(ty, *mutbl, variance); + self.add_constraints_from_mt(ty, mutbl, variance); } TyKind::Array(typ, len) => { - self.add_constraints_from_const(len, variance); + self.add_constraints_from_const(len); self.add_constraints_from_ty(typ, variance); } TyKind::Slice(typ) => { self.add_constraints_from_ty(typ, variance); } - TyKind::Raw(mutbl, ty) => { - self.add_constraints_from_mt(ty, *mutbl, variance); + TyKind::RawPtr(ty, mutbl) => { + self.add_constraints_from_mt(ty, mutbl, variance); } - TyKind::Tuple(_, subtys) => { - for subty in subtys.type_parameters(Interner) { - self.add_constraints_from_ty(&subty, variance); + TyKind::Tuple(subtys) => { + for subty in subtys { + self.add_constraints_from_ty(subty, variance); } } TyKind::Adt(def, args) => { - self.add_constraints_from_args(def.0.into(), args.as_slice(Interner), variance); + self.add_constraints_from_args(def.def_id().0.into(), args, variance); } - TyKind::Alias(AliasTy::Opaque(opaque)) => { - self.add_constraints_from_invariant_args( - opaque.substitution.as_slice(Interner), - variance, - ); + TyKind::Alias(_, alias) => { + // FIXME: Probably not correct wrt. opaques. + self.add_constraints_from_invariant_args(alias.args); } - TyKind::Alias(AliasTy::Projection(proj)) => { - self.add_constraints_from_invariant_args( - proj.substitution.as_slice(Interner), - variance, - ); - } - // FIXME: check this - TyKind::AssociatedType(_, subst) => { - self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); - } - // FIXME: check this - TyKind::OpaqueType(_, subst) => { - self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); - } - TyKind::Dyn(it) => { + TyKind::Dynamic(bounds, region) => { // The type `dyn Trait +'a` is covariant w/r/t `'a`: - self.add_constraints_from_region(&it.lifetime, variance); + self.add_constraints_from_region(region, variance); - if let Some(trait_ref) = it.principal() { - // Trait are always invariant so we can take advantage of that. - self.add_constraints_from_invariant_args( - trait_ref - .map(|it| it.map(|it| it.substitution.clone())) - .substitute( - Interner, - &[GenericArg::new( - Interner, - chalk_ir::GenericArgData::Ty(TyKind::Error.intern(Interner)), - )], - ) - .skip_binders() - .as_slice(Interner), - variance, - ); + for bound in bounds { + match bound.skip_binder() { + ExistentialPredicate::Trait(trait_ref) => { + self.add_constraints_from_invariant_args(trait_ref.args) + } + ExistentialPredicate::Projection(projection) => { + self.add_constraints_from_invariant_args(projection.args); + match projection.term { + Term::Ty(ty) => { + self.add_constraints_from_ty(ty, Variance::Invariant) + } + Term::Const(konst) => self.add_constraints_from_const(konst), + } + } + ExistentialPredicate::AutoTrait(_) => {} + } } - - // FIXME - // for projection in data.projection_bounds() { - // match projection.skip_binder().term.unpack() { - // TyKind::TermKind::Ty(ty) => { - // self.add_constraints_from_ty( ty, self.invariant); - // } - // TyKind::TermKind::Const(c) => { - // self.add_constraints_from_const( c, self.invariant) - // } - // } - // } } // Chalk has no params, so use placeholders for now? - TyKind::Placeholder(index) => { - let idx = crate::from_placeholder_idx(self.db, *index).0; - let index = self.generics.type_or_const_param_idx(idx).unwrap(); - self.constrain(index, variance); + TyKind::Param(param) => self.constrain(param.index as usize, variance), + TyKind::FnPtr(sig, _) => { + self.add_constraints_from_sig(sig.skip_binder().inputs_and_output.iter(), variance); } - TyKind::Function(f) => { - self.add_constraints_from_sig( - f.substitution.0.iter(Interner).filter_map(move |p| p.ty(Interner)), - variance, - ); - } - TyKind::Error => { + TyKind::Error(_) => { // we encounter this when walking the trait references for object // types, where we use Error as the Self type } - TyKind::CoroutineWitness(..) | TyKind::BoundVar(..) | TyKind::InferenceVar(..) => { + TyKind::Bound(..) => {} + TyKind::CoroutineWitness(..) + | TyKind::Placeholder(..) + | TyKind::Infer(..) + | TyKind::UnsafeBinder(..) + | TyKind::Pat(..) => { never!("unexpected type encountered in variance inference: {:?}", ty) } } } - fn add_constraints_from_invariant_args(&mut self, args: &[GenericArg], variance: Variance) { - let variance_i = variance.invariant(); - - for k in args { - match k.data(Interner) { - GenericArgData::Lifetime(lt) => self.add_constraints_from_region(lt, variance_i), - GenericArgData::Ty(ty) => self.add_constraints_from_ty(ty, variance_i), - GenericArgData::Const(val) => self.add_constraints_from_const(val, variance_i), + fn add_constraints_from_invariant_args(&mut self, args: GenericArgs<'db>) { + for k in args.iter() { + match k { + GenericArg::Lifetime(lt) => { + self.add_constraints_from_region(lt, Variance::Invariant) + } + GenericArg::Ty(ty) => self.add_constraints_from_ty(ty, Variance::Invariant), + GenericArg::Const(val) => self.add_constraints_from_const(val), } } } @@ -401,51 +281,40 @@ impl Context<'_> { fn add_constraints_from_args( &mut self, def_id: GenericDefId, - args: &[GenericArg], + args: GenericArgs<'db>, variance: Variance, ) { - // We don't record `inferred_starts` entries for empty generics. if args.is_empty() { return; } - let Some(variances) = self.db.variances_of(def_id) else { - return; - }; + let variances = self.db.variances_of(def_id); - for (i, k) in args.iter().enumerate() { - match k.data(Interner) { - GenericArgData::Lifetime(lt) => { - self.add_constraints_from_region(lt, variance.xform(variances[i])) - } - GenericArgData::Ty(ty) => { - self.add_constraints_from_ty(ty, variance.xform(variances[i])) - } - GenericArgData::Const(val) => self.add_constraints_from_const(val, variance), + for (k, v) in args.iter().zip(variances) { + match k { + GenericArg::Lifetime(lt) => self.add_constraints_from_region(lt, variance.xform(v)), + GenericArg::Ty(ty) => self.add_constraints_from_ty(ty, variance.xform(v)), + GenericArg::Const(val) => self.add_constraints_from_const(val), } } } /// Adds constraints appropriate for a const expression `val` /// in a context with ambient variance `variance` - fn add_constraints_from_const(&mut self, c: &Const, variance: Variance) { - match &c.data(Interner).value { - chalk_ir::ConstValue::Concrete(c) => { - if let ConstScalar::UnevaluatedConst(_, subst) = &c.interned { - self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); - } - } + fn add_constraints_from_const(&mut self, c: Const<'db>) { + match c.kind() { + ConstKind::Unevaluated(c) => self.add_constraints_from_invariant_args(c.args), _ => {} } } /// Adds constraints appropriate for a function with signature /// `sig` appearing in a context with ambient variance `variance` - fn add_constraints_from_sig<'a>( + fn add_constraints_from_sig( &mut self, - mut sig_tys: impl DoubleEndedIterator, + mut sig_tys: impl DoubleEndedIterator>, variance: Variance, ) { - let contra = variance.contravariant(); + let contra = variance.xform(Variance::Contravariant); let Some(output) = sig_tys.next_back() else { return never!("function signature has no return type"); }; @@ -457,27 +326,26 @@ impl Context<'_> { /// Adds constraints appropriate for a region appearing in a /// context with ambient variance `variance` - fn add_constraints_from_region(&mut self, region: &Lifetime, variance: Variance) { + fn add_constraints_from_region(&mut self, region: Region<'db>, variance: Variance) { tracing::debug!( "add_constraints_from_region(region={:?}, variance={:?})", region, variance ); - match region.data(Interner) { - LifetimeData::Placeholder(index) => { - let idx = crate::lt_from_placeholder_idx(self.db, *index).0; - let inferred = self.generics.lifetime_idx(idx).unwrap(); - self.constrain(inferred, variance); - } - LifetimeData::Static => {} - LifetimeData::BoundVar(..) => { + match region.kind() { + RegionKind::ReEarlyParam(param) => self.constrain(param.index as usize, variance), + RegionKind::ReStatic => {} + RegionKind::ReBound(..) => { // Either a higher-ranked region inside of a type or a // late-bound function parameter. // // We do not compute constraints for either of these. } - LifetimeData::Error => {} - LifetimeData::Phantom(..) | LifetimeData::InferenceVar(..) | LifetimeData::Erased => { + RegionKind::ReError(_) => {} + RegionKind::ReLateParam(..) + | RegionKind::RePlaceholder(..) + | RegionKind::ReVar(..) + | RegionKind::ReErased => { // We don't expect to see anything but 'static or bound // regions when visiting member types or method types. never!( @@ -491,11 +359,11 @@ impl Context<'_> { /// Adds constraints appropriate for a mutability-type pair /// appearing in a context with ambient variance `variance` - fn add_constraints_from_mt(&mut self, ty: &Ty, mt: Mutability, variance: Variance) { + fn add_constraints_from_mt(&mut self, ty: Ty<'db>, mt: Mutability, variance: Variance) { self.add_constraints_from_ty( ty, match mt { - Mutability::Mut => variance.invariant(), + Mutability::Mut => Variance::Invariant, Mutability::Not => variance, }, ); @@ -508,7 +376,7 @@ impl Context<'_> { self.variances[index], variance ); - self.variances[index] = self.variances[index].glb(variance); + self.variances[index] = glb(self.variances[index], variance); } } @@ -519,6 +387,7 @@ mod tests { AdtId, GenericDefId, ModuleDefId, hir::generics::GenericParamDataRef, src::HasSource, }; use itertools::Itertools; + use rustc_type_ir::{Variance, inherent::SliceLike}; use stdx::format_to; use syntax::{AstNode, ast::HasName}; use test_fixture::WithFixture; @@ -1037,26 +906,21 @@ struct FixedPoint(&'static FixedPoint<(), T, U>, V); let loc = it.lookup(&db); loc.source(&db).value.name().unwrap() } - GenericDefId::TraitId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::TypeAliasId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::ImplId(_) => return None, - GenericDefId::ConstId(_) => return None, - GenericDefId::StaticId(_) => return None, + GenericDefId::TraitId(_) + | GenericDefId::TypeAliasId(_) + | GenericDefId::ImplId(_) + | GenericDefId::ConstId(_) + | GenericDefId::StaticId(_) => return None, }, )) }) .sorted_by_key(|(_, n)| n.syntax().text_range().start()); let mut res = String::new(); for (def, name) in defs { - let Some(variances) = db.variances_of(def) else { + let variances = db.variances_of(def); + if variances.is_empty() { continue; - }; + } format_to!( res, "{name}[{}]\n", @@ -1072,10 +936,16 @@ struct FixedPoint(&'static FixedPoint<(), T, U>, V); &lifetime_param_data.name } }) - .zip_eq(&*variances) + .zip_eq(variances) .format_with(", ", |(name, var), f| f(&format_args!( - "{}: {var}", - name.as_str() + "{}: {}", + name.as_str(), + match var { + Variance::Covariant => "covariant", + Variance::Invariant => "invariant", + Variance::Contravariant => "contravariant", + Variance::Bivariant => "bivariant", + }, ))) ); } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index fc516a6764a5..36c8c3051cf5 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -36,6 +36,7 @@ pub mod term_search; mod display; use std::{ + fmt, mem::discriminant, ops::{ControlFlow, Not}, }; @@ -160,7 +161,7 @@ pub use { // FIXME: Properly encapsulate mir hir_ty::mir, hir_ty::{ - CastError, FnAbi, PointerCast, Variance, attach_db, attach_db_allow_change, + CastError, FnAbi, PointerCast, attach_db, attach_db_allow_change, consteval::ConstEvalError, diagnostics::UnsafetyReason, display::{ClosureStyle, DisplayTarget, HirDisplay, HirDisplayError, HirWrite}, @@ -4110,7 +4111,39 @@ impl GenericParam { GenericParam::ConstParam(_) => return None, GenericParam::LifetimeParam(it) => generics.lifetime_idx(it.id)?, }; - db.variances_of(parent)?.get(index).copied() + db.variances_of(parent).get(index).map(Into::into) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Variance { + Bivariant, + Covariant, + Contravariant, + Invariant, +} + +impl From for Variance { + #[inline] + fn from(value: rustc_type_ir::Variance) -> Self { + match value { + rustc_type_ir::Variance::Covariant => Variance::Covariant, + rustc_type_ir::Variance::Invariant => Variance::Invariant, + rustc_type_ir::Variance::Contravariant => Variance::Contravariant, + rustc_type_ir::Variance::Bivariant => Variance::Bivariant, + } + } +} + +impl fmt::Display for Variance { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let description = match self { + Variance::Bivariant => "bivariant", + Variance::Covariant => "covariant", + Variance::Contravariant => "contravariant", + Variance::Invariant => "invariant", + }; + f.pad(description) } } From 369715b77c2b663184535e4beee710784f0ead6f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 17 Oct 2025 06:46:34 +0300 Subject: [PATCH 042/170] Remove lint allows from new solver stuff --- .../crates/hir-ty/src/builder.rs | 23 +- .../crates/hir-ty/src/consteval.rs | 64 +---- .../crates/hir-ty/src/display.rs | 6 +- .../crates/hir-ty/src/dyn_compatibility.rs | 12 +- .../crates/hir-ty/src/generics.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 18 +- .../crates/hir-ty/src/infer/closure.rs | 16 +- .../crates/hir-ty/src/infer/coerce.rs | 19 +- .../crates/hir-ty/src/infer/expr.rs | 6 +- .../crates/hir-ty/src/infer/pat.rs | 2 +- .../crates/hir-ty/src/infer/path.rs | 4 +- .../crates/hir-ty/src/infer/unify.rs | 10 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 2 +- .../crates/hir-ty/src/lower/path.rs | 7 +- .../crates/hir-ty/src/lower_nextsolver.rs | 224 ++++++------------ .../hir-ty/src/lower_nextsolver/path.rs | 56 +---- .../crates/hir-ty/src/method_resolution.rs | 4 +- .../crates/hir-ty/src/next_solver.rs | 1 - .../crates/hir-ty/src/next_solver/consts.rs | 9 +- .../crates/hir-ty/src/next_solver/fold.rs | 7 +- .../crates/hir-ty/src/next_solver/fulfill.rs | 19 +- .../hir-ty/src/next_solver/fulfill/errors.rs | 209 ++-------------- .../hir-ty/src/next_solver/generic_arg.rs | 80 ++----- .../crates/hir-ty/src/next_solver/generics.rs | 37 +-- .../crates/hir-ty/src/next_solver/infer/at.rs | 66 +----- .../infer/canonical/canonicalizer.rs | 2 +- .../infer/canonical/instantiate.rs | 15 +- .../src/next_solver/infer/canonical/mod.rs | 19 +- .../hir-ty/src/next_solver/infer/context.rs | 20 +- .../hir-ty/src/next_solver/infer/mod.rs | 71 +++--- .../src/next_solver/infer/opaque_types/mod.rs | 32 +-- .../next_solver/infer/opaque_types/table.rs | 20 +- .../infer/region_constraints/mod.rs | 6 +- .../next_solver/infer/relate/generalize.rs | 9 +- .../next_solver/infer/relate/higher_ranked.rs | 3 - .../src/next_solver/infer/relate/lattice.rs | 15 +- .../hir-ty/src/next_solver/infer/resolve.rs | 5 +- .../hir-ty/src/next_solver/infer/select.rs | 40 ++-- .../src/next_solver/infer/snapshot/fudge.rs | 5 +- .../next_solver/infer/snapshot/undo_log.rs | 4 +- .../hir-ty/src/next_solver/infer/traits.rs | 26 +- .../hir-ty/src/next_solver/infer/unify_key.rs | 29 +-- .../crates/hir-ty/src/next_solver/inspect.rs | 51 ++-- .../crates/hir-ty/src/next_solver/interner.rs | 141 ++++------- .../crates/hir-ty/src/next_solver/ir_print.rs | 2 - .../crates/hir-ty/src/next_solver/mapping.rs | 97 ++++---- .../hir-ty/src/next_solver/normalize.rs | 1 - .../hir-ty/src/next_solver/obligation_ctxt.rs | 37 +-- .../crates/hir-ty/src/next_solver/opaques.rs | 3 +- .../hir-ty/src/next_solver/predicate.rs | 13 +- .../crates/hir-ty/src/next_solver/region.rs | 3 +- .../crates/hir-ty/src/next_solver/solver.rs | 66 ++---- .../crates/hir-ty/src/next_solver/ty.rs | 26 +- .../crates/hir-ty/src/next_solver/util.rs | 46 +--- .../crates/hir-ty/src/primitive.rs | 67 +----- src/tools/rust-analyzer/crates/hir/src/lib.rs | 7 +- .../rust-analyzer/crates/hir/src/semantics.rs | 9 +- 57 files changed, 526 insertions(+), 1267 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs index 5c4eb8475bbc..4cd0af28f33f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs @@ -1,10 +1,10 @@ //! `TyBuilder`, a helper for building instances of `Ty` and related types. use chalk_ir::{ - DebruijnIndex, Scalar, + DebruijnIndex, cast::{Cast, Caster}, }; -use hir_def::{GenericDefId, GenericParamId, TraitId, builtin_type::BuiltinType}; +use hir_def::{GenericDefId, GenericParamId, TraitId}; use smallvec::SmallVec; use crate::{ @@ -18,7 +18,7 @@ use crate::{ DbInterner, EarlyBinder, mapping::{ChalkToNextSolver, NextSolverToChalk}, }, - primitive, to_chalk_trait_id, + to_chalk_trait_id, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -137,23 +137,6 @@ impl TyBuilder<()> { TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(Interner) } - pub(crate) fn builtin(builtin: BuiltinType) -> Ty { - match builtin { - BuiltinType::Char => TyKind::Scalar(Scalar::Char).intern(Interner), - BuiltinType::Bool => TyKind::Scalar(Scalar::Bool).intern(Interner), - BuiltinType::Str => TyKind::Str.intern(Interner), - BuiltinType::Int(t) => { - TyKind::Scalar(Scalar::Int(primitive::int_ty_from_builtin(t))).intern(Interner) - } - BuiltinType::Uint(t) => { - TyKind::Scalar(Scalar::Uint(primitive::uint_ty_from_builtin(t))).intern(Interner) - } - BuiltinType::Float(t) => { - TyKind::Scalar(Scalar::Float(primitive::float_ty_from_builtin(t))).intern(Interner) - } - } - } - pub(crate) fn unknown_subst( db: &dyn HirDatabase, def: impl Into, 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 761d72243e9f..18ebe7d7a539 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -6,74 +6,28 @@ mod tests; use base_db::Crate; use hir_def::{ EnumVariantId, GeneralConstId, HasModule, StaticId, - expr_store::{Body, HygieneId, path::Path}, + expr_store::Body, hir::{Expr, ExprId}, - resolver::{Resolver, ValueNs}, type_ref::LiteralConstRef, }; use hir_expand::Lookup; -use rustc_type_ir::{UnevaluatedConst, inherent::IntoKind}; -use stdx::never; +use rustc_type_ir::inherent::IntoKind; use triomphe::Arc; use crate::{ - MemoryMap, TraitEnvironment, + LifetimeElisionKind, MemoryMap, TraitEnvironment, TyLoweringContext, db::HirDatabase, display::DisplayTarget, - generics::Generics, infer::InferenceContext, mir::{MirEvalError, MirLowerError}, next_solver::{ Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, - ParamConst, SolverDefId, Ty, ValueConst, + SolverDefId, Ty, ValueConst, }, }; use super::mir::{interpret_mir, lower_to_mir, pad16}; -pub(crate) fn path_to_const<'a, 'g>( - db: &'a dyn HirDatabase, - resolver: &Resolver<'a>, - path: &Path, - args: impl FnOnce() -> &'g Generics, - _expected_ty: Ty<'a>, -) -> Option> { - let interner = DbInterner::new_with(db, Some(resolver.krate()), None); - match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) { - Some(ValueNs::GenericParam(p)) => { - let args = args(); - match args - .type_or_const_param(p.into()) - .and_then(|(idx, p)| p.const_param().map(|p| (idx, p.clone()))) - { - Some((idx, _param)) => { - Some(Const::new_param(interner, ParamConst { index: idx as u32, id: p })) - } - None => { - never!( - "Generic list doesn't contain this param: {:?}, {:?}, {:?}", - args, - path, - p - ); - None - } - } - } - Some(ValueNs::ConstId(c)) => { - let args = GenericArgs::new_from_iter(interner, []); - Some(Const::new( - interner, - rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new( - SolverDefId::ConstId(c), - args, - )), - )) - } - _ => None, - } -} - pub fn unknown_const<'db>(_ty: Ty<'db>) -> Const<'db> { Const::new(DbInterner::conjure(), rustc_type_ir::ConstKind::Error(ErrorGuaranteed)) } @@ -279,8 +233,14 @@ pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'_, 'd return unknown_const(infer[expr]); } if let Expr::Path(p) = &ctx.body[expr] { - let resolver = &ctx.resolver; - if let Some(c) = path_to_const(ctx.db, resolver, p, || ctx.generics(), infer[expr]) { + let mut ctx = TyLoweringContext::new( + ctx.db, + &ctx.resolver, + ctx.body, + ctx.generic_def, + LifetimeElisionKind::Infer, + ); + if let Some(c) = ctx.path_to_const(p) { return c; } } 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 210e1ac52e58..2c6cbdd03f13 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1062,9 +1062,9 @@ impl<'db> HirDisplay<'db> for Ty<'db> { TyKind::Str => write!(f, "str")?, TyKind::Bool => write!(f, "bool")?, TyKind::Char => write!(f, "char")?, - TyKind::Float(t) => write!(f, "{}", primitive::float_ty_to_string_ns(t))?, - TyKind::Int(t) => write!(f, "{}", primitive::int_ty_to_string_ns(t))?, - TyKind::Uint(t) => write!(f, "{}", primitive::uint_ty_to_string_ns(t))?, + TyKind::Float(t) => write!(f, "{}", primitive::float_ty_to_string(t))?, + TyKind::Int(t) => write!(f, "{}", primitive::int_ty_to_string(t))?, + TyKind::Uint(t) => write!(f, "{}", primitive::uint_ty_to_string(t))?, TyKind::Slice(t) => { write!(f, "[")?; t.hir_fmt(f)?; 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 b2406a088958..e35a79870329 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 @@ -7,7 +7,6 @@ use hir_def::{ TypeAliasId, TypeOrConstParamId, TypeParamId, hir::generics::LocalTypeOrConstParamId, lang_item::LangItem, signatures::TraitFlags, }; -use intern::Symbol; use rustc_hash::FxHashSet; use rustc_type_ir::{ AliasTyKind, ClauseKind, PredicatePolarity, TypeSuperVisitable as _, TypeVisitable as _, @@ -441,8 +440,7 @@ fn receiver_is_dispatchable<'db>( // Type `U` // FIXME: That seems problematic to fake a generic param like that? - let unsized_self_ty = - crate::next_solver::Ty::new_param(interner, self_param_id, u32::MAX, Symbol::empty()); + let unsized_self_ty = crate::next_solver::Ty::new_param(interner, self_param_id, u32::MAX); // `Receiver[Self => U]` let unsized_receiver_ty = receiver_for_self_ty(interner, func, receiver_ty, unsized_self_ty); @@ -454,8 +452,8 @@ fn receiver_is_dispatchable<'db>( TraitRef::new(interner, unsize_did.into(), [self_param_ty, unsized_self_ty]); // U: Trait - let args = GenericArgs::for_item(interner, trait_.into(), |name, index, kind, _| { - if index == 0 { unsized_self_ty.into() } else { mk_param(interner, index, name, kind) } + let args = GenericArgs::for_item(interner, trait_.into(), |index, kind, _| { + if index == 0 { unsized_self_ty.into() } else { mk_param(interner, index, kind) } }); let trait_predicate = TraitRef::new_from_args(interner, trait_.into(), args); @@ -494,8 +492,8 @@ fn receiver_for_self_ty<'db>( let args = crate::next_solver::GenericArgs::for_item( interner, SolverDefId::FunctionId(func), - |name, index, kind, _| { - if index == 0 { self_ty.into() } else { mk_param(interner, index, name, kind) } + |index, kind, _| { + if index == 0 { self_ty.into() } else { mk_param(interner, index, kind) } }, ); 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 2053a099ed78..3ca5f0dcb247 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -258,7 +258,7 @@ impl Generics { } /// Returns a Substitution that replaces each parameter by itself (i.e. `Ty::Param`). - pub fn placeholder_subst(&self, db: &dyn HirDatabase) -> Substitution { + pub(crate) fn placeholder_subst(&self, db: &dyn HirDatabase) -> Substitution { Substitution::from_iter( Interner, self.iter_id().enumerate().map(|(index, id)| match id { 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 041799be9602..b2dd90a3d0df 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -57,7 +57,6 @@ use triomphe::Arc; use crate::{ ImplTraitId, IncorrectGenericsLenKind, PathLoweringDiagnostic, TargetFeatures, db::{HirDatabase, InternedClosureId, InternedOpaqueTyId}, - generics::Generics, infer::{ coerce::{CoerceMany, DynamicCoerceMany}, diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext}, @@ -72,10 +71,7 @@ use crate::{ Tys, abi::Safety, fold::fold_tys, - infer::{ - DefineOpaqueTypes, - traits::{Obligation, ObligationCause}, - }, + infer::traits::{Obligation, ObligationCause}, mapping::ChalkToNextSolver, }, traits::FnTrait, @@ -763,8 +759,7 @@ 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, TargetFeatureIsSafeInTarget)>, - generic_def: GenericDefId, - generics: OnceCell, + pub(crate) generic_def: GenericDefId, table: unify::InferenceTable<'db>, /// The traits in scope, disregarding block modules. This is used for caching purposes. traits_in_scope: FxHashSet, @@ -873,7 +868,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { return_ty: types.error, // set in collect_* calls types, target_features: OnceCell::new(), - generics: OnceCell::new(), table, tuple_field_accesses_rev: Default::default(), resume_yield_tys: None, @@ -902,10 +896,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } } - pub(crate) fn generics(&self) -> &Generics { - self.generics.get_or_init(|| crate::generics::generics(self.db, self.generic_def)) - } - #[inline] fn krate(&self) -> Crate { self.resolver.krate() @@ -1133,7 +1123,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.next_var_for_param(id), ), ), None => self.err_ty(), @@ -1676,7 +1666,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { .table .infer_ctxt .at(&ObligationCause::new(), self.table.trait_env.env) - .eq(DefineOpaqueTypes::Yes, expected, actual) + .eq(expected, actual) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); if let Err(_err) = result { // FIXME: Emit diagnostic. 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 2637ed6b3ec9..3dc277023a32 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 @@ -26,7 +26,7 @@ use crate::{ PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, Ty, TyKind, abi::Safety, infer::{ - BoundRegionConversionTime, DefineOpaqueTypes, InferOk, InferResult, + BoundRegionConversionTime, InferOk, InferResult, traits::{ObligationCause, PredicateObligations}, }, util::explicit_item_bounds, @@ -307,7 +307,7 @@ impl<'db> InferenceContext<'_, 'db> { .table .infer_ctxt .at(&ObligationCause::new(), self.table.trait_env.env) - .eq(DefineOpaqueTypes::Yes, inferred_fnptr_sig, generalized_fnptr_sig) + .eq(inferred_fnptr_sig, generalized_fnptr_sig) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); let resolved_sig = @@ -692,18 +692,16 @@ impl<'db> InferenceContext<'_, 'db> { let InferOk { value: (), obligations } = table .infer_ctxt .at(&cause, table.trait_env.env) - .eq(DefineOpaqueTypes::Yes, expected_ty, supplied_ty)?; + .eq(expected_ty, supplied_ty)?; all_obligations.extend(obligations); } let supplied_output_ty = supplied_sig.output(); let cause = ObligationCause::new(); - let InferOk { value: (), obligations } = - table.infer_ctxt.at(&cause, table.trait_env.env).eq( - DefineOpaqueTypes::Yes, - expected_sigs.liberated_sig.output(), - supplied_output_ty, - )?; + let InferOk { value: (), obligations } = table + .infer_ctxt + .at(&cause, table.trait_env.env) + .eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?; all_obligations.extend(obligations); let inputs = supplied_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 4620da714743..78889ccb89a2 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 @@ -63,7 +63,7 @@ use crate::{ GenericArgs, PolyFnSig, PredicateKind, Region, RegionKind, SolverDefId, TraitRef, Ty, TyKind, infer::{ - DefineOpaqueTypes, InferCtxt, InferOk, InferResult, + InferCtxt, InferOk, InferResult, relate::RelateResult, select::{ImplSource, SelectionError}, traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations}, @@ -149,7 +149,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { let res = if this.use_lub { at.lub(b, a) } else { - at.sup(DefineOpaqueTypes::Yes, b, a) + at.sup(b, a) .map(|InferOk { value: (), obligations }| InferOk { value: b, obligations }) }; @@ -1460,19 +1460,12 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { // // Another example is `break` with no argument expression. assert!(expression_ty.is_unit(), "if let hack without unit type"); - icx.table - .infer_ctxt - .at(cause, icx.table.trait_env.env) - .eq( - // needed for tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs - DefineOpaqueTypes::Yes, - expected, - found, - ) - .map(|infer_ok| { + icx.table.infer_ctxt.at(cause, icx.table.trait_env.env).eq(expected, found).map( + |infer_ok| { icx.table.register_infer_ok(infer_ok); expression_ty - }) + }, + ) }; debug!(?result); 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 179eaccc652b..e1964608a3f0 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 @@ -46,7 +46,7 @@ use crate::{ AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, TraitRef, Ty, TyKind, TypeError, infer::{ - DefineOpaqueTypes, InferOk, + InferOk, traits::{Obligation, ObligationCause}, }, obligation_ctxt::ObligationCtxt, @@ -1333,7 +1333,7 @@ impl<'db> InferenceContext<'_, 'db> { self.interner(), box_id.into(), [inner_ty.into()], - |_, _, id, _| self.table.next_var_for_param(id), + |_, id, _| self.table.next_var_for_param(id), ), ) } else { @@ -2122,7 +2122,7 @@ impl<'db> InferenceContext<'_, 'db> { .table .infer_ctxt .at(&ObligationCause::new(), this.table.trait_env.env) - .eq(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty); + .eq(formal_input_ty, coerced_ty); // If neither check failed, the types are compatible match formal_ty_error { 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 452ae316620f..61255d31d281 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 @@ -358,7 +358,7 @@ impl<'db> InferenceContext<'_, 'db> { self.interner(), box_adt.into(), std::iter::once(inner_ty.into()).chain(alloc_ty.map(Into::into)), - |_, _, id, _| self.table.next_var_for_param(id), + |_, id, _| self.table.next_var_for_param(id), ), ) } 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 f70ed90b953a..84d17db6c663 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 @@ -118,7 +118,7 @@ impl<'db> InferenceContext<'_, 'db> { self.interner(), generic_def.into(), self_subst.iter().flat_map(|it| it.iter()).chain(substs.iter().skip(parent_substs_len)), - |_, _, id, _| GenericArg::error_from_id(self.interner(), id), + |_, id, _| GenericArg::error_from_id(self.interner(), id), ); Some(ValuePathResolution::GenericDef(value_def, generic_def, substs)) @@ -352,7 +352,7 @@ impl<'db> InferenceContext<'_, 'db> { self.interner(), trait_.into(), [ty.into()], - |_, _, id, _| self.table.next_var_for_param(id), + |_, id, _| self.table.next_var_for_param(id), ); let trait_ref = TraitRef::new(self.interner(), trait_.into(), args); self.table.register_predicate(Obligation::new( 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 8f754f0e1aaa..beb26f7d6890 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 @@ -25,7 +25,7 @@ use crate::{ SolverDefId, SolverDefIds, TraitRef, Ty, TyKind, TypingMode, fulfill::{FulfillmentCtxt, NextSolverError}, infer::{ - DbInternerInferExt, DefineOpaqueTypes, InferCtxt, InferOk, InferResult, + DbInternerInferExt, InferCtxt, InferOk, InferResult, at::ToTrace, snapshot::CombinedSnapshot, traits::{Obligation, ObligationCause, PredicateObligation}, @@ -148,7 +148,7 @@ fn could_unify_impl<'db>( let ((ty1_with_vars, ty2_with_vars), _) = infcx.instantiate_canonical(tys); let mut ctxt = ObligationCtxt::new(&infcx); let can_unify = at - .eq(DefineOpaqueTypes::No, ty1_with_vars, ty2_with_vars) + .eq(ty1_with_vars, ty2_with_vars) .map(|infer_ok| ctxt.register_infer_ok_obligations(infer_ok)) .is_ok(); can_unify && select(&mut ctxt).is_empty() @@ -452,11 +452,7 @@ impl<'db> InferenceTable<'db> { /// 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.infer_ctxt.at(&ObligationCause::new(), self.trait_env.env).eq( - DefineOpaqueTypes::Yes, - t1, - t2, - ) + self.infer_ctxt.at(&ObligationCause::new(), self.trait_env.env).eq(t1, t2) } pub(crate) fn shallow_resolve(&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 77585177c1b5..b698fd9a1454 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -82,7 +82,7 @@ use traits::FnTrait; use triomphe::Arc; use crate::{ - builder::{ParamKind, TyBuilder}, + builder::TyBuilder, chalk_ext::*, db::HirDatabase, display::{DisplayTarget, HirDisplay}, 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 09a256a86dcc..42723dc9e1dd 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 @@ -434,13 +434,16 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { } fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty { + let interner = DbInterner::conjure(); let generic_def = match typeable { - TyDefId::BuiltinType(builtin) => return TyBuilder::builtin(builtin), + TyDefId::BuiltinType(builtin) => { + return crate::next_solver::Ty::from_builtin_type(interner, builtin) + .to_chalk(interner); + } TyDefId::AdtId(it) => it.into(), TyDefId::TypeAliasId(it) => it.into(), }; let substs = self.substs_from_path_segment(generic_def, infer_args, None, false); - let interner = DbInterner::conjure(); let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); self.ctx.db.ty(typeable).instantiate(interner, args).to_chalk(interner) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index aced46bf806b..76ee1a4f2d2b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -5,8 +5,6 @@ //! - Building the type for an item: This happens through the `ty` query. //! //! This usually involves resolving names, collecting generic arguments etc. -#![allow(unused)] -// FIXME(next-solver): this should get removed as things get moved to rustc_type_ir from chalk_ir pub(crate) mod path; use std::{ @@ -20,19 +18,15 @@ use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, - LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, - TypeParamId, VariantId, - expr_store::{ - ExpressionStore, - path::{GenericArg, Path}, - }, + LocalFieldId, Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, TypeParamId, + VariantId, + expr_store::{ExpressionStore, HygieneId, path::Path}, hir::generics::{ - GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance, - WherePredicate, + GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate, }, item_tree::FieldsShape, lang_item::LangItem, - resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, + resolver::{HasResolver, LifetimeNs, Resolver, TypeNs, ValueNs}, signatures::{FunctionSignature, TraitFlags, TypeAliasFlags}, type_ref::{ ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier, @@ -40,7 +34,6 @@ use hir_def::{ }, }; use hir_expand::name::Name; -use intern::{Symbol, sym}; use la_arena::{Arena, ArenaMap, Idx}; use path::{PathDiagnosticCallback, PathLoweringContext}; use rustc_ast_ir::Mutability; @@ -50,7 +43,7 @@ use rustc_type_ir::{ AliasTyKind, ConstKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, OutlivesPredicate, TyKind::{self}, - TypeFoldable, TypeFolder, TypeVisitableExt, Upcast, + TypeVisitableExt, inherent::{GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, Ty as _}, }; use salsa::plumbing::AsId; @@ -59,19 +52,17 @@ use stdx::never; use triomphe::Arc; use crate::{ - FnAbi, ImplTraitId, Interner, ParamKind, TraitEnvironment, TyDefId, TyLoweringDiagnostic, - TyLoweringDiagnosticKind, ValueTyDefId, - consteval::{intern_const_ref, path_to_const, unknown_const_as_generic}, + FnAbi, ImplTraitId, TraitEnvironment, TyDefId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, + ValueTyDefId, + consteval::intern_const_ref, db::HirDatabase, generics::{Generics, generics, trait_self_param_idx}, lower::{Diagnostics, PathDiagnosticCallbackData, create_diagnostics}, next_solver::{ - AdtDef, AliasTy, Binder, BoundExistentialPredicates, BoundRegionKind, BoundTyKind, - BoundVarKind, BoundVarKinds, Clause, Clauses, Const, DbInterner, EarlyBinder, - EarlyParamRegion, ErrorGuaranteed, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Predicate, - Region, SolverDefId, TraitPredicate, TraitRef, Ty, Tys, - abi::Safety, - mapping::{ChalkToNextSolver, convert_ty_for_result}, + AliasTy, Binder, BoundExistentialPredicates, Clause, Clauses, Const, DbInterner, + EarlyBinder, EarlyParamRegion, ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, + ParamEnv, PolyFnSig, Predicate, Region, SolverDefId, TraitPredicate, TraitRef, Ty, Tys, + UnevaluatedConst, abi::Safety, }, }; @@ -95,11 +86,11 @@ struct ImplTraitLoweringState<'db> { mode: ImplTraitLoweringMode, // This is structured as a struct with fields and not as an enum because it helps with the borrow checker. opaque_type_data: Arena>, - param_and_variable_counter: u16, } + impl<'db> ImplTraitLoweringState<'db> { fn new(mode: ImplTraitLoweringMode) -> ImplTraitLoweringState<'db> { - Self { mode, opaque_type_data: Arena::new(), param_and_variable_counter: 0 } + Self { mode, opaque_type_data: Arena::new() } } } @@ -279,8 +270,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { let const_ref = &self.store[const_ref.expr]; match const_ref { hir_def::hir::Expr::Path(path) => { - path_to_const(self.db, self.resolver, path, || self.generics(), const_type) - .unwrap_or_else(|| unknown_const(const_type)) + self.path_to_const(path).unwrap_or_else(|| unknown_const(const_type)) } hir_def::hir::Expr::Literal(literal) => intern_const_ref( self.db, @@ -324,9 +314,39 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } } + pub(crate) fn path_to_const(&mut self, path: &Path) -> Option> { + 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 + } + } + } + Some(ValueNs::ConstId(c)) => { + let args = GenericArgs::new_from_iter(self.interner, []); + Some(Const::new( + self.interner, + rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new( + SolverDefId::ConstId(c), + args, + )), + )) + } + _ => None, + } + } + pub(crate) fn lower_path_as_const(&mut self, path: &Path, const_type: Ty<'db>) -> Const<'db> { - path_to_const(self.db, self.resolver, path, || self.generics(), const_type) - .unwrap_or_else(|| unknown_const(const_type)) + self.path_to_const(path).unwrap_or_else(|| unknown_const(const_type)) } fn generics(&self) -> &Generics { @@ -338,12 +358,12 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { .is_some_and(|disallow_params_after| index >= disallow_params_after) } - fn type_param(&mut self, id: TypeParamId, index: u32, name: Symbol) -> Ty<'db> { + fn type_param(&mut self, id: TypeParamId, index: u32) -> Ty<'db> { if self.param_index_is_disallowed(index) { // FIXME: Report an error. Ty::new_error(self.interner, ErrorGuaranteed) } else { - Ty::new_param(self.interner, id, index, name) + Ty::new_param(self.interner, id, index) } } @@ -387,20 +407,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { res = Some(TypeNs::GenericParam(type_param_id)); let generics = self.generics(); - let (idx, data) = + let (idx, _data) = generics.type_or_const_param(type_param_id.into()).expect("matching generics"); - let type_data = match data { - TypeOrConstParamData::TypeParamData(ty) => ty, - _ => unreachable!(), - }; - self.type_param( - type_param_id, - idx as u32, - type_data - .name - .as_ref() - .map_or_else(|| sym::MISSING_NAME, |d| d.symbol().clone()), - ) + self.type_param(type_param_id, idx as u32) } &TypeRef::RawPtr(inner, mutability) => { let inner_ty = self.lower_ty(inner); @@ -1058,10 +1067,7 @@ fn type_for_const<'db>(db: &'db dyn HirDatabase, def: ConstId) -> EarlyBinder<'d /// Build the declared type of a static. fn type_for_static<'db>(db: &'db dyn HirDatabase, def: StaticId) -> EarlyBinder<'db, Ty<'db>> { let resolver = def.resolver(db); - let module = resolver.module(); - let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block()); let data = db.static_signature(def); - let parent = def.loc(db).container; let mut ctx = TyLoweringContext::new( db, &resolver, @@ -1177,7 +1183,6 @@ pub(crate) fn impl_self_ty_with_diagnostics_query<'db>( impl_id: ImplId, ) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { let resolver = impl_id.resolver(db); - let interner = DbInterner::new_with(db, Some(resolver.krate()), None); let impl_data = db.impl_signature(impl_id); let mut ctx = TyLoweringContext::new( @@ -1451,7 +1456,6 @@ pub(crate) fn trait_environment_query<'db>( return TraitEnvironment::empty(def.krate(db)); } - let interner = DbInterner::new_with(db, Some(def.krate(db)), None); let resolver = def.resolver(db); let mut ctx = TyLoweringContext::new( db, @@ -1509,7 +1513,7 @@ pub(crate) fn trait_environment_query<'db>( continue; }; let idx = idx as u32 + generics.parent_count as u32; - let param_ty = Ty::new_param(ctx.interner, param_id, idx, p.name.clone()); + let param_ty = Ty::new_param(ctx.interner, param_id, idx); if explicitly_unsized_tys.contains(¶m_ty) { continue; } @@ -1635,11 +1639,7 @@ where return; } - let param_name = param_data - .name - .as_ref() - .map_or_else(|| sym::MISSING_NAME, |name| name.symbol().clone()); - let param_ty = Ty::new_param(interner, param_id, param_idx, param_name); + let param_ty = Ty::new_param(interner, param_id, param_idx); if explicitly_unsized_tys.contains(¶m_ty) { return; } @@ -1724,83 +1724,12 @@ fn implicitly_sized_clauses<'a, 'subst, 'db>( ) } -pub(crate) fn make_binders<'db, T: rustc_type_ir::TypeVisitable>>( - interner: DbInterner<'db>, - generics: &Generics, - value: T, -) -> Binder<'db, T> { - Binder::bind_with_vars( - value, - BoundVarKinds::new_from_iter( - interner, - generics.iter_id().map(|x| match x { - hir_def::GenericParamId::ConstParamId(_) => BoundVarKind::Const, - hir_def::GenericParamId::TypeParamId(_) => BoundVarKind::Ty(BoundTyKind::Anon), - hir_def::GenericParamId::LifetimeParamId(_) => { - BoundVarKind::Region(BoundRegionKind::Anon) - } - }), - ), - ) -} - -/// Checks if the provided generic arg matches its expected kind, then lower them via -/// provided closures. Use unknown if there was kind mismatch. -/// -pub(crate) fn lower_generic_arg<'a, 'db, T>( - db: &'db dyn HirDatabase, - kind_id: GenericParamId, - arg: &'a GenericArg, - this: &mut T, - store: &ExpressionStore, - for_type: impl FnOnce(&mut T, TypeRefId) -> Ty<'db> + 'a, - for_const: impl FnOnce(&mut T, &ConstRef, Ty<'db>) -> Const<'db> + 'a, - for_const_ty_path_fallback: impl FnOnce(&mut T, &Path, Ty<'db>) -> Const<'db> + 'a, - for_lifetime: impl FnOnce(&mut T, &LifetimeRefId) -> Region<'db> + 'a, -) -> crate::next_solver::GenericArg<'db> { - let interner = DbInterner::new_with(db, None, None); - let kind = match kind_id { - GenericParamId::TypeParamId(_) => ParamKind::Type, - GenericParamId::ConstParamId(id) => { - let ty = db.const_param_ty(id); - ParamKind::Const(ty) - } - GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime, - }; - match (arg, kind) { - (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, *type_ref).into(), - (GenericArg::Const(c), ParamKind::Const(c_ty)) => { - for_const(this, c, c_ty.to_nextsolver(interner)).into() - } - (GenericArg::Lifetime(lifetime_ref), ParamKind::Lifetime) => { - for_lifetime(this, lifetime_ref).into() - } - (GenericArg::Const(_), ParamKind::Type) => Ty::new_error(interner, ErrorGuaranteed).into(), - (GenericArg::Lifetime(_), ParamKind::Type) => { - Ty::new_error(interner, ErrorGuaranteed).into() - } - (GenericArg::Type(t), ParamKind::Const(c_ty)) => match &store[*t] { - TypeRef::Path(p) => { - for_const_ty_path_fallback(this, p, c_ty.to_nextsolver(interner)).into() - } - _ => unknown_const_as_generic(c_ty.to_nextsolver(interner)), - }, - (GenericArg::Lifetime(_), ParamKind::Const(c_ty)) => { - unknown_const(c_ty.to_nextsolver(interner)).into() - } - (GenericArg::Type(_), ParamKind::Lifetime) => Region::error(interner).into(), - (GenericArg::Const(_), ParamKind::Lifetime) => Region::error(interner).into(), - } -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct GenericDefaults<'db>( - Option>>]>>, -); +pub struct GenericDefaults<'db>(Option>>]>>); impl<'db> GenericDefaults<'db> { #[inline] - pub fn get(&self, idx: usize) -> Option>> { + pub fn get(&self, idx: usize) -> Option>> { self.0.as_ref()?[idx] } } @@ -1837,17 +1766,17 @@ pub(crate) fn generic_defaults_with_diagnostics_query( let mut has_any_default = false; let mut defaults = generic_params .iter_parents_with_store() - .map(|((id, p), store)| { + .map(|((_id, p), store)| { ctx.store = store; - let (result, has_default) = handle_generic_param(&mut ctx, idx, id, p, &generic_params); + 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. - defaults.extend(generic_params.iter_self().map(|(id, p)| { - let (result, has_default) = handle_generic_param(&mut ctx, idx, id, p, &generic_params); + defaults.extend(generic_params.iter_self().map(|(_id, p)| { + let (result, has_default) = handle_generic_param(&mut ctx, idx, p); has_any_default |= has_default; idx += 1; result @@ -1863,10 +1792,8 @@ pub(crate) fn generic_defaults_with_diagnostics_query( fn handle_generic_param<'db>( ctx: &mut TyLoweringContext<'db, '_>, idx: usize, - id: GenericParamId, p: GenericParamDataRef<'_>, - generic_params: &Generics, - ) -> (Option>>, bool) { + ) -> (Option>>, bool) { ctx.lowering_param_default(idx as u32); match p { GenericParamDataRef::TypeParamData(p) => { @@ -1874,11 +1801,7 @@ pub(crate) fn generic_defaults_with_diagnostics_query( (ty.map(|ty| EarlyBinder::bind(ty.into())), p.default.is_some()) } GenericParamDataRef::ConstParamData(p) => { - let GenericParamId::ConstParamId(id) = id else { - unreachable!("Unexpected lifetime or type argument") - }; - - let mut val = p.default.map(|c| { + let val = p.default.map(|c| { let param_ty = ctx.lower_ty(p.ty); let c = ctx.lower_const(c, param_ty); c.into() @@ -1999,11 +1922,6 @@ pub(crate) fn associated_ty_item_bounds<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, ) -> EarlyBinder<'db, BoundExistentialPredicates<'db>> { - let trait_ = match type_alias.lookup(db).container { - ItemContainerId::TraitId(t) => t, - _ => panic!("associated type not in trait"), - }; - let type_alias_data = db.type_alias_signature(type_alias); let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); let interner = DbInterner::new_with(db, Some(resolver.krate()), None); @@ -2051,7 +1969,7 @@ pub(crate) fn associated_ty_item_bounds<'db>( p.term, )), ), - rustc_type_ir::ClauseKind::TypeOutlives(outlives_predicate) => None, + rustc_type_ir::ClauseKind::TypeOutlives(_) => None, rustc_type_ir::ClauseKind::RegionOutlives(_) | rustc_type_ir::ClauseKind::ConstArgHasType(_, _) | rustc_type_ir::ClauseKind::WellFormed(_) @@ -2066,15 +1984,15 @@ pub(crate) fn associated_ty_item_bounds<'db>( }); } - if !ctx.unsized_types.contains(&self_ty) { - let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); + if !ctx.unsized_types.contains(&self_ty) + && let Some(sized_trait) = LangItem::Sized.resolve_trait(db, resolver.krate()) + { let sized_clause = Binder::dummy(ExistentialPredicate::Trait(ExistentialTraitRef::new( interner, - trait_.into(), - [] as [crate::next_solver::GenericArg<'_>; 0], + sized_trait.into(), + [] as [GenericArg<'_>; 0], ))); bounds.push(sized_clause); - bounds.shrink_to_fit(); } EarlyBinder::bind(BoundExistentialPredicates::new_from_iter(interner, bounds)) @@ -2117,7 +2035,6 @@ fn named_associated_type_shorthand_candidates<'db, R>( ) -> Option { let db = interner.db; let mut search = |t: TraitRef<'db>| -> Option { - let trait_id = t.def_id.0; let mut checked_traits = FxHashSet::default(); let mut check_trait = |trait_ref: TraitRef<'db>| { let trait_id = trait_ref.def_id.0; @@ -2192,10 +2109,7 @@ fn named_associated_type_shorthand_candidates<'db, R>( let trait_generics = generics(db, trait_id.into()); tracing::debug!(?trait_generics); if trait_generics[param_id.local_id()].is_trait_self() { - let args = crate::next_solver::GenericArgs::identity_for_item( - interner, - trait_id.into(), - ); + let args = GenericArgs::identity_for_item(interner, trait_id.into()); let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); tracing::debug!(?args, ?trait_ref); return search(trait_ref); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs index 6bfe266b460c..a4ff47e3892a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -1,11 +1,8 @@ //! A wrapper around [`TyLoweringContext`] specifically for lowering paths. -use std::ops::Deref; - use either::Either; use hir_def::{ - AssocItemId, GenericDefId, GenericParamId, Lookup, TraitId, TypeAliasId, - builtin_type::BuiltinType, + GenericDefId, GenericParamId, Lookup, TraitId, TypeAliasId, expr_store::{ ExpressionStore, HygieneId, path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments}, @@ -18,13 +15,11 @@ use hir_def::{ type_ref::{TypeRef, TypeRefId}, }; use hir_expand::name::Name; -use intern::sym; -use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTerm, AliasTy, AliasTyKind, TypeVisitableExt, - inherent::{GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _}, + AliasTerm, AliasTy, AliasTyKind, + inherent::{GenericArgs as _, Region as _, SliceLike, Ty as _}, }; -use smallvec::{SmallVec, smallvec}; +use smallvec::SmallVec; use stdx::never; use crate::{ @@ -34,16 +29,12 @@ use crate::{ db::HirDatabase, generics::{Generics, generics}, lower::PathDiagnosticCallbackData, - lower_nextsolver::{ - LifetimeElisionKind, PredicateFilter, generic_predicates_filtered_by, - named_associated_type_shorthand_candidates, - }, + lower_nextsolver::{LifetimeElisionKind, named_associated_type_shorthand_candidates}, next_solver::{ - AdtDef, Binder, Clause, Const, DbInterner, ErrorGuaranteed, Predicate, ProjectionPredicate, - Region, SolverDefId, TraitRef, Ty, + Binder, Clause, Const, DbInterner, ErrorGuaranteed, Predicate, ProjectionPredicate, Region, + TraitRef, Ty, mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, }, - primitive, }; use super::{ @@ -173,22 +164,6 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { } } - fn prohibit_parenthesized_generic_args(&mut self) -> bool { - if let Some(generic_args) = self.current_or_prev_segment.args_and_bindings { - match generic_args.parenthesized { - GenericArgsParentheses::No => {} - GenericArgsParentheses::ReturnTypeNotation | GenericArgsParentheses::ParenSugar => { - let segment = self.current_segment_u32(); - self.on_diagnostic( - PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, - ); - return true; - } - } - } - false - } - // When calling this, the current segment is the resolved segment (we don't advance it yet). pub(crate) fn lower_partly_resolved_path( &mut self, @@ -274,19 +249,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { Ty::new_error(self.ctx.interner, ErrorGuaranteed) } Some(idx) => { - let (pidx, param) = generics.iter().nth(idx).unwrap(); + let (pidx, _param) = generics.iter().nth(idx).unwrap(); assert_eq!(pidx, param_id.into()); - let p = match param { - GenericParamDataRef::TypeParamData(p) => p, - _ => unreachable!(), - }; - self.ctx.type_param( - param_id, - idx as u32, - p.name - .as_ref() - .map_or_else(|| sym::MISSING_NAME.clone(), |p| p.symbol().clone()), - ) + self.ctx.type_param(param_id, idx as u32) } } } @@ -520,11 +485,10 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { let Some(res) = res else { return Ty::new_error(self.ctx.interner, ErrorGuaranteed); }; - let db = self.ctx.db; let def = self.ctx.def; let segment = self.current_or_prev_segment; let assoc_name = segment.name; - let mut check_alias = |name: &Name, t: TraitRef<'db>, associated_ty: TypeAliasId| { + let check_alias = |name: &Name, t: TraitRef<'db>, associated_ty: TypeAliasId| { if name != assoc_name { 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 06c7cdd4e416..bce17905037c 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 @@ -37,7 +37,7 @@ use crate::{ Canonical, DbInterner, ErrorGuaranteed, GenericArgs, Goal, Predicate, Region, SolverDefId, TraitRef, Ty, TyKind, TypingMode, infer::{ - DbInternerInferExt, DefineOpaqueTypes, + DbInternerInferExt, traits::{Obligation, ObligationCause, PredicateObligation}, }, obligation_ctxt::ObligationCtxt, @@ -1654,7 +1654,7 @@ fn is_valid_trait_method_candidate<'db>( let res = table .infer_ctxt .at(&ObligationCause::dummy(), table.trait_env.env) - .relate(DefineOpaqueTypes::No, expected_receiver, variance, receiver_ty); + .relate(expected_receiver, variance, receiver_ty); let Ok(infer_ok) = res else { return IsValidCandidate::No; }; 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 776e0d956f40..f5b4fa1e2a00 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 @@ -1,5 +1,4 @@ //! Things relevant to the next trait solver. -#![allow(unused, unreachable_pub)] pub mod abi; mod consts; 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 c5a1e7d31546..8d81a382c362 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 @@ -2,10 +2,9 @@ use std::hash::Hash; -use hir_def::{ConstParamId, TypeOrConstParamId}; -use intern::{Interned, Symbol}; +use hir_def::ConstParamId; use macros::{TypeFoldable, TypeVisitable}; -use rustc_ast_ir::{try_visit, visit::VisitorResult}; +use rustc_ast_ir::visit::VisitorResult; use rustc_type_ir::{ BoundVar, DebruijnIndex, FlagComputation, Flags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, WithCachedTypeInfo, @@ -14,7 +13,7 @@ use rustc_type_ir::{ }; use crate::{ - ConstScalar, MemoryMap, + MemoryMap, interner::InternedWrapperNoDebug, next_solver::{ClauseKind, ParamEnv}, }; @@ -429,7 +428,7 @@ impl<'db> PlaceholderLike> for PlaceholderConst { impl<'db> Relate> for ExprConst { fn relate>>( - relation: &mut R, + _relation: &mut R, a: Self, b: Self, ) -> rustc_type_ir::relate::RelateResult, Self> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs index a42fdb094304..588d42857493 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs @@ -1,9 +1,8 @@ //! Fold impls for the next-trait-solver. use rustc_type_ir::{ - BoundVar, DebruijnIndex, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitableExt, - inherent::{IntoKind, Region as _}, + DebruijnIndex, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + inherent::IntoKind, }; use crate::next_solver::BoundConst; @@ -55,7 +54,7 @@ pub(crate) struct BoundVarReplacer<'db, D> { } impl<'db, D: BoundVarReplacerDelegate<'db>> BoundVarReplacer<'db, D> { - pub fn new(tcx: DbInterner<'db>, delegate: D) -> Self { + pub(crate) fn new(tcx: DbInterner<'db>, delegate: D) -> Self { BoundVarReplacer { interner: tcx, current_index: DebruijnIndex::ZERO, delegate } } } 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 262da858d466..7783075d1a36 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 @@ -2,7 +2,7 @@ mod errors; -use std::{marker::PhantomData, mem, ops::ControlFlow, vec::ExtractIf}; +use std::{mem, ops::ControlFlow}; use rustc_hash::FxHashSet; use rustc_next_trait_solver::{ @@ -46,6 +46,7 @@ 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, } @@ -69,10 +70,6 @@ impl<'db> ObligationStorage<'db> { self.pending.push((obligation, stalled_on)); } - fn has_pending_obligations(&self) -> bool { - !self.pending.is_empty() || !self.overflowed.is_empty() - } - fn clone_pending(&self) -> PredicateObligations<'db> { let mut obligations: PredicateObligations<'db> = self.pending.iter().map(|(o, _)| o.clone()).collect(); @@ -125,10 +122,10 @@ 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()`. @@ -138,7 +135,7 @@ impl<'db> FulfillmentCtxt<'db> { 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()`. @@ -148,7 +145,7 @@ impl<'db> FulfillmentCtxt<'db> { pub(crate) fn collect_remaining_errors( &mut self, - infcx: &InferCtxt<'db>, + _infcx: &InferCtxt<'db>, ) -> Vec> { self.obligations .pending @@ -235,10 +232,6 @@ impl<'db> FulfillmentCtxt<'db> { self.collect_remaining_errors(infcx) } - fn has_pending_obligations(&self) -> bool { - self.obligations.has_pending_obligations() - } - pub(crate) fn pending_obligations(&self) -> PredicateObligations<'db> { self.obligations.clone_pending() } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs index ab4a229fbc05..82dbf9403cab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs @@ -9,15 +9,15 @@ use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt}; use rustc_type_ir::{ AliasRelationDirection, AliasTermKind, HostEffectPredicate, Interner, PredicatePolarity, error::ExpectedFound, - inherent::{IntoKind, PlaceholderConst, SliceLike, Span as _}, + inherent::{IntoKind, SliceLike, Span as _}, lang_items::SolverTraitLangItem, - solve::{CandidateSource, Certainty, GoalSource, MaybeCause, NoSolution}, + solve::{Certainty, GoalSource, MaybeCause, NoSolution}, }; use tracing::{instrument, trace}; use crate::next_solver::{ AliasTerm, Binder, ClauseKind, Const, ConstKind, DbInterner, PolyTraitPredicate, PredicateKind, - SolverContext, SolverDefId, Span, Term, TraitPredicate, Ty, TyKind, TypeError, + SolverContext, Span, Term, TraitPredicate, Ty, TyKind, TypeError, fulfill::NextSolverError, infer::{ InferCtxt, @@ -529,7 +529,6 @@ impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> { } } - 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())); @@ -542,34 +541,27 @@ impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> { recursion_depth: self.obligation.recursion_depth + 1, }; - let obligation; - match (child_mode, nested_goal.source()) { + 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(); - impl_where_bound_count += 1; + (ChildMode::Trait(_parent_trait_pred), GoalSource::ImplWhereBound) => { + make_obligation() } ( - ChildMode::Host(parent_host_pred), + ChildMode::Host(_parent_host_pred), GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition, - ) => { - obligation = make_obligation(); - impl_where_bound_count += 1; - } + ) => make_obligation(), // Skip over a higher-ranked predicate. - (_, GoalSource::InstantiateHigherRanked) => { - obligation = self.obligation.clone(); - } + (_, GoalSource::InstantiateHigherRanked) => self.obligation.clone(), (ChildMode::PassThrough, _) | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => { - obligation = make_obligation(); + make_obligation() } - } + }; self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; } @@ -628,35 +620,29 @@ impl<'db> NextSolverError<'db> { } mod wf { - use std::iter; - use hir_def::ItemContainerId; use rustc_type_ir::inherent::{ - AdtDef, BoundExistentialPredicates, GenericArg, GenericArgs as _, IntoKind, SliceLike, - Term as _, Ty as _, + AdtDef, BoundExistentialPredicates, GenericArgs as _, IntoKind, SliceLike, Term as _, + Ty as _, }; use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::{ - Interner, PredicatePolarity, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, - TypeVisitor, + Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; - use tracing::{debug, instrument, trace}; + use tracing::{debug, instrument}; use crate::next_solver::infer::InferCtxt; - use crate::next_solver::infer::traits::{ - Obligation, ObligationCause, PredicateObligation, PredicateObligations, - }; + use crate::next_solver::infer::traits::{Obligation, ObligationCause, PredicateObligations}; use crate::next_solver::{ - AliasTerm, Binder, ClauseKind, Const, ConstKind, Ctor, DbInterner, ExistentialPredicate, - GenericArgs, ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitPredicate, - TraitRef, Ty, TyKind, + Binder, ClauseKind, Const, ConstKind, Ctor, DbInterner, ExistentialPredicate, GenericArgs, + ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, TyKind, }; /// Compute the predicates that are required for a type to be well-formed. /// /// This is only intended to be used in the new solver, since it does not /// take into account recursion depth or proper error-reporting spans. - pub fn unnormalized_obligations<'db>( + pub(crate) fn unnormalized_obligations<'db>( infcx: &InferCtxt<'db>, param_env: ParamEnv<'db>, term: Term<'db>, @@ -683,158 +669,11 @@ mod wf { recursion_depth: usize, } - /// Controls whether we "elaborate" supertraits and so forth on the WF - /// predicates. This is a kind of hack to address #43784. The - /// underlying problem in that issue was a trait structure like: - /// - /// ```ignore (illustrative) - /// trait Foo: Copy { } - /// trait Bar: Foo { } - /// impl Foo for T { } - /// impl Bar for T { } - /// ``` - /// - /// Here, in the `Foo` impl, we will check that `T: Copy` holds -- but - /// we decide that this is true because `T: Bar` is in the - /// where-clauses (and we can elaborate that to include `T: - /// Copy`). This wouldn't be a problem, except that when we check the - /// `Bar` impl, we decide that `T: Foo` must hold because of the `Foo` - /// impl. And so nowhere did we check that `T: Copy` holds! - /// - /// To resolve this, we elaborate the WF requirements that must be - /// proven when checking impls. This means that (e.g.) the `impl Bar - /// for T` will be forced to prove not only that `T: Foo` but also `T: - /// Copy` (which it won't be able to do, because there is no `Copy` - /// impl for `T`). - #[derive(Debug, PartialEq, Eq, Copy, Clone)] - enum Elaborate { - All, - None, - } - impl<'a, 'db> WfPredicates<'a, 'db> { fn interner(&self) -> DbInterner<'db> { self.infcx.interner } - /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. - fn add_wf_preds_for_trait_pred( - &mut self, - trait_pred: TraitPredicate<'db>, - elaborate: Elaborate, - ) { - let tcx = self.interner(); - let trait_ref = trait_pred.trait_ref; - - // Negative trait predicates don't require supertraits to hold, just - // that their args are WF. - if trait_pred.polarity == PredicatePolarity::Negative { - self.add_wf_preds_for_negative_trait_pred(trait_ref); - return; - } - - // if the trait predicate is not const, the wf obligations should not be const as well. - let obligations = self.nominal_obligations(trait_ref.def_id.0.into(), trait_ref.args); - - debug!("compute_trait_pred obligations {:?}", obligations); - let param_env = self.param_env; - let depth = self.recursion_depth; - - let extend = |PredicateObligation { predicate, mut cause, .. }| { - Obligation::with_depth(tcx, cause, depth, param_env, predicate) - }; - - if let Elaborate::All = elaborate { - let implied_obligations = rustc_type_ir::elaborate::elaborate(tcx, obligations); - let implied_obligations = implied_obligations.map(extend); - self.out.extend(implied_obligations); - } else { - self.out.extend(obligations); - } - - self.out.extend( - trait_ref - .args - .iter() - .enumerate() - .filter_map(|(i, arg)| arg.as_term().map(|t| (i, t))) - .filter(|(_, term)| !term.has_escaping_bound_vars()) - .map(|(i, term)| { - let mut cause = ObligationCause::misc(); - // The first arg is the self ty - use the correct span for it. - Obligation::with_depth( - tcx, - cause, - depth, - param_env, - ClauseKind::WellFormed(term), - ) - }), - ); - } - - // Compute the obligations that are required for `trait_ref` to be WF, - // given that it is a *negative* trait predicate. - fn add_wf_preds_for_negative_trait_pred(&mut self, trait_ref: TraitRef<'db>) { - for arg in trait_ref.args { - if let Some(term) = arg.as_term() { - self.add_wf_preds_for_term(term); - } - } - } - - /// Pushes the obligations required for an alias (except inherent) to be WF - /// into `self.out`. - fn add_wf_preds_for_alias_term(&mut self, data: AliasTerm<'db>) { - // A projection is well-formed if - // - // (a) its predicates hold (*) - // (b) its args are wf - // - // (*) The predicates of an associated type include the predicates of - // the trait that it's contained in. For example, given - // - // trait A: Clone { - // type X where T: Copy; - // } - // - // The predicates of `<() as A>::X` are: - // [ - // `(): Sized` - // `(): Clone` - // `(): A` - // `i32: Sized` - // `i32: Clone` - // `i32: Copy` - // ] - let obligations = self.nominal_obligations(data.def_id, data.args); - self.out.extend(obligations); - - self.add_wf_preds_for_projection_args(data.args); - } - - fn add_wf_preds_for_projection_args(&mut self, args: GenericArgs<'db>) { - let tcx = self.interner(); - let cause = ObligationCause::new(); - let param_env = self.param_env; - let depth = self.recursion_depth; - - self.out.extend( - args.iter() - .filter_map(|arg| arg.as_term()) - .filter(|term| !term.has_escaping_bound_vars()) - .map(|term| { - Obligation::with_depth( - tcx, - cause.clone(), - depth, - param_env, - ClauseKind::WellFormed(term), - ) - }), - ); - } - fn require_sized(&mut self, subty: Ty<'db>) { if !subty.has_escaping_bound_vars() { let cause = ObligationCause::new(); @@ -895,7 +734,7 @@ mod wf { fn add_wf_preds_for_dyn_ty( &mut self, - ty: Ty<'db>, + _ty: Ty<'db>, data: &[Binder<'db, ExistentialPredicate<'db>>], region: Region<'db>, ) { @@ -1013,7 +852,7 @@ mod wf { )); } - TyKind::Pat(base_ty, pat) => { + TyKind::Pat(base_ty, _pat) => { self.require_sized(base_ty); } @@ -1036,7 +875,7 @@ mod wf { let obligations = self.nominal_obligations(data.def_id, data.args); self.out.extend(obligations); } - TyKind::Alias(rustc_type_ir::Inherent, data) => { + TyKind::Alias(rustc_type_ir::Inherent, _data) => { return; } @@ -1148,7 +987,7 @@ mod wf { // Let the visitor iterate into the argument/return // types appearing in the fn signature. } - TyKind::UnsafeBinder(ty) => {} + TyKind::UnsafeBinder(_ty) => {} TyKind::Dynamic(data, r) => { // WfObject @@ -1291,7 +1130,7 @@ mod wf { /// /// Requires that trait definitions have been processed so that we can /// elaborate predicates and walk supertraits. - pub fn object_region_bounds<'db>( + pub(crate) fn object_region_bounds<'db>( interner: DbInterner<'db>, existential_predicates: &[Binder<'db, ExistentialPredicate<'db>>], ) -> Vec> { 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 38293c45422c..b2632ba63709 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 @@ -1,27 +1,20 @@ //! Things related to generic args in the next-trait-solver. use hir_def::{GenericDefId, GenericParamId}; -use intern::{Interned, Symbol}; use macros::{TypeFoldable, TypeVisitable}; -use rustc_type_ir::inherent::Const as _; use rustc_type_ir::{ - ClosureArgs, CollectAndApply, ConstVid, CoroutineArgs, CoroutineClosureArgs, FnSig, FnSigTys, - GenericArgKind, IntTy, Interner, TermKind, TyKind, TyVid, TypeFoldable, TypeVisitable, - Variance, - inherent::{ - GenericArg as _, GenericArgs as _, GenericsOf, IntoKind, SliceLike, Term as _, Ty as _, - }, + ClosureArgs, CollectAndApply, ConstVid, CoroutineArgs, CoroutineClosureArgs, FnSigTys, + GenericArgKind, Interner, TermKind, TyKind, TyVid, Variance, + inherent::{GenericArg as _, GenericsOf, IntoKind, SliceLike, Term as _, Ty as _}, relate::{Relate, VarianceDiagInfo}, }; use smallvec::SmallVec; -use crate::db::HirDatabase; -use crate::next_solver::{Binder, PolyFnSig}; +use crate::next_solver::{PolyFnSig, interned_vec_db}; use super::{ Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId, Ty, Tys, - generics::{GenericParamDef, Generics}, - interned_vec_db, + generics::Generics, }; #[derive(Copy, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] @@ -191,7 +184,7 @@ impl<'db> GenericArgs<'db> { mut mk_kind: F, ) -> GenericArgs<'db> where - F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { let defs = interner.generics_of(def_id); let count = defs.count(); @@ -202,9 +195,7 @@ impl<'db> GenericArgs<'db> { /// Creates an all-error `GenericArgs`. pub fn error_for_item(interner: DbInterner<'db>, def_id: SolverDefId) -> GenericArgs<'db> { - GenericArgs::for_item(interner, def_id, |_, _, id, _| { - GenericArg::error_from_id(interner, id) - }) + GenericArgs::for_item(interner, def_id, |_, id, _| GenericArg::error_from_id(interner, id)) } /// Like `for_item`, but prefers the default of a parameter if it has any. @@ -214,14 +205,12 @@ impl<'db> GenericArgs<'db> { mut fallback: F, ) -> GenericArgs<'db> where - F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { let defaults = interner.db.generic_defaults_ns(def_id); - Self::for_item(interner, def_id.into(), |name, idx, id, prev| { - match defaults.get(idx as usize) { - Some(default) => default.instantiate(interner, prev), - None => fallback(name, idx, id, prev), - } + Self::for_item(interner, def_id.into(), |idx, id, prev| match defaults.get(idx as usize) { + Some(default) => default.instantiate(interner, prev), + None => fallback(idx, id, prev), }) } @@ -233,11 +222,11 @@ impl<'db> GenericArgs<'db> { mut fallback: F, ) -> GenericArgs<'db> where - F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { let mut iter = first.into_iter(); - Self::for_item(interner, def_id, |name, idx, id, prev| { - iter.next().unwrap_or_else(|| fallback(name, idx, id, prev)) + Self::for_item(interner, def_id, |idx, id, prev| { + iter.next().unwrap_or_else(|| fallback(idx, id, prev)) }) } @@ -249,14 +238,14 @@ impl<'db> GenericArgs<'db> { mut fallback: F, ) -> GenericArgs<'db> where - F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { let defaults = interner.db.generic_defaults_ns(def_id); - Self::fill_rest(interner, def_id.into(), first, |name, idx, id, prev| { + Self::fill_rest(interner, def_id.into(), first, |idx, id, prev| { defaults .get(idx as usize) .map(|default| default.instantiate(interner, prev)) - .unwrap_or_else(|| fallback(name, idx, id, prev)) + .unwrap_or_else(|| fallback(idx, id, prev)) }) } @@ -266,9 +255,8 @@ impl<'db> GenericArgs<'db> { defs: Generics, mk_kind: &mut F, ) where - F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { - let self_len = defs.own_params.len() as u32; 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); @@ -278,12 +266,11 @@ impl<'db> GenericArgs<'db> { fn fill_single(args: &mut SmallVec<[GenericArg<'db>; 8]>, defs: &Generics, mk_kind: &mut F) where - F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { - let start_len = args.len(); args.reserve(defs.own_params.len()); for param in &defs.own_params { - let kind = mk_kind(¶m.name, args.len() as u32, param.id, args); + let kind = mk_kind(args.len() as u32, param.id, args); args.push(kind); } } @@ -374,9 +361,7 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< interner: DbInterner<'db>, def_id: as rustc_type_ir::Interner>::DefId, ) -> as rustc_type_ir::Interner>::GenericArgs { - Self::for_item(interner, def_id, |name, index, kind, _| { - mk_param(interner, index, name, kind) - }) + Self::for_item(interner, def_id, |index, kind, _| mk_param(interner, index, kind)) } fn extend_with_error( @@ -384,7 +369,7 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< def_id: as rustc_type_ir::Interner>::DefId, original_args: &[ as rustc_type_ir::Interner>::GenericArg], ) -> as rustc_type_ir::Interner>::GenericArgs { - Self::for_item(interner, def_id, |name, index, kind, _| { + Self::for_item(interner, def_id, |index, kind, _| { if let Some(arg) = original_args.get(index as usize) { *arg } else { @@ -461,7 +446,7 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< signature_parts_ty, tupled_upvars_ty, coroutine_captures_by_ref_ty, - coroutine_witness_ty, + _coroutine_witness_ty, ] => rustc_type_ir::CoroutineClosureArgsParts { parent_args: GenericArgs::new_from_iter( DbInterner::conjure(), @@ -494,18 +479,12 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< } } -pub fn mk_param<'db>( - interner: DbInterner<'db>, - index: u32, - name: &Symbol, - id: GenericParamId, -) -> GenericArg<'db> { - let name = name.clone(); +pub fn mk_param<'db>(interner: DbInterner<'db>, index: u32, id: GenericParamId) -> GenericArg<'db> { match id { GenericParamId::LifetimeParamId(id) => { Region::new_early_param(interner, EarlyParamRegion { index, id }).into() } - GenericParamId::TypeParamId(id) => Ty::new_param(interner, id, index, name).into(), + GenericParamId::TypeParamId(id) => Ty::new_param(interner, id, index).into(), GenericParamId::ConstParamId(id) => { Const::new_param(interner, ParamConst { index, id }).into() } @@ -596,13 +575,4 @@ impl<'db> DbInterner<'db> { { T::collect_and_apply(iter, |xs| self.mk_args(xs)) } - - pub(super) fn check_args_compatible(self, def_id: SolverDefId, args: GenericArgs<'db>) -> bool { - // TODO - true - } - - pub(super) fn debug_assert_args_compatible(self, def_id: SolverDefId, args: GenericArgs<'db>) { - // TODO - } } 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 5ec9a18a6c20..d5a9a6f527bb 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,36 +1,25 @@ //! Things related to generics in the next-trait-solver. use hir_def::{ - ConstParamId, GenericDefId, GenericParamId, ItemContainerId, LifetimeParamId, Lookup, - TypeOrConstParamId, TypeParamId, - db::DefDatabase, - expr_store::ExpressionStore, + ConstParamId, GenericDefId, GenericParamId, LifetimeParamId, TypeOrConstParamId, TypeParamId, hir::generics::{ - GenericParamDataRef, GenericParams, LifetimeParamData, LocalLifetimeParamId, - LocalTypeOrConstParamId, TypeOrConstParamData, TypeParamData, TypeParamProvenance, - WherePredicate, + GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData, TypeParamData, + TypeParamProvenance, }, }; -use hir_expand::name::Name; -use intern::{Symbol, sym}; -use la_arena::Arena; -use rustc_type_ir::inherent::Ty as _; -use triomphe::Arc; -use crate::{db::HirDatabase, generics::parent_generic_def, next_solver::Ty}; +use crate::{db::HirDatabase, generics::parent_generic_def}; -use super::{Const, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId}; +use super::SolverDefId; -use super::{DbInterner, GenericArg}; +use super::DbInterner; pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { - let mk_lt = |parent, index, local_id, lt: &LifetimeParamData| { - let name = lt.name.symbol().clone(); + let mk_lt = |parent, index, local_id| { let id = GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id }); - GenericParamDef { name, index, id } + GenericParamDef { index, id } }; let mk_ty = |parent, index, local_id, p: &TypeOrConstParamData| { - let name = p.name().map(|n| n.symbol().clone()).unwrap_or_else(|| sym::MISSING_NAME); let id = TypeOrConstParamId { parent, local_id }; let id = match p { TypeOrConstParamData::TypeParamData(_) => { @@ -40,7 +29,7 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)) } }; - GenericParamDef { name, index, id } + GenericParamDef { index, id } }; let own_params_for_generic_params = |parent, params: &GenericParams| { let mut result = Vec::with_capacity(params.len()); @@ -51,8 +40,8 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { type_and_consts.next(); index += 1; } - result.extend(params.iter_lt().map(|(local_id, data)| { - let lt = mk_lt(parent, index, local_id, data); + result.extend(params.iter_lt().map(|(local_id, _data)| { + let lt = mk_lt(parent, index, local_id); index += 1; lt })); @@ -78,7 +67,7 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => { (Some(type_alias_id.into()), Vec::new()) } - crate::ImplTraitId::AsyncBlockTypeImplTrait(def, _) => { + crate::ImplTraitId::AsyncBlockTypeImplTrait(_def, _) => { let param = TypeOrConstParamData::TypeParamData(TypeParamData { name: None, default: None, @@ -121,8 +110,6 @@ pub struct Generics { #[derive(Debug)] pub struct GenericParamDef { - pub(crate) name: Symbol, - //def_id: GenericDefId, index: u32, pub(crate) id: GenericParamId, } 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 8dfffe0d365e..70b659406f86 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 @@ -36,7 +36,7 @@ use crate::next_solver::{ AliasTerm, AliasTy, Binder, Const, DbInterner, GenericArg, Goal, ParamEnv, PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, Predicate, Region, Span, Term, TraitRef, Ty, - fulfill::{FulfillmentCtxt, NextSolverError}, + fulfill::NextSolverError, infer::relate::lattice::{LatticeOp, LatticeOpKind}, }; @@ -45,16 +45,6 @@ use super::{ traits::{Obligation, ObligationCause}, }; -/// Whether we should define opaque types or just treat them opaquely. -/// -/// Currently only used to prevent predicate matching from matching anything -/// against opaque types. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum DefineOpaqueTypes { - Yes, - No, -} - #[derive(Clone, Copy)] pub struct At<'a, 'db> { pub infcx: &'a InferCtxt<'db>, @@ -107,12 +97,7 @@ impl<'a, 'db> At<'a, 'db> { /// call like `foo(x)`, where `foo: fn(i32)`, you might have /// `sup(i32, x)`, since the "expected" type is the type that /// appears in the signature. - pub fn sup( - self, - define_opaque_types: DefineOpaqueTypes, - expected: T, - actual: T, - ) -> InferResult<'db, ()> + pub fn sup(self, expected: T, actual: T) -> InferResult<'db, ()> where T: ToTrace<'db>, { @@ -128,12 +113,7 @@ impl<'a, 'db> At<'a, 'db> { } /// Makes `expected <: actual`. - pub fn sub( - self, - define_opaque_types: DefineOpaqueTypes, - expected: T, - actual: T, - ) -> InferResult<'db, ()> + pub fn sub(self, expected: T, actual: T) -> InferResult<'db, ()> where T: ToTrace<'db>, { @@ -149,31 +129,7 @@ impl<'a, 'db> At<'a, 'db> { } /// Makes `expected == actual`. - pub fn eq( - self, - define_opaque_types: DefineOpaqueTypes, - expected: T, - actual: T, - ) -> InferResult<'db, ()> - where - T: ToTrace<'db>, - { - self.eq_trace( - define_opaque_types, - ToTrace::to_trace(self.cause, expected, actual), - expected, - actual, - ) - } - - /// Makes `expected == actual`. - pub fn eq_trace( - self, - define_opaque_types: DefineOpaqueTypes, - trace: TypeTrace<'db>, - expected: T, - actual: T, - ) -> InferResult<'db, ()> + pub fn eq(self, expected: T, actual: T) -> InferResult<'db, ()> where T: Relate>, { @@ -188,20 +144,14 @@ impl<'a, 'db> At<'a, 'db> { .map(|goals| self.goals_to_obligations(goals)) } - pub fn relate( - self, - define_opaque_types: DefineOpaqueTypes, - expected: T, - variance: Variance, - actual: T, - ) -> InferResult<'db, ()> + pub fn relate(self, expected: T, variance: Variance, actual: T) -> InferResult<'db, ()> where T: ToTrace<'db>, { match variance { - Variance::Covariant => self.sub(define_opaque_types, expected, actual), - Variance::Invariant => self.eq(define_opaque_types, expected, actual), - Variance::Contravariant => self.sup(define_opaque_types, expected, actual), + Variance::Covariant => self.sub(expected, actual), + Variance::Invariant => self.eq(expected, actual), + Variance::Contravariant => self.sup(expected, actual), // We could make this make sense but it's not readily // exposed and I don't feel like dealing with it. Note diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs index beaac11a2de4..e6a818fdf3bc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs @@ -8,7 +8,7 @@ use rustc_hash::FxHashMap; use rustc_index::Idx; use rustc_type_ir::InferTy::{self, FloatVar, IntVar, TyVar}; -use rustc_type_ir::inherent::{Const as _, IntoKind as _, Region as _, SliceLike, Ty as _}; +use rustc_type_ir::inherent::{Const as _, IntoKind as _, SliceLike, Ty as _}; use rustc_type_ir::{ BoundVar, CanonicalQueryInput, DebruijnIndex, Flags, InferConst, RegionKind, TyVid, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex, 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 6c7a87ef5249..64287fe47261 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 @@ -8,22 +8,11 @@ use crate::next_solver::BoundConst; use crate::next_solver::{ - AliasTy, Binder, BoundRegion, BoundTy, Canonical, CanonicalVarValues, Const, DbInterner, Goal, - ParamEnv, Predicate, PredicateKind, Region, Ty, TyKind, - fold::FnMutDelegate, - infer::{ - DefineOpaqueTypes, InferCtxt, TypeTrace, - traits::{Obligation, PredicateObligations}, - }, + BoundRegion, BoundTy, Canonical, CanonicalVarValues, DbInterner, fold::FnMutDelegate, }; use rustc_type_ir::{ - AliasRelationDirection, AliasTyKind, BoundVar, GenericArgKind, InferTy, TypeFoldable, Upcast, - Variance, + GenericArgKind, TypeFoldable, inherent::{IntoKind, SliceLike}, - relate::{ - Relate, TypeRelation, VarianceDiagInfo, - combine::{super_combine_consts, super_combine_tys}, - }, }; pub trait CanonicalExt<'db, V> { 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 d0669f5c3bcc..b3bd0a437b8d 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 @@ -22,26 +22,13 @@ //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html use crate::next_solver::{ - AliasTy, Binder, Canonical, CanonicalVarValues, CanonicalVars, Const, DbInterner, GenericArg, - Goal, ParamEnv, PlaceholderConst, PlaceholderRegion, PlaceholderTy, Predicate, PredicateKind, - Region, Ty, TyKind, - infer::{ - DefineOpaqueTypes, InferCtxt, TypeTrace, - traits::{Obligation, PredicateObligations}, - }, + Canonical, CanonicalVarValues, Const, DbInterner, GenericArg, PlaceholderConst, + PlaceholderRegion, PlaceholderTy, Region, Ty, TyKind, infer::InferCtxt, }; use instantiate::CanonicalExt; use rustc_index::IndexVec; use rustc_type_ir::inherent::IntoKind; -use rustc_type_ir::{ - AliasRelationDirection, AliasTyKind, CanonicalVarKind, InferTy, TypeFoldable, UniverseIndex, - Upcast, Variance, - inherent::{SliceLike, Ty as _}, - relate::{ - Relate, TypeRelation, VarianceDiagInfo, - combine::{super_combine_consts, super_combine_tys}, - }, -}; +use rustc_type_ir::{CanonicalVarKind, InferTy, TypeFoldable, UniverseIndex, inherent::Ty as _}; pub mod canonicalizer; pub mod instantiate; 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 5aa5ad14af55..397986e2edd3 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 @@ -1,19 +1,19 @@ //! Definition of `InferCtxtLike` from the librarified type layer. use rustc_type_ir::{ - ConstVid, FloatVarValue, FloatVid, GenericArgKind, InferConst, InferTy, IntTy, IntVarValue, - IntVid, RegionVid, TyVid, TypeFoldable, TypingMode, UniverseIndex, - inherent::{Const as _, IntoKind, Span as _, Ty as _}, + ConstVid, FloatVarValue, FloatVid, GenericArgKind, InferConst, InferTy, IntVarValue, IntVid, + RegionVid, TyVid, TypeFoldable, TypingMode, UniverseIndex, + inherent::{Const as _, IntoKind, Ty as _}, relate::combine::PredicateEmittingRelation, }; use crate::next_solver::{ - Binder, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, OpaqueTypeKey, ParamEnv, - Region, SolverDefId, Span, Ty, TyKind, + Binder, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, OpaqueTypeKey, Region, + SolverDefId, Span, Ty, TyKind, infer::opaque_types::{OpaqueHiddenType, table::OpaqueTypeStorageEntries}, }; -use super::{BoundRegionConversionTime, InferCtxt, relate::RelateResult, traits::ObligationCause}; +use super::{BoundRegionConversionTime, InferCtxt, relate::RelateResult}; impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { type Interner = DbInterner<'db>; @@ -250,16 +250,16 @@ impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { self.probe(|_| probe()) } - fn sub_regions(&self, sub: Region<'db>, sup: Region<'db>, span: Span) { + fn sub_regions(&self, sub: Region<'db>, sup: Region<'db>, _span: Span) { self.inner.borrow_mut().unwrap_region_constraints().make_subregion(sub, sup); } - fn equate_regions(&self, a: Region<'db>, b: Region<'db>, span: Span) { + fn equate_regions(&self, a: Region<'db>, b: Region<'db>, _span: Span) { self.inner.borrow_mut().unwrap_region_constraints().make_eqregion(a, b); } - fn register_ty_outlives(&self, ty: Ty<'db>, r: Region<'db>, span: Span) { - //self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy_with_span(Span::dummy())); + fn register_ty_outlives(&self, _ty: Ty<'db>, _r: Region<'db>, _span: Span) { + // self.register_type_outlives_constraint(ty, r, &ObligationCause::dummy()); } type OpaqueTypeStorageEntries = OpaqueTypeStorageEntries; 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 e1a46fa0694c..36c6c48c5a0b 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 @@ -6,32 +6,23 @@ use std::ops::Range; use std::sync::Arc; pub use BoundRegionConversionTime::*; -pub use at::DefineOpaqueTypes; -use ena::undo_log::UndoLogs; use ena::unify as ut; use hir_def::GenericParamId; use hir_def::lang_item::LangItem; -use intern::Symbol; use opaque_types::{OpaqueHiddenType, OpaqueTypeStorage}; -use region_constraints::{ - GenericKind, RegionConstraintCollector, RegionConstraintStorage, UndoLog, VarInfos, VerifyBound, -}; -pub use relate::StructurallyRelateAliases; -pub use relate::combine::PredicateEmittingRelation; -use rustc_hash::{FxHashMap, FxHashSet}; +use region_constraints::{RegionConstraintCollector, RegionConstraintStorage}; use rustc_next_trait_solver::solve::SolverDelegateEvalExt; use rustc_pattern_analysis::Captures; +use rustc_type_ir::TypeFoldable; use rustc_type_ir::error::{ExpectedFound, TypeError}; use rustc_type_ir::inherent::{ - Const as _, GenericArg as _, GenericArgs as _, IntoKind, ParamEnv as _, SliceLike, Term as _, - Ty as _, + Const as _, GenericArg as _, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _, }; use rustc_type_ir::{ - BoundVar, ClosureKind, ConstVid, FloatTy, FloatVarValue, FloatVid, GenericArgKind, InferConst, - InferTy, IntTy, IntVarValue, IntVid, OutlivesPredicate, RegionVid, TyVid, UniverseIndex, + ClosureKind, ConstVid, FloatVarValue, FloatVid, GenericArgKind, InferConst, InferTy, + IntVarValue, IntVid, OutlivesPredicate, RegionVid, TyVid, UniverseIndex, }; use rustc_type_ir::{TermKind, TypeVisitableExt}; -use rustc_type_ir::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use snapshot::undo_log::InferCtxtUndoLogs; use tracing::{debug, instrument}; use traits::{ObligationCause, PredicateObligations}; @@ -39,19 +30,17 @@ use type_variable::TypeVariableOrigin; use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use crate::next_solver::fold::BoundVarReplacerDelegate; -use crate::next_solver::infer::opaque_types::table::OpaqueTypeStorageEntries; use crate::next_solver::infer::select::EvaluationResult; use crate::next_solver::infer::traits::PredicateObligation; use crate::next_solver::obligation_ctxt::ObligationCtxt; use crate::next_solver::{BoundConst, BoundRegion, BoundTy, BoundVarKind, Goal, SolverContext}; -use super::generics::GenericParamDef; use super::{ - AliasTerm, Binder, BoundRegionKind, CanonicalQueryInput, CanonicalVarValues, Const, ConstKind, - DbInterner, ErrorGuaranteed, FxIndexMap, GenericArg, GenericArgs, OpaqueTypeKey, ParamEnv, - PlaceholderRegion, PolyCoercePredicate, PolyExistentialProjection, PolyExistentialTraitRef, - PolyFnSig, PolyRegionOutlivesPredicate, PolySubtypePredicate, Predicate, Region, SolverDefId, - SubtypePredicate, Term, TraitPredicate, TraitRef, Ty, TyKind, TypingMode, + AliasTerm, Binder, CanonicalQueryInput, CanonicalVarValues, Const, ConstKind, DbInterner, + ErrorGuaranteed, GenericArg, GenericArgs, OpaqueTypeKey, ParamEnv, PolyCoercePredicate, + PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, PolyRegionOutlivesPredicate, + PolySubtypePredicate, Region, SolverDefId, SubtypePredicate, Term, TraitRef, Ty, TyKind, + TypingMode, }; pub mod at; @@ -82,8 +71,6 @@ pub struct InferOk<'db, T> { } pub type InferResult<'db, T> = Result, TypeError>>; -pub(crate) type FixupResult = Result; // "fixup result" - pub(crate) type UnificationTable<'a, 'db, T> = ut::UnificationTable< ut::InPlace, &'a mut InferCtxtUndoLogs<'db>>, >; @@ -440,6 +427,7 @@ impl<'db> InferCtxt<'db> { /// check::<&'_ T>(); /// } /// ``` + #[expect(dead_code, reason = "this is used in rustc")] fn predicate_must_hold_considering_regions( &self, obligation: &PredicateObligation<'db>, @@ -452,14 +440,13 @@ 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 { self.evaluate_obligation(obligation).must_apply_modulo_regions() } /// Evaluate a given predicate, capturing overflow and propagating it back. fn evaluate_obligation(&self, obligation: &PredicateObligation<'db>) -> EvaluationResult { - let param_env = obligation.param_env; - self.probe(|snapshot| { let mut ocx = ObligationCtxt::new(self); ocx.register_obligation(obligation.clone()); @@ -583,16 +570,16 @@ impl<'db> InferCtxt<'db> { self.enter_forall(predicate, |SubtypePredicate { a_is_expected, a, b }| { if a_is_expected { - Ok(self.at(cause, param_env).sub(DefineOpaqueTypes::Yes, a, b)) + Ok(self.at(cause, param_env).sub(a, b)) } else { - Ok(self.at(cause, param_env).sup(DefineOpaqueTypes::Yes, b, a)) + Ok(self.at(cause, param_env).sup(b, a)) } }) } pub fn region_outlives_predicate( &self, - cause: &traits::ObligationCause, + _cause: &traits::ObligationCause, predicate: PolyRegionOutlivesPredicate<'db>, ) { self.enter_forall(predicate, |OutlivesPredicate(r_a, r_b)| { @@ -632,7 +619,7 @@ impl<'db> InferCtxt<'db> { } pub fn next_const_var(&self) -> Const<'db> { - self.next_const_var_with_origin(ConstVariableOrigin { param_def_id: None }) + self.next_const_var_with_origin(ConstVariableOrigin {}) } pub fn next_const_vid(&self) -> ConstVid { @@ -640,7 +627,7 @@ impl<'db> InferCtxt<'db> { .borrow_mut() .const_unification_table() .new_key(ConstVariableValue::Unknown { - origin: ConstVariableOrigin { param_def_id: None }, + origin: ConstVariableOrigin {}, universe: self.universe(), }) .vid @@ -657,7 +644,7 @@ impl<'db> InferCtxt<'db> { } pub fn next_const_var_in_universe(&self, universe: UniverseIndex) -> Const<'db> { - let origin = ConstVariableOrigin { param_def_id: None }; + let origin = ConstVariableOrigin {}; let vid = self .inner .borrow_mut() @@ -738,7 +725,7 @@ impl<'db> InferCtxt<'db> { self.next_region_var_in_universe(universe) } - fn var_for_def(&self, id: GenericParamId, name: &Symbol) -> GenericArg<'db> { + fn var_for_def(&self, id: GenericParamId) -> GenericArg<'db> { match id { GenericParamId::LifetimeParamId(_) => { // Create a region inference variable for the given @@ -763,7 +750,7 @@ impl<'db> InferCtxt<'db> { Ty::new_var(self.interner, ty_var_id).into() } GenericParamId::ConstParamId(_) => { - let origin = ConstVariableOrigin { param_def_id: None }; + let origin = ConstVariableOrigin {}; let const_var_id = self .inner .borrow_mut() @@ -778,9 +765,7 @@ impl<'db> InferCtxt<'db> { /// 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, |name, index, kind, _| { - self.var_for_def(kind, name) - }) + GenericArgs::for_item(self.interner, def_id, |_index, kind, _| self.var_for_def(kind)) } /// Like `fresh_args_for_item()`, but first uses the args from `first`. @@ -789,8 +774,8 @@ impl<'db> InferCtxt<'db> { def_id: SolverDefId, first: impl IntoIterator>, ) -> GenericArgs<'db> { - GenericArgs::fill_rest(self.interner, def_id, first, |name, index, kind, _| { - self.var_for_def(kind, name) + GenericArgs::fill_rest(self.interner, def_id, first, |_index, kind, _| { + self.var_for_def(kind) }) } @@ -828,8 +813,8 @@ impl<'db> InferCtxt<'db> { defining_opaque_types_and_generators.contains(&id.into()) } TypingMode::Coherence | TypingMode::PostAnalysis => false, - TypingMode::Borrowck { defining_opaque_types } => unimplemented!(), - TypingMode::PostBorrowckAnalysis { defined_opaque_types } => unimplemented!(), + TypingMode::Borrowck { defining_opaque_types: _ } => unimplemented!(), + TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => unimplemented!(), } } @@ -998,7 +983,7 @@ impl<'db> InferCtxt<'db> { // use [`InferCtxt::enter_forall`] instead. pub fn instantiate_binder_with_fresh_vars( &self, - lbrct: BoundRegionConversionTime, + _lbrct: BoundRegionConversionTime, value: Binder<'db, T>, ) -> T where @@ -1014,7 +999,7 @@ impl<'db> InferCtxt<'db> { for bound_var_kind in bound_vars { let arg: GenericArg<'db> = match bound_var_kind { BoundVarKind::Ty(_) => self.next_ty_var().into(), - BoundVarKind::Region(br) => self.next_region_var().into(), + BoundVarKind::Region(_) => self.next_region_var().into(), BoundVarKind::Const => self.next_const_var().into(), }; args.push(arg); @@ -1070,7 +1055,7 @@ impl<'db> InferCtxt<'db> { #[inline] pub fn is_ty_infer_var_definitely_unchanged<'a>( &'a self, - ) -> (impl Fn(TyOrConstInferVar) -> bool + Captures<'db> + 'a) { + ) -> impl Fn(TyOrConstInferVar) -> bool + Captures<'db> + 'a { // This hoists the borrow/release out of the loop body. let inner = self.inner.try_borrow(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs index 0f68ec8cdb5b..06d998488e15 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs @@ -1,40 +1,10 @@ //! Things related to the infer context of the next-trait-solver. -use std::sync::Arc; - -use tracing::{debug, instrument}; - -use crate::next_solver::{ - Clause, ClauseKind, FxIndexMap, GenericArgs, OpaqueTypeKey, ProjectionPredicate, SolverDefId, - TypingMode, util::BottomUpFolder, -}; - pub(crate) mod table; pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable}; -use crate::next_solver::{ - AliasTy, Binder, BoundRegion, BoundTy, Canonical, CanonicalVarValues, Const, DbInterner, Goal, - ParamEnv, Predicate, PredicateKind, Region, Ty, TyKind, - fold::FnMutDelegate, - infer::{ - DefineOpaqueTypes, InferCtxt, TypeTrace, - traits::{Obligation, PredicateObligations}, - }, -}; -use rustc_type_ir::{ - AliasRelationDirection, AliasTyKind, BoundConstness, BoundVar, Flags, GenericArgKind, InferTy, - Interner, RegionKind, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, Upcast, Variance, - error::{ExpectedFound, TypeError}, - inherent::{DefId, GenericArgs as _, IntoKind, SliceLike}, - relate::{ - Relate, TypeRelation, VarianceDiagInfo, - combine::{super_combine_consts, super_combine_tys}, - }, -}; - -use super::{InferOk, traits::ObligationCause}; +use crate::next_solver::{OpaqueTypeKey, Ty, infer::InferCtxt}; #[derive(Copy, Clone, Debug)] pub struct OpaqueHiddenType<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs index 8ab409d78281..0f8b23870fd0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs @@ -54,7 +54,7 @@ impl<'db> OpaqueTypeStorage<'db> { assert!(entry.is_some()); } - pub fn is_empty(&self) -> bool { + pub(crate) fn is_empty(&self) -> bool { let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; opaque_types.is_empty() && duplicate_entries.is_empty() } @@ -66,14 +66,14 @@ impl<'db> OpaqueTypeStorage<'db> { std::mem::take(opaque_types).into_iter().chain(std::mem::take(duplicate_entries)) } - pub fn num_entries(&self) -> OpaqueTypeStorageEntries { + pub(crate) fn num_entries(&self) -> OpaqueTypeStorageEntries { OpaqueTypeStorageEntries { opaque_types: self.opaque_types.len(), duplicate_entries: self.duplicate_entries.len(), } } - pub fn opaque_types_added_since( + pub(crate) fn opaque_types_added_since( &self, prev_entries: OpaqueTypeStorageEntries, ) -> impl Iterator, OpaqueHiddenType<'db>)> { @@ -89,7 +89,7 @@ impl<'db> OpaqueTypeStorage<'db> { /// /// Outside of canonicalization one should generally use `iter_opaque_types` /// to also consider duplicate entries. - pub fn iter_lookup_table( + pub(crate) fn iter_lookup_table( &self, ) -> impl Iterator, OpaqueHiddenType<'db>)> { self.opaque_types.iter().map(|(k, v)| (*k, *v)) @@ -100,13 +100,13 @@ impl<'db> OpaqueTypeStorage<'db> { /// These have to considered when checking all opaque type uses but are e.g. /// irrelevant for canonical inputs as nested queries never meaningfully /// accesses them. - pub fn iter_duplicate_entries( + pub(crate) fn iter_duplicate_entries( &self, ) -> impl Iterator, OpaqueHiddenType<'db>)> { self.duplicate_entries.iter().copied() } - pub fn iter_opaque_types( + pub(crate) fn iter_opaque_types( &self, ) -> impl Iterator, OpaqueHiddenType<'db>)> { let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; @@ -144,7 +144,7 @@ impl<'db> Deref for OpaqueTypeTable<'_, 'db> { impl<'a, 'db> OpaqueTypeTable<'a, 'db> { #[instrument(skip(self), level = "debug")] - pub fn register( + pub(crate) fn register( &mut self, key: OpaqueTypeKey<'db>, hidden_type: OpaqueHiddenType<'db>, @@ -159,7 +159,11 @@ impl<'a, 'db> OpaqueTypeTable<'a, 'db> { None } - pub fn add_duplicate(&mut self, key: OpaqueTypeKey<'db>, hidden_type: OpaqueHiddenType<'db>) { + pub(crate) fn add_duplicate( + &mut self, + key: OpaqueTypeKey<'db>, + hidden_type: OpaqueHiddenType<'db>, + ) { self.storage.duplicate_entries.push((key, hidden_type)); self.undo_log.push(UndoLog::DuplicateOpaqueType); } 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 7f15a467b3e8..ae5930d55c72 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 @@ -1,7 +1,6 @@ //! See `README.md`. use std::ops::Range; -use std::sync::Arc; use std::{cmp, fmt, mem}; use ena::undo_log::{Rollback, UndoLogs}; @@ -18,9 +17,7 @@ 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, OpaqueTypeKey, ParamTy, PlaceholderTy, Region, Ty, -}; +use crate::next_solver::{AliasTy, Binder, DbInterner, ParamTy, PlaceholderTy, Region, Ty}; #[derive(Debug, Clone, Default)] pub struct RegionConstraintStorage<'db> { @@ -254,6 +251,7 @@ pub(crate) enum UndoLog<'db> { AddConstraint(usize), /// We added the given `verify`. + #[expect(dead_code, reason = "this is used in rustc")] AddVerify(usize), /// We added a GLB/LUB "combination variable". 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 7e2735db3b77..d06984cac11c 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 @@ -7,8 +7,8 @@ use rustc_type_ir::error::TypeError; use rustc_type_ir::inherent::{Const as _, IntoKind, Ty as _}; use rustc_type_ir::relate::VarianceDiagInfo; use rustc_type_ir::{ - AliasRelationDirection, AliasTyKind, ConstVid, InferConst, InferCtxtLike, InferTy, RegionKind, - TermKind, TyVid, UniverseIndex, Variance, + AliasRelationDirection, ConstVid, InferConst, InferCtxtLike, InferTy, RegionKind, TermKind, + TyVid, UniverseIndex, Variance, }; use rustc_type_ir::{Interner, TypeVisitable, TypeVisitableExt}; use tracing::{debug, instrument, warn}; @@ -21,9 +21,8 @@ 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::{ - AliasTy, Binder, ClauseKind, Const, ConstKind, DbInterner, GenericArgs, PredicateKind, - ProjectionPredicate, Region, SolverDefId, Term, TermVid, Ty, TyKind, TypingMode, - UnevaluatedConst, + AliasTy, Binder, ClauseKind, Const, ConstKind, DbInterner, GenericArgs, PredicateKind, Region, + SolverDefId, Term, TermVid, Ty, TyKind, TypingMode, UnevaluatedConst, }; impl<'db> InferCtxt<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs index 62028e0e7039..c523751e03e3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs @@ -2,13 +2,10 @@ //! the end of the file for details. use rustc_type_ir::TypeFoldable; -use rustc_type_ir::{BoundVar, UniverseIndex}; use tracing::{debug, instrument}; -use super::RelateResult; use crate::next_solver::fold::FnMutDelegate; use crate::next_solver::infer::InferCtxt; -use crate::next_solver::infer::snapshot::CombinedSnapshot; use crate::next_solver::{ Binder, BoundConst, BoundRegion, BoundTy, Const, DbInterner, PlaceholderConst, PlaceholderRegion, PlaceholderTy, Region, Ty, 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 c7f771ffe37f..374895c337c7 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 @@ -30,7 +30,7 @@ use crate::next_solver::{ AliasTy, Binder, Const, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Region, Span, Ty, TyKind, infer::{ - DefineOpaqueTypes, InferCtxt, TypeTrace, + InferCtxt, TypeTrace, relate::RelateResult, traits::{Obligation, PredicateObligations}, }, @@ -92,10 +92,7 @@ impl<'db> TypeRelation> for LatticeOp<'_, 'db> { match variance { Variance::Invariant => { self.obligations.extend( - self.infcx - .at(&self.trace.cause, self.param_env) - .eq_trace(DefineOpaqueTypes::Yes, self.trace.clone(), a, b)? - .into_obligations(), + self.infcx.at(&self.trace.cause, self.param_env).eq(a, b)?.into_obligations(), ); Ok(a) } @@ -213,12 +210,12 @@ impl<'infcx, 'db> LatticeOp<'infcx, 'db> { let at = self.infcx.at(&self.trace.cause, self.param_env); match self.kind { LatticeOpKind::Glb => { - self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, v, a)?.into_obligations()); - self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, v, b)?.into_obligations()); + self.obligations.extend(at.sub(v, a)?.into_obligations()); + self.obligations.extend(at.sub(v, b)?.into_obligations()); } LatticeOpKind::Lub => { - self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, a, v)?.into_obligations()); - self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, b, v)?.into_obligations()); + self.obligations.extend(at.sub(a, v)?.into_obligations()); + self.obligations.extend(at.sub(b, v)?.into_obligations()); } } Ok(()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs index 4bd3fbd4985d..b6e5225e5a7e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs @@ -1,15 +1,14 @@ //! Things for resolving vars in the infer context of the next-trait-solver. use rustc_type_ir::{ - ConstKind, FallibleTypeFolder, InferConst, InferTy, RegionKind, TyKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, data_structures::DelayedMap, - inherent::{Const as _, IntoKind, Ty as _}, + inherent::{Const as _, Ty as _}, }; use crate::next_solver::{Const, DbInterner, ErrorGuaranteed, Region, Ty}; -use super::{FixupError, FixupResult, InferCtxt}; +use super::InferCtxt; /////////////////////////////////////////////////////////////////////////// // OPPORTUNISTIC VAR RESOLVER 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 79b0a2933236..d2f584b38cf4 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 @@ -1,3 +1,5 @@ +#![expect(dead_code, reason = "this is used by rustc")] + use std::ops::ControlFlow; use hir_def::{ImplId, TraitId}; @@ -61,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 enum EvaluationResult { +pub(crate) enum EvaluationResult { /// Evaluation successful. EvaluatedToOk, /// Evaluation successful, but there were unevaluated region obligations. @@ -91,17 +93,17 @@ pub enum EvaluationResult { impl EvaluationResult { /// Returns `true` if this evaluation result is known to apply, even /// considering outlives constraints. - pub fn must_apply_considering_regions(self) -> bool { + pub(crate) fn must_apply_considering_regions(self) -> bool { self == EvaluatedToOk } /// Returns `true` if this evaluation result is known to apply, ignoring /// outlives constraints. - pub fn must_apply_modulo_regions(self) -> bool { + pub(crate) fn must_apply_modulo_regions(self) -> bool { self <= EvaluatedToOkModuloRegions } - pub fn may_apply(self) -> bool { + pub(crate) fn may_apply(self) -> bool { match self { EvaluatedToOkModuloOpaqueTypes | EvaluatedToOk @@ -113,7 +115,7 @@ impl EvaluationResult { } } - pub fn is_stack_dependent(self) -> bool { + pub(crate) fn is_stack_dependent(self) -> bool { match self { EvaluatedToAmbigStackDependent => true, @@ -135,9 +137,9 @@ pub enum OverflowError { #[derive(Clone, Debug, PartialEq, Eq)] pub struct SignatureMismatchData<'db> { - pub found_trait_ref: TraitRef<'db>, - pub expected_trait_ref: TraitRef<'db>, - pub terr: TypeError<'db>, + pub(crate) found_trait_ref: TraitRef<'db>, + pub(crate) expected_trait_ref: TraitRef<'db>, + pub(crate) terr: TypeError<'db>, } /// When performing resolution, it is typically the case that there @@ -147,7 +149,7 @@ pub struct SignatureMismatchData<'db> { /// - `Ok(None)`: could not definitely determine anything, usually due /// to inconclusive type inference. /// - `Err(e)`: error `e` occurred -pub type SelectionResult<'db, T> = Result, SelectionError<'db>>; +pub(crate) type SelectionResult<'db, T> = Result, SelectionError<'db>>; /// Given the successful resolution of an obligation, the `ImplSource` /// indicates where the impl comes from. @@ -179,7 +181,7 @@ pub type SelectionResult<'db, T> = Result, SelectionError<'db>>; /// /// See explanation on `ImplSourceUserDefinedData`. #[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] -pub enum ImplSource<'db, N> { +pub(crate) enum ImplSource<'db, N> { /// ImplSource identifying a particular impl. UserDefined(ImplSourceUserDefinedData<'db, N>), @@ -194,28 +196,28 @@ pub enum ImplSource<'db, N> { } impl<'db, N> ImplSource<'db, N> { - pub fn nested_obligations(self) -> Vec { + pub(crate) fn nested_obligations(self) -> Vec { match self { ImplSource::UserDefined(i) => i.nested, ImplSource::Param(n) | ImplSource::Builtin(_, n) => n, } } - pub fn borrow_nested_obligations(&self) -> &[N] { + pub(crate) fn borrow_nested_obligations(&self) -> &[N] { match self { ImplSource::UserDefined(i) => &i.nested, ImplSource::Param(n) | ImplSource::Builtin(_, n) => n, } } - pub fn borrow_nested_obligations_mut(&mut self) -> &mut [N] { + pub(crate) fn borrow_nested_obligations_mut(&mut self) -> &mut [N] { match self { ImplSource::UserDefined(i) => &mut i.nested, ImplSource::Param(n) | ImplSource::Builtin(_, n) => n, } } - pub fn map(self, f: F) -> ImplSource<'db, M> + pub(crate) fn map(self, f: F) -> ImplSource<'db, M> where F: FnMut(N) -> M, { @@ -244,15 +246,15 @@ impl<'db, N> ImplSource<'db, N> { /// is `()`, because codegen only requires a shallow resolution of an /// impl, and nested obligations are satisfied later. #[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] -pub struct ImplSourceUserDefinedData<'db, N> { +pub(crate) struct ImplSourceUserDefinedData<'db, N> { #[type_visitable(ignore)] #[type_foldable(identity)] - pub impl_def_id: ImplId, - pub args: GenericArgs<'db>, - pub nested: Vec, + pub(crate) impl_def_id: ImplId, + pub(crate) args: GenericArgs<'db>, + pub(crate) nested: Vec, } -pub type Selection<'db> = ImplSource<'db, PredicateObligation<'db>>; +pub(crate) type Selection<'db> = ImplSource<'db, PredicateObligation<'db>>; impl<'db> InferCtxt<'db> { pub(crate) fn select( 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 74353574e329..5902f8043b5e 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 @@ -41,9 +41,7 @@ fn const_vars_since_snapshot<'db>( range.clone(), iter_idx_range(range) .map(|index| match table.probe_value(index) { - ConstVariableValue::Known { value: _ } => { - ConstVariableOrigin { param_def_id: None } - } + ConstVariableValue::Known { value: _ } => ConstVariableOrigin {}, ConstVariableValue::Unknown { origin, universe: _ } => origin, }) .collect(), @@ -228,7 +226,6 @@ 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) { - let idx = vid.index() - self.snapshot_vars.region_vars.start.index(); self.infcx.next_region_var() } else { r diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs index 05a1013b3fbd..c8ec8da7f31c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs @@ -1,7 +1,5 @@ //! Snapshotting in the infer ctxt of the next-trait-solver. -use std::marker::PhantomData; - use ena::snapshot_vec as sv; use ena::undo_log::{Rollback, UndoLogs}; use ena::unify as ut; @@ -14,7 +12,6 @@ use crate::next_solver::infer::opaque_types::OpaqueHiddenType; use crate::next_solver::infer::unify_key::ConstVidKey; use crate::next_solver::infer::unify_key::RegionVidKey; use crate::next_solver::infer::{InferCtxtInner, region_constraints, type_variable}; -use crate::traits; pub struct Snapshot { pub(crate) undo_len: usize, @@ -31,6 +28,7 @@ pub(crate) enum UndoLog<'db> { FloatUnificationTable(sv::UndoLog>), RegionConstraintCollector(region_constraints::UndoLog<'db>), RegionUnificationTable(sv::UndoLog>>), + #[expect(dead_code, reason = "this is used in rustc")] PushRegionObligation, } 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 bc905c2e0b95..4f000c24cc73 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 @@ -9,17 +9,13 @@ use std::{ use hir_def::TraitId; use macros::{TypeFoldable, TypeVisitable}; +use rustc_type_ir::Upcast; use rustc_type_ir::elaborate::Elaboratable; -use rustc_type_ir::{ - PredicatePolarity, Upcast, - solve::{Certainty, NoSolution}, -}; -use rustc_type_ir::{TypeFoldable, TypeVisitable}; use tracing::debug; use crate::next_solver::{ - Binder, Clause, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, SolverDefId, Span, - TraitPredicate, TraitRef, Ty, + Clause, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, Span, TraitPredicate, + TraitRef, Ty, }; use super::InferCtxt; @@ -106,9 +102,9 @@ impl<'db> Elaboratable> for PredicateObligation<'db> { fn child_with_derived_cause( &self, clause: Clause<'db>, - span: Span, - parent_trait_pred: PolyTraitPredicate<'db>, - index: usize, + _span: Span, + _parent_trait_pred: PolyTraitPredicate<'db>, + _index: usize, ) -> Self { let cause = ObligationCause::new(); Obligation { @@ -153,16 +149,16 @@ impl<'db, P> From> for Goal<'db, P> { } } -pub type PredicateObligation<'db> = Obligation<'db, Predicate<'db>>; -pub type TraitObligation<'db> = Obligation<'db, TraitPredicate<'db>>; +pub(crate) type PredicateObligation<'db> = Obligation<'db, Predicate<'db>>; +pub(crate) type TraitObligation<'db> = Obligation<'db, TraitPredicate<'db>>; -pub type PredicateObligations<'db> = Vec>; +pub(crate) type PredicateObligations<'db> = Vec>; impl<'db> PredicateObligation<'db> { /// Flips the polarity of the inner predicate. /// /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. - pub fn flip_polarity(&self, tcx: DbInterner<'db>) -> Option> { + pub fn flip_polarity(&self, _interner: DbInterner<'db>) -> Option> { Some(PredicateObligation { cause: self.cause.clone(), param_env: self.param_env, @@ -215,7 +211,7 @@ impl<'db, O> Obligation<'db, O> { /// `bound` or is not known to meet bound (note that this is /// conservative towards *no impl*, which is the opposite of the /// `evaluate` methods). -pub fn type_known_to_meet_bound_modulo_regions<'tcx>( +pub(crate) fn type_known_to_meet_bound_modulo_regions<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>, 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 dc913b262a7c..a09f65f082d9 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,18 +6,18 @@ 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, SolverDefId, Ty}; +use crate::next_solver::{Const, Region}; #[derive(Clone, Debug)] -pub enum RegionVariableValue<'db> { +pub(crate) enum RegionVariableValue<'db> { Known { value: Region<'db> }, Unknown { universe: UniverseIndex }, } #[derive(PartialEq, Copy, Clone, Debug)] -pub struct RegionVidKey<'db> { - pub vid: RegionVid, - pub phantom: PhantomData>, +pub(crate) struct RegionVidKey<'db> { + pub(crate) vid: RegionVid, + pub(crate) phantom: PhantomData>, } impl<'db> From for RegionVidKey<'db> { @@ -41,7 +41,7 @@ impl<'db> UnifyKey for RegionVidKey<'db> { } } -pub struct RegionUnificationError; +pub(crate) struct RegionUnificationError; impl<'db> UnifyValue for RegionVariableValue<'db> { type Error = RegionUnificationError; @@ -90,15 +90,10 @@ impl<'db> UnifyValue for RegionVariableValue<'db> { // Generic consts. #[derive(Copy, Clone, Debug)] -pub struct ConstVariableOrigin { - /// `DefId` of the const parameter this was instantiated for, if any. - /// - /// This should only be used for diagnostics. - pub param_def_id: Option, -} +pub struct ConstVariableOrigin {} #[derive(Clone, Debug)] -pub enum ConstVariableValue<'db> { +pub(crate) enum ConstVariableValue<'db> { Known { value: Const<'db> }, Unknown { origin: ConstVariableOrigin, universe: UniverseIndex }, } @@ -106,7 +101,7 @@ pub enum ConstVariableValue<'db> { impl<'db> ConstVariableValue<'db> { /// If this value is known, returns the const it is known to be. /// Otherwise, `None`. - pub fn known(&self) -> Option> { + pub(crate) fn known(&self) -> Option> { match self { ConstVariableValue::Unknown { .. } => None, ConstVariableValue::Known { value } => Some(*value), @@ -115,9 +110,9 @@ impl<'db> ConstVariableValue<'db> { } #[derive(PartialEq, Copy, Clone, Debug)] -pub struct ConstVidKey<'db> { - pub vid: ConstVid, - pub phantom: PhantomData>, +pub(crate) struct ConstVidKey<'db> { + pub(crate) vid: ConstVid, + pub(crate) phantom: PhantomData>, } impl<'db> From for ConstVidKey<'db> { 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 0db474672175..d66aa9f277c7 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 @@ -1,4 +1,4 @@ -pub use rustc_next_trait_solver::solve::inspect::*; +pub(crate) use rustc_next_trait_solver::solve::inspect::*; use rustc_ast_ir::try_visit; use rustc_next_trait_solver::{ @@ -23,11 +23,11 @@ use crate::next_solver::{ obligation_ctxt::ObligationCtxt, }; -pub struct InspectConfig { - pub max_depth: usize, +pub(crate) struct InspectConfig { + pub(crate) max_depth: usize, } -pub struct InspectGoal<'a, 'db> { +pub(crate) struct InspectGoal<'a, 'db> { infcx: &'a SolverContext<'db>, depth: usize, orig_values: Vec>, @@ -103,7 +103,7 @@ impl<'db> NormalizesToTermHack<'db> { } } -pub struct InspectCandidate<'a, 'db> { +pub(crate) struct InspectCandidate<'a, 'db> { goal: &'a InspectGoal<'a, 'db>, kind: inspect::ProbeKind>, steps: Vec<&'a inspect::ProbeStep>>, @@ -113,15 +113,15 @@ pub struct InspectCandidate<'a, 'db> { } impl<'a, 'db> InspectCandidate<'a, 'db> { - pub fn kind(&self) -> inspect::ProbeKind> { + pub(crate) fn kind(&self) -> inspect::ProbeKind> { self.kind } - pub fn result(&self) -> Result { + pub(crate) fn result(&self) -> Result { self.result.map(|c| c.value.certainty) } - pub fn goal(&self) -> &'a InspectGoal<'a, 'db> { + pub(crate) fn goal(&self) -> &'a InspectGoal<'a, 'db> { self.goal } @@ -133,14 +133,17 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { /// /// This is *not* the certainty of the candidate's full nested evaluation, which /// can be accessed with [`Self::result`] instead. - pub fn shallow_certainty(&self) -> Certainty { + pub(crate) fn shallow_certainty(&self) -> Certainty { self.shallow_certainty } /// Visit all nested goals of this candidate without rolling /// back their inference constraints. This function modifies /// the state of the `infcx`. - pub fn visit_nested_no_probe>(&self, visitor: &mut V) -> V::Result { + pub(crate) fn visit_nested_no_probe>( + &self, + visitor: &mut V, + ) -> V::Result { for goal in self.instantiate_nested_goals() { try_visit!(goal.visit_with(visitor)); } @@ -152,7 +155,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 fn instantiate_nested_goals(&self) -> Vec> { + pub(crate) fn instantiate_nested_goals(&self) -> Vec> { let infcx = self.goal.infcx; let param_env = self.goal.goal.param_env; let mut orig_values = self.goal.orig_values.to_vec(); @@ -200,7 +203,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { /// Instantiate the args of an impl if this candidate came from a /// `CandidateSource::Impl`. This function modifies the state of the /// `infcx`. - pub fn instantiate_impl_args(&self) -> GenericArgs<'db> { + pub(crate) fn instantiate_impl_args(&self) -> 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(); @@ -241,7 +244,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { panic!("expected impl args probe step for `instantiate_impl_args`"); } - pub fn instantiate_proof_tree_for_nested_goal( + pub(crate) fn instantiate_proof_tree_for_nested_goal( &self, source: GoalSource, goal: Goal<'db, Predicate<'db>>, @@ -307,29 +310,33 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { /// Visit all nested goals of this candidate, rolling back /// all inference constraints. - pub fn visit_nested_in_probe>(&self, visitor: &mut V) -> V::Result { + #[expect(dead_code, reason = "used in rustc")] + pub(crate) fn visit_nested_in_probe>( + &self, + visitor: &mut V, + ) -> V::Result { self.goal.infcx.probe(|_| self.visit_nested_no_probe(visitor)) } } impl<'a, 'db> InspectGoal<'a, 'db> { - pub fn infcx(&self) -> &'a InferCtxt<'db> { + pub(crate) fn infcx(&self) -> &'a InferCtxt<'db> { self.infcx } - pub fn goal(&self) -> Goal<'db, Predicate<'db>> { + pub(crate) fn goal(&self) -> Goal<'db, Predicate<'db>> { self.goal } - pub fn result(&self) -> Result { + pub(crate) fn result(&self) -> Result { self.result } - pub fn source(&self) -> GoalSource { + pub(crate) fn source(&self) -> GoalSource { self.source } - pub fn depth(&self) -> usize { + pub(crate) fn depth(&self) -> usize { self.depth } @@ -405,7 +412,7 @@ impl<'a, 'db> InspectGoal<'a, 'db> { } } - pub fn candidates(&'a self) -> Vec> { + pub(crate) fn candidates(&'a self) -> Vec> { let mut candidates = vec![]; let mut nested_goals = vec![]; self.candidates_recur(&mut candidates, &mut nested_goals, &self.final_revision); @@ -415,7 +422,7 @@ impl<'a, 'db> InspectGoal<'a, 'db> { /// Returns the single candidate applicable for the current goal, if it exists. /// /// Returns `None` if there are either no or multiple applicable candidates. - pub fn unique_applicable_candidate(&'a self) -> Option> { + pub(crate) fn unique_applicable_candidate(&'a self) -> Option> { // FIXME(-Znext-solver): This does not handle impl candidates // hidden by env candidates. let mut candidates = self.candidates(); @@ -467,7 +474,7 @@ impl<'a, 'db> InspectGoal<'a, 'db> { } /// The public API to interact with proof trees. -pub trait ProofTreeVisitor<'db> { +pub(crate) trait ProofTreeVisitor<'db> { type Result: VisitorResult; fn config(&self) -> InspectConfig { 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 7be891106df3..331bcdcb26d3 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 @@ -1,72 +1,48 @@ //! Things related to the Interner in the next-trait-solver. -#![allow(unused)] // FIXME(next-solver): Remove this. use std::{fmt, ops::ControlFlow}; pub use tls_db::{attach_db, attach_db_allow_change, with_attached_db}; use base_db::Crate; -use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variances}; use hir_def::{ - AdtId, AttrDefId, BlockId, CallableDefId, EnumVariantId, GenericDefId, ItemContainerId, Lookup, - StructId, TypeAliasId, UnionId, VariantId, + AdtId, AttrDefId, BlockId, CallableDefId, EnumVariantId, ItemContainerId, StructId, UnionId, + VariantId, lang_item::LangItem, signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags}, }; -use intern::sym::non_exhaustive; -use intern::{Interned, impl_internable, sym}; use la_arena::Idx; -use rustc_abi::{Align, ReprFlags, ReprOptions}; -use rustc_ast_ir::visit::VisitorResult; +use rustc_abi::{ReprFlags, ReprOptions}; use rustc_hash::FxHashSet; -use rustc_index::{IndexVec, bit_set::DenseBitSet}; +use rustc_index::bit_set::DenseBitSet; use rustc_type_ir::{ - AliasTerm, AliasTermKind, AliasTy, AliasTyKind, BoundVar, CollectAndApply, DebruijnIndex, - EarlyBinder, FlagComputation, Flags, GenericArgKind, ImplPolarity, InferTy, - ProjectionPredicate, RegionKind, TermKind, TraitPredicate, TraitRef, TypeVisitableExt, - UniverseIndex, Upcast, Variance, WithCachedTypeInfo, - elaborate::{self, elaborate}, + AliasTermKind, AliasTyKind, BoundVar, CollectAndApply, DebruijnIndex, EarlyBinder, + FlagComputation, Flags, GenericArgKind, ImplPolarity, InferTy, TraitRef, TypeVisitableExt, + UniverseIndex, Upcast, Variance, + elaborate::elaborate, error::TypeError, - inherent::{ - self, AdtDef as _, Const as _, GenericArgs as _, GenericsOf, IntoKind, ParamEnv as _, - Region as _, SliceLike as _, Span as _, Ty as _, - }, - ir_print, + inherent::{self, GenericsOf, IntoKind, SliceLike as _, Span as _, Ty as _}, lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}, - relate, solve::SizedTraitKind, }; -use salsa::plumbing::AsId; -use smallvec::{SmallVec, smallvec}; -use syntax::ast::SelfParamKind; -use tracing::debug; -use triomphe::Arc; use crate::{ - ConstScalar, FnAbi, Interner, + FnAbi, db::HirDatabase, - lower_nextsolver::{self, TyLoweringContext}, method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint}, next_solver::{ AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, ImplIdWrapper, InternedWrapperNoDebug, RegionAssumptions, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, - TypingMode, - infer::{ - DbInternerInferExt, InferCtxt, - traits::{Obligation, ObligationCause}, - }, - obligation_ctxt::ObligationCtxt, util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls}, }, }; use super::{ - Binder, BoundExistentialPredicate, BoundExistentialPredicates, BoundTy, BoundTyKind, Clause, - ClauseKind, Clauses, Const, ConstKind, ErrorGuaranteed, ExprConst, ExternalConstraints, - ExternalConstraintsData, GenericArg, GenericArgs, InternedClausesWrapper, ParamConst, ParamEnv, - ParamTy, PlaceholderConst, PlaceholderTy, PredefinedOpaques, PredefinedOpaquesData, Predicate, - PredicateKind, SolverDefId, Term, Ty, TyKind, Tys, Valtree, ValueConst, + Binder, BoundExistentialPredicates, BoundTy, BoundTyKind, Clause, ClauseKind, Clauses, Const, + ErrorGuaranteed, ExprConst, ExternalConstraints, GenericArg, GenericArgs, ParamConst, ParamEnv, + ParamTy, PlaceholderConst, PlaceholderTy, PredefinedOpaques, Predicate, SolverDefId, Term, Ty, + TyKind, Tys, Valtree, ValueConst, abi::Safety, fold::{BoundVarReplacer, BoundVarReplacerDelegate, FnMutDelegate}, generics::{Generics, generics}, @@ -631,7 +607,6 @@ impl<'db> inherent::AdtDef> for AdtDef { self, interner: DbInterner<'db>, ) -> Option, Ty<'db>>> { - let db = interner.db(); let hir_def::AdtId::StructId(struct_id) = self.inner().id else { return None; }; @@ -647,23 +622,10 @@ impl<'db> inherent::AdtDef> for AdtDef { ) -> EarlyBinder, impl IntoIterator>> { let db = interner.db(); // FIXME: this is disabled just to match the behavior with chalk right now - let field_tys = |id: VariantId| { - let variant_data = id.fields(db); - let fields = if variant_data.fields().is_empty() { - vec![] - } else { - let field_types = db.field_types_ns(id); - variant_data - .fields() - .iter() - .map(|(idx, _)| { - let ty = field_types[idx]; - ty.skip_binder() - }) - .collect() - }; + let _field_tys = |id: VariantId| { + db.field_types_ns(id).iter().map(|(_, ty)| ty.skip_binder()).collect::>() }; - let field_tys = |id: VariantId| vec![]; + let field_tys = |_id: VariantId| vec![]; let tys: Vec<_> = match self.inner().id { hir_def::AdtId::StructId(id) => field_tys(id.into()), hir_def::AdtId::UnionId(id) => field_tys(id.into()), @@ -696,7 +658,7 @@ impl<'db> inherent::AdtDef> for AdtDef { fn destructor( self, - interner: DbInterner<'db>, + _interner: DbInterner<'db>, ) -> Option { // FIXME(next-solver) None @@ -742,7 +704,7 @@ impl<'db> inherent::Features> for Features { false } - fn feature_bound_holds_in_crate(self, symbol: ()) -> bool { + fn feature_bound_holds_in_crate(self, _symbol: ()) -> bool { false } } @@ -1002,7 +964,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn mk_tracked( self, data: T, - dep_node: Self::DepNodeIndex, + _dep_node: Self::DepNodeIndex, ) -> Self::Tracked { Tracked(data) } @@ -1024,7 +986,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn canonical_param_env_cache_get_or_insert( self, - param_env: Self::ParamEnv, + _param_env: Self::ParamEnv, f: impl FnOnce() -> rustc_type_ir::CanonicalParamEnvCacheEntry, from_entry: impl FnOnce(&rustc_type_ir::CanonicalParamEnvCacheEntry) -> R, ) -> R { @@ -1162,17 +1124,17 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { (TraitRef::new_from_args(self, trait_def_id.try_into().unwrap(), trait_args), alias_args) } - fn check_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) -> bool { + fn check_args_compatible(self, _def_id: Self::DefId, _args: Self::GenericArgs) -> bool { // FIXME true } - fn debug_assert_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) {} + fn debug_assert_args_compatible(self, _def_id: Self::DefId, _args: Self::GenericArgs) {} fn debug_assert_existential_args_compatible( self, - def_id: Self::DefId, - args: Self::GenericArgs, + _def_id: Self::DefId, + _args: Self::GenericArgs, ) { } @@ -1240,11 +1202,11 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self.db().callable_item_signature(def_id.0) } - fn coroutine_movability(self, def_id: Self::CoroutineId) -> rustc_ast_ir::Movability { + fn coroutine_movability(self, _def_id: Self::CoroutineId) -> rustc_ast_ir::Movability { unimplemented!() } - fn coroutine_for_closure(self, def_id: Self::CoroutineId) -> Self::CoroutineId { + fn coroutine_for_closure(self, _def_id: Self::CoroutineId) -> Self::CoroutineId { unimplemented!() } @@ -1421,9 +1383,10 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { }) } + #[expect(unreachable_code)] fn const_conditions( self, - def_id: Self::DefId, + _def_id: Self::DefId, ) -> EarlyBinder< Self, impl IntoIterator>>, @@ -1431,7 +1394,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { EarlyBinder::bind([unimplemented!()]) } - fn has_target_features(self, def_id: Self::FunctionId) -> bool { + fn has_target_features(self, _def_id: Self::FunctionId) -> bool { false } @@ -1462,7 +1425,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { hir_def::lang_item::LangItemTarget::Union(union_id) => union_id.into(), hir_def::lang_item::LangItemTarget::TypeAlias(type_alias_id) => type_alias_id.into(), hir_def::lang_item::LangItemTarget::Trait(trait_id) => trait_id.into(), - hir_def::lang_item::LangItemTarget::EnumVariant(enum_variant_id) => unimplemented!(), + hir_def::lang_item::LangItemTarget::EnumVariant(_) => unimplemented!(), } } @@ -1552,7 +1515,6 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { CoroutineReturn, CoroutineYield, FutureOutput, - AsyncFnOnceOutput, CallRefFuture, CallOnceFuture, AsyncFnOnceOutput, @@ -1596,7 +1558,6 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { AsyncFnMut, AsyncFnOnce, AsyncFnOnceOutput, - AsyncFnOnceOutput, ) } @@ -1636,7 +1597,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { }; if fps.is_empty() { - for_trait_impls( + _ = for_trait_impls( self.db(), self.krate.expect("Must have self.krate"), self.block, @@ -1658,7 +1619,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { }, ); } else { - for_trait_impls( + _ = for_trait_impls( self.db(), self.krate.expect("Must have self.krate"), self.block, @@ -1698,7 +1659,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } } - fn has_item_definition(self, def_id: Self::DefId) -> bool { + fn has_item_definition(self, _def_id: Self::DefId) -> bool { // FIXME(next-solver): should check if the associated item has a value. true } @@ -1746,13 +1707,13 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { trait_data.flags.contains(TraitFlags::FUNDAMENTAL) } - fn trait_may_be_implemented_via_object(self, trait_def_id: Self::TraitId) -> bool { + fn trait_may_be_implemented_via_object(self, _trait_def_id: Self::TraitId) -> bool { // FIXME(next-solver): should check the `TraitFlags` for // the `#[rustc_do_not_implement_via_object]` flag true } - fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool { + fn is_impl_trait_in_trait(self, _def_id: Self::DefId) -> bool { // FIXME(next-solver) false } @@ -1761,22 +1722,22 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { panic!("Bug encountered in next-trait-solver: {}", msg.to_string()) } - fn is_general_coroutine(self, coroutine_def_id: Self::CoroutineId) -> bool { + fn is_general_coroutine(self, _coroutine_def_id: Self::CoroutineId) -> bool { // FIXME(next-solver) true } - fn coroutine_is_async(self, coroutine_def_id: Self::CoroutineId) -> bool { + fn coroutine_is_async(self, _coroutine_def_id: Self::CoroutineId) -> bool { // FIXME(next-solver) true } - fn coroutine_is_gen(self, coroutine_def_id: Self::CoroutineId) -> bool { + fn coroutine_is_gen(self, _coroutine_def_id: Self::CoroutineId) -> bool { // FIXME(next-solver) false } - fn coroutine_is_async_gen(self, coroutine_def_id: Self::CoroutineId) -> bool { + fn coroutine_is_async_gen(self, _coroutine_def_id: Self::CoroutineId) -> bool { // FIXME(next-solver) false } @@ -1870,19 +1831,19 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { Binder::bind_with_vars(inner, bound_vars) } - fn opaque_types_defined_by(self, defining_anchor: Self::LocalDefId) -> Self::LocalDefIds { + fn opaque_types_defined_by(self, _defining_anchor: Self::LocalDefId) -> Self::LocalDefIds { // FIXME(next-solver) SolverDefIds::new_from_iter(self, []) } - fn alias_has_const_conditions(self, def_id: Self::DefId) -> bool { + fn alias_has_const_conditions(self, _def_id: Self::DefId) -> bool { // FIXME(next-solver) false } fn explicit_implied_const_bounds( self, - def_id: Self::DefId, + _def_id: Self::DefId, ) -> EarlyBinder< Self, impl IntoIterator>>, @@ -1899,14 +1860,14 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self.db().function_signature(id).flags.contains(FnFlags::CONST) } - fn impl_is_const(self, def_id: Self::ImplId) -> bool { + fn impl_is_const(self, _def_id: Self::ImplId) -> bool { false } fn opt_alias_variances( self, - kind: impl Into, - def_id: Self::DefId, + _kind: impl Into, + _def_id: Self::DefId, ) -> Option { None } @@ -1933,7 +1894,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn coroutine_hidden_types( self, - def_id: Self::CoroutineId, + _def_id: Self::CoroutineId, ) -> EarlyBinder>> { // FIXME(next-solver) @@ -1952,14 +1913,14 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self.db().trait_signature(trait_.0).flags.contains(TraitFlags::UNSAFE) } - fn impl_self_is_guaranteed_unsized(self, def_id: Self::ImplId) -> bool { + fn impl_self_is_guaranteed_unsized(self, _def_id: Self::ImplId) -> bool { false } fn impl_specializes( self, - specializing_impl_def_id: Self::ImplId, - parent_impl_def_id: Self::ImplId, + _specializing_impl_def_id: Self::ImplId, + _parent_impl_def_id: Self::ImplId, ) -> bool { false } @@ -1970,7 +1931,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn opaque_types_and_coroutines_defined_by( self, - defining_anchor: Self::LocalDefId, + _defining_anchor: Self::LocalDefId, ) -> Self::LocalDefIds { Default::default() } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs index 69afcf5dde90..dab0fe9e4a90 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs @@ -5,8 +5,6 @@ use std::any::type_name_of_val; use rustc_type_ir::inherent::SliceLike; use rustc_type_ir::{self as ty, ir_print::IrPrint}; -use crate::db::HirDatabase; - use super::SolverDefId; use super::interner::DbInterner; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index adbc6094a221..671f06f1b88a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -1,35 +1,25 @@ //! Things useful for mapping to/from Chalk and next-trait-solver types. -use base_db::Crate; use chalk_ir::{ - CanonicalVarKind, CanonicalVarKinds, FnPointer, InferenceVar, Substitution, TyVariableKind, - WellFormed, cast::Cast, fold::Shift, interner::HasInterner, -}; -use hir_def::{ - CallableDefId, ConstParamId, FunctionId, GeneralConstId, LifetimeParamId, TypeAliasId, - TypeOrConstParamId, TypeParamId, signatures::TraitFlags, + InferenceVar, Substitution, TyVariableKind, WellFormed, cast::Cast, fold::Shift, + interner::HasInterner, }; +use hir_def::{CallableDefId, ConstParamId, GeneralConstId, TypeParamId, signatures::TraitFlags}; use hir_def::{GenericDefId, GenericParamId}; -use intern::sym; use rustc_type_ir::{ - AliasTerm, BoundVar, DebruijnIndex, ExistentialProjection, ExistentialTraitRef, Interner as _, + AliasTerm, BoundVar, DebruijnIndex, ExistentialProjection, ExistentialTraitRef, OutlivesPredicate, ProjectionPredicate, TypeFoldable, TypeSuperFoldable, TypeVisitable, - TypeVisitableExt, UniverseIndex, elaborate, - inherent::{BoundVarLike, Clause as _, IntoKind, PlaceholderLike, SliceLike, Ty as _}, + UniverseIndex, elaborate, + inherent::{BoundVarLike, IntoKind, SliceLike, Ty as _}, shift_vars, solve::Goal, }; -use salsa::plumbing::FromId; -use salsa::{Id, plumbing::AsId}; use crate::next_solver::BoundConst; use crate::{ - ConstScalar, ImplTraitId, Interner, MemoryMap, - db::{ - HirDatabase, InternedClosureId, InternedCoroutineId, InternedLifetimeParamId, - InternedOpaqueTyId, InternedTypeOrConstParamId, - }, - from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, + ConstScalar, Interner, MemoryMap, + db::{InternedClosureId, InternedCoroutineId, InternedOpaqueTyId}, + from_assoc_type_id, from_chalk_trait_id, mapping::ToChalk, next_solver::{ Binder, ClauseKind, ConstBytes, TraitPredicate, UnevaluatedConst, @@ -42,11 +32,10 @@ use crate::{ }; use super::{ - BoundExistentialPredicate, BoundExistentialPredicates, BoundRegion, BoundRegionKind, BoundTy, - BoundTyKind, Canonical, CanonicalVars, Clause, Clauses, Const, Ctor, EarlyParamRegion, - ErrorGuaranteed, ExistentialPredicate, GenericArg, GenericArgs, ParamConst, ParamEnv, ParamTy, - Placeholder, PlaceholderConst, PlaceholderRegion, PlaceholderTy, Predicate, PredicateKind, - Region, SolverDefId, SubtypePredicate, Term, TraitRef, Ty, Tys, ValueConst, VariancesOf, + BoundExistentialPredicates, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, Canonical, + CanonicalVars, Clause, Clauses, Const, EarlyParamRegion, ErrorGuaranteed, ExistentialPredicate, + GenericArg, GenericArgs, ParamConst, ParamEnv, ParamTy, Predicate, PredicateKind, Region, + SolverDefId, SubtypePredicate, Term, TraitRef, Ty, Tys, ValueConst, }; // FIXME: This should urgently go (as soon as we finish the migration off Chalk, that is). @@ -167,7 +156,7 @@ where } impl NextSolverToChalk<'_, chalk_ir::Mutability> for rustc_ast_ir::Mutability { - fn to_chalk(self, interner: DbInterner<'_>) -> chalk_ir::Mutability { + fn to_chalk(self, _interner: DbInterner<'_>) -> chalk_ir::Mutability { match self { rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, @@ -176,7 +165,7 @@ impl NextSolverToChalk<'_, chalk_ir::Mutability> for rustc_ast_ir::Mutability { } impl NextSolverToChalk<'_, chalk_ir::Safety> for crate::next_solver::abi::Safety { - fn to_chalk(self, interner: DbInterner<'_>) -> chalk_ir::Safety { + fn to_chalk(self, _interner: DbInterner<'_>) -> chalk_ir::Safety { match self { crate::next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, crate::next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, @@ -349,8 +338,6 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { let id = from_assoc_type_id(projection.associated_ty_id); let def_id = SolverDefId::TypeAliasId(id); - let generics = interner.generics_of(def_id); - let parent_len = generics.parent_count; let substs = projection.substitution.iter(Interner).skip(1); let args = GenericArgs::new_from_iter( @@ -363,7 +350,7 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { ); (def_id, args) } - chalk_ir::AliasTy::Opaque(opaque_ty) => { + chalk_ir::AliasTy::Opaque(_opaque_ty) => { panic!("Invalid ExistentialPredicate (opaques can't be named)."); } }; @@ -379,10 +366,10 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { ); ExistentialPredicate::Projection(projection) } - chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => { + chalk_ir::WhereClause::LifetimeOutlives(_lifetime_outlives) => { return None; } - chalk_ir::WhereClause::TypeOutlives(type_outlives) => return None, + chalk_ir::WhereClause::TypeOutlives(_type_outlives) => return None, }; Some(Binder::bind_with_vars(clause, bound_vars)) @@ -621,7 +608,7 @@ impl<'db> NextSolverToChalk<'db, chalk_ir::VariableKinds> for BoundVar } impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKind { + fn to_nextsolver(&self, _interner: DbInterner<'db>) -> BoundVarKind { match self { chalk_ir::VariableKind::Ty(_ty_variable_kind) => BoundVarKind::Ty(BoundTyKind::Anon), chalk_ir::VariableKind::Lifetime => BoundVarKind::Region(BoundRegionKind::Anon), @@ -631,7 +618,7 @@ impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind NextSolverToChalk<'db, chalk_ir::VariableKind> for BoundVarKind { - fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::VariableKind { + fn to_chalk(self, _interner: DbInterner<'db>) -> chalk_ir::VariableKind { match self { BoundVarKind::Ty(_) => chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), BoundVarKind::Region(_) => chalk_ir::VariableKind::Lifetime, @@ -676,7 +663,7 @@ impl<'db> ChalkToNextSolver<'db, crate::lower_nextsolver::ImplTraitIdx<'db>> { fn to_nextsolver( &self, - interner: DbInterner<'db>, + _interner: DbInterner<'db>, ) -> crate::lower_nextsolver::ImplTraitIdx<'db> { crate::lower_nextsolver::ImplTraitIdx::from_raw(self.into_raw()) } @@ -739,7 +726,7 @@ impl<'db> NextSolverToChalk<'db, chalk_ir::UniverseIndex> for rustc_type_ir::Uni impl<'db> ChalkToNextSolver<'db, rustc_type_ir::InferTy> for (chalk_ir::InferenceVar, chalk_ir::TyVariableKind) { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::InferTy { + fn to_nextsolver(&self, _interner: DbInterner<'db>) -> rustc_type_ir::InferTy { match self.1 { chalk_ir::TyVariableKind::General => { rustc_type_ir::InferTy::TyVar(rustc_type_ir::TyVid::from_u32(self.0.index())) @@ -755,7 +742,7 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::InferTy> } impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutability { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_ast_ir::Mutability { + fn to_nextsolver(&self, _interner: DbInterner<'db>) -> rustc_ast_ir::Mutability { match self { chalk_ir::Mutability::Mut => rustc_ast_ir::Mutability::Mut, chalk_ir::Mutability::Not => rustc_ast_ir::Mutability::Not, @@ -808,7 +795,7 @@ impl<'db, T: HasInterner + ChalkToNextSolver<'db, U>, U> chalk_ir::VariableKind::Lifetime => { rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ROOT) } - chalk_ir::VariableKind::Const(ty) => { + chalk_ir::VariableKind::Const(_ty) => { rustc_type_ir::CanonicalVarKind::Const(UniverseIndex::ROOT) } }), @@ -863,25 +850,25 @@ impl<'db, T: NextSolverToChalk<'db, U>, U: HasInterner> impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal { fn to_nextsolver(&self, interner: DbInterner<'db>) -> Predicate<'db> { match self.data(Interner) { - chalk_ir::GoalData::Quantified(quantifier_kind, binders) => { + chalk_ir::GoalData::Quantified(_quantifier_kind, binders) => { if !binders.binders.is_empty(Interner) { panic!("Should not be constructed."); } let (val, _) = binders.clone().into_value_and_skipped_binders(); val.shifted_out(Interner).unwrap().to_nextsolver(interner) } - chalk_ir::GoalData::Implies(program_clauses, goal) => { + chalk_ir::GoalData::Implies(_program_clauses, _goal) => { panic!("Should not be constructed.") } - chalk_ir::GoalData::All(goals) => panic!("Should not be constructed."), - chalk_ir::GoalData::Not(goal) => panic!("Should not be constructed."), + chalk_ir::GoalData::All(_goals) => panic!("Should not be constructed."), + chalk_ir::GoalData::Not(_goal) => panic!("Should not be constructed."), chalk_ir::GoalData::EqGoal(eq_goal) => { let arg_to_term = |g: &chalk_ir::GenericArg| match g.data(Interner) { chalk_ir::GenericArgData::Ty(ty) => Term::Ty(ty.to_nextsolver(interner)), chalk_ir::GenericArgData::Const(const_) => { Term::Const(const_.to_nextsolver(interner)) } - chalk_ir::GenericArgData::Lifetime(lifetime) => unreachable!(), + chalk_ir::GenericArgData::Lifetime(_lifetime) => unreachable!(), }; let pred_kind = PredicateKind::AliasRelate( arg_to_term(&eq_goal.a), @@ -1112,16 +1099,16 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal panic!("Should not be constructed."), - chalk_ir::DomainGoal::IsUpstream(ty) => panic!("Should not be constructed."), - chalk_ir::DomainGoal::IsFullyVisible(ty) => panic!("Should not be constructed."), - chalk_ir::DomainGoal::LocalImplAllowed(trait_ref) => { + chalk_ir::DomainGoal::IsLocal(_ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::IsUpstream(_ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::IsFullyVisible(_ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::LocalImplAllowed(_trait_ref) => { panic!("Should not be constructed.") } chalk_ir::DomainGoal::Compatible => panic!("Should not be constructed."), - chalk_ir::DomainGoal::DownstreamType(ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::DownstreamType(_ty) => panic!("Should not be constructed."), chalk_ir::DomainGoal::Reveal => panic!("Should not be constructed."), - chalk_ir::DomainGoal::ObjectSafe(trait_id) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::ObjectSafe(_trait_id) => panic!("Should not be constructed."), } } } @@ -1176,7 +1163,7 @@ impl<'db> NextSolverToChalk<'db, chalk_ir::GoalData> for PredicateKind rustc_type_ir::PredicateKind::AliasRelate( alias_term, target_term, - alias_relation_direction, + _alias_relation_direction, ) => { let term_to_generic_arg = |term: Term<'db>| match term { Term::Ty(ty) => chalk_ir::GenericArg::new( @@ -1462,7 +1449,7 @@ pub fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) -> cra }, // For `Placeholder`, `Bound` and `Param`, see the comment on the reverse conversion. - rustc_type_ir::TyKind::Placeholder(placeholder) => { + rustc_type_ir::TyKind::Placeholder(_placeholder) => { unimplemented!( "A `rustc_type_ir::TyKind::Placeholder` doesn't have a direct \ correspondence in Chalk, as it represents a universally instantiated `Bound`.\n\ @@ -1511,10 +1498,10 @@ pub fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) -> cra let binders = chalk_ir::VariableKinds::from_iter( Interner, p.bound_vars().iter().map(|b| match b { - BoundVarKind::Ty(kind) => { + BoundVarKind::Ty(_kind) => { chalk_ir::VariableKind::Ty(TyVariableKind::General) } - BoundVarKind::Region(kind) => chalk_ir::VariableKind::Lifetime, + BoundVarKind::Region(_kind) => chalk_ir::VariableKind::Lifetime, BoundVarKind::Const => { chalk_ir::VariableKind::Const(crate::TyKind::Error.intern(Interner)) } @@ -1644,7 +1631,7 @@ pub fn convert_const_for_result<'db>( rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(var)) => { chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(var.as_u32())) } - rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Fresh(fresh)) => { + rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Fresh(_fresh)) => { panic!("Vars should not be freshened.") } rustc_type_ir::ConstKind::Param(param) => { @@ -1657,7 +1644,7 @@ pub fn convert_const_for_result<'db>( var.var.index(), )) } - rustc_type_ir::ConstKind::Placeholder(placeholder_const) => { + rustc_type_ir::ConstKind::Placeholder(_placeholder_const) => { unimplemented!( "A `rustc_type_ir::ConstKind::Placeholder` doesn't have a direct \ correspondence in Chalk, as it represents a universally instantiated `Bound`.\n\ @@ -1717,7 +1704,7 @@ pub fn convert_region_for_result<'db>( bound.var.as_usize(), )) } - rustc_type_ir::RegionKind::RePlaceholder(placeholder) => unimplemented!( + rustc_type_ir::RegionKind::RePlaceholder(_placeholder) => unimplemented!( "A `rustc_type_ir::RegionKind::RePlaceholder` doesn't have a direct \ correspondence in Chalk, as it represents a universally instantiated `Bound`.\n\ It therefore feels safer to leave it panicking, but if you hit this panic \ 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 2f241f8fecbe..bd678b3e78ff 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 @@ -5,7 +5,6 @@ use rustc_type_ir::{ inherent::{IntoKind, Term as _}, }; -use crate::next_solver::SolverDefId; use crate::next_solver::{ Binder, Const, ConstKind, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Term, Ty, TyKind, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs index e85574a8826f..ae92aea85527 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs @@ -1,14 +1,15 @@ use hir_def::TraitId; -use rustc_type_ir::relate::Relate; use rustc_type_ir::{TypeFoldable, Upcast, Variance}; -use crate::next_solver::fulfill::{FulfillmentCtxt, NextSolverError}; -use crate::next_solver::infer::at::ToTrace; -use crate::next_solver::infer::traits::{ - Obligation, ObligationCause, PredicateObligation, PredicateObligations, +use crate::next_solver::{ + Const, DbInterner, ParamEnv, Term, TraitRef, Ty, TypeError, + fulfill::{FulfillmentCtxt, NextSolverError}, + infer::{ + InferCtxt, InferOk, + at::ToTrace, + traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations}, + }, }; -use crate::next_solver::infer::{DefineOpaqueTypes, InferCtxt, InferOk, TypeTrace}; -use crate::next_solver::{Const, DbInterner, ParamEnv, Term, TraitRef, Ty, TypeError}; /// Used if you want to have pleasant experience when dealing /// with obligations outside of hir or mir typeck. @@ -69,21 +70,7 @@ impl<'a, 'db> ObligationCtxt<'a, 'db> { ) -> Result<(), TypeError<'db>> { self.infcx .at(cause, param_env) - .eq(DefineOpaqueTypes::Yes, expected, actual) - .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) - } - - pub fn eq_trace>>( - &mut self, - cause: &ObligationCause, - param_env: ParamEnv<'db>, - trace: TypeTrace<'db>, - expected: T, - actual: T, - ) -> Result<(), TypeError<'db>> { - self.infcx - .at(cause, param_env) - .eq_trace(DefineOpaqueTypes::Yes, trace, expected, actual) + .eq(expected, actual) .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } @@ -97,7 +84,7 @@ impl<'a, 'db> ObligationCtxt<'a, 'db> { ) -> Result<(), TypeError<'db>> { self.infcx .at(cause, param_env) - .sub(DefineOpaqueTypes::Yes, expected, actual) + .sub(expected, actual) .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } @@ -111,7 +98,7 @@ impl<'a, 'db> ObligationCtxt<'a, 'db> { ) -> Result<(), TypeError<'db>> { self.infcx .at(cause, param_env) - .relate(DefineOpaqueTypes::Yes, expected, variance, actual) + .relate(expected, variance, actual) .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } @@ -125,7 +112,7 @@ impl<'a, 'db> ObligationCtxt<'a, 'db> { ) -> Result<(), TypeError<'db>> { self.infcx .at(cause, param_env) - .sup(DefineOpaqueTypes::Yes, expected, actual) + .sup(expected, actual) .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs index 0aee779ed04f..8714c95f27d8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs @@ -1,11 +1,10 @@ //! Things related to opaques in the next-trait-solver. -use intern::Interned; use rustc_ast_ir::try_visit; use crate::next_solver::SolverDefId; -use super::{CanonicalVarKind, DbInterner, interned_vec_nolifetime_salsa}; +use super::{DbInterner, interned_vec_nolifetime_salsa}; pub type OpaqueTypeKey<'db> = rustc_type_ir::OpaqueTypeKey>; pub type PredefinedOpaquesData<'db> = rustc_type_ir::solve::PredefinedOpaquesData>; 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 6a0a07705a8c..70b6f20ede04 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 @@ -2,19 +2,16 @@ use std::cmp::Ordering; -use intern::Interned; use macros::{TypeFoldable, TypeVisitable}; -use rustc_ast_ir::try_visit; use rustc_type_ir::{ self as ty, CollectAndApply, DebruijnIndex, EarlyBinder, FlagComputation, Flags, PredicatePolarity, TypeFlags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, - TypeVisitable, Upcast, UpcastFrom, VisitorResult, WithCachedTypeInfo, + TypeVisitable, Upcast, UpcastFrom, WithCachedTypeInfo, elaborate::Elaboratable, error::{ExpectedFound, TypeError}, inherent::{IntoKind, SliceLike}, - relate::Relate, }; -use smallvec::{SmallVec, smallvec}; +use smallvec::SmallVec; use crate::next_solver::TraitIdWrapper; @@ -56,11 +53,11 @@ fn stable_cmp_existential_predicate<'db>( // FIXME: this is actual unstable - see impl in predicate.rs in `rustc_middle` match (a, b) { (ExistentialPredicate::Trait(_), ExistentialPredicate::Trait(_)) => Ordering::Equal, - (ExistentialPredicate::Projection(a), ExistentialPredicate::Projection(b)) => { + (ExistentialPredicate::Projection(_a), ExistentialPredicate::Projection(_b)) => { // Should sort by def path hash Ordering::Equal } - (ExistentialPredicate::AutoTrait(a), ExistentialPredicate::AutoTrait(b)) => { + (ExistentialPredicate::AutoTrait(_a), ExistentialPredicate::AutoTrait(_b)) => { // Should sort by def path hash Ordering::Equal } @@ -283,8 +280,6 @@ impl<'db> std::hash::Hash for InternedClausesWrapper<'db> { } } -type InternedClauses<'db> = Interned>; - #[salsa::interned(constructor = new_)] pub struct Clauses<'db> { #[returns(ref)] 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 5e7eb7532bb0..a3cfa65eb373 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 @@ -1,10 +1,9 @@ //! Things related to regions. use hir_def::LifetimeParamId; -use intern::{Interned, Symbol}; +use intern::Symbol; use rustc_type_ir::{ BoundVar, DebruijnIndex, Flags, INNERMOST, RegionVid, TypeFlags, TypeFoldable, TypeVisitable, - VisitorResult, inherent::{IntoKind, PlaceholderLike, SliceLike}, relate::Relate, }; 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 2457447ee39b..487d164f8691 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 @@ -1,29 +1,22 @@ //! Defining `SolverContext` for next-trait-solver. -use hir_def::{AssocItemId, GeneralConstId, TypeAliasId}; +use hir_def::{AssocItemId, GeneralConstId}; use rustc_next_trait_solver::delegate::SolverDelegate; use rustc_type_ir::GenericArgKind; use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::{ - InferCtxtLike, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt, UniverseIndex, - inherent::{IntoKind, SliceLike, Span as _, Term as _, Ty as _}, + InferCtxtLike, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt, + inherent::{IntoKind, Term as _, Ty as _}, solve::{Certainty, NoSolution}, }; -use crate::next_solver::mapping::NextSolverToChalk; use crate::next_solver::{CanonicalVarKind, ImplIdWrapper}; -use crate::{ - TraitRefExt, - db::HirDatabase, - next_solver::{ - ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate, mapping::ChalkToNextSolver, - util::sizedness_fast_path, - }, +use crate::next_solver::{ + ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate, util::sizedness_fast_path, }; use super::{ - Canonical, CanonicalVarValues, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, - ParamEnv, Predicate, SolverDefId, Span, Ty, UnevaluatedConst, + DbInterner, ErrorGuaranteed, GenericArg, SolverDefId, Span, infer::{DbInternerInferExt, InferCtxt, canonical::instantiate::CanonicalExt}, }; @@ -66,7 +59,7 @@ impl<'db> SolverDelegate for SolverContext<'db> { (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(), @@ -76,15 +69,15 @@ impl<'db> SolverDelegate for SolverContext<'db> { fn leak_check( &self, - max_input_universe: rustc_type_ir::UniverseIndex, + _max_input_universe: rustc_type_ir::UniverseIndex, ) -> Result<(), NoSolution> { Ok(()) } fn well_formed_goals( &self, - param_env: ::ParamEnv, - arg: ::Term, + _param_env: ::ParamEnv, + _arg: ::Term, ) -> Option< Vec< rustc_type_ir::solve::Goal< @@ -123,7 +116,7 @@ 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> { @@ -132,11 +125,11 @@ impl<'db> SolverDelegate for SolverContext<'db> { fn add_item_bounds_for_hidden_type( &self, - def_id: ::DefId, - args: ::GenericArgs, - param_env: ::ParamEnv, - hidden_ty: ::Ty, - goals: &mut Vec< + _def_id: ::DefId, + _args: ::GenericArgs, + _param_env: ::ParamEnv, + _hidden_ty: ::Ty, + _goals: &mut Vec< rustc_type_ir::solve::Goal< Self::Interner, ::Predicate, @@ -148,21 +141,10 @@ impl<'db> SolverDelegate for SolverContext<'db> { fn fetch_eligible_assoc_item( &self, - goal_trait_ref: rustc_type_ir::TraitRef, + _goal_trait_ref: rustc_type_ir::TraitRef, trait_assoc_def_id: SolverDefId, impl_id: ImplIdWrapper, ) -> Result, ErrorGuaranteed> { - let trait_ = self - .0 - .interner - .db() - .impl_trait(impl_id.0) - // ImplIds for impls where the trait ref can't be resolved should never reach solver - .expect("invalid impl passed to next-solver") - .skip_binder() - .def_id - .0; - let trait_data = trait_.trait_items(self.0.interner.db()); let impl_items = impl_id.0.impl_items(self.0.interner.db()); let id = match trait_assoc_def_id { SolverDefId::TypeAliasId(trait_assoc_id) => { @@ -208,16 +190,16 @@ impl<'db> SolverDelegate for SolverContext<'db> { fn is_transmutable( &self, - dst: ::Ty, - src: ::Ty, - assume: ::Const, + _dst: ::Ty, + _src: ::Ty, + _assume: ::Const, ) -> Result { unimplemented!() } fn evaluate_const( &self, - param_env: ::ParamEnv, + _param_env: ::ParamEnv, uv: rustc_type_ir::UnevaluatedConst, ) -> Option<::Const> { let c = match uv.def { @@ -236,7 +218,7 @@ impl<'db> SolverDelegate for SolverContext<'db> { Self::Interner, ::Predicate, >, - span: ::Span, + _span: ::Span, ) -> Option { if let Some(trait_pred) = goal.predicate.as_trait_clause() { if self.shallow_resolve(trait_pred.self_ty().skip_binder()).is_ty_var() @@ -279,8 +261,8 @@ 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::Clause(ClauseKind::RegionOutlives(_outlives)) => Some(Certainty::Yes), + PredicateKind::Clause(ClauseKind::TypeOutlives(_outlives)) => 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() { 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 44b85abba0ef..8932f519785c 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 @@ -1,34 +1,30 @@ //! Things related to tys in the next-trait-solver. -use std::iter; use std::ops::ControlFlow; use hir_def::{ - AdtId, DefWithBodyId, GenericDefId, HasModule, TypeOrConstParamId, TypeParamId, + AdtId, HasModule, TypeParamId, hir::generics::{TypeOrConstParamData, TypeParamProvenance}, lang_item::LangItem, }; use hir_def::{TraitId, type_ref::Rawness}; -use intern::{Interned, Symbol, sym}; use rustc_abi::{Float, Integer, Size}; use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; use rustc_type_ir::{ - AliasTyKind, BoundVar, ClosureKind, CollectAndApply, FlagComputation, Flags, FloatTy, FloatVid, - InferTy, IntTy, IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, WithCachedTypeInfo, + AliasTyKind, BoundVar, ClosureKind, FlagComputation, Flags, FloatTy, FloatVid, InferTy, IntTy, + IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, UintTy, Upcast, WithCachedTypeInfo, inherent::{ - Abi, AdtDef as _, BoundExistentialPredicates, BoundVarLike, Const as _, GenericArgs as _, + AdtDef as _, BoundExistentialPredicates, BoundVarLike, Const as _, GenericArgs as _, IntoKind, ParamLike, PlaceholderLike, Safety as _, SliceLike, Ty as _, }, relate::Relate, solve::SizedTraitKind, walk::TypeWalker, }; -use salsa::plumbing::{AsId, FromId}; -use smallvec::SmallVec; use crate::{ - FnAbi, ImplTraitId, + ImplTraitId, db::HirDatabase, interner::InternedWrapperNoDebug, next_solver::{ @@ -83,7 +79,7 @@ impl<'db> Ty<'db> { Ty::new(interner, TyKind::Adt(AdtDef::new(adt_id, interner), args)) } - pub fn new_param(interner: DbInterner<'db>, id: TypeParamId, index: u32, name: Symbol) -> Self { + pub fn new_param(interner: DbInterner<'db>, id: TypeParamId, index: u32) -> Self { Ty::new(interner, TyKind::Param(ParamTy { id, index })) } @@ -404,7 +400,7 @@ impl<'db> Ty<'db> { Some(interner.fn_sig(callable).instantiate(interner, args)) } TyKind::FnPtr(sig, hdr) => Some(sig.with(hdr)), - TyKind::Closure(closure_id, closure_args) => closure_args + TyKind::Closure(_, closure_args) => closure_args .split_closure_args_untupled() .closure_sig_as_fn_ptr_ty .callable_sig(interner), @@ -1222,7 +1218,7 @@ pub struct ParamTy { impl ParamTy { pub fn to_ty<'db>(self, interner: DbInterner<'db>) -> Ty<'db> { - Ty::new_param(interner, self.id, self.index, sym::MISSING_NAME.clone()) + Ty::new_param(interner, self.id, self.index) } } @@ -1269,11 +1265,11 @@ impl<'db> TypeVisitable> for ErrorGuaranteed { impl<'db> TypeFoldable> for ErrorGuaranteed { fn try_fold_with>>( self, - folder: &mut F, + _folder: &mut F, ) -> Result { Ok(self) } - fn fold_with>>(self, folder: &mut F) -> Self { + fn fold_with>>(self, _folder: &mut F) -> Self { self } } 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 ae240a942f57..97f536305805 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 @@ -5,20 +5,19 @@ use std::ops::{self, ControlFlow}; use base_db::Crate; use hir_def::lang_item::LangItem; -use hir_def::{BlockId, HasModule, ItemContainerId, Lookup}; +use hir_def::{BlockId, HasModule}; use intern::sym; use la_arena::Idx; use rustc_abi::{Float, HasDataLayout, Integer, IntegerType, Primitive, ReprOptions}; use rustc_type_ir::data_structures::IndexMap; use rustc_type_ir::inherent::{ - AdtDef, Const as _, GenericArg as _, GenericArgs as _, ParamEnv as _, Region as _, SliceLike, - Ty as _, + AdtDef, GenericArg as _, GenericArgs as _, ParamEnv as _, SliceLike, Ty as _, }; use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ BoundVar, Canonical, DebruijnIndex, GenericArgKind, INNERMOST, Interner, PredicatePolarity, - TypeFlags, TypeVisitable, TypeVisitableExt, + TypeVisitableExt, }; use rustc_type_ir::{ ConstKind, CoroutineArgs, FloatTy, IntTy, RegionKind, TypeFolder, TypeSuperFoldable, @@ -29,17 +28,14 @@ use rustc_type_ir::{InferCtxtLike, TypeFoldable}; use crate::lower_nextsolver::{LifetimeElisionKind, TyLoweringContext}; use crate::next_solver::infer::InferCtxt; use crate::next_solver::{ - BoundConst, CanonicalVarKind, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, - PlaceholderRegion, TypingMode, + BoundConst, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion, }; use crate::{ db::HirDatabase, - from_foreign_def_id, method_resolution::{TraitImpls, TyFingerprint}, }; use super::fold::{BoundVarReplacer, FnMutDelegate}; -use super::generics::generics; use super::{ AliasTerm, AliasTy, Binder, BoundRegion, BoundTy, BoundTyKind, BoundVarKind, BoundVarKinds, CanonicalVars, Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder, GenericArg, @@ -530,7 +526,7 @@ pub(crate) fn mini_canonicalize<'db, T: TypeFoldable>>( max_universe: UniverseIndex::from_u32(1), variables: CanonicalVars::new_from_iter( context.cx(), - vars.iter().enumerate().map(|(idx, (k, v))| match (*k).kind() { + vars.iter().enumerate().map(|(idx, (k, _v))| match (*k).kind() { GenericArgKind::Type(ty) => match ty.kind() { TyKind::Int(..) | TyKind::Uint(..) => rustc_type_ir::CanonicalVarKind::Int, TyKind::Float(..) => rustc_type_ir::CanonicalVarKind::Float, @@ -617,7 +613,7 @@ impl<'db> TypeFolder> for MiniCanonicalizer<'_, 'db> { } r } - RegionKind::ReVar(vid) => { + RegionKind::ReVar(_vid) => { let len = self.vars.len(); let var = *self.vars.entry(r.into()).or_insert(len); Region::new( @@ -646,7 +642,7 @@ impl<'db> TypeFolder> for MiniCanonicalizer<'_, 'db> { } c } - ConstKind::Infer(infer) => { + ConstKind::Infer(_infer) => { let len = self.vars.len(); let var = *self.vars.entry(c.into()).or_insert(len); Const::new( @@ -666,14 +662,8 @@ pub fn explicit_item_bounds<'db>( let db = interner.db(); match def_id { SolverDefId::TypeAliasId(type_alias) => { - let trait_ = match type_alias.lookup(db).container { - ItemContainerId::TraitId(t) => t, - _ => panic!("associated type not in trait"), - }; - // Lower bounds -- we could/should maybe move this to a separate query in `lower` let type_alias_data = db.type_alias_signature(type_alias); - let generic_params = generics(db, type_alias.into()); let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); let mut ctx = TyLoweringContext::new( db, @@ -805,7 +795,7 @@ pub fn explicit_item_bounds<'db>( GenericArgs::new_from_iter(interner, [item_ty.into()]), ), term: match out.kind() { - GenericArgKind::Lifetime(lt) => panic!(), + GenericArgKind::Lifetime(_lt) => panic!(), GenericArgKind::Type(ty) => Term::Ty(ty), GenericArgKind::Const(const_) => Term::Const(const_), }, @@ -993,26 +983,6 @@ impl<'db> TypeFolder> for PlaceholderReplacer<'_, 'db> { } } -pub(crate) fn needs_normalization<'db, T: TypeVisitable>>( - infcx: &InferCtxt<'db>, - value: &T, -) -> bool { - let mut flags = TypeFlags::HAS_ALIAS; - - // Opaques are treated as rigid outside of `TypingMode::PostAnalysis`, - // so we can ignore those. - match infcx.typing_mode() { - // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis - TypingMode::Coherence - | TypingMode::Analysis { .. } - | TypingMode::Borrowck { .. } - | TypingMode::PostBorrowckAnalysis { .. } => flags.remove(TypeFlags::HAS_TY_OPAQUE), - TypingMode::PostAnalysis => {} - } - - value.has_type_flags(flags) -} - pub fn sizedness_fast_path<'db>( tcx: DbInterner<'db>, predicate: Predicate<'db>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/primitive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/primitive.rs index d2901f7fc53d..9ffb112fe617 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/primitive.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/primitive.rs @@ -1,7 +1,7 @@ //! A few helper functions for dealing with primitives. -pub use chalk_ir::{FloatTy, IntTy, UintTy}; pub use hir_def::builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}; +pub use rustc_type_ir::{FloatTy, IntTy, UintTy}; pub fn int_ty_to_string(ty: IntTy) -> &'static str { match ty { @@ -33,68 +33,3 @@ pub fn float_ty_to_string(ty: FloatTy) -> &'static str { FloatTy::F128 => "f128", } } - -pub fn int_ty_to_string_ns(ty: rustc_type_ir::IntTy) -> &'static str { - use rustc_type_ir::IntTy; - match ty { - IntTy::Isize => "isize", - IntTy::I8 => "i8", - IntTy::I16 => "i16", - IntTy::I32 => "i32", - IntTy::I64 => "i64", - IntTy::I128 => "i128", - } -} - -pub fn uint_ty_to_string_ns(ty: rustc_type_ir::UintTy) -> &'static str { - use rustc_type_ir::UintTy; - match ty { - UintTy::Usize => "usize", - UintTy::U8 => "u8", - UintTy::U16 => "u16", - UintTy::U32 => "u32", - UintTy::U64 => "u64", - UintTy::U128 => "u128", - } -} - -pub fn float_ty_to_string_ns(ty: rustc_type_ir::FloatTy) -> &'static str { - use rustc_type_ir::FloatTy; - match ty { - FloatTy::F16 => "f16", - FloatTy::F32 => "f32", - FloatTy::F64 => "f64", - FloatTy::F128 => "f128", - } -} - -pub(super) fn int_ty_from_builtin(t: BuiltinInt) -> IntTy { - match t { - BuiltinInt::Isize => IntTy::Isize, - BuiltinInt::I8 => IntTy::I8, - BuiltinInt::I16 => IntTy::I16, - BuiltinInt::I32 => IntTy::I32, - BuiltinInt::I64 => IntTy::I64, - BuiltinInt::I128 => IntTy::I128, - } -} - -pub(super) fn uint_ty_from_builtin(t: BuiltinUint) -> UintTy { - match t { - BuiltinUint::Usize => UintTy::Usize, - BuiltinUint::U8 => UintTy::U8, - BuiltinUint::U16 => UintTy::U16, - BuiltinUint::U32 => UintTy::U32, - BuiltinUint::U64 => UintTy::U64, - BuiltinUint::U128 => UintTy::U128, - } -} - -pub(super) fn float_ty_from_builtin(t: BuiltinFloat) -> FloatTy { - match t { - BuiltinFloat::F16 => FloatTy::F16, - BuiltinFloat::F32 => FloatTy::F32, - BuiltinFloat::F64 => FloatTy::F64, - BuiltinFloat::F128 => FloatTy::F128, - } -} diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 36c8c3051cf5..78b4533a94b0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1803,7 +1803,7 @@ impl Adt { let env = db.trait_environment(self.into()); let interner = DbInterner::new_with(db, Some(env.krate), env.block); let adt_id = AdtId::from(self); - let args = GenericArgs::for_item_with_defaults(interner, adt_id.into(), |_, _, id, _| { + let args = GenericArgs::for_item_with_defaults(interner, adt_id.into(), |_, id, _| { GenericArg::error_from_id(interner, id) }); db.layout_of_adt(adt_id, args, env) @@ -4184,8 +4184,7 @@ impl TypeParam { let resolver = self.id.parent().resolver(db); let interner = DbInterner::new_with(db, None, None); let index = hir_ty::param_idx(db, self.id.into()).unwrap(); - let name = self.name(db).symbol().clone(); - let ty = Ty::new_param(interner, self.id, index as u32, name); + let ty = Ty::new_param(interner, self.id, index as u32); Type::new_with_resolver_inner(db, &resolver, ty) } @@ -6438,7 +6437,7 @@ fn generic_args_from_tys<'db>( args: impl IntoIterator>, ) -> GenericArgs<'db> { let mut args = args.into_iter(); - GenericArgs::for_item(interner, def_id, |_, _, id, _| { + GenericArgs::for_item(interner, def_id, |_, id, _| { if matches!(id, GenericParamId::TypeParamId(_)) && let Some(arg) = args.next() { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index eecca0244091..62ce3daab75d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1657,14 +1657,11 @@ impl<'db> SemanticsImpl<'db> { ) -> Option { let interner = DbInterner::new_with(self.db, None, None); let mut subst = subst.into_iter(); - let substs = hir_ty::next_solver::GenericArgs::for_item( - interner, - trait_.id.into(), - |_, _, id, _| { + let substs = + hir_ty::next_solver::GenericArgs::for_item(interner, trait_.id.into(), |_, id, _| { assert!(matches!(id, hir_def::GenericParamId::TypeParamId(_)), "expected a type"); subst.next().expect("too few subst").ty.into() - }, - ); + }); assert!(subst.next().is_none(), "too many subst"); Some(self.db.lookup_impl_method(env.env, func.into(), substs).0.into()) } From 2c48c398f77c4a490fc3a6c2a59d53d6cca4ed25 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 20 Oct 2025 15:04:51 +0800 Subject: [PATCH 043/170] Fix missing RestPat for convert_named_struct_to_tuple_struct Example --- ```rust struct Inner; struct A$0 { inner: Inner } fn foo(A { .. }: A) {} ``` **Before this PR**: ```rust struct Inner; struct A(Inner); fn foo(A(): A) {} ``` **After this PR**: ```rust struct Inner; struct A(Inner); fn foo(A(..): A) {} ``` --- .../convert_named_struct_to_tuple_struct.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) 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 8d27574eb2ca..0847719d6922 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 @@ -202,6 +202,9 @@ fn process_struct_name_reference( .record_pat_field_list()? .fields() .filter_map(|pat| pat.pat()) + .chain(record_struct_pat.record_pat_field_list()? + .rest_pat() + .map(Into::into)) ) .to_string() ); @@ -346,6 +349,37 @@ impl A { ); } + #[test] + fn convert_struct_and_rest_pat() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct Inner; +struct A$0 { inner: Inner } +fn foo(A { .. }: A) {} +"#, + r#" +struct Inner; +struct A(Inner); +fn foo(A(..): A) {} +"#, + ); + + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct Inner; +struct A$0 { inner: Inner, extra: Inner } +fn foo(A { inner, .. }: A) {} +"#, + r#" +struct Inner; +struct A(Inner, Inner); +fn foo(A(inner, ..): A) {} +"#, + ); + } + #[test] fn convert_simple_struct_cursor_on_visibility_keyword() { check_assist( From 9b0d532687e1f8039be5311d0893f2a77e4d5b61 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 17 Oct 2025 08:45:08 +0300 Subject: [PATCH 044/170] Rip Chalk out of the codebase! --- src/tools/rust-analyzer/Cargo.lock | 24 - src/tools/rust-analyzer/Cargo.toml | 4 - .../rust-analyzer/crates/hir-ty/Cargo.toml | 2 - .../crates/hir-ty/src/builder.rs | 211 -- .../crates/hir-ty/src/chalk_db.rs | 9 - .../crates/hir-ty/src/chalk_ext.rs | 46 - .../crates/hir-ty/src/consteval_chalk.rs | 108 - .../rust-analyzer/crates/hir-ty/src/db.rs | 233 +- .../diagnostics/match_check/pat_analysis.rs | 2 +- .../crates/hir-ty/src/display.rs | 22 +- .../rust-analyzer/crates/hir-ty/src/drop.rs | 4 +- .../crates/hir-ty/src/dyn_compatibility.rs | 52 +- .../crates/hir-ty/src/generics.rs | 47 - .../rust-analyzer/crates/hir-ty/src/infer.rs | 14 +- .../crates/hir-ty/src/infer/cast.rs | 2 +- .../crates/hir-ty/src/infer/diagnostics.rs | 4 +- .../crates/hir-ty/src/infer/expr.rs | 10 +- .../crates/hir-ty/src/infer/mutability.rs | 2 +- .../crates/hir-ty/src/infer/pat.rs | 6 +- .../crates/hir-ty/src/infer/path.rs | 4 +- .../crates/hir-ty/src/infer/unify.rs | 2 +- .../crates/hir-ty/src/inhabitedness.rs | 2 +- .../crates/hir-ty/src/interner.rs | 403 --- .../rust-analyzer/crates/hir-ty/src/layout.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 408 +-- .../rust-analyzer/crates/hir-ty/src/lower.rs | 2487 +++++++++++------ .../crates/hir-ty/src/lower/path.rs | 777 +++-- .../crates/hir-ty/src/lower_nextsolver.rs | 2138 -------------- .../hir-ty/src/lower_nextsolver/path.rs | 1327 --------- .../crates/hir-ty/src/mapping.rs | 169 -- .../crates/hir-ty/src/method_resolution.rs | 15 +- .../rust-analyzer/crates/hir-ty/src/mir.rs | 2 +- .../crates/hir-ty/src/mir/eval.rs | 16 +- .../crates/hir-ty/src/mir/eval/shim.rs | 2 +- .../crates/hir-ty/src/mir/eval/shim/simd.rs | 2 +- .../crates/hir-ty/src/next_solver.rs | 2 +- .../crates/hir-ty/src/next_solver/consts.rs | 3 +- .../hir-ty/src/next_solver/generic_arg.rs | 4 +- .../crates/hir-ty/src/next_solver/interner.rs | 19 +- .../crates/hir-ty/src/next_solver/mapping.rs | 1733 +----------- .../hir-ty/src/next_solver/predicate.rs | 5 +- .../crates/hir-ty/src/next_solver/ty.rs | 8 +- .../crates/hir-ty/src/next_solver/util.rs | 197 +- .../crates/hir-ty/src/tests/incremental.rs | 28 +- .../rust-analyzer/crates/hir-ty/src/tls.rs | 155 - .../rust-analyzer/crates/hir-ty/src/traits.rs | 199 +- .../rust-analyzer/crates/hir-ty/src/utils.rs | 137 +- .../crates/hir-ty/src/variance.rs | 2 +- .../rust-analyzer/crates/hir/src/display.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 18 +- .../crates/hir/src/source_analyzer.rs | 10 +- .../crates/ide/src/hover/tests.rs | 2 +- 52 files changed, 2421 insertions(+), 8663 deletions(-) delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/builder.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/consteval_chalk.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/interner.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/tls.rs diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 539f8cf1b933..5eb71eb9d4f0 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -258,28 +258,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "chalk-derive" -version = "0.104.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea9b1e80910f66ae87c772247591432032ef3f6a67367ff17f8343db05beafa" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "chalk-ir" -version = "0.104.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7047a516de16226cd17344d41a319d0ea1064bf9e60bd612ab341ab4a34bbfa8" -dependencies = [ - "bitflags 2.9.4", - "chalk-derive", -] - [[package]] name = "clap" version = "4.5.48" @@ -777,8 +755,6 @@ dependencies = [ "arrayvec", "base-db", "bitflags 2.9.4", - "chalk-derive", - "chalk-ir", "cov-mark", "either", "ena", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index f94fd37e52a7..8a108974681a 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -37,8 +37,6 @@ debug = 2 [patch.'crates-io'] # rowan = { path = "../rowan" } -# chalk-ir = { path = "../chalk/chalk-ir" } -# chalk-derive = { path = "../chalk/chalk-derive" } # line-index = { path = "lib/line-index" } # la-arena = { path = "lib/la-arena" } # lsp-server = { path = "lib/lsp-server" } @@ -110,8 +108,6 @@ arrayvec = "0.7.6" bitflags = "2.9.1" cargo_metadata = "0.21.0" camino = "1.1.10" -chalk-ir = "0.104.0" -chalk-derive = "0.104.0" crossbeam-channel = "0.5.15" dissimilar = "1.0.10" dot = "0.1.4" diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index ec6563315407..378a0f0382c3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -24,8 +24,6 @@ oorandom = "11.1.5" tracing.workspace = true rustc-hash.workspace = true scoped-tls = "1.0.1" -chalk-ir.workspace = true -chalk-derive.workspace = true la-arena.workspace = true triomphe.workspace = true typed-arena = "2.0.2" diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs deleted file mode 100644 index 4cd0af28f33f..000000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ /dev/null @@ -1,211 +0,0 @@ -//! `TyBuilder`, a helper for building instances of `Ty` and related types. - -use chalk_ir::{ - DebruijnIndex, - cast::{Cast, Caster}, -}; -use hir_def::{GenericDefId, GenericParamId, TraitId}; -use smallvec::SmallVec; - -use crate::{ - BoundVar, GenericArg, GenericArgData, Interner, Substitution, TraitRef, Ty, TyKind, - consteval::unknown_const_as_generic, - db::HirDatabase, - error_lifetime, - generics::generics, - infer::unify::InferenceTable, - next_solver::{ - DbInterner, EarlyBinder, - mapping::{ChalkToNextSolver, NextSolverToChalk}, - }, - to_chalk_trait_id, -}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) enum ParamKind { - Type, - Lifetime, - Const(Ty), -} - -/// This is a builder for `Ty` or anything that needs a `Substitution`. -pub(crate) struct TyBuilder { - /// The `data` field is used to keep track of what we're building (e.g. an - /// ADT, a `TraitRef`, ...). - data: D, - vec: SmallVec<[GenericArg; 2]>, - param_kinds: SmallVec<[ParamKind; 2]>, - parent_subst: Substitution, -} - -impl TyBuilder { - fn with_data(self, data: B) -> TyBuilder { - TyBuilder { - data, - vec: self.vec, - param_kinds: self.param_kinds, - parent_subst: self.parent_subst, - } - } -} - -impl TyBuilder { - fn new( - data: D, - param_kinds: SmallVec<[ParamKind; 2]>, - parent_subst: Option, - ) -> Self { - let parent_subst = parent_subst.unwrap_or_else(|| Substitution::empty(Interner)); - Self { data, vec: SmallVec::with_capacity(param_kinds.len()), param_kinds, parent_subst } - } - - fn build_internal(self) -> (D, Substitution) { - assert_eq!( - self.vec.len(), - self.param_kinds.len(), - "{} args received, {} expected ({:?})", - self.vec.len(), - self.param_kinds.len(), - &self.param_kinds - ); - for (a, e) in self.vec.iter().zip(self.param_kinds.iter()) { - self.assert_match_kind(a, e); - } - let subst = Substitution::from_iter( - Interner, - self.parent_subst.iter(Interner).cloned().chain(self.vec), - ); - (self.data, subst) - } - - pub(crate) fn remaining(&self) -> usize { - self.param_kinds.len() - self.vec.len() - } - - pub(crate) fn fill_with_bound_vars( - self, - debruijn: DebruijnIndex, - starting_from: usize, - ) -> Self { - // self.fill is inlined to make borrow checker happy - let mut this = self; - let other = &this.param_kinds[this.vec.len()..]; - let filler = (starting_from..).zip(other).map(|(idx, kind)| match kind { - ParamKind::Type => BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner), - ParamKind::Const(ty) => { - BoundVar::new(debruijn, idx).to_const(Interner, ty.clone()).cast(Interner) - } - ParamKind::Lifetime => { - BoundVar::new(debruijn, idx).to_lifetime(Interner).cast(Interner) - } - }); - this.vec.extend(filler.take(this.remaining()).casted(Interner)); - assert_eq!(this.remaining(), 0); - this - } - - #[tracing::instrument(skip_all)] - pub(crate) fn fill_with_inference_vars(self, table: &mut InferenceTable<'_>) -> Self { - self.fill(|x| { - match x { - ParamKind::Type => crate::next_solver::GenericArg::Ty(table.next_ty_var()), - ParamKind::Const(_) => table.next_const_var().into(), - ParamKind::Lifetime => table.next_region_var().into(), - } - .to_chalk(table.interner()) - }) - } - - pub(crate) fn fill(mut self, filler: impl FnMut(&ParamKind) -> GenericArg) -> Self { - self.vec.extend(self.param_kinds[self.vec.len()..].iter().map(filler)); - assert_eq!(self.remaining(), 0); - self - } - - fn assert_match_kind(&self, a: &chalk_ir::GenericArg, e: &ParamKind) { - match (a.data(Interner), e) { - (GenericArgData::Ty(_), ParamKind::Type) - | (GenericArgData::Const(_), ParamKind::Const(_)) - | (GenericArgData::Lifetime(_), ParamKind::Lifetime) => (), - _ => panic!("Mismatched kinds: {a:?}, {:?}, {:?}", self.vec, self.param_kinds), - } - } -} - -impl TyBuilder<()> { - pub(crate) fn usize() -> Ty { - TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(Interner) - } - - pub(crate) fn unknown_subst( - db: &dyn HirDatabase, - def: impl Into, - ) -> Substitution { - let interner = DbInterner::conjure(); - let params = generics(db, def.into()); - Substitution::from_iter( - Interner, - params.iter_id().map(|id| match id { - GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), - GenericParamId::ConstParamId(id) => { - unknown_const_as_generic(db.const_param_ty_ns(id)) - .to_chalk(interner) - .cast(Interner) - } - GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), - }), - ) - } - - #[tracing::instrument(skip_all)] - pub(crate) fn subst_for_def( - db: &dyn HirDatabase, - def: impl Into, - parent_subst: Option, - ) -> TyBuilder<()> { - let generics = generics(db, def.into()); - assert!(generics.parent_generics().is_some() == parent_subst.is_some()); - let params = generics - .iter_self() - .map(|(id, _data)| match id { - GenericParamId::TypeParamId(_) => ParamKind::Type, - GenericParamId::ConstParamId(id) => ParamKind::Const(db.const_param_ty(id)), - GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime, - }) - .collect(); - TyBuilder::new((), params, parent_subst) - } - - pub(crate) fn build(self) -> Substitution { - let ((), subst) = self.build_internal(); - subst - } -} - -impl TyBuilder { - pub(crate) fn trait_ref(db: &dyn HirDatabase, def: TraitId) -> TyBuilder { - TyBuilder::subst_for_def(db, def, None).with_data(def) - } - - pub(crate) fn build(self) -> TraitRef { - let (trait_id, substitution) = self.build_internal(); - TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution } - } -} - -impl<'db, T: rustc_type_ir::TypeFoldable>> TyBuilder> { - pub(crate) fn build(self, interner: DbInterner<'db>) -> T { - let (b, subst) = self.build_internal(); - let args: crate::next_solver::GenericArgs<'db> = subst.to_nextsolver(interner); - b.instantiate(interner, args) - } -} - -impl<'db> TyBuilder>> { - pub(crate) fn impl_self_ty( - db: &'db dyn HirDatabase, - def: hir_def::ImplId, - ) -> TyBuilder>> { - TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def)) - } -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs deleted file mode 100644 index a6b859b37210..000000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! The implementation of `RustIrDatabase` for Chalk, which provides information -//! about the code that Chalk needs. - -use crate::Interner; - -pub(crate) type AssocTypeId = chalk_ir::AssocTypeId; -pub(crate) type TraitId = chalk_ir::TraitId; -pub(crate) type AdtId = chalk_ir::AdtId; -pub(crate) type ImplId = chalk_ir::ImplId; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs deleted file mode 100644 index 4ea563d46e6e..000000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! Various extensions traits for Chalk types. - -use hir_def::{ItemContainerId, Lookup, TraitId}; - -use crate::{ - Interner, ProjectionTy, Substitution, TraitRef, Ty, db::HirDatabase, from_assoc_type_id, - from_chalk_trait_id, generics::generics, to_chalk_trait_id, -}; - -pub(crate) trait ProjectionTyExt { - fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef; - fn trait_(&self, db: &dyn HirDatabase) -> TraitId; - fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty; -} - -impl ProjectionTyExt for ProjectionTy { - fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef { - // FIXME: something like `Split` trait from chalk-solve might be nice. - let generics = generics(db, from_assoc_type_id(self.associated_ty_id).into()); - let parent_len = generics.parent_generics().map_or(0, |g| g.len_self()); - let substitution = - Substitution::from_iter(Interner, self.substitution.iter(Interner).take(parent_len)); - TraitRef { trait_id: to_chalk_trait_id(self.trait_(db)), substitution } - } - - fn trait_(&self, db: &dyn HirDatabase) -> TraitId { - match from_assoc_type_id(self.associated_ty_id).lookup(db).container { - ItemContainerId::TraitId(it) => it, - _ => panic!("projection ty without parent trait"), - } - } - - fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty { - self.trait_ref(db).self_type_parameter(Interner) - } -} - -pub(crate) trait TraitRefExt { - fn hir_trait_id(&self) -> TraitId; -} - -impl TraitRefExt for TraitRef { - fn hir_trait_id(&self) -> TraitId { - from_chalk_trait_id(self.trait_id) - } -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_chalk.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_chalk.rs deleted file mode 100644 index 07b783ea9292..000000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_chalk.rs +++ /dev/null @@ -1,108 +0,0 @@ -//! Constant evaluation details - -use base_db::Crate; -use chalk_ir::{BoundVar, DebruijnIndex, cast::Cast}; -use hir_def::{ - expr_store::{HygieneId, path::Path}, - resolver::{Resolver, ValueNs}, - type_ref::LiteralConstRef, -}; -use stdx::never; - -use crate::{ - Const, ConstData, ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, - TraitEnvironment, Ty, - db::HirDatabase, - generics::Generics, - lower::ParamLoweringMode, - next_solver::{DbInterner, mapping::ChalkToNextSolver}, - to_placeholder_idx, -}; - -pub(crate) fn path_to_const<'g>( - db: &dyn HirDatabase, - resolver: &Resolver<'_>, - path: &Path, - mode: ParamLoweringMode, - args: impl FnOnce() -> &'g Generics, - debruijn: DebruijnIndex, - expected_ty: Ty, -) -> Option { - match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) { - Some(ValueNs::GenericParam(p)) => { - let ty = db.const_param_ty(p); - let args = args(); - let value = match mode { - ParamLoweringMode::Placeholder => { - let idx = args.type_or_const_param_idx(p.into()).unwrap(); - ConstValue::Placeholder(to_placeholder_idx(db, p.into(), idx as u32)) - } - ParamLoweringMode::Variable => match args.type_or_const_param_idx(p.into()) { - Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)), - None => { - never!( - "Generic list doesn't contain this param: {:?}, {:?}, {:?}", - args, - path, - p - ); - return None; - } - }, - }; - Some(ConstData { ty, value }.intern(Interner)) - } - Some(ValueNs::ConstId(c)) => Some(intern_const_scalar( - ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)), - expected_ty, - )), - // FIXME: With feature(adt_const_params), we also need to consider other things here, e.g. struct constructors. - _ => None, - } -} - -pub(crate) fn unknown_const(ty: Ty) -> Const { - ConstData { - ty, - value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: ConstScalar::Unknown }), - } - .intern(Interner) -} - -pub(crate) fn unknown_const_as_generic(ty: Ty) -> GenericArg { - unknown_const(ty).cast(Interner) -} - -/// Interns a constant scalar with the given type -pub(crate) fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const { - ConstData { ty, value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: value }) } - .intern(Interner) -} - -/// Interns a constant scalar with the given type -pub(crate) fn intern_const_ref( - db: &dyn HirDatabase, - value: &LiteralConstRef, - ty: Ty, - krate: Crate, -) -> Const { - let interner = DbInterner::new_with(db, Some(krate), None); - let layout = || db.layout_of_ty(ty.to_nextsolver(interner), TraitEnvironment::empty(krate)); - let bytes = match value { - LiteralConstRef::Int(i) => { - // FIXME: We should handle failure of layout better. - let size = layout().map(|it| it.size.bytes_usize()).unwrap_or(16); - ConstScalar::Bytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()) - } - LiteralConstRef::UInt(i) => { - let size = layout().map(|it| it.size.bytes_usize()).unwrap_or(16); - ConstScalar::Bytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()) - } - LiteralConstRef::Bool(b) => ConstScalar::Bytes(Box::new([*b as u8]), MemoryMap::default()), - LiteralConstRef::Char(c) => { - ConstScalar::Bytes((*c as u32).to_le_bytes().into(), MemoryMap::default()) - } - LiteralConstRef::Unknown => ConstScalar::Unknown, - }; - intern_const_scalar(bytes, ty) -} 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 a4c19eea162e..c79ff9857898 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -1,8 +1,7 @@ //! The home of `HirDatabase`, which is the Salsa database containing all the //! type inference-related queries. -use base_db::Crate; -use base_db::target::TargetLoadError; +use base_db::{Crate, target::TargetLoadError}; use hir_def::{ AdtId, BlockId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId, @@ -16,13 +15,14 @@ use smallvec::SmallVec; use triomphe::Arc; use crate::{ - Binders, ImplTraitId, ImplTraits, InferenceResult, TraitEnvironment, Ty, TyDefId, ValueTyDefId, + ImplTraitId, InferenceResult, TraitEnvironment, TyDefId, ValueTyDefId, consteval::ConstEvalError, dyn_compatibility::DynCompatibilityViolation, layout::{Layout, LayoutError}, - lower::{Diagnostics, GenericDefaults, GenericPredicates}, + lower::{Diagnostics, GenericDefaults, GenericPredicates, ImplTraits}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, + next_solver::{Const, EarlyBinder, GenericArgs, PolyFnSig, TraitRef, Ty, VariancesOf}, }; #[query_group::query_group] @@ -51,7 +51,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { fn monomorphized_mir_body<'db>( &'db self, def: DefWithBodyId, - subst: crate::next_solver::GenericArgs<'db>, + subst: GenericArgs<'db>, env: Arc>, ) -> Result>, MirLowerError<'db>>; @@ -59,7 +59,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { fn monomorphized_mir_body_for_closure<'db>( &'db self, def: InternedClosureId, - subst: crate::next_solver::GenericArgs<'db>, + subst: GenericArgs<'db>, env: Arc>, ) -> Result>, MirLowerError<'db>>; @@ -75,16 +75,13 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { fn const_eval<'db>( &'db self, def: GeneralConstId, - subst: crate::next_solver::GenericArgs<'db>, + subst: GenericArgs<'db>, trait_env: Option>>, - ) -> Result, ConstEvalError<'db>>; + ) -> Result, ConstEvalError<'db>>; #[salsa::invoke(crate::consteval::const_eval_static_query)] #[salsa::cycle(cycle_result = crate::consteval::const_eval_static_cycle_result)] - fn const_eval_static<'db>( - &'db self, - def: StaticId, - ) -> Result, ConstEvalError<'db>>; + fn const_eval_static<'db>(&'db self, def: StaticId) -> Result, ConstEvalError<'db>>; #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)] #[salsa::cycle(cycle_result = crate::consteval::const_eval_discriminant_cycle_result)] @@ -99,8 +96,8 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { &'db self, env: Arc>, func: FunctionId, - fn_subst: crate::next_solver::GenericArgs<'db>, - ) -> (FunctionId, crate::next_solver::GenericArgs<'db>); + fn_subst: GenericArgs<'db>, + ) -> (FunctionId, GenericArgs<'db>); // endregion:mir @@ -109,7 +106,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { fn layout_of_adt<'db>( &'db self, def: AdtId, - args: crate::next_solver::GenericArgs<'db>, + args: GenericArgs<'db>, trait_env: Arc>, ) -> Result, LayoutError>; @@ -117,7 +114,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::cycle(cycle_result = crate::layout::layout_of_ty_cycle_result)] fn layout_of_ty<'db>( &'db self, - ty: crate::next_solver::Ty<'db>, + ty: Ty<'db>, env: Arc>, ) -> Result, LayoutError>; @@ -127,149 +124,130 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)] fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option; - #[salsa::invoke(crate::lower_nextsolver::ty_query)] + #[salsa::invoke(crate::lower::ty_query)] #[salsa::transparent] - fn ty<'db>( - &'db self, - def: TyDefId, - ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; + fn ty<'db>(&'db self, def: TyDefId) -> EarlyBinder<'db, Ty<'db>>; - #[salsa::invoke(crate::lower_nextsolver::type_for_type_alias_with_diagnostics_query)] - #[salsa::cycle(cycle_result = crate::lower_nextsolver::type_for_type_alias_with_diagnostics_cycle_result)] + #[salsa::invoke(crate::lower::type_for_type_alias_with_diagnostics_query)] + #[salsa::cycle(cycle_result = crate::lower::type_for_type_alias_with_diagnostics_cycle_result)] fn type_for_type_alias_with_diagnostics<'db>( &'db self, def: TypeAliasId, - ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics); + ) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics); /// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is /// a `StructId` or `EnumVariantId` with a record constructor. - #[salsa::invoke(crate::lower_nextsolver::value_ty_query)] - fn value_ty<'db>( - &'db self, - def: ValueTyDefId, - ) -> Option>>; + #[salsa::invoke(crate::lower::value_ty_query)] + fn value_ty<'db>(&'db self, def: ValueTyDefId) -> Option>>; - #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_with_diagnostics_query)] - #[salsa::cycle(cycle_result = crate::lower_nextsolver::impl_self_ty_with_diagnostics_cycle_result)] + #[salsa::invoke(crate::lower::impl_self_ty_with_diagnostics_query)] + #[salsa::cycle(cycle_result = crate::lower::impl_self_ty_with_diagnostics_cycle_result)] fn impl_self_ty_with_diagnostics<'db>( &'db self, def: ImplId, - ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics); + ) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics); - #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_query)] + #[salsa::invoke(crate::lower::impl_self_ty_query)] #[salsa::transparent] - fn impl_self_ty<'db>( - &'db self, - def: ImplId, - ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; + fn impl_self_ty<'db>(&'db self, def: ImplId) -> EarlyBinder<'db, Ty<'db>>; // FIXME: Make this a non-interned query. - #[salsa::invoke_interned(crate::lower_nextsolver::const_param_ty_with_diagnostics_query)] - #[salsa::cycle(cycle_result = crate::lower_nextsolver::const_param_ty_with_diagnostics_cycle_result)] - fn const_param_ty_with_diagnostics<'db>( - &'db self, - def: ConstParamId, - ) -> (crate::next_solver::Ty<'db>, Diagnostics); + #[salsa::invoke_interned(crate::lower::const_param_ty_with_diagnostics_query)] + #[salsa::cycle(cycle_result = crate::lower::const_param_ty_with_diagnostics_cycle_result)] + fn const_param_ty_with_diagnostics<'db>(&'db self, def: ConstParamId) + -> (Ty<'db>, Diagnostics); - // FIXME: Make this a non-interned query. - #[salsa::invoke_interned(crate::lower::const_param_ty_query)] - #[salsa::cycle(cycle_result = crate::lower::const_param_ty_cycle_result)] - fn const_param_ty(&self, def: ConstParamId) -> Ty; + #[salsa::invoke(crate::lower::const_param_ty_query)] + #[salsa::transparent] + fn const_param_ty_ns<'db>(&'db self, def: ConstParamId) -> Ty<'db>; - #[salsa::invoke(crate::lower_nextsolver::impl_trait_with_diagnostics_query)] + #[salsa::invoke(crate::lower::impl_trait_with_diagnostics_query)] fn impl_trait_with_diagnostics<'db>( &'db self, def: ImplId, - ) -> Option<( - crate::next_solver::EarlyBinder<'db, crate::next_solver::TraitRef<'db>>, - Diagnostics, - )>; + ) -> Option<(EarlyBinder<'db, TraitRef<'db>>, Diagnostics)>; - #[salsa::invoke(crate::lower_nextsolver::impl_trait_query)] + #[salsa::invoke(crate::lower::impl_trait_query)] #[salsa::transparent] - fn impl_trait<'db>( - &'db self, - def: ImplId, - ) -> Option>>; + fn impl_trait<'db>(&'db self, def: ImplId) -> Option>>; - #[salsa::invoke(crate::lower_nextsolver::field_types_with_diagnostics_query)] + #[salsa::invoke(crate::lower::field_types_with_diagnostics_query)] fn field_types_with_diagnostics<'db>( &'db self, var: VariantId, - ) -> ( - Arc< - ArenaMap< - LocalFieldId, - crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, - >, - >, - Diagnostics, - ); + ) -> (Arc>>>, Diagnostics); #[salsa::invoke(crate::lower::field_types_query)] #[salsa::transparent] - fn field_types(&self, var: VariantId) -> Arc>>; + fn field_types<'db>( + &'db self, + var: VariantId, + ) -> Arc>>>; - #[salsa::invoke(crate::lower_nextsolver::callable_item_signature_query)] + #[salsa::invoke(crate::lower::callable_item_signature_query)] fn callable_item_signature<'db>( &'db self, def: CallableDefId, - ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::PolyFnSig<'db>>; + ) -> EarlyBinder<'db, PolyFnSig<'db>>; #[salsa::invoke(crate::lower::return_type_impl_traits)] - fn return_type_impl_traits(&self, def: FunctionId) -> Option>>; + fn return_type_impl_traits<'db>( + &'db self, + def: FunctionId, + ) -> Option>>>; #[salsa::invoke(crate::lower::type_alias_impl_traits)] - fn type_alias_impl_traits(&self, def: TypeAliasId) -> Option>>; + fn type_alias_impl_traits<'db>( + &'db self, + def: TypeAliasId, + ) -> Option>>>; - #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] - #[salsa::cycle(cycle_result = crate::lower::generic_predicates_for_param_cycle_result)] - fn generic_predicates_for_param( - &self, - def: GenericDefId, - param_id: TypeOrConstParamId, - assoc_name: Option, - ) -> GenericPredicates; - - #[salsa::invoke(crate::lower::generic_predicates_query)] - fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates; - - #[salsa::invoke( - crate::lower_nextsolver::generic_predicates_without_parent_with_diagnostics_query - )] + #[salsa::invoke(crate::lower::generic_predicates_without_parent_with_diagnostics_query)] fn generic_predicates_without_parent_with_diagnostics<'db>( &'db self, def: GenericDefId, - ) -> (crate::lower_nextsolver::GenericPredicates<'db>, Diagnostics); + ) -> (GenericPredicates<'db>, Diagnostics); - #[salsa::invoke(crate::lower_nextsolver::generic_predicates_without_parent_query)] + #[salsa::invoke(crate::lower::generic_predicates_without_parent_query)] #[salsa::transparent] fn generic_predicates_without_parent<'db>( &'db self, def: GenericDefId, - ) -> crate::lower_nextsolver::GenericPredicates<'db>; + ) -> GenericPredicates<'db>; - #[salsa::invoke(crate::lower_nextsolver::trait_environment_for_body_query)] + #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] + #[salsa::cycle(cycle_result = crate::lower::generic_predicates_for_param_cycle_result)] + fn generic_predicates_for_param<'db>( + &'db self, + def: GenericDefId, + param_id: TypeOrConstParamId, + assoc_name: Option, + ) -> GenericPredicates<'db>; + + #[salsa::invoke(crate::lower::generic_predicates_query)] + fn generic_predicates<'db>(&'db self, def: GenericDefId) -> GenericPredicates<'db>; + + #[salsa::invoke(crate::lower::trait_environment_for_body_query)] #[salsa::transparent] fn trait_environment_for_body<'db>(&'db self, def: DefWithBodyId) -> Arc>; - #[salsa::invoke(crate::lower_nextsolver::trait_environment_query)] + #[salsa::invoke(crate::lower::trait_environment_query)] fn trait_environment<'db>(&'db self, def: GenericDefId) -> Arc>; #[salsa::invoke(crate::lower::generic_defaults_with_diagnostics_query)] #[salsa::cycle(cycle_result = crate::lower::generic_defaults_with_diagnostics_cycle_result)] - fn generic_defaults_with_diagnostics( - &self, + fn generic_defaults_with_diagnostics<'db>( + &'db self, def: GenericDefId, - ) -> (GenericDefaults, Diagnostics); + ) -> (GenericDefaults<'db>, Diagnostics); /// This returns an empty list if no parameter has default. /// /// The binders of the returned defaults are only up to (not including) this parameter. #[salsa::invoke(crate::lower::generic_defaults_query)] #[salsa::transparent] - fn generic_defaults(&self, def: GenericDefId) -> GenericDefaults; + fn generic_defaults<'db>(&'db self, def: GenericDefId) -> GenericDefaults<'db>; #[salsa::invoke(InherentImpls::inherent_impls_in_crate_query)] fn inherent_impls_in_crate(&self, krate: Crate) -> Arc; @@ -297,7 +275,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)] fn trait_impls_in_deps(&self, krate: Crate) -> Arc<[Arc]>; - // Interned IDs for Chalk integration + // Interned IDs for solver integration #[salsa::interned] fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId; @@ -313,66 +291,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { // cycle_initial = crate::variance::variances_of_cycle_initial, cycle_result = crate::variance::variances_of_cycle_initial, )] - fn variances_of(&self, def: GenericDefId) -> crate::next_solver::VariancesOf<'_>; - - // next trait solver - - #[salsa::invoke(crate::lower_nextsolver::const_param_ty_query)] - #[salsa::transparent] - fn const_param_ty_ns<'db>(&'db self, def: ConstParamId) -> crate::next_solver::Ty<'db>; - - #[salsa::invoke(crate::lower_nextsolver::field_types_query)] - #[salsa::transparent] - fn field_types_ns<'db>( - &'db self, - var: VariantId, - ) -> Arc< - ArenaMap>>, - >; - - #[salsa::invoke(crate::lower_nextsolver::return_type_impl_traits)] - fn return_type_impl_traits_ns<'db>( - &'db self, - def: FunctionId, - ) -> Option>>>; - - #[salsa::invoke(crate::lower_nextsolver::type_alias_impl_traits)] - fn type_alias_impl_traits_ns<'db>( - &'db self, - def: TypeAliasId, - ) -> Option>>>; - - #[salsa::invoke(crate::lower_nextsolver::generic_predicates_for_param_query)] - #[salsa::cycle(cycle_result = crate::lower_nextsolver::generic_predicates_for_param_cycle_result)] - fn generic_predicates_for_param_ns<'db>( - &'db self, - def: GenericDefId, - param_id: TypeOrConstParamId, - assoc_name: Option, - ) -> crate::lower_nextsolver::GenericPredicates<'db>; - - #[salsa::invoke(crate::lower_nextsolver::generic_predicates_query)] - fn generic_predicates_ns<'db>( - &'db self, - def: GenericDefId, - ) -> crate::lower_nextsolver::GenericPredicates<'db>; - - #[salsa::invoke(crate::lower_nextsolver::generic_defaults_with_diagnostics_query)] - #[salsa::cycle(cycle_result = crate::lower_nextsolver::generic_defaults_with_diagnostics_cycle_result)] - fn generic_defaults_ns_with_diagnostics<'db>( - &'db self, - def: GenericDefId, - ) -> (crate::lower_nextsolver::GenericDefaults<'db>, Diagnostics); - - /// This returns an empty list if no parameter has default. - /// - /// The binders of the returned defaults are only up to (not including) this parameter. - #[salsa::invoke(crate::lower_nextsolver::generic_defaults_query)] - #[salsa::transparent] - fn generic_defaults_ns<'db>( - &'db self, - def: GenericDefId, - ) -> crate::lower_nextsolver::GenericDefaults<'db>; + fn variances_of(&self, def: GenericDefId) -> VariancesOf<'_>; } #[test] 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 f0efadeafcea..fb942e336e65 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 @@ -150,7 +150,7 @@ impl<'a, 'db> MatchCheckCtx<'a, 'db> { ) -> impl Iterator)> { let (_, substs) = ty.as_adt().unwrap(); - let field_tys = self.db.field_types_ns(variant); + let field_tys = self.db.field_types(variant); let fields_len = variant.fields(self.db).fields().len() as u32; (0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).map(move |fid| { 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 2c6cbdd03f13..2b92408f0f6b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -626,7 +626,7 @@ fn write_projection<'db>( // 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 = - f.db.generic_predicates_ns(param.id.parent()) + f.db.generic_predicates(param.id.parent()) .instantiate_identity() .into_iter() .flatten() @@ -902,7 +902,7 @@ fn render_const_scalar_inner<'db>( hir_def::AdtId::StructId(s) => { let data = f.db.struct_signature(s); write!(f, "{}", data.name.display(f.db, f.edition()))?; - let field_types = f.db.field_types_ns(s.into()); + let field_types = f.db.field_types(s.into()); render_variant_after_name( s.fields(f.db), f, @@ -934,7 +934,7 @@ fn render_const_scalar_inner<'db>( .1 .display(f.db, f.edition()) )?; - let field_types = f.db.field_types_ns(var_id.into()); + let field_types = f.db.field_types(var_id.into()); render_variant_after_name( var_id.fields(f.db), f, @@ -1121,7 +1121,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id); if let ImplTraitId::ReturnTypeImplTrait(func, idx) = impl_trait_id { let datas = db - .return_type_impl_traits_ns(func) + .return_type_impl_traits(func) .expect("impl trait id without data"); let data = (*datas).as_ref().map_bound(|rpit| { &rpit.impl_traits[idx.to_nextsolver(interner)].predicates @@ -1353,9 +1353,8 @@ impl<'db> HirDisplay<'db> for Ty<'db> { let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id); match impl_trait_id { ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let datas = db - .return_type_impl_traits_ns(func) - .expect("impl trait id without data"); + let datas = + db.return_type_impl_traits(func).expect("impl trait id without data"); let data = (*datas).as_ref().map_bound(|rpit| { &rpit.impl_traits[idx.to_nextsolver(interner)].predicates }); @@ -1373,9 +1372,8 @@ impl<'db> HirDisplay<'db> for Ty<'db> { // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution } ImplTraitId::TypeAliasImplTrait(alias, idx) => { - let datas = db - .type_alias_impl_traits_ns(alias) - .expect("impl trait id without data"); + let datas = + db.type_alias_impl_traits(alias).expect("impl trait id without data"); let data = (*datas).as_ref().map_bound(|rpit| { &rpit.impl_traits[idx.to_nextsolver(interner)].predicates }); @@ -1501,7 +1499,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } TypeParamProvenance::ArgumentImplTrait => { let bounds = db - .generic_predicates_ns(param.id.parent()) + .generic_predicates(param.id.parent()) .instantiate_identity() .into_iter() .flatten() @@ -1621,7 +1619,7 @@ fn generic_args_sans_defaults<'ga, 'db>( parameters: &'ga [GenericArg<'db>], ) -> &'ga [GenericArg<'db>] { if f.display_kind.is_source_code() || f.omit_verbose_types() { - match generic_def.map(|generic_def_id| f.db.generic_defaults_ns(generic_def_id)) { + match generic_def.map(|generic_def_id| f.db.generic_defaults(generic_def_id)) { None => parameters, Some(default_parameters) => { let should_show = |arg: GenericArg<'db>, i: usize| match default_parameters.get(i) { 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 aaf274799c63..b09d1fb196c4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -85,7 +85,7 @@ fn has_drop_glue_impl<'db>( { return DropGlue::None; } - db.field_types_ns(id.into()) + db.field_types(id.into()) .iter() .map(|(_, field_ty)| { has_drop_glue_impl( @@ -105,7 +105,7 @@ fn has_drop_glue_impl<'db>( .variants .iter() .map(|&(variant, _, _)| { - db.field_types_ns(variant.into()) + db.field_types(variant.into()) .iter() .map(|(_, field_ty)| { has_drop_glue_impl( 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 e35a79870329..437141e41db9 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 @@ -18,10 +18,10 @@ use smallvec::SmallVec; use crate::{ ImplTraitId, db::{HirDatabase, InternedOpaqueTyId}, - lower_nextsolver::associated_ty_item_bounds, + lower::associated_ty_item_bounds, next_solver::{ - Clause, Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, TraitRef, - TypingMode, infer::DbInternerInferExt, mk_param, + Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, Goal, ParamEnv, ParamTy, + SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, infer::DbInternerInferExt, mk_param, }, traits::next_trait_solve_in_ctxt, }; @@ -136,7 +136,7 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b }; let interner = DbInterner::new_with(db, Some(krate), None); - let predicates = db.generic_predicates_ns(def); + let predicates = db.generic_predicates(def); // FIXME: We should use `explicit_predicates_of` here, which hasn't been implemented to // rust-analyzer yet // https://github.com/rust-lang/rust/blob/ddaf12390d3ffb7d5ba74491a48f3cd528e5d777/compiler/rustc_hir_analysis/src/collect/predicates_of.rs#L490 @@ -162,7 +162,7 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b // but we don't have good way to render such locations. // So, just return single boolean value for existence of such `Self` reference fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { - db.generic_predicates_ns(trait_.into()) + db.generic_predicates(trait_.into()) .iter() .any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No)) } @@ -378,7 +378,7 @@ where }) = pred && let trait_data = db.trait_signature(pred_trait_ref.def_id.0) && trait_data.flags.contains(TraitFlags::AUTO) - && let rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0, .. }) = + && let rustc_type_ir::TyKind::Param(ParamTy { index: 0, .. }) = pred_trait_ref.self_ty().kind() { continue; @@ -397,10 +397,7 @@ fn receiver_is_dispatchable<'db>( db: &dyn HirDatabase, trait_: TraitId, func: FunctionId, - sig: &crate::next_solver::EarlyBinder< - 'db, - crate::next_solver::Binder<'db, rustc_type_ir::FnSig>>, - >, + sig: &EarlyBinder<'db, Binder<'db, rustc_type_ir::FnSig>>>, ) -> bool { let sig = sig.instantiate_identity(); @@ -409,10 +406,8 @@ fn receiver_is_dispatchable<'db>( parent: trait_.into(), local_id: LocalTypeOrConstParamId::from_raw(la_arena::RawIdx::from_u32(0)), }); - let self_param_ty = crate::next_solver::Ty::new( - interner, - rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0, id: self_param_id }), - ); + let self_param_ty = + Ty::new(interner, rustc_type_ir::TyKind::Param(ParamTy { index: 0, id: self_param_id })); // `self: Self` can't be dispatched on, but this is already considered dyn-compatible // See rustc's comment on https://github.com/rust-lang/rust/blob/3f121b9461cce02a703a0e7e450568849dfaa074/compiler/rustc_trait_selection/src/traits/object_safety.rs#L433-L437 @@ -440,12 +435,12 @@ fn receiver_is_dispatchable<'db>( // Type `U` // FIXME: That seems problematic to fake a generic param like that? - let unsized_self_ty = crate::next_solver::Ty::new_param(interner, self_param_id, u32::MAX); + let unsized_self_ty = Ty::new_param(interner, self_param_id, u32::MAX); // `Receiver[Self => U]` let unsized_receiver_ty = receiver_for_self_ty(interner, func, receiver_ty, unsized_self_ty); let param_env = { - let generic_predicates = &*db.generic_predicates_ns(func.into()); + let generic_predicates = &*db.generic_predicates(func.into()); // Self: Unsize let unsize_predicate = @@ -475,7 +470,7 @@ 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 = crate::next_solver::Goal::new(interner, param_env, predicate); + let goal = Goal::new(interner, param_env, predicate); let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); // the receiver is dispatchable iff the obligation holds @@ -486,26 +481,19 @@ fn receiver_is_dispatchable<'db>( fn receiver_for_self_ty<'db>( interner: DbInterner<'db>, func: FunctionId, - receiver_ty: crate::next_solver::Ty<'db>, - self_ty: crate::next_solver::Ty<'db>, -) -> crate::next_solver::Ty<'db> { - let args = crate::next_solver::GenericArgs::for_item( - interner, - SolverDefId::FunctionId(func), - |index, kind, _| { - if index == 0 { self_ty.into() } else { mk_param(interner, index, kind) } - }, - ); + receiver_ty: Ty<'db>, + self_ty: Ty<'db>, +) -> Ty<'db> { + let args = GenericArgs::for_item(interner, SolverDefId::FunctionId(func), |index, kind, _| { + if index == 0 { self_ty.into() } else { mk_param(interner, index, kind) } + }); - crate::next_solver::EarlyBinder::bind(receiver_ty).instantiate(interner, args) + EarlyBinder::bind(receiver_ty).instantiate(interner, args) } fn contains_illegal_impl_trait_in_trait<'db>( db: &'db dyn HirDatabase, - sig: &crate::next_solver::EarlyBinder< - 'db, - crate::next_solver::Binder<'db, rustc_type_ir::FnSig>>, - >, + sig: &EarlyBinder<'db, Binder<'db, rustc_type_ir::FnSig>>>, ) -> Option { struct OpaqueTypeCollector(FxHashSet); 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 3ca5f0dcb247..26e03aa01a1d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -9,7 +9,6 @@ //! where parent follows the same scheme. use std::ops; -use chalk_ir::{BoundVar, DebruijnIndex, cast::Cast as _}; use hir_def::{ ConstParamId, GenericDefId, GenericParamId, ItemContainerId, LifetimeParamId, Lookup, TypeOrConstParamId, TypeParamId, @@ -23,8 +22,6 @@ use hir_def::{ use itertools::chain; use triomphe::Arc; -use crate::{Interner, Substitution, db::HirDatabase, lt_to_placeholder_idx, to_placeholder_idx}; - pub fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics { let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def))); let (params, store) = db.generic_params_and_store(def); @@ -230,50 +227,6 @@ impl Generics { pub(crate) fn parent_generics(&self) -> Option<&Generics> { self.parent_generics.as_deref() } - - pub(crate) fn parent_or_self(&self) -> &Generics { - self.parent_generics.as_deref().unwrap_or(self) - } - - /// Returns a Substitution that replaces each parameter by a bound variable. - pub(crate) fn bound_vars_subst( - &self, - db: &dyn HirDatabase, - debruijn: DebruijnIndex, - ) -> Substitution { - Substitution::from_iter( - Interner, - self.iter_id().enumerate().map(|(idx, id)| match id { - GenericParamId::ConstParamId(id) => BoundVar::new(debruijn, idx) - .to_const(Interner, db.const_param_ty(id)) - .cast(Interner), - GenericParamId::TypeParamId(_) => { - BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner) - } - GenericParamId::LifetimeParamId(_) => { - BoundVar::new(debruijn, idx).to_lifetime(Interner).cast(Interner) - } - }), - ) - } - - /// Returns a Substitution that replaces each parameter by itself (i.e. `Ty::Param`). - pub(crate) fn placeholder_subst(&self, db: &dyn HirDatabase) -> Substitution { - Substitution::from_iter( - Interner, - self.iter_id().enumerate().map(|(index, id)| match id { - GenericParamId::TypeParamId(id) => { - to_placeholder_idx(db, id.into(), index as u32).to_ty(Interner).cast(Interner) - } - GenericParamId::ConstParamId(id) => to_placeholder_idx(db, id.into(), index as u32) - .to_const(Interner, db.const_param_ty(id)) - .cast(Interner), - GenericParamId::LifetimeParamId(id) => { - lt_to_placeholder_idx(db, id, index as u32).to_lifetime(Interner).cast(Interner) - } - }), - ) - } } pub(crate) fn trait_self_param_idx(db: &dyn DefDatabase, def: GenericDefId) -> Option { 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 b2dd90a3d0df..9891f3f248bd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -63,8 +63,9 @@ use crate::{ expr::ExprIsRead, unify::InferenceTable, }, - lower::diagnostics::TyLoweringDiagnostic, - lower_nextsolver::{ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind}, + lower::{ + ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic, + }, mir::MirSpan, next_solver::{ AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Region, Ty, TyKind, @@ -1159,7 +1160,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { }, ); let return_ty = self.insert_type_vars(return_ty); - if let Some(rpits) = self.db.return_type_impl_traits_ns(func) { + if let Some(rpits) = self.db.return_type_impl_traits(func) { let mut mode = ImplTraitReplacingMode::ReturnPosition(FxHashSet::default()); let result = self.insert_inference_vars_for_impl_trait(return_ty, &mut mode); if let ImplTraitReplacingMode::ReturnPosition(taits) = mode { @@ -1234,7 +1235,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } return ty; } - (self.db.return_type_impl_traits_ns(def), idx) + (self.db.return_type_impl_traits(def), idx) } ImplTraitId::TypeAliasImplTrait(def, idx) => { if let ImplTraitReplacingMode::ReturnPosition(taits) = mode { @@ -1243,7 +1244,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { taits.insert(ty); return ty; } - (self.db.type_alias_impl_traits_ns(def), idx) + (self.db.type_alias_impl_traits(def), idx) } _ => unreachable!(), }; @@ -1604,8 +1605,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { match ty.kind() { TyKind::Adt(adt_def, substs) => match adt_def.def_id().0 { AdtId::StructId(struct_id) => { - match self.db.field_types_ns(struct_id.into()).values().next_back().copied() - { + match self.db.field_types(struct_id.into()).values().next_back().copied() { Some(field) => { ty = field.instantiate(self.interner(), substs); } 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 990281a7c896..c128977d7b08 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 @@ -394,7 +394,7 @@ fn pointer_kind<'db>( let struct_data = id.fields(ctx.db); if let Some((last_field, _)) = struct_data.fields().iter().last() { let last_field_ty = - ctx.db.field_types_ns(id.into())[last_field].instantiate(ctx.interner(), subst); + ctx.db.field_types(id.into())[last_field].instantiate(ctx.interner(), subst); pointer_kind(last_field_ty, ctx) } else { Ok(Some(PointerKind::Thin)) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs index 39e70c262a24..844eb02ab0d4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs @@ -15,8 +15,8 @@ use la_arena::{Idx, RawIdx}; use crate::{ InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnostic, db::HirDatabase, - lower_nextsolver::path::{PathDiagnosticCallback, PathLoweringContext}, - lower_nextsolver::{LifetimeElisionKind, TyLoweringContext}, + lower::path::{PathDiagnosticCallback, PathLoweringContext}, + lower::{LifetimeElisionKind, TyLoweringContext}, }; // Unfortunately, this struct needs to use interior mutability (but we encapsulate it) 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 e1964608a3f0..efb7244ff637 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 @@ -37,7 +37,7 @@ use crate::{ pat::contains_explicit_ref_binding, }, lang_items::lang_items_for_bin_op, - lower_nextsolver::{ + lower::{ LifetimeElisionKind, lower_mutability, path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, }, @@ -564,7 +564,7 @@ impl<'db> InferenceContext<'_, 'db> { match def_id { _ if fields.is_empty() => {} Some(def) => { - let field_types = self.db.field_types_ns(def); + let field_types = self.db.field_types(def); let variant_data = def.fields(self.db); let visibilities = self.db.field_visibilities(def); for field in fields.iter() { @@ -1622,7 +1622,7 @@ impl<'db> InferenceContext<'_, 'db> { } return None; } - let ty = self.db.field_types_ns(field_id.parent)[field_id.local_id] + let ty = self.db.field_types(field_id.parent)[field_id.local_id] .instantiate(interner, parameters); Some((Either::Left(field_id), ty)) }); @@ -1637,7 +1637,7 @@ impl<'db> InferenceContext<'_, 'db> { None => { let (field_id, subst) = private_field?; let adjustments = autoderef.adjust_steps(); - let ty = self.db.field_types_ns(field_id.parent)[field_id.local_id] + let ty = self.db.field_types(field_id.parent)[field_id.local_id] .instantiate(self.interner(), subst); let ty = self.process_remote_user_written_ty(ty); @@ -2320,7 +2320,7 @@ impl<'db> InferenceContext<'_, '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 = - self.db.generic_predicates_ns(GenericDefId::from_callable(self.db, fn_def.0)); + self.db.generic_predicates(GenericDefId::from_callable(self.db, fn_def.0)); if let Some(predicates) = generic_predicates.instantiate(self.interner(), parameters) { let interner = self.interner(); let param_env = self.table.trait_env.env; 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 9edbc9dda0f1..71a9c94bf5e5 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 @@ -18,7 +18,7 @@ use crate::next_solver::{GenericArgs, TraitRef}; use crate::{ Adjust, Adjustment, AutoBorrow, OverloadedDeref, infer::{Expectation, InferenceContext, expr::ExprIsRead}, - lower_nextsolver::lower_mutability, + lower::lower_mutability, next_solver::TyKind, }; 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 61255d31d281..8019844b5df3 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 @@ -19,7 +19,7 @@ use crate::{ AllowTwoPhase, BindingMode, Expectation, InferenceContext, TypeMismatch, coerce::CoerceNever, expr::ExprIsRead, }, - lower_nextsolver::lower_mutability, + lower::lower_mutability, next_solver::{GenericArgs, Ty, TyKind}, }; @@ -59,7 +59,7 @@ impl<'db> InferenceContext<'_, 'db> { match def { _ if subs.is_empty() => {} Some(def) => { - let field_types = self.db.field_types_ns(def); + let field_types = self.db.field_types(def); let variant_data = def.fields(self.db); let visibilities = self.db.field_visibilities(def); @@ -128,7 +128,7 @@ impl<'db> InferenceContext<'_, 'db> { match def { _ if subs.len() == 0 => {} Some(def) => { - let field_types = self.db.field_types_ns(def); + let field_types = self.db.field_types(def); let variant_data = def.fields(self.db); let visibilities = self.db.field_visibilities(def); 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 84d17db6c663..2dae7cb04ffa 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 @@ -13,7 +13,7 @@ use crate::{ InferenceDiagnostic, ValueTyDefId, generics::generics, infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, - lower_nextsolver::LifetimeElisionKind, + lower::LifetimeElisionKind, method_resolution::{self, VisibleFromModule}, next_solver::{ GenericArg, GenericArgs, TraitRef, Ty, @@ -221,7 +221,7 @@ impl<'db> InferenceContext<'_, 'db> { def: GenericDefId, subst: GenericArgs<'db>, ) { - let predicates = self.db.generic_predicates_ns(def); + let predicates = self.db.generic_predicates(def); let interner = self.interner(); let param_env = self.table.trait_env.env; if let Some(predicates) = predicates.instantiate(self.interner(), subst) { 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 beb26f7d6890..a18cdda559d0 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 @@ -800,7 +800,7 @@ impl<'db> InferenceTable<'db> { 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_ns(id.into())[last_field] + let last_field_ty = self.db.field_types(id.into())[last_field] .instantiate(self.interner(), subst); if structs.contains(&ty) { // A struct recursively contains itself as a tail field somewhere. 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 7ebc2df6f75d..8aed2608d6cd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -156,7 +156,7 @@ impl<'a, 'db> UninhabitedFrom<'a, 'db> { } let is_enum = matches!(variant, VariantId::EnumVariantId(..)); - let field_tys = self.db().field_types_ns(variant); + let field_tys = self.db().field_types(variant); let field_vis = if is_enum { None } else { Some(self.db().field_visibilities(variant)) }; for (fid, _) in fields.iter() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs deleted file mode 100644 index 57ef5523b433..000000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs +++ /dev/null @@ -1,403 +0,0 @@ -//! Implementation of the Chalk `Interner` trait, which allows customizing the -//! representation of the various objects Chalk deals with (types, goals etc.). - -use crate::{ - AliasTy, CanonicalVarKind, CanonicalVarKinds, ClosureId, Const, ConstData, ConstScalar, FnAbi, - FnDefId, GenericArg, GenericArgData, Goal, GoalData, InEnvironment, Lifetime, LifetimeData, - OpaqueTy, OpaqueTyId, ProgramClause, ProjectionTy, QuantifiedWhereClause, - QuantifiedWhereClauses, Substitution, Ty, TyKind, VariableKind, chalk_db, tls, -}; -use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variance}; -use hir_def::TypeAliasId; -use intern::{Interned, impl_internable}; -use smallvec::SmallVec; -use std::fmt; -use triomphe::Arc; - -type TyData = chalk_ir::TyData; -type VariableKinds = chalk_ir::VariableKinds; -type Goals = chalk_ir::Goals; -type ProgramClauseData = chalk_ir::ProgramClauseData; -type Constraint = chalk_ir::Constraint; -type Constraints = chalk_ir::Constraints; -type ProgramClauses = chalk_ir::ProgramClauses; - -#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] -pub struct Interner; - -#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] -pub struct InternedWrapper(pub(crate) T); - -impl fmt::Debug for InternedWrapper { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } -} - -#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] -pub struct InternedWrapperNoDebug(pub(crate) T); - -impl std::ops::Deref for InternedWrapper { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl_internable!( - InternedWrapper>, - InternedWrapper>, - InternedWrapper, - InternedWrapper, - InternedWrapper, - InternedWrapper, - InternedWrapper>, - InternedWrapper>, - InternedWrapper>, - InternedWrapper>, -); - -impl chalk_ir::interner::Interner for Interner { - type InternedType = Interned>; - type InternedLifetime = Interned>; - type InternedConst = Interned>; - type InternedConcreteConst = ConstScalar; - type InternedGenericArg = GenericArgData; - // We could do the following, but that saves "only" 20mb on self while increasing inference - // time by ~2.5% - // type InternedGoal = Interned>; - type InternedGoal = Arc; - type InternedGoals = Vec; - type InternedSubstitution = Interned>>; - type InternedProgramClauses = Interned>>; - type InternedProgramClause = ProgramClauseData; - type InternedQuantifiedWhereClauses = Interned>>; - type InternedVariableKinds = Interned>>; - type InternedCanonicalVarKinds = Interned>>; - type InternedConstraints = Vec>; - type InternedVariances = SmallVec<[Variance; 16]>; - type DefId = salsa::Id; - type InternedAdtId = hir_def::AdtId; - type Identifier = TypeAliasId; - type FnAbi = FnAbi; - - fn debug_adt_id( - type_kind_id: chalk_db::AdtId, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_struct_id(type_kind_id, fmt))) - } - - fn debug_trait_id( - type_kind_id: chalk_db::TraitId, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_trait_id(type_kind_id, fmt))) - } - - fn debug_assoc_type_id( - id: chalk_db::AssocTypeId, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt))) - } - - fn debug_opaque_ty_id( - opaque_ty_id: OpaqueTyId, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "OpaqueTy#{:?}", opaque_ty_id.0)) - } - - fn debug_fn_def_id(fn_def_id: FnDefId, fmt: &mut fmt::Formatter<'_>) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_fn_def_id(fn_def_id, fmt))) - } - - fn debug_closure_id( - _fn_def_id: ClosureId, - _fmt: &mut fmt::Formatter<'_>, - ) -> Option { - None - } - - fn debug_alias(alias: &AliasTy, fmt: &mut fmt::Formatter<'_>) -> Option { - use std::fmt::Debug; - match alias { - AliasTy::Projection(projection_ty) => Interner::debug_projection_ty(projection_ty, fmt), - AliasTy::Opaque(opaque_ty) => Some(opaque_ty.fmt(fmt)), - } - } - - fn debug_projection_ty( - proj: &ProjectionTy, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt))) - .or_else(|| Some(fmt.write_str("ProjectionTy"))) - } - - fn debug_opaque_ty(opaque_ty: &OpaqueTy, fmt: &mut fmt::Formatter<'_>) -> Option { - Some(write!(fmt, "{:?}", opaque_ty.opaque_ty_id)) - } - - fn debug_ty(ty: &Ty, fmt: &mut fmt::Formatter<'_>) -> Option { - Some(write!(fmt, "{:?}", ty.data(Interner))) - } - - fn debug_lifetime(lifetime: &Lifetime, fmt: &mut fmt::Formatter<'_>) -> Option { - Some(write!(fmt, "{:?}", lifetime.data(Interner))) - } - - fn debug_const(constant: &Const, fmt: &mut fmt::Formatter<'_>) -> Option { - Some(write!(fmt, "{:?}", constant.data(Interner))) - } - - fn debug_generic_arg( - parameter: &GenericArg, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "{:?}", parameter.data(Interner).inner_debug())) - } - - fn debug_variable_kinds( - variable_kinds: &VariableKinds, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "{:?}", variable_kinds.as_slice(Interner))) - } - - fn debug_variable_kinds_with_angles( - variable_kinds: &VariableKinds, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "{:?}", variable_kinds.inner_debug(Interner))) - } - - fn debug_canonical_var_kinds( - canonical_var_kinds: &CanonicalVarKinds, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "{:?}", canonical_var_kinds.as_slice(Interner))) - } - fn debug_goal(goal: &Goal, fmt: &mut fmt::Formatter<'_>) -> Option { - let goal_data = goal.data(Interner); - Some(write!(fmt, "{goal_data:?}")) - } - fn debug_goals(goals: &Goals, fmt: &mut fmt::Formatter<'_>) -> Option { - Some(write!(fmt, "{:?}", goals.debug(Interner))) - } - fn debug_program_clause_implication( - pci: &ProgramClauseImplication, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "{:?}", pci.debug(Interner))) - } - fn debug_program_clause( - clause: &ProgramClause, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "{:?}", clause.data(Interner))) - } - fn debug_program_clauses( - clauses: &ProgramClauses, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "{:?}", clauses.as_slice(Interner))) - } - fn debug_substitution( - substitution: &Substitution, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "{:?}", substitution.debug(Interner))) - } - fn debug_separator_trait_ref( - separator_trait_ref: &SeparatorTraitRef<'_, Interner>, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "{:?}", separator_trait_ref.debug(Interner))) - } - - fn debug_quantified_where_clauses( - clauses: &QuantifiedWhereClauses, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "{:?}", clauses.as_slice(Interner))) - } - - fn debug_constraints( - _clauses: &Constraints, - _fmt: &mut fmt::Formatter<'_>, - ) -> Option { - None - } - - fn intern_ty(self, kind: TyKind) -> Self::InternedType { - let flags = kind.compute_flags(self); - Interned::new(InternedWrapper(TyData { kind, flags })) - } - - fn ty_data(self, ty: &Self::InternedType) -> &TyData { - &ty.0 - } - - fn intern_lifetime(self, lifetime: LifetimeData) -> Self::InternedLifetime { - Interned::new(InternedWrapper(lifetime)) - } - - fn lifetime_data(self, lifetime: &Self::InternedLifetime) -> &LifetimeData { - &lifetime.0 - } - - fn intern_const(self, constant: ConstData) -> Self::InternedConst { - Interned::new(InternedWrapper(constant)) - } - - fn const_data(self, constant: &Self::InternedConst) -> &ConstData { - &constant.0 - } - - fn const_eq( - self, - _ty: &Self::InternedType, - c1: &Self::InternedConcreteConst, - c2: &Self::InternedConcreteConst, - ) -> bool { - !matches!(c1, ConstScalar::Bytes(..)) || !matches!(c2, ConstScalar::Bytes(..)) || (c1 == c2) - } - - fn intern_generic_arg(self, parameter: GenericArgData) -> Self::InternedGenericArg { - parameter - } - - fn generic_arg_data(self, parameter: &Self::InternedGenericArg) -> &GenericArgData { - parameter - } - - fn intern_goal(self, goal: GoalData) -> Self::InternedGoal { - Arc::new(goal) - } - - fn goal_data(self, goal: &Self::InternedGoal) -> &GoalData { - goal - } - - fn intern_goals( - self, - data: impl IntoIterator>, - ) -> Result { - // let hash = - // std::hash::BuildHasher::hash_one(&BuildHasherDefault::::default(), &goal); - // Interned::new(InternedWrapper(PreHashedWrapper(goal, hash))) - data.into_iter().collect() - } - - fn goals_data(self, goals: &Self::InternedGoals) -> &[Goal] { - goals - } - - fn intern_substitution( - self, - data: impl IntoIterator>, - ) -> Result { - Ok(Interned::new(InternedWrapper(data.into_iter().collect::>()?))) - } - - fn substitution_data(self, substitution: &Self::InternedSubstitution) -> &[GenericArg] { - &substitution.as_ref().0 - } - - fn intern_program_clause(self, data: ProgramClauseData) -> Self::InternedProgramClause { - data - } - - fn program_clause_data(self, clause: &Self::InternedProgramClause) -> &ProgramClauseData { - clause - } - - fn intern_program_clauses( - self, - data: impl IntoIterator>, - ) -> Result { - Ok(Interned::new(InternedWrapper(data.into_iter().collect::>()?))) - } - - fn program_clauses_data(self, clauses: &Self::InternedProgramClauses) -> &[ProgramClause] { - clauses - } - - fn intern_quantified_where_clauses( - self, - data: impl IntoIterator>, - ) -> Result { - Ok(Interned::new(InternedWrapper(data.into_iter().collect::>()?))) - } - - fn quantified_where_clauses_data( - self, - clauses: &Self::InternedQuantifiedWhereClauses, - ) -> &[QuantifiedWhereClause] { - clauses - } - - fn intern_generic_arg_kinds( - self, - data: impl IntoIterator>, - ) -> Result { - Ok(Interned::new(InternedWrapper(data.into_iter().collect::>()?))) - } - - fn variable_kinds_data(self, parameter_kinds: &Self::InternedVariableKinds) -> &[VariableKind] { - ¶meter_kinds.as_ref().0 - } - - fn intern_canonical_var_kinds( - self, - data: impl IntoIterator>, - ) -> Result { - Ok(Interned::new(InternedWrapper(data.into_iter().collect::>()?))) - } - - fn canonical_var_kinds_data( - self, - canonical_var_kinds: &Self::InternedCanonicalVarKinds, - ) -> &[CanonicalVarKind] { - canonical_var_kinds - } - fn intern_constraints( - self, - data: impl IntoIterator, E>>, - ) -> Result { - data.into_iter().collect() - } - fn constraints_data( - self, - constraints: &Self::InternedConstraints, - ) -> &[InEnvironment] { - constraints - } - - fn intern_variances( - self, - data: impl IntoIterator>, - ) -> Result { - data.into_iter().collect::>() - } - - fn variances_data(self, variances: &Self::InternedVariances) -> &[Variance] { - variances - } -} - -impl chalk_ir::interner::HasInterner for Interner { - type Interner = Self; -} - -#[macro_export] -macro_rules! has_interner { - ($t:ty) => { - impl HasInterner for $t { - type Interner = $crate::Interner; - } - }; -} 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 a857602fa08a..fc0b9d30b333 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -138,7 +138,7 @@ fn layout_of_simd_ty<'db>( // * #[repr(simd)] struct S([T; 4]) // // where T is a primitive scalar (integer/float/pointer). - let fields = db.field_types_ns(id.into()); + let fields = db.field_types(id.into()); let mut fields = fields.iter(); let Some(TyKind::Array(e_ty, e_len)) = fields .next() @@ -401,7 +401,7 @@ fn field_ty<'a>( fd: LocalFieldId, args: &GenericArgs<'a>, ) -> Ty<'a> { - db.field_types_ns(def)[fd].instantiate(DbInterner::new_with(db, None, None), args) + db.field_types(def)[fd].instantiate(DbInterner::new_with(db, None, None), args) } fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar { 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 b698fd9a1454..536c81ab03b2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -23,23 +23,15 @@ extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver; extern crate self as hir_ty; -mod builder; -mod chalk_db; -mod chalk_ext; mod infer; mod inhabitedness; -mod interner; mod lower; -mod lower_nextsolver; -mod mapping; pub mod next_solver; mod target_feature; -mod tls; mod utils; pub mod autoderef; pub mod consteval; -mod consteval_chalk; pub mod db; pub mod diagnostics; pub mod display; @@ -61,16 +53,11 @@ mod variance; use std::hash::Hash; -use chalk_ir::{ - VariableKinds, - fold::{Shift, TypeFoldable}, - interner::HasInterner, -}; -use hir_def::{CallableDefId, GeneralConstId, TypeOrConstParamId, hir::ExprId, type_ref::Rawness}; +use hir_def::{CallableDefId, TypeOrConstParamId, hir::ExprId, type_ref::Rawness}; use hir_expand::name::Name; use indexmap::{IndexMap, map::Entry}; use intern::{Symbol, sym}; -use la_arena::{Arena, Idx}; +use la_arena::Idx; use mir::{MirEvalError, VTableMap}; use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; use rustc_type_ir::{ @@ -82,13 +69,14 @@ use traits::FnTrait; use triomphe::Arc; use crate::{ - builder::TyBuilder, - chalk_ext::*, db::HirDatabase, display::{DisplayTarget, HirDisplay}, - generics::Generics, infer::unify::InferenceTable, - next_solver::DbInterner, + next_solver::{ + AliasTy, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, Canonical, + CanonicalVarKind, CanonicalVars, Const, ConstKind, DbInterner, FnSig, PolyFnSig, Predicate, + Region, RegionKind, TraitRef, Ty, TyKind, Tys, abi, + }, }; pub use autoderef::autoderef; @@ -99,15 +87,9 @@ pub use infer::{ closure::analysis::{CaptureKind, CapturedItem}, could_coerce, could_unify, could_unify_deeply, }; -pub use interner::Interner; -pub use lower::{ImplTraitLoweringMode, ParamLoweringMode, TyDefId, ValueTyDefId, diagnostics::*}; -pub use lower_nextsolver::{ - LifetimeElisionKind, TyLoweringContext, associated_type_shorthand_candidates, -}; -pub use mapping::{ - ToChalk, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, - lt_from_placeholder_idx, lt_to_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, - to_foreign_def_id, to_placeholder_idx, to_placeholder_idx_no_index, +pub use lower::{ + LifetimeElisionKind, TyDefId, TyLoweringContext, ValueTyDefId, + associated_type_shorthand_candidates, diagnostics::*, }; pub use method_resolution::check_orphan_rules; pub use next_solver::interner::{attach_db, attach_db_allow_change, with_attached_db}; @@ -118,76 +100,6 @@ pub use utils::{ is_fn_unsafe_to_call, target_feature_is_safe_in_target, }; -use chalk_ir::{BoundVar, DebruijnIndex, Safety, Scalar}; - -pub(crate) type ForeignDefId = chalk_ir::ForeignDefId; -pub(crate) type AssocTypeId = chalk_ir::AssocTypeId; -pub(crate) type FnDefId = chalk_ir::FnDefId; -pub(crate) type ClosureId = chalk_ir::ClosureId; -pub(crate) type OpaqueTyId = chalk_ir::OpaqueTyId; -pub(crate) type PlaceholderIndex = chalk_ir::PlaceholderIndex; - -pub(crate) type CanonicalVarKinds = chalk_ir::CanonicalVarKinds; - -pub(crate) type VariableKind = chalk_ir::VariableKind; -/// Represents generic parameters and an item bound by them. When the item has parent, the binders -/// also contain the generic parameters for its parent. See chalk's documentation for details. -/// -/// One thing to keep in mind when working with `Binders` (and `Substitution`s, which represent -/// generic arguments) in rust-analyzer is that the ordering within *is* significant - the generic -/// parameters/arguments for an item MUST come before those for its parent. This is to facilitate -/// the integration with chalk-solve, which mildly puts constraints as such. See #13335 for its -/// motivation in detail. -pub(crate) type Binders = chalk_ir::Binders; -/// Interned list of generic arguments for an item. When an item has parent, the `Substitution` for -/// it contains generic arguments for both its parent and itself. See chalk's documentation for -/// details. -/// -/// See `Binders` for the constraint on the ordering. -pub(crate) type Substitution = chalk_ir::Substitution; -pub(crate) type GenericArg = chalk_ir::GenericArg; -pub(crate) type GenericArgData = chalk_ir::GenericArgData; - -pub(crate) type Ty = chalk_ir::Ty; -pub type TyKind = chalk_ir::TyKind; -pub(crate) type DynTy = chalk_ir::DynTy; -pub(crate) type FnPointer = chalk_ir::FnPointer; -pub(crate) use chalk_ir::FnSubst; // a re-export so we don't lose the tuple constructor - -pub type AliasTy = chalk_ir::AliasTy; - -pub(crate) type ProjectionTy = chalk_ir::ProjectionTy; -pub(crate) type OpaqueTy = chalk_ir::OpaqueTy; - -pub(crate) type Lifetime = chalk_ir::Lifetime; -pub(crate) type LifetimeData = chalk_ir::LifetimeData; -pub(crate) type LifetimeOutlives = chalk_ir::LifetimeOutlives; - -pub(crate) type ConstValue = chalk_ir::ConstValue; - -pub(crate) type Const = chalk_ir::Const; -pub(crate) type ConstData = chalk_ir::ConstData; - -pub(crate) type TraitRef = chalk_ir::TraitRef; -pub(crate) type QuantifiedWhereClause = Binders; -pub(crate) type Canonical = chalk_ir::Canonical; - -pub(crate) type ChalkTraitId = chalk_ir::TraitId; -pub(crate) type QuantifiedWhereClauses = chalk_ir::QuantifiedWhereClauses; - -pub(crate) type FnSig = chalk_ir::FnSig; - -pub(crate) type InEnvironment = chalk_ir::InEnvironment; -pub type AliasEq = chalk_ir::AliasEq; -pub type WhereClause = chalk_ir::WhereClause; - -pub(crate) type DomainGoal = chalk_ir::DomainGoal; -pub(crate) type Goal = chalk_ir::Goal; - -pub(crate) type CanonicalVarKind = chalk_ir::CanonicalVarKind; -pub(crate) type GoalData = chalk_ir::GoalData; -pub(crate) type ProgramClause = chalk_ir::ProgramClause; - /// A constant can have reference to other things. Memory map job is holding /// the necessary bits of memory of the const eval session to keep the constant /// meaningful. @@ -221,7 +133,7 @@ impl ComplexMemoryMap<'_> { } impl<'db> MemoryMap<'db> { - pub fn vtable_ty(&self, id: usize) -> Result, MirEvalError<'db>> { + pub fn vtable_ty(&self, id: usize) -> Result, MirEvalError<'db>> { match self { MemoryMap::Empty | MemoryMap::Simple(_) => Err(MirEvalError::InvalidVTableId(id)), MemoryMap::Complex(cm) => cm.vtable.ty(id), @@ -271,118 +183,11 @@ impl<'db> MemoryMap<'db> { } } -// FIXME(next-solver): add a lifetime to this -/// A concrete constant value -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ConstScalar { - Bytes(Box<[u8]>, MemoryMap<'static>), - // FIXME: this is a hack to get around chalk not being able to represent unevaluatable - // constants - UnevaluatedConst(GeneralConstId, Substitution), - /// Case of an unknown value that rustc might know but we don't - // FIXME: this is a hack to get around chalk not being able to represent unevaluatable - // constants - // https://github.com/rust-lang/rust-analyzer/pull/8813#issuecomment-840679177 - // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348 - Unknown, -} - -impl Hash for ConstScalar { - fn hash(&self, state: &mut H) { - core::mem::discriminant(self).hash(state); - if let ConstScalar::Bytes(b, _) = self { - b.hash(state) - } - } -} - -/// A concrete constant value -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ConstScalarNs<'db> { - Bytes(Box<[u8]>, MemoryMap<'db>), - // FIXME: this is a hack to get around chalk not being able to represent unevaluatable - // constants - UnevaluatedConst(GeneralConstId, Substitution), - /// Case of an unknown value that rustc might know but we don't - // FIXME: this is a hack to get around chalk not being able to represent unevaluatable - // constants - // https://github.com/rust-lang/rust-analyzer/pull/8813#issuecomment-840679177 - // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348 - Unknown, -} - -impl Hash for ConstScalarNs<'_> { - fn hash(&self, state: &mut H) { - core::mem::discriminant(self).hash(state); - if let ConstScalarNs::Bytes(b, _) = self { - b.hash(state) - } - } -} - /// 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 { generics::generics(db, id.parent).type_or_const_param_idx(id) } -pub(crate) fn wrap_empty_binders(value: T) -> Binders -where - T: TypeFoldable + HasInterner, -{ - Binders::empty(Interner, value.shifted_in_from(Interner, DebruijnIndex::ONE)) -} - -pub(crate) fn make_single_type_binders>( - value: T, -) -> Binders { - Binders::new( - chalk_ir::VariableKinds::from_iter( - Interner, - std::iter::once(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)), - ), - value, - ) -} - -pub(crate) fn make_binders>( - db: &dyn HirDatabase, - generics: &Generics, - value: T, -) -> Binders { - Binders::new(variable_kinds_from_iter(db, generics.iter_id()), value) -} - -pub(crate) fn variable_kinds_from_iter( - db: &dyn HirDatabase, - iter: impl Iterator, -) -> VariableKinds { - VariableKinds::from_iter( - Interner, - iter.map(|x| match x { - hir_def::GenericParamId::ConstParamId(id) => { - chalk_ir::VariableKind::Const(db.const_param_ty(id)) - } - hir_def::GenericParamId::TypeParamId(_) => { - chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) - } - hir_def::GenericParamId::LifetimeParamId(_) => chalk_ir::VariableKind::Lifetime, - }), - ) -} - -// FIXME: get rid of this, just replace it by FnPointer -/// A function signature as seen by type inference: Several parameter types and -/// one return type. -#[derive(Clone, PartialEq, Eq, Debug)] -pub(crate) struct CallableSig { - params_and_return: Arc<[Ty]>, - is_varargs: bool, - safety: Safety, - abi: FnAbi, -} - -has_interner!(CallableSig); - #[derive(Debug, Copy, Clone, Eq)] pub enum FnAbi { Aapcs, @@ -534,81 +339,21 @@ pub enum ImplTraitId { } #[derive(PartialEq, Eq, Debug, Hash)] -pub struct ImplTraits { - pub(crate) impl_traits: Arena, -} - -has_interner!(ImplTraits); - -#[derive(PartialEq, Eq, Debug, Hash)] -pub struct ImplTrait { - pub(crate) bounds: Binders>, -} +pub struct ImplTrait {} pub type ImplTraitIdx = Idx; -pub fn static_lifetime() -> Lifetime { - LifetimeData::Static.intern(Interner) -} - -pub fn error_lifetime() -> Lifetime { - LifetimeData::Error.intern(Interner) -} - -pub(crate) fn fold_free_vars + TypeFoldable>( - t: T, - for_ty: impl FnMut(BoundVar, DebruijnIndex) -> Ty, - for_const: impl FnMut(Ty, BoundVar, DebruijnIndex) -> Const, -) -> T { - use chalk_ir::fold::TypeFolder; - - #[derive(chalk_derive::FallibleTypeFolder)] - #[has_interner(Interner)] - struct FreeVarFolder< - F1: FnMut(BoundVar, DebruijnIndex) -> Ty, - F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const, - >(F1, F2); - impl Ty, F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const> - TypeFolder for FreeVarFolder - { - fn as_dyn(&mut self) -> &mut dyn TypeFolder { - self - } - - fn interner(&self) -> Interner { - Interner - } - - fn fold_free_var_ty(&mut self, bound_var: BoundVar, outer_binder: DebruijnIndex) -> Ty { - self.0(bound_var, outer_binder) - } - - fn fold_free_var_const( - &mut self, - ty: Ty, - bound_var: BoundVar, - outer_binder: DebruijnIndex, - ) -> Const { - self.1(ty, bound_var, outer_binder) - } - } - t.fold_with(&mut FreeVarFolder(for_ty, for_const), DebruijnIndex::INNERMOST) -} - /// 'Canonicalizes' the `t` by replacing any errors with new variables. Also /// ensures there are no unbound variables or inference variables anywhere in /// the `t`. -pub fn replace_errors_with_variables<'db, T>( - interner: DbInterner<'db>, - t: &T, -) -> crate::next_solver::Canonical<'db, T> +pub fn replace_errors_with_variables<'db, T>(interner: DbInterner<'db>, t: &T) -> Canonical<'db, T> where T: rustc_type_ir::TypeFoldable> + Clone, { use rustc_type_ir::{FallibleTypeFolder, TypeSuperFoldable}; struct ErrorReplacer<'db> { interner: DbInterner<'db>, - vars: Vec>, + vars: Vec>, binder: rustc_type_ir::DebruijnIndex, } impl<'db> FallibleTypeFolder> for ErrorReplacer<'db> { @@ -621,10 +366,7 @@ where self.interner } - fn try_fold_binder( - &mut self, - t: crate::next_solver::Binder<'db, T>, - ) -> Result, Self::Error> + fn try_fold_binder(&mut self, t: Binder<'db, T>) -> Result, Self::Error> where T: rustc_type_ir::TypeFoldable>, { @@ -634,10 +376,7 @@ where result } - fn try_fold_ty( - &mut self, - t: crate::next_solver::Ty<'db>, - ) -> Result, Self::Error> { + fn try_fold_ty(&mut self, t: Ty<'db>) -> Result, Self::Error> { if !t.has_type_flags( rustc_type_ir::TypeFlags::HAS_ERROR | rustc_type_ir::TypeFlags::HAS_TY_INFER @@ -650,39 +389,28 @@ where #[cfg(debug_assertions)] let error = || Err(()); #[cfg(not(debug_assertions))] - let error = || { - Ok(crate::next_solver::Ty::new_error( - self.interner, - crate::next_solver::ErrorGuaranteed, - )) - }; + let error = || Ok(Ty::new_error(self.interner, crate::next_solver::ErrorGuaranteed)); match t.kind() { - crate::next_solver::TyKind::Error(_) => { + TyKind::Error(_) => { let var = rustc_type_ir::BoundVar::from_usize(self.vars.len()); - self.vars.push(crate::next_solver::CanonicalVarKind::Ty { + self.vars.push(CanonicalVarKind::Ty { ui: rustc_type_ir::UniverseIndex::ZERO, sub_root: var, }); - Ok(crate::next_solver::Ty::new_bound( + Ok(Ty::new_bound( self.interner, self.binder, - crate::next_solver::BoundTy { - var, - kind: crate::next_solver::BoundTyKind::Anon, - }, + BoundTy { var, kind: BoundTyKind::Anon }, )) } - crate::next_solver::TyKind::Infer(_) => error(), - crate::next_solver::TyKind::Bound(index, _) if index > self.binder => error(), + TyKind::Infer(_) => error(), + TyKind::Bound(index, _) if index > self.binder => error(), _ => t.try_super_fold_with(self), } } - fn try_fold_const( - &mut self, - ct: crate::next_solver::Const<'db>, - ) -> Result, Self::Error> { + fn try_fold_const(&mut self, ct: Const<'db>) -> Result, Self::Error> { if !ct.has_type_flags( rustc_type_ir::TypeFlags::HAS_ERROR | rustc_type_ir::TypeFlags::HAS_TY_INFER @@ -695,52 +423,38 @@ where #[cfg(debug_assertions)] let error = || Err(()); #[cfg(not(debug_assertions))] - let error = || Ok(crate::next_solver::Const::error(self.interner)); + let error = || Ok(Const::error(self.interner)); match ct.kind() { - crate::next_solver::ConstKind::Error(_) => { + ConstKind::Error(_) => { let var = rustc_type_ir::BoundVar::from_usize(self.vars.len()); - self.vars.push(crate::next_solver::CanonicalVarKind::Const( - rustc_type_ir::UniverseIndex::ZERO, - )); - Ok(crate::next_solver::Const::new_bound( - self.interner, - self.binder, - crate::next_solver::BoundConst { var }, - )) + self.vars.push(CanonicalVarKind::Const(rustc_type_ir::UniverseIndex::ZERO)); + Ok(Const::new_bound(self.interner, self.binder, BoundConst { var })) } - crate::next_solver::ConstKind::Infer(_) => error(), - crate::next_solver::ConstKind::Bound(index, _) if index > self.binder => error(), + ConstKind::Infer(_) => error(), + ConstKind::Bound(index, _) if index > self.binder => error(), _ => ct.try_super_fold_with(self), } } - fn try_fold_region( - &mut self, - region: crate::next_solver::Region<'db>, - ) -> Result, Self::Error> { + fn try_fold_region(&mut self, region: Region<'db>) -> Result, Self::Error> { #[cfg(debug_assertions)] let error = || Err(()); #[cfg(not(debug_assertions))] - let error = || Ok(crate::next_solver::Region::error(self.interner)); + let error = || Ok(Region::error(self.interner)); match region.kind() { - crate::next_solver::RegionKind::ReError(_) => { + RegionKind::ReError(_) => { let var = rustc_type_ir::BoundVar::from_usize(self.vars.len()); - self.vars.push(crate::next_solver::CanonicalVarKind::Region( - rustc_type_ir::UniverseIndex::ZERO, - )); - Ok(crate::next_solver::Region::new_bound( + self.vars.push(CanonicalVarKind::Region(rustc_type_ir::UniverseIndex::ZERO)); + Ok(Region::new_bound( self.interner, self.binder, - crate::next_solver::BoundRegion { - var, - kind: crate::next_solver::BoundRegionKind::Anon, - }, + BoundRegion { var, kind: BoundRegionKind::Anon }, )) } - crate::next_solver::RegionKind::ReVar(_) => error(), - crate::next_solver::RegionKind::ReBound(index, _) if index > self.binder => error(), + RegionKind::ReVar(_) => error(), + RegionKind::ReBound(index, _) if index > self.binder => error(), _ => Ok(region), } } @@ -752,18 +466,18 @@ where Ok(t) => t, Err(_) => panic!("Encountered unbound or inference vars in {t:?}"), }; - crate::next_solver::Canonical { + Canonical { value, max_universe: rustc_type_ir::UniverseIndex::ZERO, - variables: crate::next_solver::CanonicalVars::new_from_iter(interner, error_replacer.vars), + variables: CanonicalVars::new_from_iter(interner, error_replacer.vars), } } pub fn callable_sig_from_fn_trait<'db>( - self_ty: crate::next_solver::Ty<'db>, + self_ty: Ty<'db>, trait_env: Arc>, db: &'db dyn HirDatabase, -) -> Option<(FnTrait, crate::next_solver::PolyFnSig<'db>)> { +) -> Option<(FnTrait, PolyFnSig<'db>)> { let krate = trait_env.krate; let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?; let output_assoc_type = fn_once_trait @@ -771,54 +485,46 @@ pub fn callable_sig_from_fn_trait<'db>( .associated_type_by_name(&Name::new_symbol_root(sym::Output))?; let mut table = InferenceTable::new(db, trait_env.clone()); - let b = TyBuilder::trait_ref(db, fn_once_trait); - if b.remaining() != 2 { - return None; - } // Register two obligations: // - Self: FnOnce // - >::Output == ?ret_ty let args_ty = table.next_ty_var(); let args = [self_ty, args_ty]; - let trait_ref = crate::next_solver::TraitRef::new(table.interner(), fn_once_trait.into(), args); - let projection = crate::next_solver::Ty::new_alias( + let trait_ref = TraitRef::new(table.interner(), fn_once_trait.into(), args); + let projection = Ty::new_alias( table.interner(), rustc_type_ir::AliasTyKind::Projection, - crate::next_solver::AliasTy::new(table.interner(), output_assoc_type.into(), args), + AliasTy::new(table.interner(), output_assoc_type.into(), args), ); - let pred = crate::next_solver::Predicate::upcast_from(trait_ref, table.interner()); + 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(db, krate)?; - let trait_ref = - crate::next_solver::TraitRef::new(table.interner(), fn_x_trait.into(), args); + let trait_ref = TraitRef::new(table.interner(), fn_x_trait.into(), args); if !table - .try_obligation(crate::next_solver::Predicate::upcast_from( - trait_ref, - table.interner(), - )) + .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 crate::next_solver::TyKind::Tuple(params) = args_ty.kind() else { + let TyKind::Tuple(params) = args_ty.kind() else { return None; }; - let inputs_and_output = crate::next_solver::Tys::new_from_iter( + let inputs_and_output = Tys::new_from_iter( table.interner(), params.iter().chain(std::iter::once(ret_ty)), ); return Some(( fn_x, - crate::next_solver::Binder::dummy(crate::next_solver::FnSig { + Binder::dummy(FnSig { inputs_and_output, c_variadic: false, - safety: crate::next_solver::abi::Safety::Safe, + safety: abi::Safety::Safe, abi: FnAbi::RustCall, }), )); @@ -837,16 +543,16 @@ struct ParamCollector { impl<'db> rustc_type_ir::TypeVisitor> for ParamCollector { type Result = (); - fn visit_ty(&mut self, ty: crate::next_solver::Ty<'db>) -> Self::Result { - if let crate::next_solver::TyKind::Param(param) = ty.kind() { + fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result { + if let TyKind::Param(param) = ty.kind() { self.params.insert(param.id.into()); } ty.super_visit_with(self); } - fn visit_const(&mut self, konst: crate::next_solver::Const<'db>) -> Self::Result { - if let crate::next_solver::ConstKind::Param(param) = konst.kind() { + fn visit_const(&mut self, konst: Const<'db>) -> Self::Result { + if let ConstKind::Param(param) = konst.kind() { self.params.insert(param.id.into()); } @@ -865,7 +571,7 @@ where } pub fn known_const_to_ast<'db>( - konst: crate::next_solver::Const<'db>, + konst: Const<'db>, db: &'db dyn HirDatabase, display_target: DisplayTarget, ) -> Option { 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 b18d713c411e..42f7290962bd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -11,82 +11,93 @@ pub(crate) mod path; use std::{ cell::OnceCell, iter, mem, - ops::{self, Not as _}, + ops::{self, Deref, Not as _}, }; use base_db::Crate; -use chalk_ir::{ - Mutability, Safety, TypeOutlives, - cast::Cast, - fold::{Shift, TypeFoldable}, - interner::HasInterner, -}; - use either::Either; use hir_def::{ - AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, - GenericParamId, LocalFieldId, Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, + AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, + FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, + LocalFieldId, Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId, builtin_type::BuiltinType, - expr_store::{ExpressionStore, path::Path}, - hir::generics::{GenericParamDataRef, TypeOrConstParamData, WherePredicate}, + expr_store::{ExpressionStore, HygieneId, path::Path}, + hir::generics::{ + GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate, + }, + item_tree::FieldsShape, lang_item::LangItem, - resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, - signatures::TraitFlags, + resolver::{HasResolver, LifetimeNs, Resolver, TypeNs, ValueNs}, + signatures::{FunctionSignature, TraitFlags, TypeAliasFlags}, type_ref::{ - ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier, TypeBound, TypeRef, - TypeRefId, + ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier, + TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, }, }; use hir_expand::name::Name; -use la_arena::{Arena, ArenaMap}; +use la_arena::{Arena, ArenaMap, Idx}; +use path::{PathDiagnosticCallback, PathLoweringContext}; +use rustc_ast_ir::Mutability; use rustc_hash::FxHashSet; +use rustc_pattern_analysis::Captures; +use rustc_type_ir::{ + AliasTyKind, ConstKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, + ExistentialTraitRef, FnSig, OutlivesPredicate, + TyKind::{self}, + TypeVisitableExt, + inherent::{GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, Ty as _}, +}; +use salsa::plumbing::AsId; +use smallvec::{SmallVec, smallvec}; use stdx::{impl_from, never}; use triomphe::{Arc, ThinArc}; use crate::{ - AliasTy, Binders, BoundVar, Const, DebruijnIndex, DynTy, FnAbi, FnPointer, FnSig, FnSubst, - ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, LifetimeData, LifetimeOutlives, - QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitRef, TraitRefExt, Ty, - TyBuilder, TyKind, WhereClause, all_super_traits, - consteval_chalk::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic}, + FnAbi, ImplTraitId, TraitEnvironment, TyLoweringDiagnostic, TyLoweringDiagnosticKind, + consteval::intern_const_ref, db::HirDatabase, - error_lifetime, generics::{Generics, generics, trait_self_param_idx}, - lower::{ - diagnostics::*, - path::{PathDiagnosticCallback, PathLoweringContext}, - }, - make_binders, - mapping::{from_chalk_trait_id, lt_to_placeholder_idx}, next_solver::{ - DbInterner, - mapping::{ChalkToNextSolver, NextSolverToChalk}, + AliasTy, Binder, BoundExistentialPredicates, Clause, Clauses, Const, DbInterner, + EarlyBinder, EarlyParamRegion, ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, + ParamEnv, PolyFnSig, Predicate, Region, SolverDefId, TraitPredicate, TraitRef, Ty, Tys, + UnevaluatedConst, abi::Safety, }, - static_lifetime, to_chalk_trait_id, to_placeholder_idx, - utils::all_super_trait_refs, - variable_kinds_from_iter, }; +pub(crate) struct PathDiagnosticCallbackData(pub(crate) TypeRefId); + +#[derive(PartialEq, Eq, Debug, Hash)] +pub struct ImplTraits<'db> { + pub(crate) impl_traits: Arena>, +} + +#[derive(PartialEq, Eq, Debug, Hash)] +pub struct ImplTrait<'db> { + pub(crate) predicates: Vec>, +} + +pub type ImplTraitIdx<'db> = Idx>; + #[derive(Debug, Default)] -struct ImplTraitLoweringState { +struct ImplTraitLoweringState<'db> { /// When turning `impl Trait` into opaque types, we have to collect the /// bounds at the same time to get the IDs correct (without becoming too /// complicated). mode: ImplTraitLoweringMode, // This is structured as a struct with fields and not as an enum because it helps with the borrow checker. - opaque_type_data: Arena, + opaque_type_data: Arena>, } -impl ImplTraitLoweringState { - fn new(mode: ImplTraitLoweringMode) -> ImplTraitLoweringState { + +impl<'db> ImplTraitLoweringState<'db> { + fn new(mode: ImplTraitLoweringMode) -> ImplTraitLoweringState<'db> { Self { mode, opaque_type_data: Arena::new() } } } -pub(crate) struct PathDiagnosticCallbackData(pub(crate) TypeRefId); - #[derive(Debug, Clone)] -pub(crate) enum LifetimeElisionKind { +pub enum LifetimeElisionKind<'db> { /// Create a new anonymous lifetime parameter and reference it. /// /// If `report_in_path`, report an error when encountering lifetime elision in a path: @@ -104,75 +115,109 @@ pub(crate) enum LifetimeElisionKind { AnonymousCreateParameter { report_in_path: bool }, /// Replace all anonymous lifetimes by provided lifetime. - Elided(Lifetime), + Elided(Region<'db>), /// Give a hard error when either `&` or `'_` is written. Used to /// rule out things like `where T: Foo<'_>`. Does not imply an /// error on default object bounds (e.g., `Box`). AnonymousReportError, + /// Resolves elided lifetimes to `'static` if there are no other lifetimes in scope, + /// otherwise give a warning that the previous behavior of introducing a new early-bound + /// lifetime is a bug and will be removed (if `only_lint` is enabled). + StaticIfNoLifetimeInScope { only_lint: bool }, + + /// Signal we cannot find which should be the anonymous lifetime. + ElisionFailure, + /// Infer all elided lifetimes. Infer, } -impl LifetimeElisionKind { +impl<'db> LifetimeElisionKind<'db> { #[inline] - pub(crate) fn for_fn_ret() -> LifetimeElisionKind { + pub(crate) fn for_const( + interner: DbInterner<'db>, + const_parent: ItemContainerId, + ) -> LifetimeElisionKind<'db> { + match const_parent { + ItemContainerId::ExternBlockId(_) | ItemContainerId::ModuleId(_) => { + LifetimeElisionKind::Elided(Region::new_static(interner)) + } + ItemContainerId::ImplId(_) => { + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: true } + } + ItemContainerId::TraitId(_) => { + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: false } + } + } + } + + #[inline] + pub(crate) fn for_fn_params(data: &FunctionSignature) -> LifetimeElisionKind<'db> { + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: data.is_async() } + } + + #[inline] + pub(crate) fn for_fn_ret(interner: DbInterner<'db>) -> LifetimeElisionKind<'db> { // FIXME: We should use the elided lifetime here, or `ElisionFailure`. - LifetimeElisionKind::Elided(error_lifetime()) + LifetimeElisionKind::Elided(Region::error(interner)) } } #[derive(Debug)] -pub(crate) struct TyLoweringContext<'db> { +pub struct TyLoweringContext<'db, 'a> { pub db: &'db dyn HirDatabase, - resolver: &'db Resolver<'db>, - store: &'db ExpressionStore, + interner: DbInterner<'db>, + resolver: &'a Resolver<'db>, + store: &'a ExpressionStore, def: GenericDefId, generics: OnceCell, in_binders: DebruijnIndex, - /// Note: Conceptually, it's thinkable that we could be in a location where - /// some type params should be represented as placeholders, and others - /// should be converted to variables. I think in practice, this isn't - /// possible currently, so this should be fine for now. - pub type_param_mode: ParamLoweringMode, - impl_trait_mode: ImplTraitLoweringState, + impl_trait_mode: ImplTraitLoweringState<'db>, /// Tracks types with explicit `?Sized` bounds. - pub(crate) unsized_types: FxHashSet, + pub(crate) unsized_types: FxHashSet>, pub(crate) diagnostics: Vec, - lifetime_elision: LifetimeElisionKind, + lifetime_elision: LifetimeElisionKind<'db>, + /// When lowering the defaults for generic params, this contains the index of the currently lowered param. + /// We disallow referring to later params, or to ADT's `Self`. + lowering_param_default: Option, } -impl<'db> TyLoweringContext<'db> { - pub(crate) fn new( +impl<'db, 'a> TyLoweringContext<'db, 'a> { + pub fn new( db: &'db dyn HirDatabase, - resolver: &'db Resolver<'db>, - store: &'db ExpressionStore, + resolver: &'a Resolver<'db>, + store: &'a ExpressionStore, def: GenericDefId, - lifetime_elision: LifetimeElisionKind, + lifetime_elision: LifetimeElisionKind<'db>, ) -> Self { let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed); - let type_param_mode = ParamLoweringMode::Placeholder; - let in_binders = DebruijnIndex::INNERMOST; + let in_binders = DebruijnIndex::ZERO; Self { db, + interner: DbInterner::new_with(db, Some(resolver.krate()), None), resolver, def, generics: Default::default(), store, in_binders, impl_trait_mode, - type_param_mode, unsized_types: FxHashSet::default(), diagnostics: Vec::new(), lifetime_elision, + lowering_param_default: None, } } + pub(crate) fn set_lifetime_elision(&mut self, lifetime_elision: LifetimeElisionKind<'db>) { + self.lifetime_elision = lifetime_elision; + } + pub(crate) fn with_debruijn( &mut self, debruijn: DebruijnIndex, - f: impl FnOnce(&mut TyLoweringContext<'_>) -> T, + f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> T, ) -> T { let old_debruijn = mem::replace(&mut self.in_binders, debruijn); let result = f(self); @@ -183,28 +228,22 @@ impl<'db> TyLoweringContext<'db> { pub(crate) fn with_shifted_in( &mut self, debruijn: DebruijnIndex, - f: impl FnOnce(&mut TyLoweringContext<'_>) -> T, + f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> T, ) -> T { - self.with_debruijn(self.in_binders.shifted_in_from(debruijn), f) - } - - fn with_lifetime_elision( - &mut self, - lifetime_elision: LifetimeElisionKind, - f: impl FnOnce(&mut TyLoweringContext<'_>) -> T, - ) -> T { - let old_lifetime_elision = mem::replace(&mut self.lifetime_elision, lifetime_elision); - let result = f(self); - self.lifetime_elision = old_lifetime_elision; - result + self.with_debruijn(self.in_binders.shifted_in(debruijn.as_u32()), f) } pub(crate) fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { Self { impl_trait_mode: ImplTraitLoweringState::new(impl_trait_mode), ..self } } - pub(crate) fn with_type_param_mode(self, type_param_mode: ParamLoweringMode) -> Self { - Self { type_param_mode, ..self } + pub(crate) fn impl_trait_mode(&mut self, impl_trait_mode: ImplTraitLoweringMode) -> &mut Self { + self.impl_trait_mode = ImplTraitLoweringState::new(impl_trait_mode); + self + } + + pub(crate) fn lowering_param_default(&mut self, index: u32) { + self.lowering_param_default = Some(index); } pub(crate) fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) { @@ -213,7 +252,7 @@ impl<'db> TyLoweringContext<'db> { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] -pub enum ImplTraitLoweringMode { +pub(crate) enum ImplTraitLoweringMode { /// `impl Trait` gets lowered into an opaque type that doesn't unify with /// anything except itself. This is used in places where values flow 'out', /// i.e. for arguments of the function we're currently checking, and return @@ -224,30 +263,17 @@ pub enum ImplTraitLoweringMode { Disallowed, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum ParamLoweringMode { - Placeholder, - Variable, -} - -impl<'db> TyLoweringContext<'db> { - pub(crate) fn lower_ty(&mut self, type_ref: TypeRefId) -> Ty { +impl<'db, 'a> TyLoweringContext<'db, 'a> { + pub fn lower_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> { self.lower_ty_ext(type_ref).0 } - pub(crate) fn lower_const(&mut self, const_ref: &ConstRef, const_type: Ty) -> Const { + pub(crate) fn lower_const(&mut self, const_ref: ConstRef, const_type: Ty<'db>) -> Const<'db> { let const_ref = &self.store[const_ref.expr]; match const_ref { - hir_def::hir::Expr::Path(path) => path_to_const( - self.db, - self.resolver, - path, - self.type_param_mode, - || self.generics(), - self.in_binders, - const_type.clone(), - ) - .unwrap_or_else(|| unknown_const(const_type)), + hir_def::hir::Expr::Path(path) => { + self.path_to_const(path).unwrap_or_else(|| unknown_const(const_type)) + } hir_def::hir::Expr::Literal(literal) => intern_const_ref( self.db, &match *literal { @@ -290,32 +316,88 @@ impl<'db> TyLoweringContext<'db> { } } - pub(crate) fn lower_path_as_const(&mut self, path: &Path, const_type: Ty) -> Const { - path_to_const( - self.db, - self.resolver, - path, - self.type_param_mode, - || self.generics(), - self.in_binders, - const_type.clone(), - ) - .unwrap_or_else(|| unknown_const(const_type)) + pub(crate) fn path_to_const(&mut self, path: &Path) -> Option> { + 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 + } + } + } + Some(ValueNs::ConstId(c)) => { + let args = GenericArgs::new_from_iter(self.interner, []); + Some(Const::new( + self.interner, + rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new( + SolverDefId::ConstId(c), + args, + )), + )) + } + _ => None, + } + } + + pub(crate) fn lower_path_as_const(&mut self, path: &Path, const_type: Ty<'db>) -> Const<'db> { + self.path_to_const(path).unwrap_or_else(|| unknown_const(const_type)) } fn generics(&self) -> &Generics { self.generics.get_or_init(|| generics(self.db, self.def)) } - pub(crate) fn lower_ty_ext(&mut self, type_ref_id: TypeRefId) -> (Ty, Option) { + fn param_index_is_disallowed(&self, index: u32) -> bool { + self.lowering_param_default + .is_some_and(|disallow_params_after| index >= disallow_params_after) + } + + fn type_param(&mut self, id: TypeParamId, index: u32) -> Ty<'db> { + if self.param_index_is_disallowed(index) { + // FIXME: Report an error. + Ty::new_error(self.interner, ErrorGuaranteed) + } else { + Ty::new_param(self.interner, id, index) + } + } + + fn const_param(&mut self, id: ConstParamId, index: u32) -> Const<'db> { + if self.param_index_is_disallowed(index) { + // FIXME: Report an error. + Const::error(self.interner) + } else { + Const::new_param(self.interner, ParamConst { id, index }) + } + } + + fn region_param(&mut self, id: LifetimeParamId, index: u32) -> Region<'db> { + if self.param_index_is_disallowed(index) { + // FIXME: Report an error. + Region::error(self.interner) + } else { + Region::new_early_param(self.interner, EarlyParamRegion { id, index }) + } + } + + #[tracing::instrument(skip(self), ret)] + pub fn lower_ty_ext(&mut self, type_ref_id: TypeRefId) -> (Ty<'db>, Option) { + let interner = self.interner; let mut res = None; let type_ref = &self.store[type_ref_id]; + tracing::debug!(?type_ref); let ty = match type_ref { - TypeRef::Never => TyKind::Never.intern(Interner), + TypeRef::Never => Ty::new(interner, TyKind::Never), TypeRef::Tuple(inner) => { let inner_tys = inner.iter().map(|&tr| self.lower_ty(tr)); - TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys)) - .intern(Interner) + Ty::new_tup_from_iter(interner, inner_tys) } TypeRef::Path(path) => { let (ty, res_) = @@ -325,81 +407,61 @@ impl<'db> TyLoweringContext<'db> { } &TypeRef::TypeParam(type_param_id) => { res = Some(TypeNs::GenericParam(type_param_id)); - match self.type_param_mode { - ParamLoweringMode::Placeholder => { - let generics = self.generics(); - let idx = generics.type_or_const_param_idx(type_param_id.into()).unwrap(); - TyKind::Placeholder(to_placeholder_idx( - self.db, - type_param_id.into(), - idx as u32, - )) - } - ParamLoweringMode::Variable => { - let idx = - self.generics().type_or_const_param_idx(type_param_id.into()).unwrap(); - TyKind::BoundVar(BoundVar::new(self.in_binders, idx)) - } - } - .intern(Interner) + + 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) } &TypeRef::RawPtr(inner, mutability) => { let inner_ty = self.lower_ty(inner); - TyKind::Raw(lower_to_chalk_mutability(mutability), inner_ty).intern(Interner) + Ty::new(interner, TyKind::RawPtr(inner_ty, lower_mutability(mutability))) } TypeRef::Array(array) => { let inner_ty = self.lower_ty(array.ty); - let const_len = self.lower_const(&array.len, TyBuilder::usize()); - TyKind::Array(inner_ty, const_len).intern(Interner) + let const_len = self.lower_const(array.len, Ty::new_usize(interner)); + Ty::new_array_with_const_len(interner, inner_ty, const_len) } &TypeRef::Slice(inner) => { let inner_ty = self.lower_ty(inner); - TyKind::Slice(inner_ty).intern(Interner) + Ty::new_slice(interner, inner_ty) } TypeRef::Reference(ref_) => { let inner_ty = self.lower_ty(ref_.ty); - // FIXME: It should infer the eldided lifetimes instead of stubbing with static + // FIXME: It should infer the eldided lifetimes instead of stubbing with error let lifetime = ref_ .lifetime - .as_ref() - .map_or_else(error_lifetime, |&lr| self.lower_lifetime(lr)); - TyKind::Ref(lower_to_chalk_mutability(ref_.mutability), lifetime, inner_ty) - .intern(Interner) + .map_or_else(|| Region::error(interner), |lr| self.lower_lifetime(lr)); + Ty::new_ref(interner, lifetime, inner_ty, lower_mutability(ref_.mutability)) } - TypeRef::Placeholder => TyKind::Error.intern(Interner), + TypeRef::Placeholder => Ty::new_error(interner, ErrorGuaranteed), TypeRef::Fn(fn_) => { - let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { - let (params, ret) = fn_.split_params_and_ret(); - let mut subst = Vec::with_capacity(fn_.params.len()); - ctx.with_lifetime_elision( - LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }, - |ctx| { - subst.extend(params.iter().map(|&(_, tr)| ctx.lower_ty(tr))); - }, - ); - ctx.with_lifetime_elision(LifetimeElisionKind::for_fn_ret(), |ctx| { - subst.push(ctx.lower_ty(ret)); - }); - Substitution::from_iter(Interner, subst) - }); - TyKind::Function(FnPointer { - num_binders: 0, // FIXME lower `for<'a> fn()` correctly - sig: FnSig { + let substs = self.with_shifted_in( + DebruijnIndex::from_u32(1), + |ctx: &mut TyLoweringContext<'_, '_>| { + Tys::new_from_iter( + interner, + fn_.params.iter().map(|&(_, tr)| ctx.lower_ty(tr)), + ) + }, + ); + Ty::new_fn_ptr( + interner, + Binder::dummy(FnSig { abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, - variadic: fn_.is_varargs, - }, - substitution: FnSubst(substs), - }) - .intern(Interner) + c_variadic: fn_.is_varargs, + inputs_and_output: substs, + }), + ) } TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds), TypeRef::ImplTrait(bounds) => { match self.impl_trait_mode.mode { ImplTraitLoweringMode::Opaque => { - let origin = match self.def { - GenericDefId::FunctionId(it) => Either::Left(it), - GenericDefId::TypeAliasId(it) => Either::Right(it), + let origin = match self.resolver.generic_def() { + Some(GenericDefId::FunctionId(it)) => Either::Left(it), + Some(GenericDefId::TypeAliasId(it)) => Either::Right(it), _ => panic!( "opaque impl trait lowering must be in function or type alias" ), @@ -408,9 +470,19 @@ impl<'db> TyLoweringContext<'db> { // this dance is to make sure the data is in the right // place even if we encounter more opaque types while // lowering the bounds - let idx = self.impl_trait_mode.opaque_type_data.alloc(ImplTrait { - bounds: crate::make_single_type_binders(Vec::default()), - }); + let idx = self + .impl_trait_mode + .opaque_type_data + .alloc(ImplTrait { predicates: Vec::default() }); + + // FIXME(next-solver): this from_raw/into_raw dance isn't nice, but it's minimal + let impl_trait_id = origin.either( + |f| ImplTraitId::ReturnTypeImplTrait(f, Idx::from_raw(idx.into_raw())), + |a| ImplTraitId::TypeAliasImplTrait(a, Idx::from_raw(idx.into_raw())), + ); + let opaque_ty_id: SolverDefId = + self.db.intern_impl_trait_id(impl_trait_id).into(); + // We don't want to lower the bounds inside the binders // we're currently in, because they don't end up inside // those binders. E.g. when we have `impl Trait TyLoweringContext<'db> { // parameter of the outer function, it's just one binder // away instead of two. let actual_opaque_type_data = self - .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { - ctx.lower_impl_trait(bounds, self.resolver.krate()) + .with_debruijn(DebruijnIndex::ZERO, |ctx| { + ctx.lower_impl_trait(opaque_ty_id, bounds, self.resolver.krate()) }); self.impl_trait_mode.opaque_type_data[idx] = actual_opaque_type_data; - let impl_trait_id = origin.either( - |f| ImplTraitId::ReturnTypeImplTrait(f, idx), - |a| ImplTraitId::TypeAliasImplTrait(a, idx), - ); - let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); - let generics = generics(self.db, origin.either(|f| f.into(), |a| a.into())); - let parameters = generics.bound_vars_subst(self.db, self.in_binders); - TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner) + let args = GenericArgs::identity_for_item(self.interner, opaque_ty_id); + Ty::new_alias( + self.interner, + AliasTyKind::Opaque, + AliasTy::new_from_args(self.interner, opaque_ty_id, args), + ) } ImplTraitLoweringMode::Disallowed => { // FIXME: report error - TyKind::Error.intern(Interner) + Ty::new_error(self.interner, ErrorGuaranteed) } } } - TypeRef::Error => TyKind::Error.intern(Interner), + TypeRef::Error => Ty::new_error(self.interner, ErrorGuaranteed), }; (ty, res) } @@ -449,8 +519,8 @@ impl<'db> TyLoweringContext<'db> { /// This is only for `generic_predicates_for_param`, where we can't just /// lower the self types of the predicates since that could lead to cycles. /// So we just check here if the `type_ref` resolves to a generic param, and which. - fn lower_ty_only_param(&mut self, type_ref_id: TypeRefId) -> Option { - let type_ref = &self.store[type_ref_id]; + fn lower_ty_only_param(&self, type_ref: TypeRefId) -> Option { + let type_ref = &self.store[type_ref]; let path = match type_ref { TypeRef::Path(path) => path, &TypeRef::TypeParam(idx) => return Some(idx.into()), @@ -462,9 +532,8 @@ impl<'db> TyLoweringContext<'db> { if path.segments().len() > 1 { return None; } - let mut ctx = self.at_path(PathId::from_type_ref_unchecked(type_ref_id)); - let resolution = match ctx.resolve_path_in_type_ns() { - Some((it, None)) => it, + let resolution = match self.resolver.resolve_path_in_type_ns(self.db, path) { + Some((it, None, _)) => it, _ => return None, }; match resolution { @@ -474,7 +543,7 @@ impl<'db> TyLoweringContext<'db> { } #[inline] - fn on_path_diagnostic_callback<'a>(type_ref: TypeRefId) -> PathDiagnosticCallback<'a, 'db> { + fn on_path_diagnostic_callback<'b>(type_ref: TypeRefId) -> PathDiagnosticCallback<'b, 'db> { PathDiagnosticCallback { data: Either::Left(PathDiagnosticCallbackData(type_ref)), callback: |data, this, diag| { @@ -485,7 +554,7 @@ impl<'db> TyLoweringContext<'db> { } #[inline] - fn at_path(&mut self, path_id: PathId) -> PathLoweringContext<'_, 'db> { + fn at_path(&mut self, path_id: PathId) -> PathLoweringContext<'_, 'a, 'db> { PathLoweringContext::new( self, Self::on_path_diagnostic_callback(path_id.type_ref()), @@ -493,7 +562,7 @@ impl<'db> TyLoweringContext<'db> { ) } - pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty, Option) { + pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty<'db>, Option) { // Resolve the path (in type namespace) if let Some(type_ref) = path.type_anchor() { let (ty, res) = self.lower_ty_ext(type_ref); @@ -504,7 +573,7 @@ impl<'db> TyLoweringContext<'db> { let mut ctx = self.at_path(path_id); let (resolution, remaining_index) = match ctx.resolve_path_in_type_ns() { Some(it) => it, - None => return (TyKind::Error.intern(Interner), None), + None => return (Ty::new_error(self.interner, ErrorGuaranteed), None), }; if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() { @@ -520,8 +589,8 @@ impl<'db> TyLoweringContext<'db> { fn lower_trait_ref_from_path( &mut self, path_id: PathId, - explicit_self_ty: Ty, - ) -> Option<(TraitRef, PathLoweringContext<'_, 'db>)> { + explicit_self_ty: Ty<'db>, + ) -> Option<(TraitRef<'db>, PathLoweringContext<'_, 'a, 'db>)> { let mut ctx = self.at_path(path_id); let resolved = match ctx.resolve_path_in_type_ns_fully()? { // FIXME(trait_alias): We need to handle trait alias here. @@ -531,26 +600,57 @@ impl<'db> TyLoweringContext<'db> { Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty, false), ctx)) } - /// When lowering predicates from parents (impl, traits) for children defs (fns, consts, types), `generics` should - /// contain the `Generics` for the **child**, while `predicate_owner` should contain the `GenericDefId` of the - /// **parent**. This is important so we generate the correct bound var/placeholder. + fn lower_trait_ref( + &mut self, + trait_ref: &HirTraitRef, + explicit_self_ty: Ty<'db>, + ) -> Option> { + self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty).map(|it| it.0) + } + pub(crate) fn lower_where_predicate<'b>( &'b mut self, where_predicate: &'b WherePredicate, ignore_bindings: bool, - ) -> impl Iterator + use<'db, 'b> { + generics: &Generics, + predicate_filter: PredicateFilter, + ) -> impl Iterator> + use<'a, 'b, 'db> { match where_predicate { WherePredicate::ForLifetime { target, bound, .. } | WherePredicate::TypeBound { target, bound } => { + if let PredicateFilter::SelfTrait = predicate_filter { + let target_type = &self.store[*target]; + let self_type = 'is_self: { + if let TypeRef::Path(path) = target_type + && path.is_self_type() + { + break 'is_self true; + } + if let TypeRef::TypeParam(param) = target_type + && generics[param.local_id()].is_trait_self() + { + break 'is_self true; + } + false + }; + if !self_type { + return Either::Left(Either::Left(iter::empty())); + } + } let self_ty = self.lower_ty(*target); - Either::Left(self.lower_type_bound(bound, self_ty, ignore_bindings)) + Either::Left(Either::Right(self.lower_type_bound(bound, self_ty, ignore_bindings))) + } + &WherePredicate::Lifetime { bound, target } => { + Either::Right(iter::once(Clause(Predicate::new( + self.interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::RegionOutlives(OutlivesPredicate( + self.lower_lifetime(bound), + self.lower_lifetime(target), + )), + )), + )))) } - &WherePredicate::Lifetime { bound, target } => Either::Right(iter::once( - crate::wrap_empty_binders(WhereClause::LifetimeOutlives(LifetimeOutlives { - a: self.lower_lifetime(bound), - b: self.lower_lifetime(target), - })), - )), } .into_iter() } @@ -558,40 +658,40 @@ impl<'db> TyLoweringContext<'db> { pub(crate) fn lower_type_bound<'b>( &'b mut self, bound: &'b TypeBound, - self_ty: Ty, + self_ty: Ty<'db>, ignore_bindings: bool, - ) -> impl Iterator + use<'b, 'db> { + ) -> impl Iterator> + use<'b, 'a, 'db> { + let interner = self.interner; let mut assoc_bounds = None; let mut clause = None; match bound { &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => { // FIXME Don't silently drop the hrtb lifetimes here - if let Some((trait_ref, mut ctx)) = - self.lower_trait_ref_from_path(path, self_ty.clone()) - { + if let Some((trait_ref, mut ctx)) = self.lower_trait_ref_from_path(path, self_ty) { // FIXME(sized-hierarchy): Remove this bound modifications once we have implemented // sized-hierarchy correctly. let meta_sized = LangItem::MetaSized .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); let pointee_sized = LangItem::PointeeSized .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); - let destruct = LangItem::Destruct - .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); - let hir_trait_id = trait_ref.hir_trait_id(); - if meta_sized.is_some_and(|it| it == hir_trait_id) - || destruct.is_some_and(|it| it == hir_trait_id) - { + if meta_sized.is_some_and(|it| it == trait_ref.def_id.0) { // Ignore this bound - } else if pointee_sized.is_some_and(|it| it == hir_trait_id) { + } else if pointee_sized.is_some_and(|it| it == trait_ref.def_id.0) { // Regard this as `?Sized` bound ctx.ty_ctx().unsized_types.insert(self_ty); } else { if !ignore_bindings { - assoc_bounds = - ctx.assoc_type_bindings_from_type_bound(trait_ref.clone()); + assoc_bounds = ctx.assoc_type_bindings_from_type_bound(trait_ref); } - clause = - Some(crate::wrap_empty_binders(WhereClause::Implemented(trait_ref))); + clause = Some(Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + ))); } } } @@ -601,95 +701,137 @@ impl<'db> TyLoweringContext<'db> { // `?Sized` has no of them. // If we got another trait here ignore the bound completely. let trait_id = self - .lower_trait_ref_from_path(path, self_ty.clone()) - .map(|(trait_ref, _)| trait_ref.hir_trait_id()); + .lower_trait_ref_from_path(path, self_ty) + .map(|(trait_ref, _)| trait_ref.def_id.0); if trait_id == sized_trait { self.unsized_types.insert(self_ty); } } &TypeBound::Lifetime(l) => { let lifetime = self.lower_lifetime(l); - clause = Some(crate::wrap_empty_binders(WhereClause::TypeOutlives(TypeOutlives { - ty: self_ty, - lifetime, - }))); + clause = Some(Clause(Predicate::new( + self.interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::TypeOutlives(OutlivesPredicate( + self_ty, lifetime, + )), + )), + ))); } TypeBound::Use(_) | TypeBound::Error => {} } clause.into_iter().chain(assoc_bounds.into_iter().flatten()) } - fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty { - let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); + fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty<'db> { + let interner = self.interner; + // FIXME: we should never create non-existential predicates in the first place + // For now, use an error type so we don't run into dummy binder issues + let self_ty = Ty::new_error(interner, ErrorGuaranteed); // INVARIANT: The principal trait bound, if present, must come first. Others may be in any // order but should be in the same order for the same set but possibly different order of // bounds in the input. // INVARIANT: If this function returns `DynTy`, there should be at least one trait bound. // These invariants are utilized by `TyExt::dyn_trait()` and chalk. let mut lifetime = None; - let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { - let mut lowered_bounds = Vec::new(); + let bounds = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { + let mut lowered_bounds: Vec< + rustc_type_ir::Binder, ExistentialPredicate>>, + > = Vec::new(); for b in bounds { - ctx.lower_type_bound(b, self_ty.clone(), false).for_each(|b| { - let filter = match b.skip_binders() { - WhereClause::Implemented(_) | WhereClause::AliasEq(_) => true, - WhereClause::LifetimeOutlives(_) => false, - WhereClause::TypeOutlives(t) => { - lifetime = Some(t.lifetime.clone()); - false - } - }; - if filter { - lowered_bounds.push(b); + let db = ctx.db; + ctx.lower_type_bound(b, self_ty, false).for_each(|b| { + if let Some(bound) = b + .kind() + .map_bound(|c| match c { + rustc_type_ir::ClauseKind::Trait(t) => { + let id = t.def_id(); + let is_auto = + db.trait_signature(id.0).flags.contains(TraitFlags::AUTO); + if is_auto { + Some(ExistentialPredicate::AutoTrait(t.def_id())) + } else { + Some(ExistentialPredicate::Trait( + ExistentialTraitRef::new_from_args( + interner, + t.def_id(), + GenericArgs::new_from_iter( + interner, + t.trait_ref.args.iter().skip(1), + ), + ), + )) + } + } + rustc_type_ir::ClauseKind::Projection(p) => { + Some(ExistentialPredicate::Projection( + ExistentialProjection::new_from_args( + interner, + p.def_id(), + GenericArgs::new_from_iter( + interner, + p.projection_term.args.iter().skip(1), + ), + p.term, + ), + )) + } + rustc_type_ir::ClauseKind::TypeOutlives(outlives_predicate) => { + lifetime = Some(outlives_predicate.1); + None + } + rustc_type_ir::ClauseKind::RegionOutlives(_) + | rustc_type_ir::ClauseKind::ConstArgHasType(_, _) + | rustc_type_ir::ClauseKind::WellFormed(_) + | rustc_type_ir::ClauseKind::ConstEvaluatable(_) + | rustc_type_ir::ClauseKind::HostEffect(_) + | rustc_type_ir::ClauseKind::UnstableFeature(_) => unreachable!(), + }) + .transpose() + { + lowered_bounds.push(bound); } - }); + }) } let mut multiple_regular_traits = false; let mut multiple_same_projection = false; lowered_bounds.sort_unstable_by(|lhs, rhs| { use std::cmp::Ordering; - match (lhs.skip_binders(), rhs.skip_binders()) { - (WhereClause::Implemented(lhs), WhereClause::Implemented(rhs)) => { - let lhs_id = lhs.trait_id; - let lhs_is_auto = ctx - .db - .trait_signature(from_chalk_trait_id(lhs_id)) - .flags - .contains(TraitFlags::AUTO); - let rhs_id = rhs.trait_id; - let rhs_is_auto = ctx - .db - .trait_signature(from_chalk_trait_id(rhs_id)) - .flags - .contains(TraitFlags::AUTO); - - if !lhs_is_auto && !rhs_is_auto { - multiple_regular_traits = true; - } - // Note that the ordering here is important; this ensures the invariant - // mentioned above. - (lhs_is_auto, lhs_id).cmp(&(rhs_is_auto, rhs_id)) + match ((*lhs).skip_binder(), (*rhs).skip_binder()) { + (ExistentialPredicate::Trait(_), ExistentialPredicate::Trait(_)) => { + multiple_regular_traits = true; + // Order doesn't matter - we error + Ordering::Equal } - (WhereClause::Implemented(_), _) => Ordering::Less, - (_, WhereClause::Implemented(_)) => Ordering::Greater, - (WhereClause::AliasEq(lhs), WhereClause::AliasEq(rhs)) => { - match (&lhs.alias, &rhs.alias) { - (AliasTy::Projection(lhs_proj), AliasTy::Projection(rhs_proj)) => { - // We only compare the `associated_ty_id`s. We shouldn't have - // multiple bounds for an associated type in the correct Rust code, - // and if we do, we error out. - if lhs_proj.associated_ty_id == rhs_proj.associated_ty_id { - multiple_same_projection = true; - } - lhs_proj.associated_ty_id.cmp(&rhs_proj.associated_ty_id) - } - // We don't produce `AliasTy::Opaque`s yet. + ( + ExistentialPredicate::AutoTrait(lhs_id), + ExistentialPredicate::AutoTrait(rhs_id), + ) => lhs_id.0.cmp(&rhs_id.0), + (ExistentialPredicate::Trait(_), _) => Ordering::Less, + (_, ExistentialPredicate::Trait(_)) => Ordering::Greater, + (ExistentialPredicate::AutoTrait(_), _) => Ordering::Less, + (_, ExistentialPredicate::AutoTrait(_)) => Ordering::Greater, + ( + ExistentialPredicate::Projection(lhs), + ExistentialPredicate::Projection(rhs), + ) => { + let lhs_id = match lhs.def_id { + SolverDefId::TypeAliasId(id) => id, _ => unreachable!(), + }; + let rhs_id = match rhs.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => unreachable!(), + }; + // We only compare the `associated_ty_id`s. We shouldn't have + // multiple bounds for an associated type in the correct Rust code, + // and if we do, we error out. + if lhs_id == rhs_id { + multiple_same_projection = true; } + lhs_id.as_id().index().cmp(&rhs_id.as_id().index()) } - // `WhereClause::{TypeOutlives, LifetimeOutlives}` have been filtered out - _ => unreachable!(), } }); @@ -697,568 +839,193 @@ impl<'db> TyLoweringContext<'db> { return None; } - lowered_bounds.first().and_then(|b| b.trait_id())?; + if !lowered_bounds.first().map_or(false, |b| { + matches!( + b.as_ref().skip_binder(), + ExistentialPredicate::Trait(_) | ExistentialPredicate::AutoTrait(_) + ) + }) { + return None; + } // As multiple occurrences of the same auto traits *are* permitted, we deduplicate the // bounds. We shouldn't have repeated elements besides auto traits at this point. lowered_bounds.dedup(); - Some(QuantifiedWhereClauses::from_iter(Interner, lowered_bounds)) + Some(BoundExistentialPredicates::new_from_iter(interner, lowered_bounds)) }); if let Some(bounds) = bounds { - let bounds = crate::make_single_type_binders(bounds); - TyKind::Dyn(DynTy { - bounds, - lifetime: match lifetime { - Some(it) => match it.bound_var(Interner) { - Some(bound_var) => bound_var - .shifted_out_to(DebruijnIndex::new(2)) - .map(|bound_var| LifetimeData::BoundVar(bound_var).intern(Interner)) - .unwrap_or(it), - None => it, - }, - None => error_lifetime(), + let region = match lifetime { + Some(it) => match it.kind() { + rustc_type_ir::RegionKind::ReBound(db, var) => Region::new_bound( + self.interner, + db.shifted_out_to_binder(DebruijnIndex::from_u32(2)), + var, + ), + _ => it, }, - }) - .intern(Interner) + None => Region::new_static(self.interner), + }; + Ty::new_dynamic(self.interner, bounds, region) } else { // FIXME: report error // (additional non-auto traits, associated type rebound, or no resolved trait) - TyKind::Error.intern(Interner) + Ty::new_error(self.interner, ErrorGuaranteed) } } - fn lower_impl_trait(&mut self, bounds: &[TypeBound], krate: Crate) -> ImplTrait { + fn lower_impl_trait( + &mut self, + def_id: SolverDefId, + bounds: &[TypeBound], + krate: Crate, + ) -> ImplTrait<'db> { + let interner = self.interner; cov_mark::hit!(lower_rpit); - let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); - let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { + let args = GenericArgs::identity_for_item(interner, def_id); + let self_ty = Ty::new_alias( + self.interner, + rustc_type_ir::AliasTyKind::Opaque, + AliasTy::new_from_args(interner, def_id, args), + ); + let predicates = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { let mut predicates = Vec::new(); for b in bounds { - predicates.extend(ctx.lower_type_bound(b, self_ty.clone(), false)); + predicates.extend(ctx.lower_type_bound(b, self_ty, false)); } if !ctx.unsized_types.contains(&self_ty) { - let sized_trait = - LangItem::Sized.resolve_trait(ctx.db, krate).map(to_chalk_trait_id); + let sized_trait = LangItem::Sized.resolve_trait(self.db, krate); let sized_clause = sized_trait.map(|trait_id| { - let clause = WhereClause::Implemented(TraitRef { - trait_id, - substitution: Substitution::from1(Interner, self_ty.clone()), - }); - crate::wrap_empty_binders(clause) + let trait_ref = TraitRef::new_from_args( + interner, + trait_id.into(), + GenericArgs::new_from_iter(interner, [self_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) }); predicates.extend(sized_clause); } predicates.shrink_to_fit(); predicates }); - ImplTrait { bounds: crate::make_single_type_binders(predicates) } + ImplTrait { predicates } } - pub(crate) fn lower_lifetime(&self, lifetime: LifetimeRefId) -> Lifetime { + pub(crate) fn lower_lifetime(&mut self, lifetime: LifetimeRefId) -> Region<'db> { match self.resolver.resolve_lifetime(&self.store[lifetime]) { Some(resolution) => match resolution { - LifetimeNs::Static => static_lifetime(), - LifetimeNs::LifetimeParam(id) => match self.type_param_mode { - ParamLoweringMode::Placeholder => { - let generics = self.generics(); - let idx = generics.lifetime_idx(id).unwrap(); - LifetimeData::Placeholder(lt_to_placeholder_idx(self.db, id, idx as u32)) - } - ParamLoweringMode::Variable => { - let idx = match self.generics().lifetime_idx(id) { - None => return error_lifetime(), - Some(idx) => idx, - }; - - LifetimeData::BoundVar(BoundVar::new(self.in_binders, idx)) - } + 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) } - .intern(Interner), }, - None => error_lifetime(), + None => Region::error(self.interner), } } } -fn named_associated_type_shorthand_candidates( - db: &dyn HirDatabase, - // If the type parameter is defined in an impl and we're in a method, there - // might be additional where clauses to consider - def: GenericDefId, - res: TypeNs, - assoc_name: Option, - // Do NOT let `cb` touch `TraitRef` outside of `TyLoweringContext`. Its substitution contains - // free `BoundVar`s that need to be shifted and only `TyLoweringContext` knows how to do that - // properly (see `TyLoweringContext::select_associated_type()`). - mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option, -) -> Option { - let mut search = |t| { - all_super_trait_refs(db, t, |t| { - let data = t.hir_trait_id().trait_items(db); - - for (name, assoc_id) in &data.items { - if let AssocItemId::TypeAliasId(alias) = assoc_id - && let Some(result) = cb(name, &t, *alias) - { - return Some(result); - } - } - None - }) - }; - - let interner = DbInterner::new_with(db, None, None); - match res { - TypeNs::SelfType(impl_id) => { - let trait_ref = db.impl_trait(impl_id)?; - - let impl_id_as_generic_def: GenericDefId = impl_id.into(); - if impl_id_as_generic_def != def { - let subst = TyBuilder::subst_for_def(db, impl_id, None) - .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0) - .build(); - let args: crate::next_solver::GenericArgs<'_> = subst.to_nextsolver(interner); - let trait_ref = trait_ref.instantiate(interner, args).to_chalk(interner); - search(trait_ref) - } else { - search(trait_ref.skip_binder().to_chalk(interner)) - } - } - TypeNs::GenericParam(param_id) => { - let predicates = db.generic_predicates_for_param(def, param_id.into(), assoc_name); - let res = predicates.iter().find_map(|pred| match pred.skip_binders().skip_binders() { - // FIXME: how to correctly handle higher-ranked bounds here? - WhereClause::Implemented(tr) => search( - tr.clone() - .shifted_out_to(Interner, DebruijnIndex::ONE) - .expect("FIXME unexpected higher-ranked trait bound"), - ), - _ => None, - }); - if res.is_some() { - return res; - } - // Handle `Self::Type` referring to own associated type in trait definitions - if let GenericDefId::TraitId(trait_id) = param_id.parent() { - let trait_generics = generics(db, trait_id.into()); - if trait_generics[param_id.local_id()].is_trait_self() { - let trait_ref = TyBuilder::trait_ref(db, trait_id) - .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0) - .build(); - return search(trait_ref); - } - } - None - } - _ => None, +pub(crate) fn lower_mutability(m: hir_def::type_ref::Mutability) -> Mutability { + match m { + hir_def::type_ref::Mutability::Shared => Mutability::Not, + hir_def::type_ref::Mutability::Mut => Mutability::Mut, } } +fn unknown_const(_ty: Ty<'_>) -> Const<'_> { + Const::new(DbInterner::conjure(), ConstKind::Error(ErrorGuaranteed)) +} + pub(crate) type Diagnostics = Option>; pub(crate) fn create_diagnostics(diagnostics: Vec) -> Diagnostics { (!diagnostics.is_empty()).then(|| ThinArc::from_header_and_iter((), diagnostics.into_iter())) } -pub(crate) fn field_types_query( - db: &dyn HirDatabase, - variant_id: VariantId, -) -> Arc>> { - field_types_with_diagnostics_query(db, variant_id).0 -} - -/// Build the type of all specific fields of a struct or enum variant. -pub(crate) fn field_types_with_diagnostics_query( - db: &dyn HirDatabase, - variant_id: VariantId, -) -> (Arc>>, Diagnostics) { - let var_data = variant_id.fields(db); - let fields = var_data.fields(); - if fields.is_empty() { - return (Arc::new(ArenaMap::default()), None); - } - - let (resolver, def): (_, GenericDefId) = match variant_id { - VariantId::StructId(it) => (it.resolver(db), it.into()), - VariantId::UnionId(it) => (it.resolver(db), it.into()), - VariantId::EnumVariantId(it) => (it.resolver(db), it.lookup(db).parent.into()), - }; - let generics = generics(db, def); - let mut res = ArenaMap::default(); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &var_data.store, - def, - LifetimeElisionKind::AnonymousReportError, - ) - .with_type_param_mode(ParamLoweringMode::Variable); - for (field_id, field_data) in fields.iter() { - res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(field_data.type_ref))); - } - (Arc::new(res), create_diagnostics(ctx.diagnostics)) -} - -/// This query exists only to be used when resolving short-hand associated types -/// like `T::Item`. -/// -/// See the analogous query in rustc and its comment: -/// -/// This is a query mostly to handle cycles somewhat gracefully; e.g. the -/// following bounds are disallowed: `T: Foo, U: Foo`, but -/// these are fine: `T: Foo, U: Foo<()>`. -pub(crate) fn generic_predicates_for_param_query( - db: &dyn HirDatabase, - def: GenericDefId, - param_id: TypeOrConstParamId, - assoc_name: Option, -) -> GenericPredicates { - let generics = generics(db, def); - if generics.has_no_predicates() && generics.is_empty() { - return GenericPredicates(None); - } - - let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - generics.store(), - def, - LifetimeElisionKind::AnonymousReportError, - ) - .with_type_param_mode(ParamLoweringMode::Variable); - - // we have to filter out all other predicates *first*, before attempting to lower them - let predicate = |pred: &_, ctx: &mut TyLoweringContext<'_>| match pred { - WherePredicate::ForLifetime { target, bound, .. } - | WherePredicate::TypeBound { target, bound, .. } => { - let invalid_target = { ctx.lower_ty_only_param(*target) != Some(param_id) }; - if invalid_target { - // FIXME(sized-hierarchy): Revisit and adjust this properly once we have implemented - // sized-hierarchy correctly. - // If this is filtered out without lowering, `?Sized` or `PointeeSized` is not gathered into - // `ctx.unsized_types` - let lower = || -> bool { - match bound { - TypeBound::Path(_, TraitBoundModifier::Maybe) => true, - TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => { - let TypeRef::Path(path) = &ctx.store[path.type_ref()] else { - return false; - }; - let Some(pointee_sized) = - LangItem::PointeeSized.resolve_trait(ctx.db, ctx.resolver.krate()) - else { - return false; - }; - // Lower the path directly with `Resolver` instead of PathLoweringContext` - // to prevent diagnostics duplications. - ctx.resolver.resolve_path_in_type_ns_fully(ctx.db, path).is_some_and( - |it| matches!(it, TypeNs::TraitId(tr) if tr == pointee_sized), - ) - } - _ => false, - } - }(); - if lower { - ctx.lower_where_predicate(pred, true).for_each(drop); - } - return false; - } - - match bound { - &TypeBound::ForLifetime(_, path) | &TypeBound::Path(path, _) => { - // Only lower the bound if the trait could possibly define the associated - // type we're looking for. - let path = &ctx.store[path]; - - let Some(assoc_name) = &assoc_name else { return true }; - let Some(TypeNs::TraitId(tr)) = - resolver.resolve_path_in_type_ns_fully(db, path) - else { - return false; - }; - - all_super_traits(db, tr).iter().any(|tr| { - tr.trait_items(db).items.iter().any(|(name, item)| { - matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name - }) - }) - } - TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false, - } - } - WherePredicate::Lifetime { .. } => false, - }; - let mut predicates = Vec::new(); - for maybe_parent_generics in - std::iter::successors(Some(&generics), |generics| generics.parent_generics()) - { - ctx.store = maybe_parent_generics.store(); - for pred in maybe_parent_generics.where_predicates() { - if predicate(pred, &mut ctx) { - predicates.extend( - ctx.lower_where_predicate(pred, true).map(|p| make_binders(db, &generics, p)), - ); - } - } - } - - let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); - if !subst.is_empty(Interner) { - let explicitly_unsized_tys = ctx.unsized_types; - if let Some(implicitly_sized_predicates) = implicitly_sized_clauses( - db, - param_id.parent, - &explicitly_unsized_tys, - &subst, - &resolver, - ) { - predicates.extend( - implicitly_sized_predicates - .map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))), - ); - }; - } - GenericPredicates(predicates.is_empty().not().then(|| predicates.into())) -} - -pub(crate) fn generic_predicates_for_param_cycle_result( - _db: &dyn HirDatabase, - _def: GenericDefId, - _param_id: TypeOrConstParamId, - _assoc_name: Option, -) -> GenericPredicates { - GenericPredicates(None) -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct GenericPredicates(Option]>>); - -impl ops::Deref for GenericPredicates { - type Target = [Binders]; - - fn deref(&self) -> &Self::Target { - self.0.as_deref().unwrap_or(&[]) - } -} - -/// Resolve the where clause(s) of an item with generics. -pub(crate) fn generic_predicates_query( - db: &dyn HirDatabase, - def: GenericDefId, -) -> GenericPredicates { - generic_predicates_filtered_by(db, def, |_, _| true).0 -} - -/// Resolve the where clause(s) of an item with generics, -/// with a given filter -fn generic_predicates_filtered_by( - db: &dyn HirDatabase, - def: GenericDefId, - filter: F, -) -> (GenericPredicates, Diagnostics) -where - F: Fn(&WherePredicate, GenericDefId) -> bool, -{ - let generics = generics(db, def); - if generics.has_no_predicates() && generics.is_empty() { - return (GenericPredicates(None), None); - } - - let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - generics.store(), - def, - LifetimeElisionKind::AnonymousReportError, - ) - .with_type_param_mode(ParamLoweringMode::Variable); - - let mut predicates = Vec::new(); - for maybe_parent_generics in - std::iter::successors(Some(&generics), |generics| generics.parent_generics()) - { - ctx.store = maybe_parent_generics.store(); - for pred in maybe_parent_generics.where_predicates() { - if filter(pred, maybe_parent_generics.def()) { - // We deliberately use `generics` and not `maybe_parent_generics` here. This is not a mistake! - // If we use the parent generics - predicates.extend( - ctx.lower_where_predicate(pred, false).map(|p| make_binders(db, &generics, p)), - ); - } - } - } - - if !generics.is_empty() { - let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); - let explicitly_unsized_tys = ctx.unsized_types; - if let Some(implicitly_sized_predicates) = - implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver) - { - predicates.extend( - implicitly_sized_predicates - .map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))), - ); - }; - } - - ( - GenericPredicates(predicates.is_empty().not().then(|| predicates.into())), - create_diagnostics(ctx.diagnostics), - ) -} - -/// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound. -/// Exception is Self of a trait def. -fn implicitly_sized_clauses<'db, 'a, 'subst: 'a>( +pub(crate) fn impl_trait_query<'db>( db: &'db dyn HirDatabase, - def: GenericDefId, - explicitly_unsized_tys: &'a FxHashSet, - substitution: &'subst Substitution, - resolver: &Resolver<'db>, -) -> Option> { - let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()).map(to_chalk_trait_id)?; - - let trait_self_idx = trait_self_param_idx(db, def); - - Some( - substitution - .iter(Interner) - .enumerate() - .filter_map( - move |(idx, generic_arg)| { - if Some(idx) == trait_self_idx { None } else { Some(generic_arg) } - }, - ) - .filter_map(|generic_arg| generic_arg.ty(Interner)) - .filter(move |&self_ty| !explicitly_unsized_tys.contains(self_ty)) - .map(move |self_ty| { - WhereClause::Implemented(TraitRef { - trait_id: sized_trait, - substitution: Substitution::from1(Interner, self_ty.clone()), - }) - }), - ) + impl_id: ImplId, +) -> Option>> { + db.impl_trait_with_diagnostics(impl_id).map(|it| it.0) } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct GenericDefaults(Option]>>); - -impl ops::Deref for GenericDefaults { - type Target = [Binders]; - - fn deref(&self) -> &Self::Target { - self.0.as_deref().unwrap_or(&[]) - } -} - -pub(crate) fn generic_defaults_query(db: &dyn HirDatabase, def: GenericDefId) -> GenericDefaults { - db.generic_defaults_with_diagnostics(def).0 -} - -/// Resolve the default type params from generics. -/// -/// Diagnostics are only returned for this `GenericDefId` (returned defaults include parents). -pub(crate) fn generic_defaults_with_diagnostics_query( - db: &dyn HirDatabase, - def: GenericDefId, -) -> (GenericDefaults, Diagnostics) { - let generic_params = generics(db, def); - if generic_params.is_empty() { - return (GenericDefaults(None), None); - } - let resolver = def.resolver(db); - +pub(crate) fn impl_trait_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + impl_id: ImplId, +) -> Option<(EarlyBinder<'db, TraitRef<'db>>, Diagnostics)> { + let impl_data = db.impl_signature(impl_id); + let resolver = impl_id.resolver(db); let mut ctx = TyLoweringContext::new( db, &resolver, - generic_params.store(), - def, - LifetimeElisionKind::AnonymousReportError, - ) - .with_impl_trait_mode(ImplTraitLoweringMode::Disallowed) - .with_type_param_mode(ParamLoweringMode::Variable); - 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 (result, has_default) = handle_generic_param(&mut ctx, idx, id, p, &generic_params); - has_any_default |= has_default; - idx += 1; - result - }) - .collect::>(); - ctx.diagnostics.clear(); // Don't include diagnostics from the parent. - defaults.extend(generic_params.iter_self().map(|(id, p)| { - let (result, has_default) = handle_generic_param(&mut ctx, idx, id, p, &generic_params); - has_any_default |= has_default; - idx += 1; - result - })); - let diagnostics = create_diagnostics(mem::take(&mut ctx.diagnostics)); - let defaults = if has_any_default { - GenericDefaults(Some(Arc::from_iter(defaults))) + &impl_data.store, + impl_id.into(), + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, + ); + let self_ty = db.impl_self_ty(impl_id).skip_binder(); + let target_trait = impl_data.target_trait.as_ref()?; + let trait_ref = EarlyBinder::bind(ctx.lower_trait_ref(target_trait, self_ty)?); + Some((trait_ref, create_diagnostics(ctx.diagnostics))) +} + +pub(crate) fn return_type_impl_traits<'db>( + db: &'db dyn HirDatabase, + def: hir_def::FunctionId, +) -> Option>>> { + // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe + let data = db.function_signature(def); + let resolver = def.resolver(db); + let mut ctx_ret = + TyLoweringContext::new(db, &resolver, &data.store, def.into(), LifetimeElisionKind::Infer) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + if let Some(ret_type) = data.ret_type { + let _ret = ctx_ret.lower_ty(ret_type); + } + let return_type_impl_traits = + ImplTraits { impl_traits: ctx_ret.impl_trait_mode.opaque_type_data }; + if return_type_impl_traits.impl_traits.is_empty() { + None } else { - GenericDefaults(None) - }; - return (defaults, diagnostics); - - fn handle_generic_param( - ctx: &mut TyLoweringContext<'_>, - idx: usize, - id: GenericParamId, - p: GenericParamDataRef<'_>, - generic_params: &Generics, - ) -> (Binders, bool) { - let binders = variable_kinds_from_iter(ctx.db, generic_params.iter_id().take(idx)); - match p { - GenericParamDataRef::TypeParamData(p) => { - let ty = p.default.as_ref().map_or_else( - || TyKind::Error.intern(Interner), - |ty| { - // Each default can only refer to previous parameters. - // Type variable default referring to parameter coming - // after it is forbidden (FIXME: report diagnostic) - fallback_bound_vars(ctx.lower_ty(*ty), idx) - }, - ); - (Binders::new(binders, ty.cast(Interner)), p.default.is_some()) - } - GenericParamDataRef::ConstParamData(p) => { - let GenericParamId::ConstParamId(id) = id else { - unreachable!("Unexpected lifetime or type argument") - }; - - let mut val = p.default.as_ref().map_or_else( - || unknown_const_as_generic(ctx.db.const_param_ty(id)), - |c| { - let param_ty = ctx.lower_ty(p.ty); - let c = ctx.lower_const(c, param_ty); - c.cast(Interner) - }, - ); - // Each default can only refer to previous parameters, see above. - val = fallback_bound_vars(val, idx); - (Binders::new(binders, val), p.default.is_some()) - } - GenericParamDataRef::LifetimeParamData(_) => { - (Binders::new(binders, error_lifetime().cast(Interner)), false) - } - } + Some(Arc::new(EarlyBinder::bind(return_type_impl_traits))) } } -pub(crate) fn generic_defaults_with_diagnostics_cycle_result( - _db: &dyn HirDatabase, - _def: GenericDefId, -) -> (GenericDefaults, Diagnostics) { - (GenericDefaults(None), None) +pub(crate) fn type_alias_impl_traits<'db>( + db: &'db dyn HirDatabase, + def: hir_def::TypeAliasId, +) -> Option>>> { + let data = db.type_alias_signature(def); + let resolver = def.resolver(db); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::AnonymousReportError, + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + if let Some(type_ref) = data.ty { + let _ty = ctx.lower_ty(type_ref); + } + let type_alias_impl_traits = ImplTraits { impl_traits: ctx.impl_trait_mode.opaque_type_data }; + if type_alias_impl_traits.impl_traits.is_empty() { + None + } else { + Some(Arc::new(EarlyBinder::bind(type_alias_impl_traits))) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -1293,18 +1060,203 @@ impl ValueTyDefId { } } -pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty { - const_param_ty_with_diagnostics_query(db, def).0 +/// Build the declared type of an item. This depends on the namespace; e.g. for +/// `struct Foo(usize)`, we have two types: The type of the struct itself, and +/// the constructor function `(usize) -> Foo` which lives in the values +/// namespace. +pub(crate) fn ty_query<'db>(db: &'db dyn HirDatabase, def: TyDefId) -> EarlyBinder<'db, Ty<'db>> { + let interner = DbInterner::new_with(db, None, None); + match def { + TyDefId::BuiltinType(it) => EarlyBinder::bind(Ty::from_builtin_type(interner, it)), + TyDefId::AdtId(it) => EarlyBinder::bind(Ty::new_adt( + interner, + it, + GenericArgs::identity_for_item(interner, it.into()), + )), + TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).0, + } +} + +/// Build the declared type of a function. This should not need to look at the +/// function body. +fn type_for_fn<'db>(db: &'db dyn HirDatabase, def: FunctionId) -> EarlyBinder<'db, Ty<'db>> { + let interner = DbInterner::new_with(db, None, None); + EarlyBinder::bind(Ty::new_fn_def( + interner, + CallableDefId::FunctionId(def).into(), + GenericArgs::identity_for_item(interner, def.into()), + )) +} + +/// Build the declared type of a const. +fn type_for_const<'db>(db: &'db dyn HirDatabase, def: ConstId) -> EarlyBinder<'db, Ty<'db>> { + let resolver = def.resolver(db); + let data = db.const_signature(def); + let parent = def.loc(db).container; + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::AnonymousReportError, + ); + ctx.set_lifetime_elision(LifetimeElisionKind::for_const(ctx.interner, parent)); + EarlyBinder::bind(ctx.lower_ty(data.type_ref)) +} + +/// Build the declared type of a static. +fn type_for_static<'db>(db: &'db dyn HirDatabase, def: StaticId) -> EarlyBinder<'db, Ty<'db>> { + let resolver = def.resolver(db); + let data = db.static_signature(def); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::AnonymousReportError, + ); + ctx.set_lifetime_elision(LifetimeElisionKind::Elided(Region::new_static(ctx.interner))); + EarlyBinder::bind(ctx.lower_ty(data.type_ref)) +} + +/// Build the type of a tuple struct constructor. +fn type_for_struct_constructor<'db>( + db: &'db dyn HirDatabase, + def: StructId, +) -> Option>> { + let struct_data = def.fields(db); + match struct_data.shape { + FieldsShape::Record => None, + FieldsShape::Unit => Some(type_for_adt(db, def.into())), + FieldsShape::Tuple => { + let interner = DbInterner::new_with(db, None, None); + Some(EarlyBinder::bind(Ty::new_fn_def( + interner, + CallableDefId::StructId(def).into(), + GenericArgs::identity_for_item(interner, def.into()), + ))) + } + } +} + +/// Build the type of a tuple enum variant constructor. +fn type_for_enum_variant_constructor<'db>( + db: &'db dyn HirDatabase, + def: EnumVariantId, +) -> Option>> { + let struct_data = def.fields(db); + match struct_data.shape { + FieldsShape::Record => None, + FieldsShape::Unit => Some(type_for_adt(db, def.loc(db).parent.into())), + FieldsShape::Tuple => { + let interner = DbInterner::new_with(db, None, None); + Some(EarlyBinder::bind(Ty::new_fn_def( + interner, + CallableDefId::EnumVariantId(def).into(), + GenericArgs::identity_for_item(interner, def.loc(db).parent.into()), + ))) + } + } +} + +pub(crate) fn value_ty_query<'db>( + db: &'db dyn HirDatabase, + def: ValueTyDefId, +) -> Option>> { + match def { + ValueTyDefId::FunctionId(it) => Some(type_for_fn(db, it)), + ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it), + ValueTyDefId::UnionId(it) => Some(type_for_adt(db, it.into())), + ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it), + ValueTyDefId::ConstId(it) => Some(type_for_const(db, it)), + ValueTyDefId::StaticId(it) => Some(type_for_static(db, it)), + } +} + +pub(crate) fn type_for_type_alias_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + t: TypeAliasId, +) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { + let type_alias_data = db.type_alias_signature(t); + let mut diags = None; + let resolver = t.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let inner = if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) { + EarlyBinder::bind(Ty::new_foreign(interner, t.into())) + } else { + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &type_alias_data.store, + t.into(), + LifetimeElisionKind::AnonymousReportError, + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + let res = EarlyBinder::bind( + type_alias_data + .ty + .map(|type_ref| ctx.lower_ty(type_ref)) + .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)), + ); + diags = create_diagnostics(ctx.diagnostics); + res + }; + (inner, diags) +} + +pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result<'db>( + db: &'db dyn HirDatabase, + _adt: TypeAliasId, +) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { + (EarlyBinder::bind(Ty::new_error(DbInterner::new_with(db, None, None), ErrorGuaranteed)), None) +} + +pub(crate) fn impl_self_ty_query<'db>( + db: &'db dyn HirDatabase, + impl_id: ImplId, +) -> EarlyBinder<'db, Ty<'db>> { + db.impl_self_ty_with_diagnostics(impl_id).0 +} + +pub(crate) fn impl_self_ty_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + impl_id: ImplId, +) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { + let resolver = impl_id.resolver(db); + + let impl_data = db.impl_signature(impl_id); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &impl_data.store, + impl_id.into(), + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, + ); + let ty = ctx.lower_ty(impl_data.self_ty); + assert!(!ty.has_escaping_bound_vars()); + (EarlyBinder::bind(ty), create_diagnostics(ctx.diagnostics)) +} + +pub(crate) fn impl_self_ty_with_diagnostics_cycle_result( + db: &dyn HirDatabase, + _impl_id: ImplId, +) -> (EarlyBinder<'_, Ty<'_>>, Diagnostics) { + (EarlyBinder::bind(Ty::new_error(DbInterner::new_with(db, None, None), ErrorGuaranteed)), None) +} + +pub(crate) fn const_param_ty_query<'db>(db: &'db dyn HirDatabase, def: ConstParamId) -> Ty<'db> { + db.const_param_ty_with_diagnostics(def).0 } // returns None if def is a type arg -pub(crate) fn const_param_ty_with_diagnostics_query( - db: &dyn HirDatabase, +pub(crate) fn const_param_ty_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, def: ConstParamId, -) -> (Ty, Diagnostics) { +) -> (Ty<'db>, Diagnostics) { let (parent_data, store) = db.generic_params_and_store(def.parent()); let data = &parent_data[def.local_id()]; let resolver = def.parent().resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); let mut ctx = TyLoweringContext::new( db, &resolver, @@ -1315,103 +1267,912 @@ pub(crate) fn const_param_ty_with_diagnostics_query( let ty = match data { TypeOrConstParamData::TypeParamData(_) => { never!(); - Ty::new(Interner, TyKind::Error) + Ty::new_error(interner, ErrorGuaranteed) } TypeOrConstParamData::ConstParamData(d) => ctx.lower_ty(d.ty), }; (ty, create_diagnostics(ctx.diagnostics)) } -pub(crate) fn const_param_ty_cycle_result( - _: &dyn HirDatabase, +pub(crate) fn const_param_ty_with_diagnostics_cycle_result<'db>( + db: &'db dyn HirDatabase, _: crate::db::HirDatabaseData, - _: ConstParamId, -) -> Ty { - TyKind::Error.intern(Interner) + def: ConstParamId, +) -> (Ty<'db>, Diagnostics) { + let resolver = def.parent().resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + (Ty::new_error(interner, ErrorGuaranteed), None) } -pub(crate) fn return_type_impl_traits( - db: &dyn HirDatabase, - def: hir_def::FunctionId, -) -> Option>> { - // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe - let data = db.function_signature(def); - let resolver = def.resolver(db); - let mut ctx_ret = - TyLoweringContext::new(db, &resolver, &data.store, def.into(), LifetimeElisionKind::Infer) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) - .with_type_param_mode(ParamLoweringMode::Variable); - if let Some(ret_type) = data.ret_type { - let _ret = ctx_ret.lower_ty(ret_type); - } - let generics = generics(db, def.into()); - let return_type_impl_traits = - ImplTraits { impl_traits: ctx_ret.impl_trait_mode.opaque_type_data }; - if return_type_impl_traits.impl_traits.is_empty() { - None - } else { - Some(Arc::new(make_binders(db, &generics, return_type_impl_traits))) - } +pub(crate) fn field_types_query<'db>( + db: &'db dyn HirDatabase, + variant_id: VariantId, +) -> Arc>>> { + db.field_types_with_diagnostics(variant_id).0 } -pub(crate) fn type_alias_impl_traits( - db: &dyn HirDatabase, - def: hir_def::TypeAliasId, -) -> Option>> { - let data = db.type_alias_signature(def); +/// Build the type of all specific fields of a struct or enum variant. +pub(crate) fn field_types_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + variant_id: VariantId, +) -> (Arc>>>, Diagnostics) { + let var_data = variant_id.fields(db); + let fields = var_data.fields(); + if fields.is_empty() { + return (Arc::new(ArenaMap::default()), None); + } + + let (resolver, def): (_, GenericDefId) = match variant_id { + VariantId::StructId(it) => (it.resolver(db), it.into()), + VariantId::UnionId(it) => (it.resolver(db), it.into()), + VariantId::EnumVariantId(it) => (it.resolver(db), it.lookup(db).parent.into()), + }; + let mut res = ArenaMap::default(); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &var_data.store, + def, + LifetimeElisionKind::AnonymousReportError, + ); + for (field_id, field_data) in var_data.fields().iter() { + res.insert(field_id, EarlyBinder::bind(ctx.lower_ty(field_data.type_ref))); + } + (Arc::new(res), create_diagnostics(ctx.diagnostics)) +} + +/// This query exists only to be used when resolving short-hand associated types +/// like `T::Item`. +/// +/// See the analogous query in rustc and its comment: +/// +/// This is a query mostly to handle cycles somewhat gracefully; e.g. the +/// following bounds are disallowed: `T: Foo, U: Foo`, but +/// these are fine: `T: Foo, U: Foo<()>`. +#[tracing::instrument(skip(db), ret)] +pub(crate) fn generic_predicates_for_param_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, + param_id: TypeOrConstParamId, + assoc_name: Option, +) -> GenericPredicates<'db> { + let generics = generics(db, def); + let interner = DbInterner::new_with(db, None, None); let resolver = def.resolver(db); let mut ctx = TyLoweringContext::new( db, &resolver, - &data.store, - def.into(), + generics.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ); + + // we have to filter out all other predicates *first*, before attempting to lower them + let predicate = |pred: &_, ctx: &mut TyLoweringContext<'_, '_>| match pred { + WherePredicate::ForLifetime { target, bound, .. } + | WherePredicate::TypeBound { target, bound, .. } => { + let invalid_target = { ctx.lower_ty_only_param(*target) != Some(param_id) }; + if invalid_target { + // FIXME(sized-hierarchy): Revisit and adjust this properly once we have implemented + // sized-hierarchy correctly. + // If this is filtered out without lowering, `?Sized` or `PointeeSized` is not gathered into + // `ctx.unsized_types` + let lower = || -> bool { + match bound { + TypeBound::Path(_, TraitBoundModifier::Maybe) => true, + TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => { + let TypeRef::Path(path) = &ctx.store[path.type_ref()] else { + return false; + }; + let Some(pointee_sized) = + LangItem::PointeeSized.resolve_trait(ctx.db, ctx.resolver.krate()) + else { + return false; + }; + // Lower the path directly with `Resolver` instead of PathLoweringContext` + // to prevent diagnostics duplications. + ctx.resolver.resolve_path_in_type_ns_fully(ctx.db, path).is_some_and( + |it| matches!(it, TypeNs::TraitId(tr) if tr == pointee_sized), + ) + } + _ => false, + } + }(); + if lower { + ctx.lower_where_predicate(pred, true, &generics, PredicateFilter::All) + .for_each(drop); + } + return false; + } + + match bound { + &TypeBound::ForLifetime(_, path) | &TypeBound::Path(path, _) => { + // Only lower the bound if the trait could possibly define the associated + // type we're looking for. + let path = &ctx.store[path]; + + let Some(assoc_name) = &assoc_name else { return true }; + let Some(TypeNs::TraitId(tr)) = + resolver.resolve_path_in_type_ns_fully(db, path) + else { + return false; + }; + + rustc_type_ir::elaborate::supertrait_def_ids(interner, tr.into()).any(|tr| { + tr.0.trait_items(db).items.iter().any(|(name, item)| { + matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name + }) + }) + } + TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false, + } + } + WherePredicate::Lifetime { .. } => false, + }; + let mut predicates = Vec::new(); + for maybe_parent_generics in + std::iter::successors(Some(&generics), |generics| generics.parent_generics()) + { + ctx.store = maybe_parent_generics.store(); + for pred in maybe_parent_generics.where_predicates() { + if predicate(pred, &mut ctx) { + predicates.extend(ctx.lower_where_predicate( + pred, + true, + maybe_parent_generics, + PredicateFilter::All, + )); + } + } + } + + let args = GenericArgs::identity_for_item(interner, def.into()); + if !args.is_empty() { + let explicitly_unsized_tys = ctx.unsized_types; + if let Some(implicitly_sized_predicates) = + implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &args, &resolver) + { + predicates.extend(implicitly_sized_predicates); + }; + } + GenericPredicates(predicates.is_empty().not().then(|| predicates.into())) +} + +pub(crate) fn generic_predicates_for_param_cycle_result( + _db: &dyn HirDatabase, + _def: GenericDefId, + _param_id: TypeOrConstParamId, + _assoc_name: Option, +) -> GenericPredicates<'_> { + GenericPredicates(None) +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GenericPredicates<'db>(Option]>>); + +impl<'db> GenericPredicates<'db> { + #[inline] + pub fn instantiate( + &self, + interner: DbInterner<'db>, + args: GenericArgs<'db>, + ) -> Option>> { + self.0 + .as_ref() + .map(|it| EarlyBinder::bind(it.iter().copied()).iter_instantiated(interner, args)) + } + + #[inline] + pub fn instantiate_identity(&self) -> Option>> { + self.0.as_ref().map(|it| it.iter().copied()) + } +} + +impl<'db> ops::Deref for GenericPredicates<'db> { + type Target = [Clause<'db>]; + + fn deref(&self) -> &Self::Target { + self.0.as_deref().unwrap_or(&[]) + } +} + +pub(crate) fn trait_environment_for_body_query( + db: &dyn HirDatabase, + def: DefWithBodyId, +) -> Arc> { + let Some(def) = def.as_generic_def_id(db) else { + let krate = def.module(db).krate(); + return TraitEnvironment::empty(krate); + }; + db.trait_environment(def) +} + +pub(crate) fn trait_environment_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> Arc> { + let generics = generics(db, def); + if generics.has_no_predicates() && generics.is_empty() { + return TraitEnvironment::empty(def.krate(db)); + } + + let resolver = def.resolver(db); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generics.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ); + let mut traits_in_scope = Vec::new(); + let mut clauses = Vec::new(); + for maybe_parent_generics in + std::iter::successors(Some(&generics), |generics| generics.parent_generics()) + { + ctx.store = maybe_parent_generics.store(); + for pred in maybe_parent_generics.where_predicates() { + for pred in ctx.lower_where_predicate(pred, false, &generics, PredicateFilter::All) { + if let rustc_type_ir::ClauseKind::Trait(tr) = pred.kind().skip_binder() { + traits_in_scope.push((tr.self_ty(), tr.def_id().0)); + } + clauses.push(pred); + } + } + } + + if let Some(trait_id) = def.assoc_trait_container(db) { + // add `Self: Trait` to the environment in trait + // function default implementations (and speculative code + // inside consts or type aliases) + cov_mark::hit!(trait_self_implements_self); + let trait_ref = TraitRef::identity(ctx.interner, trait_id.into()); + let clause = Clause(Predicate::new( + ctx.interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait( + TraitPredicate { trait_ref, polarity: rustc_type_ir::PredicatePolarity::Positive }, + ))), + )); + clauses.push(clause); + } + + let explicitly_unsized_tys = ctx.unsized_types; + + let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); + if let Some(sized_trait) = sized_trait { + let (mut generics, mut def_id) = + (crate::next_solver::generics::generics(db, def.into()), def); + loop { + let self_idx = trait_self_param_idx(db, def_id); + for (idx, p) in generics.own_params.iter().enumerate() { + if let Some(self_idx) = self_idx + && p.index() as usize == self_idx + { + continue; + } + let GenericParamId::TypeParamId(param_id) = p.id else { + continue; + }; + let idx = idx as u32 + generics.parent_count as u32; + let param_ty = Ty::new_param(ctx.interner, param_id, idx); + if explicitly_unsized_tys.contains(¶m_ty) { + continue; + } + let trait_ref = TraitRef::new_from_args( + ctx.interner, + sized_trait.into(), + GenericArgs::new_from_iter(ctx.interner, [param_ty.into()]), + ); + let clause = Clause(Predicate::new( + ctx.interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )); + clauses.push(clause); + } + + if let Some(g) = generics.parent { + generics = crate::next_solver::generics::generics(db, g.into()); + def_id = g; + } else { + break; + } + } + } + + let clauses = rustc_type_ir::elaborate::elaborate(ctx.interner, clauses); + let clauses = Clauses::new_from_iter(ctx.interner, clauses); + let env = ParamEnv { clauses }; + + TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env) +} + +#[derive(Copy, Clone, Debug)] +pub(crate) enum PredicateFilter { + SelfTrait, + All, +} + +/// Resolve the where clause(s) of an item with generics. +#[tracing::instrument(skip(db))] +pub(crate) fn generic_predicates_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> GenericPredicates<'db> { + generic_predicates_filtered_by(db, def, PredicateFilter::All, |_| true).0 +} + +pub(crate) fn generic_predicates_without_parent_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> GenericPredicates<'db> { + generic_predicates_filtered_by(db, def, PredicateFilter::All, |d| d == def).0 +} + +/// Resolve the where clause(s) of an item with generics, +/// except the ones inherited from the parent +pub(crate) fn generic_predicates_without_parent_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> (GenericPredicates<'db>, Diagnostics) { + generic_predicates_filtered_by(db, def, PredicateFilter::All, |d| d == def) +} + +/// Resolve the where clause(s) of an item with generics, +/// with a given filter +#[tracing::instrument(skip(db, filter), ret)] +pub(crate) fn generic_predicates_filtered_by<'db, F>( + db: &'db dyn HirDatabase, + def: GenericDefId, + predicate_filter: PredicateFilter, + filter: F, +) -> (GenericPredicates<'db>, Diagnostics) +where + F: Fn(GenericDefId) -> bool, +{ + let generics = generics(db, def); + let resolver = def.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generics.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ); + + let mut predicates = Vec::new(); + for maybe_parent_generics in + std::iter::successors(Some(&generics), |generics| generics.parent_generics()) + { + ctx.store = maybe_parent_generics.store(); + for pred in maybe_parent_generics.where_predicates() { + tracing::debug!(?pred); + if filter(maybe_parent_generics.def()) { + predicates.extend(ctx.lower_where_predicate( + pred, + false, + maybe_parent_generics, + predicate_filter, + )); + } + } + } + + let explicitly_unsized_tys = ctx.unsized_types; + + let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); + if let Some(sized_trait) = sized_trait { + let mut add_sized_clause = |param_idx, param_id, param_data| { + let ( + GenericParamId::TypeParamId(param_id), + GenericParamDataRef::TypeParamData(param_data), + ) = (param_id, param_data) + else { + return; + }; + + if param_data.provenance == TypeParamProvenance::TraitSelf { + return; + } + + let param_ty = Ty::new_param(interner, param_id, param_idx); + if explicitly_unsized_tys.contains(¶m_ty) { + return; + } + let trait_ref = TraitRef::new_from_args( + interner, + sized_trait.into(), + GenericArgs::new_from_iter(interner, [param_ty.into()]), + ); + let clause = Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )); + predicates.push(clause); + }; + if generics.parent_generics().is_some_and(|parent| filter(parent.def())) { + generics.iter_parent().enumerate().for_each(|(param_idx, (param_id, param_data))| { + add_sized_clause(param_idx as u32, param_id, param_data); + }); + } + if filter(def) { + let parent_params_len = generics.len_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); + }); + } + } + + // FIXME: rustc gathers more predicates by recursing through resulting trait predicates. + // See https://github.com/rust-lang/rust/blob/76c5ed2847cdb26ef2822a3a165d710f6b772217/compiler/rustc_hir_analysis/src/collect/predicates_of.rs#L689-L715 + + ( + GenericPredicates(predicates.is_empty().not().then(|| predicates.into())), + create_diagnostics(ctx.diagnostics), + ) +} + +/// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound. +/// Exception is Self of a trait def. +fn implicitly_sized_clauses<'a, 'subst, 'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, + explicitly_unsized_tys: &'a FxHashSet>, + args: &'subst GenericArgs<'db>, + resolver: &Resolver<'db>, +) -> Option> + Captures<'a> + Captures<'subst>> { + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate())?; + + let trait_self_idx = trait_self_param_idx(db, def); + + Some( + args.iter() + .enumerate() + .filter_map( + move |(idx, generic_arg)| { + if Some(idx) == trait_self_idx { None } else { Some(generic_arg) } + }, + ) + .filter_map(|generic_arg| generic_arg.as_type()) + .filter(move |self_ty| !explicitly_unsized_tys.contains(self_ty)) + .map(move |self_ty| { + let trait_ref = TraitRef::new_from_args( + interner, + sized_trait.into(), + GenericArgs::new_from_iter(interner, [self_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) + }), + ) +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GenericDefaults<'db>(Option>>]>>); + +impl<'db> GenericDefaults<'db> { + #[inline] + pub fn get(&self, idx: usize) -> Option>> { + self.0.as_ref()?[idx] + } +} + +pub(crate) fn generic_defaults_query( + db: &dyn HirDatabase, + def: GenericDefId, +) -> GenericDefaults<'_> { + db.generic_defaults_with_diagnostics(def).0 +} + +/// Resolve the default type params from generics. +/// +/// Diagnostics are only returned for this `GenericDefId` (returned defaults include parents). +pub(crate) fn generic_defaults_with_diagnostics_query( + db: &dyn HirDatabase, + def: GenericDefId, +) -> (GenericDefaults<'_>, Diagnostics) { + let generic_params = generics(db, def); + if generic_params.is_empty() { + return (GenericDefaults(None), None); + } + let resolver = def.resolver(db); + + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generic_params.store(), + def, LifetimeElisionKind::AnonymousReportError, ) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) - .with_type_param_mode(ParamLoweringMode::Variable); - if let Some(type_ref) = data.ty { - let _ty = ctx.lower_ty(type_ref); - } - let type_alias_impl_traits = ImplTraits { impl_traits: ctx.impl_trait_mode.opaque_type_data }; - if type_alias_impl_traits.impl_traits.is_empty() { - None + .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 (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. + defaults.extend(generic_params.iter_self().map(|(_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)); + let defaults = if has_any_default { + GenericDefaults(Some(Arc::from_iter(defaults))) } else { - let generics = generics(db, def.into()); - Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits))) + GenericDefaults(None) + }; + return (defaults, diagnostics); + + fn handle_generic_param<'db>( + ctx: &mut TyLoweringContext<'db, '_>, + idx: usize, + p: GenericParamDataRef<'_>, + ) -> (Option>>, bool) { + ctx.lowering_param_default(idx as u32); + match p { + GenericParamDataRef::TypeParamData(p) => { + let ty = p.default.map(|ty| ctx.lower_ty(ty)); + (ty.map(|ty| EarlyBinder::bind(ty.into())), p.default.is_some()) + } + GenericParamDataRef::ConstParamData(p) => { + let val = p.default.map(|c| { + let param_ty = ctx.lower_ty(p.ty); + let c = ctx.lower_const(c, param_ty); + c.into() + }); + (val.map(EarlyBinder::bind), p.default.is_some()) + } + GenericParamDataRef::LifetimeParamData(_) => (None, false), + } } } -pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mutability { - match m { - hir_def::type_ref::Mutability::Shared => Mutability::Not, - hir_def::type_ref::Mutability::Mut => Mutability::Mut, +pub(crate) fn generic_defaults_with_diagnostics_cycle_result( + _db: &dyn HirDatabase, + _def: GenericDefId, +) -> (GenericDefaults<'_>, Diagnostics) { + (GenericDefaults(None), None) +} + +/// Build the signature of a callable item (function, struct or enum variant). +pub(crate) fn callable_item_signature_query<'db>( + db: &'db dyn HirDatabase, + def: CallableDefId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + match def { + CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f), + CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s), + CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e), } } -/// Replaces any 'free' `BoundVar`s in `s` by `TyKind::Error` from the perspective of generic -/// parameter whose index is `param_index`. A `BoundVar` is free when it appears after the -/// generic parameter of `param_index`. -fn fallback_bound_vars + HasInterner>( - s: T, - param_index: usize, -) -> T { - let is_allowed = |index| (0..param_index).contains(&index); +fn fn_sig_for_fn<'db>( + db: &'db dyn HirDatabase, + def: FunctionId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + let data = db.function_signature(def); + let resolver = def.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let mut ctx_params = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::for_fn_params(&data), + ); + let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr)); - crate::fold_free_vars( - s, - |bound, binders| { - if bound.index_if_innermost().is_none_or(is_allowed) { - bound.shifted_in_from(binders).to_ty(Interner) - } else { - TyKind::Error.intern(Interner) - } - }, - |ty, bound, binders| { - if bound.index_if_innermost().is_none_or(is_allowed) { - bound.shifted_in_from(binders).to_const(Interner, ty) - } else { - unknown_const(ty) - } - }, - ) + let ret = match data.ret_type { + Some(ret_type) => { + let mut ctx_ret = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::for_fn_ret(interner), + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + ctx_ret.lower_ty(ret_type) + } + None => Ty::new_tup(interner, &[]), + }; + + let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret))); + // If/when we track late bound vars, we need to switch this to not be `dummy` + EarlyBinder::bind(rustc_type_ir::Binder::dummy(FnSig { + abi: data.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), + c_variadic: data.is_varargs(), + safety: if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe }, + inputs_and_output, + })) +} + +fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, Ty<'db>> { + let interner = DbInterner::new_with(db, None, None); + let args = GenericArgs::identity_for_item(interner, adt.into()); + let ty = Ty::new_adt(interner, adt, args); + EarlyBinder::bind(ty) +} + +fn fn_sig_for_struct_constructor<'db>( + db: &'db dyn HirDatabase, + def: StructId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + let field_tys = db.field_types(def.into()); + let params = field_tys.iter().map(|(_, ty)| ty.skip_binder()); + let ret = type_for_adt(db, def.into()).skip_binder(); + + let inputs_and_output = + Tys::new_from_iter(DbInterner::new_with(db, None, None), params.chain(Some(ret))); + EarlyBinder::bind(Binder::dummy(FnSig { + abi: FnAbi::RustCall, + c_variadic: false, + safety: Safety::Safe, + inputs_and_output, + })) +} + +fn fn_sig_for_enum_variant_constructor<'db>( + db: &'db dyn HirDatabase, + def: EnumVariantId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + let field_tys = db.field_types(def.into()); + let params = field_tys.iter().map(|(_, ty)| ty.skip_binder()); + let parent = def.lookup(db).parent; + let ret = type_for_adt(db, parent.into()).skip_binder(); + + let inputs_and_output = + Tys::new_from_iter(DbInterner::new_with(db, None, None), params.chain(Some(ret))); + EarlyBinder::bind(Binder::dummy(FnSig { + abi: FnAbi::RustCall, + c_variadic: false, + safety: Safety::Safe, + inputs_and_output, + })) +} + +// FIXME(next-solver): should merge this with `explicit_item_bounds` in some way +pub(crate) fn associated_ty_item_bounds<'db>( + db: &'db dyn HirDatabase, + type_alias: TypeAliasId, +) -> EarlyBinder<'db, BoundExistentialPredicates<'db>> { + let type_alias_data = db.type_alias_signature(type_alias); + let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &type_alias_data.store, + type_alias.into(), + LifetimeElisionKind::AnonymousReportError, + ); + // FIXME: we should never create non-existential predicates in the first place + // For now, use an error type so we don't run into dummy binder issues + let self_ty = Ty::new_error(interner, ErrorGuaranteed); + + let mut bounds = Vec::new(); + for bound in &type_alias_data.bounds { + ctx.lower_type_bound(bound, self_ty, false).for_each(|pred| { + if let Some(bound) = pred + .kind() + .map_bound(|c| match c { + rustc_type_ir::ClauseKind::Trait(t) => { + let id = t.def_id(); + let is_auto = db.trait_signature(id.0).flags.contains(TraitFlags::AUTO); + if is_auto { + Some(ExistentialPredicate::AutoTrait(t.def_id())) + } else { + Some(ExistentialPredicate::Trait(ExistentialTraitRef::new_from_args( + interner, + t.def_id(), + GenericArgs::new_from_iter( + interner, + t.trait_ref.args.iter().skip(1), + ), + ))) + } + } + rustc_type_ir::ClauseKind::Projection(p) => Some( + ExistentialPredicate::Projection(ExistentialProjection::new_from_args( + interner, + p.def_id(), + GenericArgs::new_from_iter( + interner, + p.projection_term.args.iter().skip(1), + ), + p.term, + )), + ), + rustc_type_ir::ClauseKind::TypeOutlives(_) => None, + rustc_type_ir::ClauseKind::RegionOutlives(_) + | rustc_type_ir::ClauseKind::ConstArgHasType(_, _) + | rustc_type_ir::ClauseKind::WellFormed(_) + | rustc_type_ir::ClauseKind::ConstEvaluatable(_) + | rustc_type_ir::ClauseKind::HostEffect(_) + | rustc_type_ir::ClauseKind::UnstableFeature(_) => unreachable!(), + }) + .transpose() + { + bounds.push(bound); + } + }); + } + + if !ctx.unsized_types.contains(&self_ty) + && let Some(sized_trait) = LangItem::Sized.resolve_trait(db, resolver.krate()) + { + let sized_clause = Binder::dummy(ExistentialPredicate::Trait(ExistentialTraitRef::new( + interner, + sized_trait.into(), + [] as [GenericArg<'_>; 0], + ))); + bounds.push(sized_clause); + } + + EarlyBinder::bind(BoundExistentialPredicates::new_from_iter(interner, bounds)) +} + +pub(crate) fn associated_type_by_name_including_super_traits<'db>( + db: &'db dyn HirDatabase, + trait_ref: TraitRef<'db>, + name: &Name, +) -> Option<(TraitRef<'db>, TypeAliasId)> { + let interner = DbInterner::new_with(db, None, None); + rustc_type_ir::elaborate::supertraits(interner, Binder::dummy(trait_ref)).find_map(|t| { + let trait_id = t.as_ref().skip_binder().def_id.0; + let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?; + Some((t.skip_binder(), assoc_type)) + }) +} + +pub fn associated_type_shorthand_candidates( + db: &dyn HirDatabase, + def: GenericDefId, + res: TypeNs, + mut cb: impl FnMut(&Name, TypeAliasId) -> bool, +) -> Option { + let interner = DbInterner::new_with(db, None, None); + named_associated_type_shorthand_candidates(interner, def, res, None, |name, _, id| { + cb(name, id).then_some(id) + }) +} + +#[tracing::instrument(skip(interner, check_alias))] +fn named_associated_type_shorthand_candidates<'db, R>( + interner: DbInterner<'db>, + // If the type parameter is defined in an impl and we're in a method, there + // might be additional where clauses to consider + def: GenericDefId, + res: TypeNs, + assoc_name: Option, + mut check_alias: impl FnMut(&Name, TraitRef<'db>, TypeAliasId) -> Option, +) -> Option { + let db = interner.db; + let mut search = |t: TraitRef<'db>| -> Option { + let mut checked_traits = FxHashSet::default(); + let mut check_trait = |trait_ref: TraitRef<'db>| { + let trait_id = trait_ref.def_id.0; + let name = &db.trait_signature(trait_id).name; + tracing::debug!(?trait_id, ?name); + if !checked_traits.insert(trait_id) { + return None; + } + let data = trait_id.trait_items(db); + + tracing::debug!(?data.items); + for (name, assoc_id) in &data.items { + if let &AssocItemId::TypeAliasId(alias) = assoc_id + && let Some(ty) = check_alias(name, trait_ref, alias) + { + return Some(ty); + } + } + None + }; + let mut stack: SmallVec<[_; 4]> = smallvec![t]; + while let Some(trait_ref) = stack.pop() { + if let Some(alias) = check_trait(trait_ref) { + return Some(alias); + } + for pred in generic_predicates_filtered_by( + db, + GenericDefId::TraitId(trait_ref.def_id.0), + PredicateFilter::SelfTrait, + // We are likely in the midst of lowering generic predicates of `def`. + // So, if we allow `pred == def` we might fall into an infinite recursion. + // Actually, we have already checked for the case `pred == def` above as we started + // with a stack including `trait_id` + |pred| pred != def && pred == GenericDefId::TraitId(trait_ref.def_id.0), + ) + .0 + .deref() + { + tracing::debug!(?pred); + let sup_trait_ref = match pred.kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(pred) => pred.trait_ref, + _ => continue, + }; + let sup_trait_ref = + EarlyBinder::bind(sup_trait_ref).instantiate(interner, trait_ref.args); + stack.push(sup_trait_ref); + } + tracing::debug!(?stack); + } + + None + }; + + match res { + TypeNs::SelfType(impl_id) => { + let trait_ref = db.impl_trait(impl_id)?; + + // FIXME(next-solver): same method in `lower` checks for impl or not + // Is that needed here? + + // we're _in_ the impl -- the binders get added back later. Correct, + // but it would be nice to make this more explicit + search(trait_ref.skip_binder()) + } + TypeNs::GenericParam(param_id) => { + // Handle `Self::Type` referring to own associated type in trait definitions + // This *must* be done first to avoid cycles with + // `generic_predicates_for_param`, but not sure that it's sufficient, + if let GenericDefId::TraitId(trait_id) = param_id.parent() { + let trait_name = &db.trait_signature(trait_id).name; + tracing::debug!(?trait_name); + let trait_generics = generics(db, trait_id.into()); + tracing::debug!(?trait_generics); + if trait_generics[param_id.local_id()].is_trait_self() { + let args = GenericArgs::identity_for_item(interner, trait_id.into()); + let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); + tracing::debug!(?args, ?trait_ref); + return search(trait_ref); + } + } + + let predicates = + db.generic_predicates_for_param(def, param_id.into(), assoc_name.clone()); + predicates + .iter() + .find_map(|pred| match (*pred).kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(trait_predicate) => Some(trait_predicate), + _ => None, + }) + .and_then(|trait_predicate| { + let trait_ref = trait_predicate.trait_ref; + assert!( + !trait_ref.has_escaping_bound_vars(), + "FIXME unexpected higher-ranked trait bound" + ); + search(trait_ref) + }) + } + _ => None, + } } 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 42723dc9e1dd..9ba0da6f4964 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 @@ -1,42 +1,52 @@ //! A wrapper around [`TyLoweringContext`] specifically for lowering paths. -use chalk_ir::{BoundVar, cast::Cast, fold::Shift}; use either::Either; use hir_def::{ - GenericDefId, GenericParamId, TraitId, + GenericDefId, GenericParamId, Lookup, TraitId, TypeAliasId, expr_store::{ - ExpressionStore, - path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments}, + ExpressionStore, HygieneId, + path::{ + GenericArg as HirGenericArg, GenericArgs as HirGenericArgs, GenericArgsParentheses, + Path, PathSegment, PathSegments, + }, }, hir::generics::{ GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance, }, - resolver::TypeNs, + resolver::{ResolveValueResult, TypeNs, ValueNs}, signatures::TraitFlags, type_ref::{TypeRef, TypeRefId}, }; +use hir_expand::name::Name; +use rustc_type_ir::{ + AliasTerm, AliasTy, AliasTyKind, + inherent::{GenericArgs as _, Region as _, SliceLike, Ty as _}, +}; use smallvec::SmallVec; use stdx::never; use crate::{ - AliasEq, AliasTy, GenericArgsProhibitedReason, ImplTraitLoweringMode, IncorrectGenericsLenKind, - Interner, ParamLoweringMode, PathGenericsSource, PathLoweringDiagnostic, ProjectionTy, - QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyDefId, TyKind, WhereClause, - consteval_chalk::{unknown_const, unknown_const_as_generic}, + GenericArgsProhibitedReason, IncorrectGenericsLenKind, PathGenericsSource, + PathLoweringDiagnostic, TyDefId, ValueTyDefId, + consteval::{unknown_const, unknown_const_as_generic}, db::HirDatabase, - error_lifetime, generics::{Generics, generics}, - lower::{LifetimeElisionKind, TyLoweringContext, named_associated_type_shorthand_candidates}, - next_solver::{ - DbInterner, - mapping::{ChalkToNextSolver, NextSolverToChalk}, + lower::{ + LifetimeElisionKind, PathDiagnosticCallbackData, named_associated_type_shorthand_candidates, }, - to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, - utils::associated_type_by_name_including_super_traits, + next_solver::{ + Binder, Clause, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Predicate, + ProjectionPredicate, Region, TraitRef, Ty, + }, +}; + +use super::{ + ImplTraitLoweringMode, TyLoweringContext, associated_type_by_name_including_super_traits, + const_param_ty_query, ty_query, }; type CallbackData<'a, 'db> = Either< - super::PathDiagnosticCallbackData, + PathDiagnosticCallbackData, crate::infer::diagnostics::PathDiagnosticCallbackData<'a, 'db>, >; @@ -45,12 +55,12 @@ type CallbackData<'a, 'db> = Either< pub(crate) struct PathDiagnosticCallback<'a, 'db> { pub(crate) data: CallbackData<'a, 'db>, pub(crate) callback: - fn(&CallbackData<'_, 'db>, &mut TyLoweringContext<'_>, PathLoweringDiagnostic), + fn(&CallbackData<'_, 'db>, &mut TyLoweringContext<'db, '_>, PathLoweringDiagnostic), } -pub(crate) struct PathLoweringContext<'a, 'b> { - ctx: &'a mut TyLoweringContext<'b>, - on_diagnostic: PathDiagnosticCallback<'a, 'b>, +pub(crate) struct PathLoweringContext<'a, 'b, 'db> { + ctx: &'a mut TyLoweringContext<'db, 'b>, + on_diagnostic: PathDiagnosticCallback<'a, 'db>, path: &'a Path, segments: PathSegments<'a>, current_segment_idx: usize, @@ -58,11 +68,11 @@ pub(crate) struct PathLoweringContext<'a, 'b> { current_or_prev_segment: PathSegment<'a>, } -impl<'a, 'b> PathLoweringContext<'a, 'b> { +impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { #[inline] pub(crate) fn new( - ctx: &'a mut TyLoweringContext<'b>, - on_diagnostic: PathDiagnosticCallback<'a, 'b>, + ctx: &'a mut TyLoweringContext<'db, 'b>, + on_diagnostic: PathDiagnosticCallback<'a, 'db>, path: &'a Path, ) -> Self { let segments = path.segments(); @@ -84,7 +94,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { } #[inline] - pub(crate) fn ty_ctx(&mut self) -> &mut TyLoweringContext<'b> { + pub(crate) fn ty_ctx(&mut self) -> &mut TyLoweringContext<'db, 'b> { self.ctx } @@ -109,11 +119,25 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { self.segments.get(self.current_segment_idx).unwrap_or(self.current_or_prev_segment); } + #[inline] + pub(crate) fn ignore_last_segment(&mut self) { + self.segments = self.segments.strip_last(); + } + + #[inline] + pub(crate) fn set_current_segment(&mut self, segment: usize) { + self.current_segment_idx = segment; + self.current_or_prev_segment = self + .segments + .get(segment) + .expect("invalid segment passed to PathLoweringContext::set_current_segment()"); + } + #[inline] fn with_lifetime_elision( &mut self, - lifetime_elision: LifetimeElisionKind, - f: impl FnOnce(&mut PathLoweringContext<'_, '_>) -> T, + lifetime_elision: LifetimeElisionKind<'db>, + f: impl FnOnce(&mut PathLoweringContext<'_, '_, 'db>) -> T, ) -> T { let old_lifetime_elision = std::mem::replace(&mut self.ctx.lifetime_elision, lifetime_elision); @@ -124,12 +148,13 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { pub(crate) fn lower_ty_relative_path( &mut self, - ty: Ty, + ty: Ty<'db>, // We need the original resolution to lower `Self::AssocTy` correctly res: Option, infer_args: bool, - ) -> (Ty, Option) { - match self.segments.len() - self.current_segment_idx { + ) -> (Ty<'db>, Option) { + let remaining_segments = self.segments.len() - self.current_segment_idx; + match remaining_segments { 0 => (ty, res), 1 => { // resolve unselected assoc types @@ -137,7 +162,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { } _ => { // FIXME report error (ambiguous associated type) - (TyKind::Error.intern(Interner), None) + (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None) } } } @@ -147,8 +172,11 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { &mut self, resolution: TypeNs, infer_args: bool, - ) -> (Ty, Option) { + ) -> (Ty<'db>, Option) { let remaining_segments = self.segments.skip(self.current_segment_idx + 1); + tracing::debug!(?remaining_segments); + let rem_seg_len = remaining_segments.len(); + tracing::debug!(?rem_seg_len); let ty = match resolution { TypeNs::TraitId(trait_) => { @@ -156,15 +184,17 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { 1 => { let trait_ref = self.lower_trait_ref_from_resolved_path( trait_, - TyKind::Error.intern(Interner), - infer_args, + Ty::new_error(self.ctx.interner, ErrorGuaranteed), + false, ); - + tracing::debug!(?trait_ref); self.skip_resolved_segment(); let segment = self.current_or_prev_segment; + let trait_id = trait_ref.def_id.0; let found = - trait_.trait_items(self.ctx.db).associated_type_by_name(segment.name); + trait_id.trait_items(self.ctx.db).associated_type_by_name(segment.name); + tracing::debug!(?found); match found { Some(associated_ty) => { // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent @@ -173,27 +203,30 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { // this point (`trait_ref.substitution`). let substitution = self.substs_from_path_segment( associated_ty.into(), - infer_args, + false, None, true, ); - let substitution = Substitution::from_iter( - Interner, - trait_ref.substitution.iter(Interner).chain( - substitution - .iter(Interner) - .skip(trait_ref.substitution.len(Interner)), - ), + let args = GenericArgs::new_from_iter( + self.ctx.interner, + trait_ref + .args + .iter() + .chain(substitution.iter().skip(trait_ref.args.len())), ); - TyKind::Alias(AliasTy::Projection(ProjectionTy { - associated_ty_id: to_assoc_type_id(associated_ty), - substitution, - })) - .intern(Interner) + Ty::new_alias( + self.ctx.interner, + AliasTyKind::Projection, + AliasTy::new_from_args( + self.ctx.interner, + associated_ty.into(), + args, + ), + ) } None => { // FIXME: report error (associated type not found) - TyKind::Error.intern(Interner) + Ty::new_error(self.ctx.interner, ErrorGuaranteed) } } } @@ -201,73 +234,34 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { // Trait object type without dyn; this should be handled in upstream. See // `lower_path()`. stdx::never!("unexpected fully resolved trait path"); - TyKind::Error.intern(Interner) + Ty::new_error(self.ctx.interner, ErrorGuaranteed) } _ => { // FIXME report error (ambiguous associated type) - TyKind::Error.intern(Interner) + Ty::new_error(self.ctx.interner, ErrorGuaranteed) } }; return (ty, None); } - TypeNs::GenericParam(param_id) => match self.ctx.type_param_mode { - ParamLoweringMode::Placeholder => { - let generics = self.ctx.generics(); - let idx = generics.type_or_const_param_idx(param_id.into()).unwrap(); - TyKind::Placeholder(to_placeholder_idx( - self.ctx.db, - param_id.into(), - idx as u32, - )) - } - ParamLoweringMode::Variable => { - let idx = match self.ctx.generics().type_or_const_param_idx(param_id.into()) { - None => { - never!("no matching generics"); - return (TyKind::Error.intern(Interner), None); - } - Some(idx) => idx, - }; - - TyKind::BoundVar(BoundVar::new(self.ctx.in_binders, idx)) - } - } - .intern(Interner), - TypeNs::SelfType(impl_id) => { + TypeNs::GenericParam(param_id) => { let generics = self.ctx.generics(); - - match self.ctx.type_param_mode { - ParamLoweringMode::Placeholder => { - // `def` can be either impl itself or item within, and we need impl itself - // now. - let generics = generics.parent_or_self(); - let interner = DbInterner::new_with(self.ctx.db, None, None); - let subst = generics.placeholder_subst(self.ctx.db); - let args: crate::next_solver::GenericArgs<'_> = - subst.to_nextsolver(interner); - self.ctx - .db - .impl_self_ty(impl_id) - .instantiate(interner, args) - .to_chalk(interner) + 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) } - ParamLoweringMode::Variable => TyBuilder::impl_self_ty(self.ctx.db, impl_id) - .fill_with_bound_vars(self.ctx.in_binders, 0) - .build(DbInterner::conjure()) - .to_chalk(DbInterner::conjure()), } } + TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty(impl_id).skip_binder(), TypeNs::AdtSelfType(adt) => { - let generics = generics(self.ctx.db, adt.into()); - let substs = match self.ctx.type_param_mode { - ParamLoweringMode::Placeholder => generics.placeholder_subst(self.ctx.db), - ParamLoweringMode::Variable => { - generics.bound_vars_subst(self.ctx.db, self.ctx.in_binders) - } - }; - let interner = DbInterner::conjure(); - let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); - self.ctx.db.ty(adt.into()).instantiate(interner, args).to_chalk(interner) + let args = GenericArgs::identity_for_item(self.ctx.interner, adt.into()); + Ty::new_adt(self.ctx.interner, adt, args) } TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args), @@ -275,15 +269,19 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { TypeNs::TypeAliasId(it) => self.lower_path_inner(it.into(), infer_args), // FIXME: report error TypeNs::EnumVariantId(_) | TypeNs::ModuleId(_) => { - return (TyKind::Error.intern(Interner), None); + return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None); } }; + tracing::debug!(?ty); + self.skip_resolved_segment(); self.lower_ty_relative_path(ty, Some(resolution), infer_args) } - fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) { + /// This returns whether to keep the resolution (`true`) of throw it (`false`). + #[must_use] + fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) -> bool { let mut prohibit_generics_on_resolved = |reason| { if self.current_or_prev_segment.args_and_bindings.is_some() { let segment = self.current_segment_u32(); @@ -302,7 +300,13 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam) } TypeNs::AdtSelfType(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + 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. + return false; + } } TypeNs::BuiltinType(_) => { prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy) @@ -315,6 +319,8 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { | TypeNs::TypeAliasId(_) | TypeNs::TraitId(_) => {} } + + true } pub(crate) fn resolve_path_in_type_ns_fully(&mut self) -> Option { @@ -325,6 +331,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { Some(res) } + #[tracing::instrument(skip(self), ret)] pub(crate) fn resolve_path_in_type_ns(&mut self) -> Option<(TypeNs, Option)> { let (resolution, remaining_index, _, prefix_info) = self.ctx.resolver.resolve_path_in_type_ns_with_prefix_info(self.ctx.db, self.path)?; @@ -347,11 +354,6 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { self.current_or_prev_segment = segments.get(resolved_segment_idx).expect("should have resolved segment"); - if matches!(self.path, Path::BarePath(..)) { - // Bare paths cannot have generics, so skip them as an optimization. - return Some((resolution, remaining_index)); - } - for (i, mod_segment) in module_segments.iter().enumerate() { if mod_segment.args_and_bindings.is_some() { self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { @@ -371,90 +373,233 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { }); } - self.handle_type_ns_resolution(&resolution); + if !self.handle_type_ns_resolution(&resolution) { + return None; + } Some((resolution, remaining_index)) } - fn select_associated_type(&mut self, res: Option, infer_args: bool) -> Ty { - let Some(res) = res else { - return TyKind::Error.intern(Interner); - }; - let segment = self.current_or_prev_segment; - let ty = named_associated_type_shorthand_candidates( + pub(crate) fn resolve_path_in_value_ns( + &mut self, + hygiene_id: HygieneId, + ) -> Option { + let (res, prefix_info) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info( self.ctx.db, - self.ctx.def, - res, - Some(segment.name.clone()), - move |name, t, associated_ty| { - if name != segment.name { - return None; - } - let generics = self.ctx.generics(); + self.path, + hygiene_id, + )?; - let parent_subst = t.substitution.clone(); - let parent_subst = match self.ctx.type_param_mode { - ParamLoweringMode::Placeholder => { - // if we're lowering to placeholders, we have to put them in now. - let s = generics.placeholder_subst(self.ctx.db); - s.apply(parent_subst, Interner) - } - ParamLoweringMode::Variable => { - // We need to shift in the bound vars, since - // `named_associated_type_shorthand_candidates` does not do that. - parent_subst.shifted_in_from(Interner, self.ctx.in_binders) + let segments = self.segments; + if segments.is_empty() || matches!(self.path, Path::LangItem(..)) { + // `segments.is_empty()` can occur with `self`. + return Some(res); + } + + let (mod_segments, enum_segment, resolved_segment_idx) = match res { + ResolveValueResult::Partial(_, unresolved_segment, _) => { + (segments.take(unresolved_segment - 1), None, unresolved_segment - 1) + } + ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _) + if prefix_info.enum_variant => + { + (segments.strip_last_two(), segments.len().checked_sub(2), segments.len() - 1) + } + ResolveValueResult::ValueNs(..) => (segments.strip_last(), None, segments.len() - 1), + }; + + self.current_segment_idx = resolved_segment_idx; + self.current_or_prev_segment = + segments.get(resolved_segment_idx).expect("should have resolved segment"); + + for (i, mod_segment) in mod_segments.iter().enumerate() { + if mod_segment.args_and_bindings.is_some() { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: i as u32, + reason: GenericArgsProhibitedReason::Module, + }); + } + } + + if let Some(enum_segment) = enum_segment + && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) + && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) + { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: (enum_segment + 1) as u32, + reason: GenericArgsProhibitedReason::EnumVariant, + }); + } + + match &res { + ResolveValueResult::ValueNs(resolution, _) => { + let resolved_segment_idx = self.current_segment_u32(); + let resolved_segment = self.current_or_prev_segment; + + let mut prohibit_generics_on_resolved = |reason| { + if resolved_segment.args_and_bindings.is_some() { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: resolved_segment_idx, + reason, + }); } }; - // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent - // generic params. It's inefficient to splice the `Substitution`s, so we may want - // that method to optionally take parent `Substitution` as we already know them at - // this point (`t.substitution`). - let substs = - self.substs_from_path_segment(associated_ty.into(), infer_args, None, true); - - let substs = Substitution::from_iter( - Interner, - parent_subst - .iter(Interner) - .chain(substs.iter(Interner).skip(parent_subst.len(Interner))), - ); - - Some( - TyKind::Alias(AliasTy::Projection(ProjectionTy { - associated_ty_id: to_assoc_type_id(associated_ty), - substitution: substs, - })) - .intern(Interner), - ) - }, - ); - - ty.unwrap_or_else(|| TyKind::Error.intern(Interner)) + match resolution { + ValueNs::ImplSelf(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy); + } + // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not + // E0109 (generic arguments provided for a type that doesn't accept them) for + // consts and statics, presumably as a defense against future in which consts + // and statics can be generic, or just because it was easier for rustc implementors. + // That means we'll show the wrong error code. Because of us it's easier to do it + // this way :) + ValueNs::GenericParam(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const) + } + ValueNs::StaticId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static) + } + ValueNs::LocalBinding(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::LocalVariable) + } + ValueNs::FunctionId(_) + | ValueNs::StructId(_) + | ValueNs::EnumVariantId(_) + | ValueNs::ConstId(_) => {} + } + } + ResolveValueResult::Partial(resolution, _, _) => { + if !self.handle_type_ns_resolution(resolution) { + return None; + } + } + }; + Some(res) } - fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty { - let interner = DbInterner::conjure(); + #[tracing::instrument(skip(self), ret)] + fn select_associated_type(&mut self, res: Option, infer_args: bool) -> Ty<'db> { + let interner = self.ctx.interner; + let Some(res) = res else { + return Ty::new_error(self.ctx.interner, ErrorGuaranteed); + }; + let def = self.ctx.def; + let segment = self.current_or_prev_segment; + let assoc_name = segment.name; + let check_alias = |name: &Name, t: TraitRef<'db>, associated_ty: TypeAliasId| { + if name != assoc_name { + return None; + } + + // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent + // generic params. It's inefficient to splice the `Substitution`s, so we may want + // that method to optionally take parent `Substitution` as we already know them at + // this point (`t.substitution`). + let substs = + self.substs_from_path_segment(associated_ty.into(), infer_args, None, true); + + let substs = GenericArgs::new_from_iter( + interner, + t.args.iter().chain(substs.iter().skip(t.args.len())), + ); + + Some(Ty::new_alias( + interner, + AliasTyKind::Projection, + AliasTy::new(interner, associated_ty.into(), substs), + )) + }; + named_associated_type_shorthand_candidates( + interner, + def, + res, + Some(assoc_name.clone()), + check_alias, + ) + .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) + } + + fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> { let generic_def = match typeable { - TyDefId::BuiltinType(builtin) => { - return crate::next_solver::Ty::from_builtin_type(interner, builtin) - .to_chalk(interner); + TyDefId::BuiltinType(builtinty) => { + return Ty::from_builtin_type(self.ctx.interner, builtinty); } TyDefId::AdtId(it) => it.into(), TyDefId::TypeAliasId(it) => it.into(), }; - let substs = self.substs_from_path_segment(generic_def, infer_args, None, false); - let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); - self.ctx.db.ty(typeable).instantiate(interner, args).to_chalk(interner) + let args = self.substs_from_path_segment(generic_def, infer_args, None, false); + let ty = ty_query(self.ctx.db, typeable); + ty.instantiate(self.ctx.interner, args) + } + + /// Collect generic arguments from a path into a `Substs`. See also + /// `create_substs_for_ast_path` and `def_to_ty` in rustc. + pub(crate) fn substs_from_path( + &mut self, + // Note that we don't call `db.value_type(resolved)` here, + // `ValueTyDefId` is just a convenient way to pass generics and + // special-case enum variants + resolved: ValueTyDefId, + infer_args: bool, + lowering_assoc_type_generics: bool, + ) -> GenericArgs<'db> { + let interner = self.ctx.interner; + let prev_current_segment_idx = self.current_segment_idx; + let prev_current_segment = self.current_or_prev_segment; + + let generic_def = match resolved { + ValueTyDefId::FunctionId(it) => it.into(), + ValueTyDefId::StructId(it) => it.into(), + ValueTyDefId::UnionId(it) => it.into(), + ValueTyDefId::ConstId(it) => it.into(), + ValueTyDefId::StaticId(_) => { + return GenericArgs::new_from_iter(interner, []); + } + ValueTyDefId::EnumVariantId(var) => { + // the generic args for an enum variant may be either specified + // on the segment referring to the enum, or on the segment + // referring to the variant. So `Option::::None` and + // `Option::None::` are both allowed (though the former is + // FIXME: This isn't strictly correct, enum variants may be used not through the enum + // (via `use Enum::Variant`). The resolver returns whether they were, but we don't have its result + // available here. The worst that can happen is that we will show some confusing diagnostics to the user, + // if generics exist on the module and they don't match with the variant. + // preferred). See also `def_ids_for_path_segments` in rustc. + // + // `wrapping_sub(1)` will return a number which `get` will return None for if current_segment_idx<2. + // This simplifies the code a bit. + let penultimate_idx = self.current_segment_idx.wrapping_sub(1); + let penultimate = self.segments.get(penultimate_idx); + if let Some(penultimate) = penultimate + && self.current_or_prev_segment.args_and_bindings.is_none() + && penultimate.args_and_bindings.is_some() + { + self.current_segment_idx = penultimate_idx; + self.current_or_prev_segment = penultimate; + } + var.lookup(self.ctx.db).parent.into() + } + }; + let result = self.substs_from_path_segment( + generic_def, + infer_args, + None, + lowering_assoc_type_generics, + ); + self.current_segment_idx = prev_current_segment_idx; + self.current_or_prev_segment = prev_current_segment; + result } pub(crate) fn substs_from_path_segment( &mut self, def: GenericDefId, infer_args: bool, - explicit_self_ty: Option, + explicit_self_ty: Option>, lowering_assoc_type_generics: bool, - ) -> Substitution { + ) -> GenericArgs<'db> { let old_lifetime_elision = self.ctx.lifetime_elision.clone(); if let Some(args) = self.current_or_prev_segment.args_and_bindings @@ -481,7 +626,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, ); - return TyBuilder::unknown_subst(self.ctx.db, def); + return unknown_subst(self.ctx.interner, def); } // `Fn()`-style generics are treated like functions for the purpose of lifetime elision. @@ -504,20 +649,20 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { pub(super) fn substs_from_args_and_bindings( &mut self, - args_and_bindings: Option<&GenericArgs>, + args_and_bindings: Option<&HirGenericArgs>, def: GenericDefId, infer_args: bool, - explicit_self_ty: Option, + explicit_self_ty: Option>, generics_source: PathGenericsSource, lowering_assoc_type_generics: bool, - lifetime_elision: LifetimeElisionKind, - ) -> Substitution { - struct LowererCtx<'a, 'b, 'c> { - ctx: &'a mut PathLoweringContext<'b, 'c>, + lifetime_elision: LifetimeElisionKind<'db>, + ) -> GenericArgs<'db> { + struct LowererCtx<'a, 'b, 'c, 'db> { + ctx: &'a mut PathLoweringContext<'b, 'c, 'db>, generics_source: PathGenericsSource, } - impl GenericArgsLowerer for LowererCtx<'_, '_, '_> { + impl<'db> GenericArgsLowerer<'db> for LowererCtx<'_, '_, '_, 'db> { fn report_len_mismatch( &mut self, def: GenericDefId, @@ -552,23 +697,24 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { &mut self, param_id: GenericParamId, param: GenericParamDataRef<'_>, - arg: &GenericArg, - ) -> crate::GenericArg { - match (param, arg) { - (GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => { - self.ctx.ctx.lower_lifetime(*lifetime).cast(Interner) + arg: &HirGenericArg, + ) -> GenericArg<'db> { + match (param, *arg) { + ( + GenericParamDataRef::LifetimeParamData(_), + HirGenericArg::Lifetime(lifetime), + ) => self.ctx.ctx.lower_lifetime(lifetime).into(), + (GenericParamDataRef::TypeParamData(_), HirGenericArg::Type(type_ref)) => { + self.ctx.ctx.lower_ty(type_ref).into() } - (GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => { - self.ctx.ctx.lower_ty(*type_ref).cast(Interner) - } - (GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => { + (GenericParamDataRef::ConstParamData(_), HirGenericArg::Const(konst)) => { let GenericParamId::ConstParamId(const_id) = param_id else { unreachable!("non-const param ID for const param"); }; self.ctx .ctx - .lower_const(konst, self.ctx.ctx.db.const_param_ty(const_id)) - .cast(Interner) + .lower_const(konst, const_param_ty_query(self.ctx.ctx.db, const_id)) + .into() } _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), } @@ -576,9 +722,9 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { fn provided_type_like_const( &mut self, - const_ty: Ty, + const_ty: Ty<'db>, arg: TypeLikeConst<'_>, - ) -> crate::Const { + ) -> Const<'db> { match arg { TypeLikeConst::Path(path) => self.ctx.ctx.lower_path_as_const(path, const_ty), TypeLikeConst::Infer => unknown_const(const_ty), @@ -591,18 +737,19 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { param_id: GenericParamId, param: GenericParamDataRef<'_>, infer_args: bool, - preceding_args: &[crate::GenericArg], - ) -> crate::GenericArg { - let default = || { - self.ctx - .ctx - .db - .generic_defaults(def) - .get(preceding_args.len()) - .map(|default| default.clone().substitute(Interner, preceding_args)) - }; + preceding_args: &[GenericArg<'db>], + ) -> GenericArg<'db> { + let default = + || { + self.ctx.ctx.db.generic_defaults(def).get(preceding_args.len()).map( + |default| default.instantiate(self.ctx.ctx.interner, preceding_args), + ) + }; match param { - GenericParamDataRef::LifetimeParamData(_) => error_lifetime().cast(Interner), + GenericParamDataRef::LifetimeParamData(_) => { + Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed)) + .into() + } GenericParamDataRef::TypeParamData(param) => { if !infer_args && param.default.is_some() @@ -610,7 +757,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { { return default; } - TyKind::Error.intern(Interner).cast(Interner) + Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() } GenericParamDataRef::ConstParamData(param) => { if !infer_args @@ -622,19 +769,23 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { let GenericParamId::ConstParamId(const_id) = param_id else { unreachable!("non-const param ID for const param"); }; - unknown_const_as_generic(self.ctx.ctx.db.const_param_ty(const_id)) - .cast(Interner) + unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id)) } } } - fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg { + fn parent_arg(&mut self, param_id: GenericParamId) -> GenericArg<'db> { match param_id { - GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), - GenericParamId::ConstParamId(const_id) => { - unknown_const_as_generic(self.ctx.ctx.db.const_param_ty(const_id)) + GenericParamId::TypeParamId(_) => { + Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() + } + GenericParamId::ConstParamId(const_id) => { + unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id)) + } + GenericParamId::LifetimeParamId(_) => { + Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed)) + .into() } - GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), } } @@ -652,6 +803,14 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { }); } + fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::ElisionFailure { + generics_source: self.generics_source, + def, + expected_count, + }); + } + fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32) { self.ctx.on_diagnostic(PathLoweringDiagnostic::MissingLifetime { generics_source: self.generics_source, @@ -677,38 +836,39 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { pub(crate) fn lower_trait_ref_from_resolved_path( &mut self, resolved: TraitId, - explicit_self_ty: Ty, + explicit_self_ty: Ty<'db>, infer_args: bool, - ) -> TraitRef { - let substs = self.trait_ref_substs_from_path(resolved, explicit_self_ty, infer_args); - TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs } + ) -> TraitRef<'db> { + let args = self.trait_ref_substs_from_path(resolved, explicit_self_ty, infer_args); + TraitRef::new_from_args(self.ctx.interner, resolved.into(), args) } fn trait_ref_substs_from_path( &mut self, resolved: TraitId, - explicit_self_ty: Ty, + explicit_self_ty: Ty<'db>, infer_args: bool, - ) -> Substitution { + ) -> GenericArgs<'db> { self.substs_from_path_segment(resolved.into(), infer_args, Some(explicit_self_ty), false) } pub(super) fn assoc_type_bindings_from_type_bound<'c>( mut self, - trait_ref: TraitRef, - ) -> Option + use<'a, 'b, 'c>> { + trait_ref: TraitRef<'db>, + ) -> Option> + use<'a, 'b, 'c, 'db>> { + let interner = self.ctx.interner; self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| { args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| { let found = associated_type_by_name_including_super_traits( self.ctx.db, - trait_ref.clone(), + trait_ref, &binding.name, ); let (super_trait_ref, associated_ty) = match found { None => return SmallVec::new(), Some(t) => t, }; - let substitution = + let args = self.with_lifetime_elision(LifetimeElisionKind::AnonymousReportError, |this| { // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent // generic params. It's inefficient to splice the `Substitution`s, so we may want @@ -718,7 +878,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { binding.args.as_ref(), associated_ty.into(), false, // this is not relevant - Some(super_trait_ref.self_type_parameter(Interner)), + Some(super_trait_ref.self_ty()), PathGenericsSource::AssocType { segment: this.current_segment_u32(), assoc_type: binding_idx as u32, @@ -727,27 +887,20 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { this.ctx.lifetime_elision.clone(), ) }); - let substitution = Substitution::from_iter( - Interner, - super_trait_ref.substitution.iter(Interner).chain( - substitution - .iter(Interner) - .skip(super_trait_ref.substitution.len(Interner)), - ), + let args = GenericArgs::new_from_iter( + interner, + super_trait_ref.args.iter().chain(args.iter().skip(super_trait_ref.args.len())), ); - let projection_ty = ProjectionTy { - associated_ty_id: to_assoc_type_id(associated_ty), - substitution, - }; + let projection_term = + AliasTerm::new_from_args(interner, associated_ty.into(), args); let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity( binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); - if let Some(type_ref) = binding.type_ref { let lifetime_elision = if args_and_bindings.parenthesized == GenericArgsParentheses::ParenSugar { // `Fn()`-style generics are elided like functions. This is `Output` (we lower to it in hir-def). - LifetimeElisionKind::for_fn_ret() + LifetimeElisionKind::for_fn_ret(self.ctx.interner) } else { self.ctx.lifetime_elision.clone() }; @@ -759,31 +912,33 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque, ) => { let ty = this.ctx.lower_ty(type_ref); - let alias_eq = AliasEq { - alias: AliasTy::Projection(projection_ty.clone()), - ty, - }; - predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq( - alias_eq, - ))); + let pred = Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Projection( + ProjectionPredicate { + projection_term, + term: ty.into(), + }, + ), + )), + )); + predicates.push(pred); } } - }); + }) + } + for bound in binding.bounds.iter() { + predicates.extend(self.ctx.lower_type_bound( + bound, + Ty::new_alias( + self.ctx.interner, + AliasTyKind::Projection, + AliasTy::new_from_args(self.ctx.interner, associated_ty.into(), args), + ), + false, + )); } - - self.with_lifetime_elision(LifetimeElisionKind::AnonymousReportError, |this| { - for bound in binding.bounds.iter() { - predicates.extend( - this.ctx.lower_type_bound( - bound, - TyKind::Alias(AliasTy::Projection(projection_ty.clone())) - .intern(Interner), - false, - ), - ); - } - }); - predicates }) }) @@ -796,7 +951,7 @@ pub(crate) enum TypeLikeConst<'a> { Path(&'a Path), } -pub(crate) trait GenericArgsLowerer { +pub(crate) trait GenericArgsLowerer<'db> { fn report_elided_lifetimes_in_path( &mut self, def: GenericDefId, @@ -804,6 +959,8 @@ pub(crate) trait GenericArgsLowerer { hard_error: bool, ); + fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32); + fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32); fn report_len_mismatch( @@ -820,10 +977,11 @@ pub(crate) trait GenericArgsLowerer { &mut self, param_id: GenericParamId, param: GenericParamDataRef<'_>, - arg: &GenericArg, - ) -> crate::GenericArg; + arg: &HirGenericArg, + ) -> GenericArg<'db>; - fn provided_type_like_const(&mut self, const_ty: Ty, arg: TypeLikeConst<'_>) -> crate::Const; + fn provided_type_like_const(&mut self, const_ty: Ty<'db>, arg: TypeLikeConst<'_>) + -> Const<'db>; fn inferred_kind( &mut self, @@ -831,21 +989,21 @@ pub(crate) trait GenericArgsLowerer { param_id: GenericParamId, param: GenericParamDataRef<'_>, infer_args: bool, - preceding_args: &[crate::GenericArg], - ) -> crate::GenericArg; + preceding_args: &[GenericArg<'db>], + ) -> GenericArg<'db>; - fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg; + fn parent_arg(&mut self, param_id: GenericParamId) -> GenericArg<'db>; } /// Returns true if there was an error. -fn check_generic_args_len( - args_and_bindings: Option<&GenericArgs>, +fn check_generic_args_len<'db>( + args_and_bindings: Option<&HirGenericArgs>, def: GenericDefId, def_generics: &Generics, infer_args: bool, - lifetime_elision: &LifetimeElisionKind, + lifetime_elision: &LifetimeElisionKind<'db>, lowering_assoc_type_generics: bool, - ctx: &mut impl GenericArgsLowerer, + ctx: &mut impl GenericArgsLowerer<'db>, ) -> bool { let mut had_error = false; @@ -854,8 +1012,10 @@ fn check_generic_args_len( let args_no_self = &args_and_bindings.args[usize::from(args_and_bindings.has_self_type)..]; for arg in args_no_self { match arg { - GenericArg::Lifetime(_) => provided_lifetimes_count += 1, - GenericArg::Type(_) | GenericArg::Const(_) => provided_types_and_consts_count += 1, + HirGenericArg::Lifetime(_) => provided_lifetimes_count += 1, + HirGenericArg::Type(_) | HirGenericArg::Const(_) => { + provided_types_and_consts_count += 1 + } } } } @@ -876,6 +1036,13 @@ fn check_generic_args_len( ctx.report_missing_lifetime(def, lifetime_args_len as u32); had_error = true } + LifetimeElisionKind::ElisionFailure => { + ctx.report_elision_failure(def, lifetime_args_len as u32); + had_error = true; + } + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => { + // FIXME: Check there are other lifetimes in scope, and error/lint. + } LifetimeElisionKind::Elided(_) => { ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, false); } @@ -922,17 +1089,21 @@ fn check_generic_args_len( had_error } -pub(crate) fn substs_from_args_and_bindings( - db: &dyn HirDatabase, +pub(crate) fn substs_from_args_and_bindings<'db>( + db: &'db dyn HirDatabase, store: &ExpressionStore, - args_and_bindings: Option<&GenericArgs>, + args_and_bindings: Option<&HirGenericArgs>, def: GenericDefId, mut infer_args: bool, - lifetime_elision: LifetimeElisionKind, + lifetime_elision: LifetimeElisionKind<'db>, lowering_assoc_type_generics: bool, - explicit_self_ty: Option, - ctx: &mut impl GenericArgsLowerer, -) -> Substitution { + explicit_self_ty: Option>, + ctx: &mut impl GenericArgsLowerer<'db>, +) -> GenericArgs<'db> { + let interner = DbInterner::new_with(db, None, None); + + tracing::debug!(?args_and_bindings); + // Order is // - Parent parameters // - Optional Self parameter @@ -943,7 +1114,7 @@ pub(crate) fn substs_from_args_and_bindings( // We do not allow inference if there are specified args, i.e. we do not allow partial inference. let has_non_lifetime_args = - args_slice.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))); + args_slice.iter().any(|arg| !matches!(arg, HirGenericArg::Lifetime(_))); infer_args &= !has_non_lifetime_args; let had_count_error = check_generic_args_len( @@ -984,7 +1155,7 @@ pub(crate) fn substs_from_args_and_bindings( let (_, self_ty) = args.next().expect("has_self_type=true, should have Self type"); ctx.provided_kind(self_param_id, self_param, self_ty) } else { - explicit_self_ty.map(|it| it.cast(Interner)).unwrap_or_else(|| { + explicit_self_ty.map(|it| it.into()).unwrap_or_else(|| { ctx.inferred_kind(def, self_param_id, self_param, infer_args, &substs) }) }; @@ -999,7 +1170,7 @@ pub(crate) fn substs_from_args_and_bindings( // input. We try to handle both sensibly. match (args.peek(), params.peek()) { (Some(&(arg_idx, arg)), Some(&(param_id, param))) => match (arg, param) { - (GenericArg::Type(_), GenericParamDataRef::TypeParamData(type_param)) + (HirGenericArg::Type(_), GenericParamDataRef::TypeParamData(type_param)) if type_param.provenance == TypeParamProvenance::ArgumentImplTrait => { // Do not allow specifying `impl Trait` explicitly. We already err at that, but if we won't handle it here @@ -1007,15 +1178,15 @@ pub(crate) fn substs_from_args_and_bindings( substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs)); params.next(); } - (GenericArg::Lifetime(_), GenericParamDataRef::LifetimeParamData(_)) - | (GenericArg::Type(_), GenericParamDataRef::TypeParamData(_)) - | (GenericArg::Const(_), GenericParamDataRef::ConstParamData(_)) => { + (HirGenericArg::Lifetime(_), GenericParamDataRef::LifetimeParamData(_)) + | (HirGenericArg::Type(_), GenericParamDataRef::TypeParamData(_)) + | (HirGenericArg::Const(_), GenericParamDataRef::ConstParamData(_)) => { substs.push(ctx.provided_kind(param_id, param, arg)); args.next(); params.next(); } ( - GenericArg::Type(_) | GenericArg::Const(_), + HirGenericArg::Type(_) | HirGenericArg::Const(_), GenericParamDataRef::LifetimeParamData(_), ) => { // We expected a lifetime argument, but got a type or const @@ -1024,13 +1195,13 @@ pub(crate) fn substs_from_args_and_bindings( params.next(); force_infer_lt = Some((arg_idx as u32, param_id)); } - (GenericArg::Type(type_ref), GenericParamDataRef::ConstParamData(_)) => { + (HirGenericArg::Type(type_ref), GenericParamDataRef::ConstParamData(_)) => { if let Some(konst) = type_looks_like_const(store, *type_ref) { let GenericParamId::ConstParamId(param_id) = param_id else { panic!("unmatching param kinds"); }; - let const_ty = db.const_param_ty(param_id); - substs.push(ctx.provided_type_like_const(const_ty, konst).cast(Interner)); + let const_ty = const_param_ty_query(db, param_id); + substs.push(ctx.provided_type_like_const(const_ty, konst).into()); args.next(); params.next(); } else { @@ -1069,7 +1240,7 @@ pub(crate) fn substs_from_args_and_bindings( // after a type or const). We want to throw an error in this case. if !had_count_error { assert!( - matches!(arg, GenericArg::Lifetime(_)), + matches!(arg, HirGenericArg::Lifetime(_)), "the only possible situation here is incorrect lifetime order" ); let (provided_arg_idx, param_id) = @@ -1084,12 +1255,16 @@ pub(crate) fn substs_from_args_and_bindings( // If there are fewer arguments than parameters, it means we're inferring the remaining arguments. let param = if let GenericParamId::LifetimeParamId(_) = param_id { match &lifetime_elision { - LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true } + LifetimeElisionKind::ElisionFailure + | LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true } | LifetimeElisionKind::AnonymousReportError => { assert!(had_count_error); ctx.inferred_kind(def, param_id, param, infer_args, &substs) } - LifetimeElisionKind::Elided(lifetime) => lifetime.clone().cast(Interner), + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => { + Region::new_static(interner).into() + } + LifetimeElisionKind::Elided(lifetime) => (*lifetime).into(), LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false } | LifetimeElisionKind::Infer => { // FIXME: With `AnonymousCreateParameter`, we need to create a new lifetime parameter here @@ -1108,7 +1283,7 @@ pub(crate) fn substs_from_args_and_bindings( } } - Substitution::from_iter(Interner, substs) + GenericArgs::new_from_iter(interner, substs) } fn type_looks_like_const( @@ -1127,3 +1302,17 @@ 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/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs deleted file mode 100644 index 76ee1a4f2d2b..000000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ /dev/null @@ -1,2138 +0,0 @@ -//! Methods for lowering the HIR to types. There are two main cases here: -//! -//! - Lowering a type reference like `&usize` or `Option` to a -//! type: The entry point for this is `TyLoweringContext::lower_ty`. -//! - Building the type for an item: This happens through the `ty` query. -//! -//! This usually involves resolving names, collecting generic arguments etc. -pub(crate) mod path; - -use std::{ - cell::OnceCell, - iter, mem, - ops::{self, Deref, Not as _}, -}; - -use base_db::Crate; -use either::Either; -use hir_def::{ - AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, - FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, - LocalFieldId, Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, TypeParamId, - VariantId, - expr_store::{ExpressionStore, HygieneId, path::Path}, - hir::generics::{ - GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate, - }, - item_tree::FieldsShape, - lang_item::LangItem, - resolver::{HasResolver, LifetimeNs, Resolver, TypeNs, ValueNs}, - signatures::{FunctionSignature, TraitFlags, TypeAliasFlags}, - type_ref::{ - ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier, - TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, - }, -}; -use hir_expand::name::Name; -use la_arena::{Arena, ArenaMap, Idx}; -use path::{PathDiagnosticCallback, PathLoweringContext}; -use rustc_ast_ir::Mutability; -use rustc_hash::FxHashSet; -use rustc_pattern_analysis::Captures; -use rustc_type_ir::{ - AliasTyKind, ConstKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, - ExistentialTraitRef, FnSig, OutlivesPredicate, - TyKind::{self}, - TypeVisitableExt, - inherent::{GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, Ty as _}, -}; -use salsa::plumbing::AsId; -use smallvec::{SmallVec, smallvec}; -use stdx::never; -use triomphe::Arc; - -use crate::{ - FnAbi, ImplTraitId, TraitEnvironment, TyDefId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, - ValueTyDefId, - consteval::intern_const_ref, - db::HirDatabase, - generics::{Generics, generics, trait_self_param_idx}, - lower::{Diagnostics, PathDiagnosticCallbackData, create_diagnostics}, - next_solver::{ - AliasTy, Binder, BoundExistentialPredicates, Clause, Clauses, Const, DbInterner, - EarlyBinder, EarlyParamRegion, ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, - ParamEnv, PolyFnSig, Predicate, Region, SolverDefId, TraitPredicate, TraitRef, Ty, Tys, - UnevaluatedConst, abi::Safety, - }, -}; - -#[derive(PartialEq, Eq, Debug, Hash)] -pub struct ImplTraits<'db> { - pub(crate) impl_traits: Arena>, -} - -#[derive(PartialEq, Eq, Debug, Hash)] -pub struct ImplTrait<'db> { - pub(crate) predicates: Vec>, -} - -pub type ImplTraitIdx<'db> = Idx>; - -#[derive(Debug, Default)] -struct ImplTraitLoweringState<'db> { - /// When turning `impl Trait` into opaque types, we have to collect the - /// bounds at the same time to get the IDs correct (without becoming too - /// complicated). - mode: ImplTraitLoweringMode, - // This is structured as a struct with fields and not as an enum because it helps with the borrow checker. - opaque_type_data: Arena>, -} - -impl<'db> ImplTraitLoweringState<'db> { - fn new(mode: ImplTraitLoweringMode) -> ImplTraitLoweringState<'db> { - Self { mode, opaque_type_data: Arena::new() } - } -} - -#[derive(Debug, Clone)] -pub enum LifetimeElisionKind<'db> { - /// Create a new anonymous lifetime parameter and reference it. - /// - /// If `report_in_path`, report an error when encountering lifetime elision in a path: - /// ```compile_fail - /// struct Foo<'a> { x: &'a () } - /// async fn foo(x: Foo) {} - /// ``` - /// - /// Note: the error should not trigger when the elided lifetime is in a pattern or - /// expression-position path: - /// ``` - /// struct Foo<'a> { x: &'a () } - /// async fn foo(Foo { x: _ }: Foo<'_>) {} - /// ``` - AnonymousCreateParameter { report_in_path: bool }, - - /// Replace all anonymous lifetimes by provided lifetime. - Elided(Region<'db>), - - /// Give a hard error when either `&` or `'_` is written. Used to - /// rule out things like `where T: Foo<'_>`. Does not imply an - /// error on default object bounds (e.g., `Box`). - AnonymousReportError, - - /// Resolves elided lifetimes to `'static` if there are no other lifetimes in scope, - /// otherwise give a warning that the previous behavior of introducing a new early-bound - /// lifetime is a bug and will be removed (if `only_lint` is enabled). - StaticIfNoLifetimeInScope { only_lint: bool }, - - /// Signal we cannot find which should be the anonymous lifetime. - ElisionFailure, - - /// Infer all elided lifetimes. - Infer, -} - -impl<'db> LifetimeElisionKind<'db> { - #[inline] - pub(crate) fn for_const( - interner: DbInterner<'db>, - const_parent: ItemContainerId, - ) -> LifetimeElisionKind<'db> { - match const_parent { - ItemContainerId::ExternBlockId(_) | ItemContainerId::ModuleId(_) => { - LifetimeElisionKind::Elided(Region::new_static(interner)) - } - ItemContainerId::ImplId(_) => { - LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: true } - } - ItemContainerId::TraitId(_) => { - LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: false } - } - } - } - - #[inline] - pub(crate) fn for_fn_params(data: &FunctionSignature) -> LifetimeElisionKind<'db> { - LifetimeElisionKind::AnonymousCreateParameter { report_in_path: data.is_async() } - } - - #[inline] - pub(crate) fn for_fn_ret(interner: DbInterner<'db>) -> LifetimeElisionKind<'db> { - // FIXME: We should use the elided lifetime here, or `ElisionFailure`. - LifetimeElisionKind::Elided(Region::error(interner)) - } -} - -#[derive(Debug)] -pub struct TyLoweringContext<'db, 'a> { - pub db: &'db dyn HirDatabase, - interner: DbInterner<'db>, - resolver: &'a Resolver<'db>, - store: &'a ExpressionStore, - def: GenericDefId, - generics: OnceCell, - in_binders: DebruijnIndex, - impl_trait_mode: ImplTraitLoweringState<'db>, - /// Tracks types with explicit `?Sized` bounds. - pub(crate) unsized_types: FxHashSet>, - pub(crate) diagnostics: Vec, - lifetime_elision: LifetimeElisionKind<'db>, - /// When lowering the defaults for generic params, this contains the index of the currently lowered param. - /// We disallow referring to later params, or to ADT's `Self`. - lowering_param_default: Option, -} - -impl<'db, 'a> TyLoweringContext<'db, 'a> { - pub fn new( - db: &'db dyn HirDatabase, - resolver: &'a Resolver<'db>, - store: &'a ExpressionStore, - def: GenericDefId, - lifetime_elision: LifetimeElisionKind<'db>, - ) -> Self { - let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed); - let in_binders = DebruijnIndex::ZERO; - Self { - db, - interner: DbInterner::new_with(db, Some(resolver.krate()), None), - resolver, - def, - generics: Default::default(), - store, - in_binders, - impl_trait_mode, - unsized_types: FxHashSet::default(), - diagnostics: Vec::new(), - lifetime_elision, - lowering_param_default: None, - } - } - - pub(crate) fn set_lifetime_elision(&mut self, lifetime_elision: LifetimeElisionKind<'db>) { - self.lifetime_elision = lifetime_elision; - } - - pub(crate) fn with_debruijn( - &mut self, - debruijn: DebruijnIndex, - f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> T, - ) -> T { - let old_debruijn = mem::replace(&mut self.in_binders, debruijn); - let result = f(self); - self.in_binders = old_debruijn; - result - } - - pub(crate) fn with_shifted_in( - &mut self, - debruijn: DebruijnIndex, - f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> T, - ) -> T { - self.with_debruijn(self.in_binders.shifted_in(debruijn.as_u32()), f) - } - - pub(crate) fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { - Self { impl_trait_mode: ImplTraitLoweringState::new(impl_trait_mode), ..self } - } - - pub(crate) fn impl_trait_mode(&mut self, impl_trait_mode: ImplTraitLoweringMode) -> &mut Self { - self.impl_trait_mode = ImplTraitLoweringState::new(impl_trait_mode); - self - } - - pub(crate) fn lowering_param_default(&mut self, index: u32) { - self.lowering_param_default = Some(index); - } - - pub(crate) fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) { - self.diagnostics.push(TyLoweringDiagnostic { source: type_ref, kind }); - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] -pub(crate) enum ImplTraitLoweringMode { - /// `impl Trait` gets lowered into an opaque type that doesn't unify with - /// anything except itself. This is used in places where values flow 'out', - /// i.e. for arguments of the function we're currently checking, and return - /// types of functions we're calling. - Opaque, - /// `impl Trait` is disallowed and will be an error. - #[default] - Disallowed, -} - -impl<'db, 'a> TyLoweringContext<'db, 'a> { - pub fn lower_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> { - self.lower_ty_ext(type_ref).0 - } - - pub(crate) fn lower_const(&mut self, const_ref: ConstRef, const_type: Ty<'db>) -> Const<'db> { - let const_ref = &self.store[const_ref.expr]; - match const_ref { - hir_def::hir::Expr::Path(path) => { - self.path_to_const(path).unwrap_or_else(|| unknown_const(const_type)) - } - hir_def::hir::Expr::Literal(literal) => intern_const_ref( - self.db, - &match *literal { - hir_def::hir::Literal::Float(_, _) - | hir_def::hir::Literal::String(_) - | hir_def::hir::Literal::ByteString(_) - | hir_def::hir::Literal::CString(_) => LiteralConstRef::Unknown, - hir_def::hir::Literal::Char(c) => LiteralConstRef::Char(c), - hir_def::hir::Literal::Bool(b) => LiteralConstRef::Bool(b), - hir_def::hir::Literal::Int(val, _) => LiteralConstRef::Int(val), - hir_def::hir::Literal::Uint(val, _) => LiteralConstRef::UInt(val), - }, - const_type, - self.resolver.krate(), - ), - hir_def::hir::Expr::UnaryOp { expr: inner_expr, op: hir_def::hir::UnaryOp::Neg } => { - if let hir_def::hir::Expr::Literal(literal) = &self.store[*inner_expr] { - // Only handle negation for signed integers and floats - match literal { - hir_def::hir::Literal::Int(_, _) | hir_def::hir::Literal::Float(_, _) => { - if let Some(negated_literal) = literal.clone().negate() { - intern_const_ref( - self.db, - &negated_literal.into(), - const_type, - self.resolver.krate(), - ) - } else { - unknown_const(const_type) - } - } - // For unsigned integers, chars, bools, etc., negation is not meaningful - _ => unknown_const(const_type), - } - } else { - unknown_const(const_type) - } - } - _ => unknown_const(const_type), - } - } - - pub(crate) fn path_to_const(&mut self, path: &Path) -> Option> { - 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 - } - } - } - Some(ValueNs::ConstId(c)) => { - let args = GenericArgs::new_from_iter(self.interner, []); - Some(Const::new( - self.interner, - rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new( - SolverDefId::ConstId(c), - args, - )), - )) - } - _ => None, - } - } - - pub(crate) fn lower_path_as_const(&mut self, path: &Path, const_type: Ty<'db>) -> Const<'db> { - self.path_to_const(path).unwrap_or_else(|| unknown_const(const_type)) - } - - fn generics(&self) -> &Generics { - self.generics.get_or_init(|| generics(self.db, self.def)) - } - - fn param_index_is_disallowed(&self, index: u32) -> bool { - self.lowering_param_default - .is_some_and(|disallow_params_after| index >= disallow_params_after) - } - - fn type_param(&mut self, id: TypeParamId, index: u32) -> Ty<'db> { - if self.param_index_is_disallowed(index) { - // FIXME: Report an error. - Ty::new_error(self.interner, ErrorGuaranteed) - } else { - Ty::new_param(self.interner, id, index) - } - } - - fn const_param(&mut self, id: ConstParamId, index: u32) -> Const<'db> { - if self.param_index_is_disallowed(index) { - // FIXME: Report an error. - Const::error(self.interner) - } else { - Const::new_param(self.interner, ParamConst { id, index }) - } - } - - fn region_param(&mut self, id: LifetimeParamId, index: u32) -> Region<'db> { - if self.param_index_is_disallowed(index) { - // FIXME: Report an error. - Region::error(self.interner) - } else { - Region::new_early_param(self.interner, EarlyParamRegion { id, index }) - } - } - - #[tracing::instrument(skip(self), ret)] - pub fn lower_ty_ext(&mut self, type_ref_id: TypeRefId) -> (Ty<'db>, Option) { - let interner = self.interner; - let mut res = None; - let type_ref = &self.store[type_ref_id]; - tracing::debug!(?type_ref); - let ty = match type_ref { - TypeRef::Never => Ty::new(interner, TyKind::Never), - TypeRef::Tuple(inner) => { - let inner_tys = inner.iter().map(|&tr| self.lower_ty(tr)); - Ty::new_tup_from_iter(interner, inner_tys) - } - TypeRef::Path(path) => { - let (ty, res_) = - self.lower_path(path, PathId::from_type_ref_unchecked(type_ref_id)); - res = res_; - ty - } - &TypeRef::TypeParam(type_param_id) => { - 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) - } - &TypeRef::RawPtr(inner, mutability) => { - let inner_ty = self.lower_ty(inner); - Ty::new(interner, TyKind::RawPtr(inner_ty, lower_mutability(mutability))) - } - TypeRef::Array(array) => { - let inner_ty = self.lower_ty(array.ty); - let const_len = self.lower_const(array.len, Ty::new_usize(interner)); - Ty::new_array_with_const_len(interner, inner_ty, const_len) - } - &TypeRef::Slice(inner) => { - let inner_ty = self.lower_ty(inner); - Ty::new_slice(interner, inner_ty) - } - TypeRef::Reference(ref_) => { - let inner_ty = self.lower_ty(ref_.ty); - // FIXME: It should infer the eldided lifetimes instead of stubbing with error - let lifetime = ref_ - .lifetime - .map_or_else(|| Region::error(interner), |lr| self.lower_lifetime(lr)); - Ty::new_ref(interner, lifetime, inner_ty, lower_mutability(ref_.mutability)) - } - TypeRef::Placeholder => Ty::new_error(interner, ErrorGuaranteed), - TypeRef::Fn(fn_) => { - let substs = self.with_shifted_in( - DebruijnIndex::from_u32(1), - |ctx: &mut TyLoweringContext<'_, '_>| { - Tys::new_from_iter( - interner, - fn_.params.iter().map(|&(_, tr)| ctx.lower_ty(tr)), - ) - }, - ); - Ty::new_fn_ptr( - interner, - Binder::dummy(FnSig { - abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), - safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, - c_variadic: fn_.is_varargs, - inputs_and_output: substs, - }), - ) - } - TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds), - TypeRef::ImplTrait(bounds) => { - match self.impl_trait_mode.mode { - ImplTraitLoweringMode::Opaque => { - let origin = match self.resolver.generic_def() { - Some(GenericDefId::FunctionId(it)) => Either::Left(it), - Some(GenericDefId::TypeAliasId(it)) => Either::Right(it), - _ => panic!( - "opaque impl trait lowering must be in function or type alias" - ), - }; - - // this dance is to make sure the data is in the right - // place even if we encounter more opaque types while - // lowering the bounds - let idx = self - .impl_trait_mode - .opaque_type_data - .alloc(ImplTrait { predicates: Vec::default() }); - - // FIXME(next-solver): this from_raw/into_raw dance isn't nice, but it's minimal - let impl_trait_id = origin.either( - |f| ImplTraitId::ReturnTypeImplTrait(f, Idx::from_raw(idx.into_raw())), - |a| ImplTraitId::TypeAliasImplTrait(a, Idx::from_raw(idx.into_raw())), - ); - let opaque_ty_id: SolverDefId = - self.db.intern_impl_trait_id(impl_trait_id).into(); - - // We don't want to lower the bounds inside the binders - // we're currently in, because they don't end up inside - // those binders. E.g. when we have `impl Trait>`, the `impl OtherTrait` can't refer - // to the self parameter from `impl Trait`, and the - // bounds aren't actually stored nested within each - // other, but separately. So if the `T` refers to a type - // parameter of the outer function, it's just one binder - // away instead of two. - let actual_opaque_type_data = self - .with_debruijn(DebruijnIndex::ZERO, |ctx| { - ctx.lower_impl_trait(opaque_ty_id, bounds, self.resolver.krate()) - }); - self.impl_trait_mode.opaque_type_data[idx] = actual_opaque_type_data; - - let args = GenericArgs::identity_for_item(self.interner, opaque_ty_id); - Ty::new_alias( - self.interner, - AliasTyKind::Opaque, - AliasTy::new_from_args(self.interner, opaque_ty_id, args), - ) - } - ImplTraitLoweringMode::Disallowed => { - // FIXME: report error - Ty::new_error(self.interner, ErrorGuaranteed) - } - } - } - TypeRef::Error => Ty::new_error(self.interner, ErrorGuaranteed), - }; - (ty, res) - } - - /// This is only for `generic_predicates_for_param`, where we can't just - /// lower the self types of the predicates since that could lead to cycles. - /// So we just check here if the `type_ref` resolves to a generic param, and which. - fn lower_ty_only_param(&self, type_ref: TypeRefId) -> Option { - let type_ref = &self.store[type_ref]; - let path = match type_ref { - TypeRef::Path(path) => path, - &TypeRef::TypeParam(idx) => return Some(idx.into()), - _ => return None, - }; - if path.type_anchor().is_some() { - return None; - } - if path.segments().len() > 1 { - return None; - } - let resolution = match self.resolver.resolve_path_in_type_ns(self.db, path) { - Some((it, None, _)) => it, - _ => return None, - }; - match resolution { - TypeNs::GenericParam(param_id) => Some(param_id.into()), - _ => None, - } - } - - #[inline] - fn on_path_diagnostic_callback<'b>(type_ref: TypeRefId) -> PathDiagnosticCallback<'b, 'db> { - PathDiagnosticCallback { - data: Either::Left(PathDiagnosticCallbackData(type_ref)), - callback: |data, this, diag| { - let type_ref = data.as_ref().left().unwrap().0; - this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag)) - }, - } - } - - #[inline] - fn at_path(&mut self, path_id: PathId) -> PathLoweringContext<'_, 'a, 'db> { - PathLoweringContext::new( - self, - Self::on_path_diagnostic_callback(path_id.type_ref()), - &self.store[path_id], - ) - } - - pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty<'db>, Option) { - // Resolve the path (in type namespace) - if let Some(type_ref) = path.type_anchor() { - let (ty, res) = self.lower_ty_ext(type_ref); - let mut ctx = self.at_path(path_id); - return ctx.lower_ty_relative_path(ty, res, false); - } - - let mut ctx = self.at_path(path_id); - let (resolution, remaining_index) = match ctx.resolve_path_in_type_ns() { - Some(it) => it, - None => return (Ty::new_error(self.interner, ErrorGuaranteed), None), - }; - - if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() { - // trait object type without dyn - let bound = TypeBound::Path(path_id, TraitBoundModifier::None); - let ty = self.lower_dyn_trait(&[bound]); - return (ty, None); - } - - ctx.lower_partly_resolved_path(resolution, false) - } - - fn lower_trait_ref_from_path( - &mut self, - path_id: PathId, - explicit_self_ty: Ty<'db>, - ) -> Option<(TraitRef<'db>, PathLoweringContext<'_, 'a, 'db>)> { - let mut ctx = self.at_path(path_id); - let resolved = match ctx.resolve_path_in_type_ns_fully()? { - // FIXME(trait_alias): We need to handle trait alias here. - TypeNs::TraitId(tr) => tr, - _ => return None, - }; - Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty, false), ctx)) - } - - fn lower_trait_ref( - &mut self, - trait_ref: &HirTraitRef, - explicit_self_ty: Ty<'db>, - ) -> Option> { - self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty).map(|it| it.0) - } - - pub(crate) fn lower_where_predicate<'b>( - &'b mut self, - where_predicate: &'b WherePredicate, - ignore_bindings: bool, - generics: &Generics, - predicate_filter: PredicateFilter, - ) -> impl Iterator> + use<'a, 'b, 'db> { - match where_predicate { - WherePredicate::ForLifetime { target, bound, .. } - | WherePredicate::TypeBound { target, bound } => { - if let PredicateFilter::SelfTrait = predicate_filter { - let target_type = &self.store[*target]; - let self_type = 'is_self: { - if let TypeRef::Path(path) = target_type - && path.is_self_type() - { - break 'is_self true; - } - if let TypeRef::TypeParam(param) = target_type - && generics[param.local_id()].is_trait_self() - { - break 'is_self true; - } - false - }; - if !self_type { - return Either::Left(Either::Left(iter::empty())); - } - } - let self_ty = self.lower_ty(*target); - Either::Left(Either::Right(self.lower_type_bound(bound, self_ty, ignore_bindings))) - } - &WherePredicate::Lifetime { bound, target } => { - Either::Right(iter::once(Clause(Predicate::new( - self.interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::RegionOutlives(OutlivesPredicate( - self.lower_lifetime(bound), - self.lower_lifetime(target), - )), - )), - )))) - } - } - .into_iter() - } - - pub(crate) fn lower_type_bound<'b>( - &'b mut self, - bound: &'b TypeBound, - self_ty: Ty<'db>, - ignore_bindings: bool, - ) -> impl Iterator> + use<'b, 'a, 'db> { - let interner = self.interner; - let mut assoc_bounds = None; - let mut clause = None; - match bound { - &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => { - // FIXME Don't silently drop the hrtb lifetimes here - if let Some((trait_ref, mut ctx)) = self.lower_trait_ref_from_path(path, self_ty) { - // FIXME(sized-hierarchy): Remove this bound modifications once we have implemented - // sized-hierarchy correctly. - let meta_sized = LangItem::MetaSized - .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); - let pointee_sized = LangItem::PointeeSized - .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); - if meta_sized.is_some_and(|it| it == trait_ref.def_id.0) { - // Ignore this bound - } else if pointee_sized.is_some_and(|it| it == trait_ref.def_id.0) { - // Regard this as `?Sized` bound - ctx.ty_ctx().unsized_types.insert(self_ty); - } else { - if !ignore_bindings { - assoc_bounds = ctx.assoc_type_bindings_from_type_bound(trait_ref); - } - clause = Some(Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), - ))); - } - } - } - &TypeBound::Path(path, TraitBoundModifier::Maybe) => { - let sized_trait = LangItem::Sized.resolve_trait(self.db, self.resolver.krate()); - // Don't lower associated type bindings as the only possible relaxed trait bound - // `?Sized` has no of them. - // If we got another trait here ignore the bound completely. - let trait_id = self - .lower_trait_ref_from_path(path, self_ty) - .map(|(trait_ref, _)| trait_ref.def_id.0); - if trait_id == sized_trait { - self.unsized_types.insert(self_ty); - } - } - &TypeBound::Lifetime(l) => { - let lifetime = self.lower_lifetime(l); - clause = Some(Clause(Predicate::new( - self.interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::TypeOutlives(OutlivesPredicate( - self_ty, lifetime, - )), - )), - ))); - } - TypeBound::Use(_) | TypeBound::Error => {} - } - clause.into_iter().chain(assoc_bounds.into_iter().flatten()) - } - - fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty<'db> { - let interner = self.interner; - // FIXME: we should never create non-existential predicates in the first place - // For now, use an error type so we don't run into dummy binder issues - let self_ty = Ty::new_error(interner, ErrorGuaranteed); - // INVARIANT: The principal trait bound, if present, must come first. Others may be in any - // order but should be in the same order for the same set but possibly different order of - // bounds in the input. - // INVARIANT: If this function returns `DynTy`, there should be at least one trait bound. - // These invariants are utilized by `TyExt::dyn_trait()` and chalk. - let mut lifetime = None; - let bounds = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { - let mut lowered_bounds: Vec< - rustc_type_ir::Binder, ExistentialPredicate>>, - > = Vec::new(); - for b in bounds { - let db = ctx.db; - ctx.lower_type_bound(b, self_ty, false).for_each(|b| { - if let Some(bound) = b - .kind() - .map_bound(|c| match c { - rustc_type_ir::ClauseKind::Trait(t) => { - let id = t.def_id(); - let is_auto = - db.trait_signature(id.0).flags.contains(TraitFlags::AUTO); - if is_auto { - Some(ExistentialPredicate::AutoTrait(t.def_id())) - } else { - Some(ExistentialPredicate::Trait( - ExistentialTraitRef::new_from_args( - interner, - t.def_id(), - GenericArgs::new_from_iter( - interner, - t.trait_ref.args.iter().skip(1), - ), - ), - )) - } - } - rustc_type_ir::ClauseKind::Projection(p) => { - Some(ExistentialPredicate::Projection( - ExistentialProjection::new_from_args( - interner, - p.def_id(), - GenericArgs::new_from_iter( - interner, - p.projection_term.args.iter().skip(1), - ), - p.term, - ), - )) - } - rustc_type_ir::ClauseKind::TypeOutlives(outlives_predicate) => { - lifetime = Some(outlives_predicate.1); - None - } - rustc_type_ir::ClauseKind::RegionOutlives(_) - | rustc_type_ir::ClauseKind::ConstArgHasType(_, _) - | rustc_type_ir::ClauseKind::WellFormed(_) - | rustc_type_ir::ClauseKind::ConstEvaluatable(_) - | rustc_type_ir::ClauseKind::HostEffect(_) - | rustc_type_ir::ClauseKind::UnstableFeature(_) => unreachable!(), - }) - .transpose() - { - lowered_bounds.push(bound); - } - }) - } - - let mut multiple_regular_traits = false; - let mut multiple_same_projection = false; - lowered_bounds.sort_unstable_by(|lhs, rhs| { - use std::cmp::Ordering; - match ((*lhs).skip_binder(), (*rhs).skip_binder()) { - (ExistentialPredicate::Trait(_), ExistentialPredicate::Trait(_)) => { - multiple_regular_traits = true; - // Order doesn't matter - we error - Ordering::Equal - } - ( - ExistentialPredicate::AutoTrait(lhs_id), - ExistentialPredicate::AutoTrait(rhs_id), - ) => lhs_id.0.cmp(&rhs_id.0), - (ExistentialPredicate::Trait(_), _) => Ordering::Less, - (_, ExistentialPredicate::Trait(_)) => Ordering::Greater, - (ExistentialPredicate::AutoTrait(_), _) => Ordering::Less, - (_, ExistentialPredicate::AutoTrait(_)) => Ordering::Greater, - ( - ExistentialPredicate::Projection(lhs), - ExistentialPredicate::Projection(rhs), - ) => { - let lhs_id = match lhs.def_id { - SolverDefId::TypeAliasId(id) => id, - _ => unreachable!(), - }; - let rhs_id = match rhs.def_id { - SolverDefId::TypeAliasId(id) => id, - _ => unreachable!(), - }; - // We only compare the `associated_ty_id`s. We shouldn't have - // multiple bounds for an associated type in the correct Rust code, - // and if we do, we error out. - if lhs_id == rhs_id { - multiple_same_projection = true; - } - lhs_id.as_id().index().cmp(&rhs_id.as_id().index()) - } - } - }); - - if multiple_regular_traits || multiple_same_projection { - return None; - } - - if !lowered_bounds.first().map_or(false, |b| { - matches!( - b.as_ref().skip_binder(), - ExistentialPredicate::Trait(_) | ExistentialPredicate::AutoTrait(_) - ) - }) { - return None; - } - - // As multiple occurrences of the same auto traits *are* permitted, we deduplicate the - // bounds. We shouldn't have repeated elements besides auto traits at this point. - lowered_bounds.dedup(); - - Some(BoundExistentialPredicates::new_from_iter(interner, lowered_bounds)) - }); - - if let Some(bounds) = bounds { - let region = match lifetime { - Some(it) => match it.kind() { - rustc_type_ir::RegionKind::ReBound(db, var) => Region::new_bound( - self.interner, - db.shifted_out_to_binder(DebruijnIndex::from_u32(2)), - var, - ), - _ => it, - }, - None => Region::new_static(self.interner), - }; - Ty::new_dynamic(self.interner, bounds, region) - } else { - // FIXME: report error - // (additional non-auto traits, associated type rebound, or no resolved trait) - Ty::new_error(self.interner, ErrorGuaranteed) - } - } - - fn lower_impl_trait( - &mut self, - def_id: SolverDefId, - bounds: &[TypeBound], - krate: Crate, - ) -> ImplTrait<'db> { - let interner = self.interner; - cov_mark::hit!(lower_rpit); - let args = GenericArgs::identity_for_item(interner, def_id); - let self_ty = Ty::new_alias( - self.interner, - rustc_type_ir::AliasTyKind::Opaque, - AliasTy::new_from_args(interner, def_id, args), - ); - let predicates = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { - let mut predicates = Vec::new(); - for b in bounds { - predicates.extend(ctx.lower_type_bound(b, self_ty, false)); - } - - if !ctx.unsized_types.contains(&self_ty) { - let sized_trait = LangItem::Sized.resolve_trait(self.db, krate); - let sized_clause = sized_trait.map(|trait_id| { - let trait_ref = TraitRef::new_from_args( - interner, - trait_id.into(), - GenericArgs::new_from_iter(interner, [self_ty.into()]), - ); - Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), - )) - }); - predicates.extend(sized_clause); - } - predicates.shrink_to_fit(); - predicates - }); - ImplTrait { predicates } - } - - pub(crate) fn lower_lifetime(&mut self, lifetime: LifetimeRefId) -> Region<'db> { - match self.resolver.resolve_lifetime(&self.store[lifetime]) { - 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) - } - }, - None => Region::error(self.interner), - } - } -} - -pub(crate) fn lower_mutability(m: hir_def::type_ref::Mutability) -> Mutability { - match m { - hir_def::type_ref::Mutability::Shared => Mutability::Not, - hir_def::type_ref::Mutability::Mut => Mutability::Mut, - } -} - -fn unknown_const(_ty: Ty<'_>) -> Const<'_> { - Const::new(DbInterner::conjure(), ConstKind::Error(ErrorGuaranteed)) -} - -pub(crate) fn impl_trait_query<'db>( - db: &'db dyn HirDatabase, - impl_id: ImplId, -) -> Option>> { - db.impl_trait_with_diagnostics(impl_id).map(|it| it.0) -} - -pub(crate) fn impl_trait_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, - impl_id: ImplId, -) -> Option<(EarlyBinder<'db, TraitRef<'db>>, Diagnostics)> { - let impl_data = db.impl_signature(impl_id); - let resolver = impl_id.resolver(db); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &impl_data.store, - impl_id.into(), - LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, - ); - let self_ty = db.impl_self_ty(impl_id).skip_binder(); - let target_trait = impl_data.target_trait.as_ref()?; - let trait_ref = EarlyBinder::bind(ctx.lower_trait_ref(target_trait, self_ty)?); - Some((trait_ref, create_diagnostics(ctx.diagnostics))) -} - -pub(crate) fn return_type_impl_traits<'db>( - db: &'db dyn HirDatabase, - def: hir_def::FunctionId, -) -> Option>>> { - // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe - let data = db.function_signature(def); - let resolver = def.resolver(db); - let mut ctx_ret = - TyLoweringContext::new(db, &resolver, &data.store, def.into(), LifetimeElisionKind::Infer) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); - if let Some(ret_type) = data.ret_type { - let _ret = ctx_ret.lower_ty(ret_type); - } - let return_type_impl_traits = - ImplTraits { impl_traits: ctx_ret.impl_trait_mode.opaque_type_data }; - if return_type_impl_traits.impl_traits.is_empty() { - None - } else { - Some(Arc::new(EarlyBinder::bind(return_type_impl_traits))) - } -} - -pub(crate) fn type_alias_impl_traits<'db>( - db: &'db dyn HirDatabase, - def: hir_def::TypeAliasId, -) -> Option>>> { - let data = db.type_alias_signature(def); - let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &data.store, - def.into(), - LifetimeElisionKind::AnonymousReportError, - ) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); - if let Some(type_ref) = data.ty { - let _ty = ctx.lower_ty(type_ref); - } - let type_alias_impl_traits = ImplTraits { impl_traits: ctx.impl_trait_mode.opaque_type_data }; - if type_alias_impl_traits.impl_traits.is_empty() { - None - } else { - Some(Arc::new(EarlyBinder::bind(type_alias_impl_traits))) - } -} - -/// Build the declared type of an item. This depends on the namespace; e.g. for -/// `struct Foo(usize)`, we have two types: The type of the struct itself, and -/// the constructor function `(usize) -> Foo` which lives in the values -/// namespace. -pub(crate) fn ty_query<'db>(db: &'db dyn HirDatabase, def: TyDefId) -> EarlyBinder<'db, Ty<'db>> { - let interner = DbInterner::new_with(db, None, None); - match def { - TyDefId::BuiltinType(it) => EarlyBinder::bind(Ty::from_builtin_type(interner, it)), - TyDefId::AdtId(it) => EarlyBinder::bind(Ty::new_adt( - interner, - it, - GenericArgs::identity_for_item(interner, it.into()), - )), - TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).0, - } -} - -/// Build the declared type of a function. This should not need to look at the -/// function body. -fn type_for_fn<'db>(db: &'db dyn HirDatabase, def: FunctionId) -> EarlyBinder<'db, Ty<'db>> { - let interner = DbInterner::new_with(db, None, None); - EarlyBinder::bind(Ty::new_fn_def( - interner, - CallableDefId::FunctionId(def).into(), - GenericArgs::identity_for_item(interner, def.into()), - )) -} - -/// Build the declared type of a const. -fn type_for_const<'db>(db: &'db dyn HirDatabase, def: ConstId) -> EarlyBinder<'db, Ty<'db>> { - let resolver = def.resolver(db); - let data = db.const_signature(def); - let parent = def.loc(db).container; - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &data.store, - def.into(), - LifetimeElisionKind::AnonymousReportError, - ); - ctx.set_lifetime_elision(LifetimeElisionKind::for_const(ctx.interner, parent)); - EarlyBinder::bind(ctx.lower_ty(data.type_ref)) -} - -/// Build the declared type of a static. -fn type_for_static<'db>(db: &'db dyn HirDatabase, def: StaticId) -> EarlyBinder<'db, Ty<'db>> { - let resolver = def.resolver(db); - let data = db.static_signature(def); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &data.store, - def.into(), - LifetimeElisionKind::AnonymousReportError, - ); - ctx.set_lifetime_elision(LifetimeElisionKind::Elided(Region::new_static(ctx.interner))); - EarlyBinder::bind(ctx.lower_ty(data.type_ref)) -} - -/// Build the type of a tuple struct constructor. -fn type_for_struct_constructor<'db>( - db: &'db dyn HirDatabase, - def: StructId, -) -> Option>> { - let struct_data = def.fields(db); - match struct_data.shape { - FieldsShape::Record => None, - FieldsShape::Unit => Some(type_for_adt(db, def.into())), - FieldsShape::Tuple => { - let interner = DbInterner::new_with(db, None, None); - Some(EarlyBinder::bind(Ty::new_fn_def( - interner, - CallableDefId::StructId(def).into(), - GenericArgs::identity_for_item(interner, def.into()), - ))) - } - } -} - -/// Build the type of a tuple enum variant constructor. -fn type_for_enum_variant_constructor<'db>( - db: &'db dyn HirDatabase, - def: EnumVariantId, -) -> Option>> { - let struct_data = def.fields(db); - match struct_data.shape { - FieldsShape::Record => None, - FieldsShape::Unit => Some(type_for_adt(db, def.loc(db).parent.into())), - FieldsShape::Tuple => { - let interner = DbInterner::new_with(db, None, None); - Some(EarlyBinder::bind(Ty::new_fn_def( - interner, - CallableDefId::EnumVariantId(def).into(), - GenericArgs::identity_for_item(interner, def.loc(db).parent.into()), - ))) - } - } -} - -pub(crate) fn value_ty_query<'db>( - db: &'db dyn HirDatabase, - def: ValueTyDefId, -) -> Option>> { - match def { - ValueTyDefId::FunctionId(it) => Some(type_for_fn(db, it)), - ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it), - ValueTyDefId::UnionId(it) => Some(type_for_adt(db, it.into())), - ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it), - ValueTyDefId::ConstId(it) => Some(type_for_const(db, it)), - ValueTyDefId::StaticId(it) => Some(type_for_static(db, it)), - } -} - -pub(crate) fn type_for_type_alias_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, - t: TypeAliasId, -) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { - let type_alias_data = db.type_alias_signature(t); - let mut diags = None; - let resolver = t.resolver(db); - let interner = DbInterner::new_with(db, Some(resolver.krate()), None); - let inner = if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) { - EarlyBinder::bind(Ty::new_foreign(interner, t.into())) - } else { - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &type_alias_data.store, - t.into(), - LifetimeElisionKind::AnonymousReportError, - ) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); - let res = EarlyBinder::bind( - type_alias_data - .ty - .map(|type_ref| ctx.lower_ty(type_ref)) - .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)), - ); - diags = create_diagnostics(ctx.diagnostics); - res - }; - (inner, diags) -} - -pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result<'db>( - db: &'db dyn HirDatabase, - _adt: TypeAliasId, -) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { - (EarlyBinder::bind(Ty::new_error(DbInterner::new_with(db, None, None), ErrorGuaranteed)), None) -} - -pub(crate) fn impl_self_ty_query<'db>( - db: &'db dyn HirDatabase, - impl_id: ImplId, -) -> EarlyBinder<'db, Ty<'db>> { - db.impl_self_ty_with_diagnostics(impl_id).0 -} - -pub(crate) fn impl_self_ty_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, - impl_id: ImplId, -) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { - let resolver = impl_id.resolver(db); - - let impl_data = db.impl_signature(impl_id); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &impl_data.store, - impl_id.into(), - LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, - ); - let ty = ctx.lower_ty(impl_data.self_ty); - assert!(!ty.has_escaping_bound_vars()); - (EarlyBinder::bind(ty), create_diagnostics(ctx.diagnostics)) -} - -pub(crate) fn impl_self_ty_with_diagnostics_cycle_result( - db: &dyn HirDatabase, - _impl_id: ImplId, -) -> (EarlyBinder<'_, Ty<'_>>, Diagnostics) { - (EarlyBinder::bind(Ty::new_error(DbInterner::new_with(db, None, None), ErrorGuaranteed)), None) -} - -pub(crate) fn const_param_ty_query<'db>(db: &'db dyn HirDatabase, def: ConstParamId) -> Ty<'db> { - db.const_param_ty_with_diagnostics(def).0 -} - -// returns None if def is a type arg -pub(crate) fn const_param_ty_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, - def: ConstParamId, -) -> (Ty<'db>, Diagnostics) { - let (parent_data, store) = db.generic_params_and_store(def.parent()); - let data = &parent_data[def.local_id()]; - let resolver = def.parent().resolver(db); - let interner = DbInterner::new_with(db, Some(resolver.krate()), None); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &store, - def.parent(), - LifetimeElisionKind::AnonymousReportError, - ); - let ty = match data { - TypeOrConstParamData::TypeParamData(_) => { - never!(); - Ty::new_error(interner, ErrorGuaranteed) - } - TypeOrConstParamData::ConstParamData(d) => ctx.lower_ty(d.ty), - }; - (ty, create_diagnostics(ctx.diagnostics)) -} - -pub(crate) fn const_param_ty_with_diagnostics_cycle_result<'db>( - db: &'db dyn HirDatabase, - _: crate::db::HirDatabaseData, - def: ConstParamId, -) -> (Ty<'db>, Diagnostics) { - let resolver = def.parent().resolver(db); - let interner = DbInterner::new_with(db, Some(resolver.krate()), None); - (Ty::new_error(interner, ErrorGuaranteed), None) -} - -pub(crate) fn field_types_query<'db>( - db: &'db dyn HirDatabase, - variant_id: VariantId, -) -> Arc>>> { - db.field_types_with_diagnostics(variant_id).0 -} - -/// Build the type of all specific fields of a struct or enum variant. -pub(crate) fn field_types_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, - variant_id: VariantId, -) -> (Arc>>>, Diagnostics) { - let var_data = variant_id.fields(db); - let fields = var_data.fields(); - if fields.is_empty() { - return (Arc::new(ArenaMap::default()), None); - } - - let (resolver, def): (_, GenericDefId) = match variant_id { - VariantId::StructId(it) => (it.resolver(db), it.into()), - VariantId::UnionId(it) => (it.resolver(db), it.into()), - VariantId::EnumVariantId(it) => (it.resolver(db), it.lookup(db).parent.into()), - }; - let mut res = ArenaMap::default(); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &var_data.store, - def, - LifetimeElisionKind::AnonymousReportError, - ); - for (field_id, field_data) in var_data.fields().iter() { - res.insert(field_id, EarlyBinder::bind(ctx.lower_ty(field_data.type_ref))); - } - (Arc::new(res), create_diagnostics(ctx.diagnostics)) -} - -/// This query exists only to be used when resolving short-hand associated types -/// like `T::Item`. -/// -/// See the analogous query in rustc and its comment: -/// -/// This is a query mostly to handle cycles somewhat gracefully; e.g. the -/// following bounds are disallowed: `T: Foo, U: Foo`, but -/// these are fine: `T: Foo, U: Foo<()>`. -#[tracing::instrument(skip(db), ret)] -pub(crate) fn generic_predicates_for_param_query<'db>( - db: &'db dyn HirDatabase, - def: GenericDefId, - param_id: TypeOrConstParamId, - assoc_name: Option, -) -> GenericPredicates<'db> { - let generics = generics(db, def); - let interner = DbInterner::new_with(db, None, None); - let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - generics.store(), - def, - LifetimeElisionKind::AnonymousReportError, - ); - - // we have to filter out all other predicates *first*, before attempting to lower them - let predicate = |pred: &_, ctx: &mut TyLoweringContext<'_, '_>| match pred { - WherePredicate::ForLifetime { target, bound, .. } - | WherePredicate::TypeBound { target, bound, .. } => { - let invalid_target = { ctx.lower_ty_only_param(*target) != Some(param_id) }; - if invalid_target { - // FIXME(sized-hierarchy): Revisit and adjust this properly once we have implemented - // sized-hierarchy correctly. - // If this is filtered out without lowering, `?Sized` or `PointeeSized` is not gathered into - // `ctx.unsized_types` - let lower = || -> bool { - match bound { - TypeBound::Path(_, TraitBoundModifier::Maybe) => true, - TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => { - let TypeRef::Path(path) = &ctx.store[path.type_ref()] else { - return false; - }; - let Some(pointee_sized) = - LangItem::PointeeSized.resolve_trait(ctx.db, ctx.resolver.krate()) - else { - return false; - }; - // Lower the path directly with `Resolver` instead of PathLoweringContext` - // to prevent diagnostics duplications. - ctx.resolver.resolve_path_in_type_ns_fully(ctx.db, path).is_some_and( - |it| matches!(it, TypeNs::TraitId(tr) if tr == pointee_sized), - ) - } - _ => false, - } - }(); - if lower { - ctx.lower_where_predicate(pred, true, &generics, PredicateFilter::All) - .for_each(drop); - } - return false; - } - - match bound { - &TypeBound::ForLifetime(_, path) | &TypeBound::Path(path, _) => { - // Only lower the bound if the trait could possibly define the associated - // type we're looking for. - let path = &ctx.store[path]; - - let Some(assoc_name) = &assoc_name else { return true }; - let Some(TypeNs::TraitId(tr)) = - resolver.resolve_path_in_type_ns_fully(db, path) - else { - return false; - }; - - rustc_type_ir::elaborate::supertrait_def_ids(interner, tr.into()).any(|tr| { - tr.0.trait_items(db).items.iter().any(|(name, item)| { - matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name - }) - }) - } - TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false, - } - } - WherePredicate::Lifetime { .. } => false, - }; - let mut predicates = Vec::new(); - for maybe_parent_generics in - std::iter::successors(Some(&generics), |generics| generics.parent_generics()) - { - ctx.store = maybe_parent_generics.store(); - for pred in maybe_parent_generics.where_predicates() { - if predicate(pred, &mut ctx) { - predicates.extend(ctx.lower_where_predicate( - pred, - true, - maybe_parent_generics, - PredicateFilter::All, - )); - } - } - } - - let args = GenericArgs::identity_for_item(interner, def.into()); - if !args.is_empty() { - let explicitly_unsized_tys = ctx.unsized_types; - if let Some(implicitly_sized_predicates) = - implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &args, &resolver) - { - predicates.extend(implicitly_sized_predicates); - }; - } - GenericPredicates(predicates.is_empty().not().then(|| predicates.into())) -} - -pub(crate) fn generic_predicates_for_param_cycle_result( - _db: &dyn HirDatabase, - _def: GenericDefId, - _param_id: TypeOrConstParamId, - _assoc_name: Option, -) -> GenericPredicates<'_> { - GenericPredicates(None) -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct GenericPredicates<'db>(Option]>>); - -impl<'db> GenericPredicates<'db> { - #[inline] - pub fn instantiate( - &self, - interner: DbInterner<'db>, - args: GenericArgs<'db>, - ) -> Option>> { - self.0 - .as_ref() - .map(|it| EarlyBinder::bind(it.iter().copied()).iter_instantiated(interner, args)) - } - - #[inline] - pub fn instantiate_identity(&self) -> Option>> { - self.0.as_ref().map(|it| it.iter().copied()) - } -} - -impl<'db> ops::Deref for GenericPredicates<'db> { - type Target = [Clause<'db>]; - - fn deref(&self) -> &Self::Target { - self.0.as_deref().unwrap_or(&[]) - } -} - -pub(crate) fn trait_environment_for_body_query( - db: &dyn HirDatabase, - def: DefWithBodyId, -) -> Arc> { - let Some(def) = def.as_generic_def_id(db) else { - let krate = def.module(db).krate(); - return TraitEnvironment::empty(krate); - }; - db.trait_environment(def) -} - -pub(crate) fn trait_environment_query<'db>( - db: &'db dyn HirDatabase, - def: GenericDefId, -) -> Arc> { - let generics = generics(db, def); - if generics.has_no_predicates() && generics.is_empty() { - return TraitEnvironment::empty(def.krate(db)); - } - - let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - generics.store(), - def, - LifetimeElisionKind::AnonymousReportError, - ); - let mut traits_in_scope = Vec::new(); - let mut clauses = Vec::new(); - for maybe_parent_generics in - std::iter::successors(Some(&generics), |generics| generics.parent_generics()) - { - ctx.store = maybe_parent_generics.store(); - for pred in maybe_parent_generics.where_predicates() { - for pred in ctx.lower_where_predicate(pred, false, &generics, PredicateFilter::All) { - if let rustc_type_ir::ClauseKind::Trait(tr) = pred.kind().skip_binder() { - traits_in_scope.push((tr.self_ty(), tr.def_id().0)); - } - clauses.push(pred); - } - } - } - - if let Some(trait_id) = def.assoc_trait_container(db) { - // add `Self: Trait` to the environment in trait - // function default implementations (and speculative code - // inside consts or type aliases) - cov_mark::hit!(trait_self_implements_self); - let trait_ref = TraitRef::identity(ctx.interner, trait_id.into()); - let clause = Clause(Predicate::new( - ctx.interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait( - TraitPredicate { trait_ref, polarity: rustc_type_ir::PredicatePolarity::Positive }, - ))), - )); - clauses.push(clause); - } - - let explicitly_unsized_tys = ctx.unsized_types; - - let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); - if let Some(sized_trait) = sized_trait { - let (mut generics, mut def_id) = - (crate::next_solver::generics::generics(db, def.into()), def); - loop { - let self_idx = trait_self_param_idx(db, def_id); - for (idx, p) in generics.own_params.iter().enumerate() { - if let Some(self_idx) = self_idx - && p.index() as usize == self_idx - { - continue; - } - let GenericParamId::TypeParamId(param_id) = p.id else { - continue; - }; - let idx = idx as u32 + generics.parent_count as u32; - let param_ty = Ty::new_param(ctx.interner, param_id, idx); - if explicitly_unsized_tys.contains(¶m_ty) { - continue; - } - let trait_ref = TraitRef::new_from_args( - ctx.interner, - sized_trait.into(), - GenericArgs::new_from_iter(ctx.interner, [param_ty.into()]), - ); - let clause = Clause(Predicate::new( - ctx.interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), - )); - clauses.push(clause); - } - - if let Some(g) = generics.parent { - generics = crate::next_solver::generics::generics(db, g.into()); - def_id = g; - } else { - break; - } - } - } - - let clauses = rustc_type_ir::elaborate::elaborate(ctx.interner, clauses); - let clauses = Clauses::new_from_iter(ctx.interner, clauses); - let env = ParamEnv { clauses }; - - TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env) -} - -#[derive(Copy, Clone, Debug)] -pub(crate) enum PredicateFilter { - SelfTrait, - All, -} - -/// Resolve the where clause(s) of an item with generics. -#[tracing::instrument(skip(db))] -pub(crate) fn generic_predicates_query<'db>( - db: &'db dyn HirDatabase, - def: GenericDefId, -) -> GenericPredicates<'db> { - generic_predicates_filtered_by(db, def, PredicateFilter::All, |_| true).0 -} - -pub(crate) fn generic_predicates_without_parent_query<'db>( - db: &'db dyn HirDatabase, - def: GenericDefId, -) -> GenericPredicates<'db> { - generic_predicates_filtered_by(db, def, PredicateFilter::All, |d| d == def).0 -} - -/// Resolve the where clause(s) of an item with generics, -/// except the ones inherited from the parent -pub(crate) fn generic_predicates_without_parent_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, - def: GenericDefId, -) -> (GenericPredicates<'db>, Diagnostics) { - generic_predicates_filtered_by(db, def, PredicateFilter::All, |d| d == def) -} - -/// Resolve the where clause(s) of an item with generics, -/// with a given filter -#[tracing::instrument(skip(db, filter), ret)] -pub(crate) fn generic_predicates_filtered_by<'db, F>( - db: &'db dyn HirDatabase, - def: GenericDefId, - predicate_filter: PredicateFilter, - filter: F, -) -> (GenericPredicates<'db>, Diagnostics) -where - F: Fn(GenericDefId) -> bool, -{ - let generics = generics(db, def); - let resolver = def.resolver(db); - let interner = DbInterner::new_with(db, Some(resolver.krate()), None); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - generics.store(), - def, - LifetimeElisionKind::AnonymousReportError, - ); - - let mut predicates = Vec::new(); - for maybe_parent_generics in - std::iter::successors(Some(&generics), |generics| generics.parent_generics()) - { - ctx.store = maybe_parent_generics.store(); - for pred in maybe_parent_generics.where_predicates() { - tracing::debug!(?pred); - if filter(maybe_parent_generics.def()) { - predicates.extend(ctx.lower_where_predicate( - pred, - false, - maybe_parent_generics, - predicate_filter, - )); - } - } - } - - let explicitly_unsized_tys = ctx.unsized_types; - - let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); - if let Some(sized_trait) = sized_trait { - let mut add_sized_clause = |param_idx, param_id, param_data| { - let ( - GenericParamId::TypeParamId(param_id), - GenericParamDataRef::TypeParamData(param_data), - ) = (param_id, param_data) - else { - return; - }; - - if param_data.provenance == TypeParamProvenance::TraitSelf { - return; - } - - let param_ty = Ty::new_param(interner, param_id, param_idx); - if explicitly_unsized_tys.contains(¶m_ty) { - return; - } - let trait_ref = TraitRef::new_from_args( - interner, - sized_trait.into(), - GenericArgs::new_from_iter(interner, [param_ty.into()]), - ); - let clause = Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), - )); - predicates.push(clause); - }; - if generics.parent_generics().is_some_and(|parent| filter(parent.def())) { - generics.iter_parent().enumerate().for_each(|(param_idx, (param_id, param_data))| { - add_sized_clause(param_idx as u32, param_id, param_data); - }); - } - if filter(def) { - let parent_params_len = generics.len_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); - }); - } - } - - // FIXME: rustc gathers more predicates by recursing through resulting trait predicates. - // See https://github.com/rust-lang/rust/blob/76c5ed2847cdb26ef2822a3a165d710f6b772217/compiler/rustc_hir_analysis/src/collect/predicates_of.rs#L689-L715 - - ( - GenericPredicates(predicates.is_empty().not().then(|| predicates.into())), - create_diagnostics(ctx.diagnostics), - ) -} - -/// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound. -/// Exception is Self of a trait def. -fn implicitly_sized_clauses<'a, 'subst, 'db>( - db: &'db dyn HirDatabase, - def: GenericDefId, - explicitly_unsized_tys: &'a FxHashSet>, - args: &'subst GenericArgs<'db>, - resolver: &Resolver<'db>, -) -> Option> + Captures<'a> + Captures<'subst>> { - let interner = DbInterner::new_with(db, Some(resolver.krate()), None); - let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate())?; - - let trait_self_idx = trait_self_param_idx(db, def); - - Some( - args.iter() - .enumerate() - .filter_map( - move |(idx, generic_arg)| { - if Some(idx) == trait_self_idx { None } else { Some(generic_arg) } - }, - ) - .filter_map(|generic_arg| generic_arg.as_type()) - .filter(move |self_ty| !explicitly_unsized_tys.contains(self_ty)) - .map(move |self_ty| { - let trait_ref = TraitRef::new_from_args( - interner, - sized_trait.into(), - GenericArgs::new_from_iter(interner, [self_ty.into()]), - ); - Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), - )) - }), - ) -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct GenericDefaults<'db>(Option>>]>>); - -impl<'db> GenericDefaults<'db> { - #[inline] - pub fn get(&self, idx: usize) -> Option>> { - self.0.as_ref()?[idx] - } -} - -pub(crate) fn generic_defaults_query( - db: &dyn HirDatabase, - def: GenericDefId, -) -> GenericDefaults<'_> { - db.generic_defaults_ns_with_diagnostics(def).0 -} - -/// Resolve the default type params from generics. -/// -/// Diagnostics are only returned for this `GenericDefId` (returned defaults include parents). -pub(crate) fn generic_defaults_with_diagnostics_query( - db: &dyn HirDatabase, - def: GenericDefId, -) -> (GenericDefaults<'_>, Diagnostics) { - let generic_params = generics(db, def); - if generic_params.is_empty() { - return (GenericDefaults(None), None); - } - let resolver = def.resolver(db); - - let mut ctx = TyLoweringContext::new( - db, - &resolver, - generic_params.store(), - def, - 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 (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. - defaults.extend(generic_params.iter_self().map(|(_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)); - let defaults = if has_any_default { - GenericDefaults(Some(Arc::from_iter(defaults))) - } else { - GenericDefaults(None) - }; - return (defaults, diagnostics); - - fn handle_generic_param<'db>( - ctx: &mut TyLoweringContext<'db, '_>, - idx: usize, - p: GenericParamDataRef<'_>, - ) -> (Option>>, bool) { - ctx.lowering_param_default(idx as u32); - match p { - GenericParamDataRef::TypeParamData(p) => { - let ty = p.default.map(|ty| ctx.lower_ty(ty)); - (ty.map(|ty| EarlyBinder::bind(ty.into())), p.default.is_some()) - } - GenericParamDataRef::ConstParamData(p) => { - let val = p.default.map(|c| { - let param_ty = ctx.lower_ty(p.ty); - let c = ctx.lower_const(c, param_ty); - c.into() - }); - (val.map(EarlyBinder::bind), p.default.is_some()) - } - GenericParamDataRef::LifetimeParamData(_) => (None, false), - } - } -} - -pub(crate) fn generic_defaults_with_diagnostics_cycle_result( - _db: &dyn HirDatabase, - _def: GenericDefId, -) -> (GenericDefaults<'_>, Diagnostics) { - (GenericDefaults(None), None) -} - -/// Build the signature of a callable item (function, struct or enum variant). -pub(crate) fn callable_item_signature_query<'db>( - db: &'db dyn HirDatabase, - def: CallableDefId, -) -> EarlyBinder<'db, PolyFnSig<'db>> { - match def { - CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f), - CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s), - CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e), - } -} - -fn fn_sig_for_fn<'db>( - db: &'db dyn HirDatabase, - def: FunctionId, -) -> EarlyBinder<'db, PolyFnSig<'db>> { - let data = db.function_signature(def); - let resolver = def.resolver(db); - let interner = DbInterner::new_with(db, Some(resolver.krate()), None); - let mut ctx_params = TyLoweringContext::new( - db, - &resolver, - &data.store, - def.into(), - LifetimeElisionKind::for_fn_params(&data), - ); - let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr)); - - let ret = match data.ret_type { - Some(ret_type) => { - let mut ctx_ret = TyLoweringContext::new( - db, - &resolver, - &data.store, - def.into(), - LifetimeElisionKind::for_fn_ret(interner), - ) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); - ctx_ret.lower_ty(ret_type) - } - None => Ty::new_tup(interner, &[]), - }; - - let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret))); - // If/when we track late bound vars, we need to switch this to not be `dummy` - EarlyBinder::bind(rustc_type_ir::Binder::dummy(FnSig { - abi: data.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), - c_variadic: data.is_varargs(), - safety: if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe }, - inputs_and_output, - })) -} - -fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, Ty<'db>> { - let interner = DbInterner::new_with(db, None, None); - let args = GenericArgs::identity_for_item(interner, adt.into()); - let ty = Ty::new_adt(interner, adt, args); - EarlyBinder::bind(ty) -} - -fn fn_sig_for_struct_constructor<'db>( - db: &'db dyn HirDatabase, - def: StructId, -) -> EarlyBinder<'db, PolyFnSig<'db>> { - let field_tys = db.field_types_ns(def.into()); - let params = field_tys.iter().map(|(_, ty)| ty.skip_binder()); - let ret = type_for_adt(db, def.into()).skip_binder(); - - let inputs_and_output = - Tys::new_from_iter(DbInterner::new_with(db, None, None), params.chain(Some(ret))); - EarlyBinder::bind(Binder::dummy(FnSig { - abi: FnAbi::RustCall, - c_variadic: false, - safety: Safety::Safe, - inputs_and_output, - })) -} - -fn fn_sig_for_enum_variant_constructor<'db>( - db: &'db dyn HirDatabase, - def: EnumVariantId, -) -> EarlyBinder<'db, PolyFnSig<'db>> { - let field_tys = db.field_types_ns(def.into()); - let params = field_tys.iter().map(|(_, ty)| ty.skip_binder()); - let parent = def.lookup(db).parent; - let ret = type_for_adt(db, parent.into()).skip_binder(); - - let inputs_and_output = - Tys::new_from_iter(DbInterner::new_with(db, None, None), params.chain(Some(ret))); - EarlyBinder::bind(Binder::dummy(FnSig { - abi: FnAbi::RustCall, - c_variadic: false, - safety: Safety::Safe, - inputs_and_output, - })) -} - -// FIXME(next-solver): should merge this with `explicit_item_bounds` in some way -pub(crate) fn associated_ty_item_bounds<'db>( - db: &'db dyn HirDatabase, - type_alias: TypeAliasId, -) -> EarlyBinder<'db, BoundExistentialPredicates<'db>> { - let type_alias_data = db.type_alias_signature(type_alias); - let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); - let interner = DbInterner::new_with(db, Some(resolver.krate()), None); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &type_alias_data.store, - type_alias.into(), - LifetimeElisionKind::AnonymousReportError, - ); - // FIXME: we should never create non-existential predicates in the first place - // For now, use an error type so we don't run into dummy binder issues - let self_ty = Ty::new_error(interner, ErrorGuaranteed); - - let mut bounds = Vec::new(); - for bound in &type_alias_data.bounds { - ctx.lower_type_bound(bound, self_ty, false).for_each(|pred| { - if let Some(bound) = pred - .kind() - .map_bound(|c| match c { - rustc_type_ir::ClauseKind::Trait(t) => { - let id = t.def_id(); - let is_auto = db.trait_signature(id.0).flags.contains(TraitFlags::AUTO); - if is_auto { - Some(ExistentialPredicate::AutoTrait(t.def_id())) - } else { - Some(ExistentialPredicate::Trait(ExistentialTraitRef::new_from_args( - interner, - t.def_id(), - GenericArgs::new_from_iter( - interner, - t.trait_ref.args.iter().skip(1), - ), - ))) - } - } - rustc_type_ir::ClauseKind::Projection(p) => Some( - ExistentialPredicate::Projection(ExistentialProjection::new_from_args( - interner, - p.def_id(), - GenericArgs::new_from_iter( - interner, - p.projection_term.args.iter().skip(1), - ), - p.term, - )), - ), - rustc_type_ir::ClauseKind::TypeOutlives(_) => None, - rustc_type_ir::ClauseKind::RegionOutlives(_) - | rustc_type_ir::ClauseKind::ConstArgHasType(_, _) - | rustc_type_ir::ClauseKind::WellFormed(_) - | rustc_type_ir::ClauseKind::ConstEvaluatable(_) - | rustc_type_ir::ClauseKind::HostEffect(_) - | rustc_type_ir::ClauseKind::UnstableFeature(_) => unreachable!(), - }) - .transpose() - { - bounds.push(bound); - } - }); - } - - if !ctx.unsized_types.contains(&self_ty) - && let Some(sized_trait) = LangItem::Sized.resolve_trait(db, resolver.krate()) - { - let sized_clause = Binder::dummy(ExistentialPredicate::Trait(ExistentialTraitRef::new( - interner, - sized_trait.into(), - [] as [GenericArg<'_>; 0], - ))); - bounds.push(sized_clause); - } - - EarlyBinder::bind(BoundExistentialPredicates::new_from_iter(interner, bounds)) -} - -pub(crate) fn associated_type_by_name_including_super_traits<'db>( - db: &'db dyn HirDatabase, - trait_ref: TraitRef<'db>, - name: &Name, -) -> Option<(TraitRef<'db>, TypeAliasId)> { - let interner = DbInterner::new_with(db, None, None); - rustc_type_ir::elaborate::supertraits(interner, Binder::dummy(trait_ref)).find_map(|t| { - let trait_id = t.as_ref().skip_binder().def_id.0; - let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?; - Some((t.skip_binder(), assoc_type)) - }) -} - -pub fn associated_type_shorthand_candidates( - db: &dyn HirDatabase, - def: GenericDefId, - res: TypeNs, - mut cb: impl FnMut(&Name, TypeAliasId) -> bool, -) -> Option { - let interner = DbInterner::new_with(db, None, None); - named_associated_type_shorthand_candidates(interner, def, res, None, |name, _, id| { - cb(name, id).then_some(id) - }) -} - -#[tracing::instrument(skip(interner, check_alias))] -fn named_associated_type_shorthand_candidates<'db, R>( - interner: DbInterner<'db>, - // If the type parameter is defined in an impl and we're in a method, there - // might be additional where clauses to consider - def: GenericDefId, - res: TypeNs, - assoc_name: Option, - mut check_alias: impl FnMut(&Name, TraitRef<'db>, TypeAliasId) -> Option, -) -> Option { - let db = interner.db; - let mut search = |t: TraitRef<'db>| -> Option { - let mut checked_traits = FxHashSet::default(); - let mut check_trait = |trait_ref: TraitRef<'db>| { - let trait_id = trait_ref.def_id.0; - let name = &db.trait_signature(trait_id).name; - tracing::debug!(?trait_id, ?name); - if !checked_traits.insert(trait_id) { - return None; - } - let data = trait_id.trait_items(db); - - tracing::debug!(?data.items); - for (name, assoc_id) in &data.items { - if let &AssocItemId::TypeAliasId(alias) = assoc_id - && let Some(ty) = check_alias(name, trait_ref, alias) - { - return Some(ty); - } - } - None - }; - let mut stack: SmallVec<[_; 4]> = smallvec![t]; - while let Some(trait_ref) = stack.pop() { - if let Some(alias) = check_trait(trait_ref) { - return Some(alias); - } - for pred in generic_predicates_filtered_by( - db, - GenericDefId::TraitId(trait_ref.def_id.0), - PredicateFilter::SelfTrait, - // We are likely in the midst of lowering generic predicates of `def`. - // So, if we allow `pred == def` we might fall into an infinite recursion. - // Actually, we have already checked for the case `pred == def` above as we started - // with a stack including `trait_id` - |pred| pred != def && pred == GenericDefId::TraitId(trait_ref.def_id.0), - ) - .0 - .deref() - { - tracing::debug!(?pred); - let sup_trait_ref = match pred.kind().skip_binder() { - rustc_type_ir::ClauseKind::Trait(pred) => pred.trait_ref, - _ => continue, - }; - let sup_trait_ref = - EarlyBinder::bind(sup_trait_ref).instantiate(interner, trait_ref.args); - stack.push(sup_trait_ref); - } - tracing::debug!(?stack); - } - - None - }; - - match res { - TypeNs::SelfType(impl_id) => { - let trait_ref = db.impl_trait(impl_id)?; - - // FIXME(next-solver): same method in `lower` checks for impl or not - // Is that needed here? - - // we're _in_ the impl -- the binders get added back later. Correct, - // but it would be nice to make this more explicit - search(trait_ref.skip_binder()) - } - TypeNs::GenericParam(param_id) => { - // Handle `Self::Type` referring to own associated type in trait definitions - // This *must* be done first to avoid cycles with - // `generic_predicates_for_param`, but not sure that it's sufficient, - if let GenericDefId::TraitId(trait_id) = param_id.parent() { - let trait_name = &db.trait_signature(trait_id).name; - tracing::debug!(?trait_name); - let trait_generics = generics(db, trait_id.into()); - tracing::debug!(?trait_generics); - if trait_generics[param_id.local_id()].is_trait_self() { - let args = GenericArgs::identity_for_item(interner, trait_id.into()); - let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); - tracing::debug!(?args, ?trait_ref); - return search(trait_ref); - } - } - - let predicates = - db.generic_predicates_for_param_ns(def, param_id.into(), assoc_name.clone()); - predicates - .iter() - .find_map(|pred| match (*pred).kind().skip_binder() { - rustc_type_ir::ClauseKind::Trait(trait_predicate) => Some(trait_predicate), - _ => None, - }) - .and_then(|trait_predicate| { - let trait_ref = trait_predicate.trait_ref; - assert!( - !trait_ref.has_escaping_bound_vars(), - "FIXME unexpected higher-ranked trait bound" - ); - search(trait_ref) - }) - } - _ => None, - } -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs deleted file mode 100644 index a4ff47e3892a..000000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ /dev/null @@ -1,1327 +0,0 @@ -//! A wrapper around [`TyLoweringContext`] specifically for lowering paths. - -use either::Either; -use hir_def::{ - GenericDefId, GenericParamId, Lookup, TraitId, TypeAliasId, - expr_store::{ - ExpressionStore, HygieneId, - path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments}, - }, - hir::generics::{ - GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance, - }, - resolver::{ResolveValueResult, TypeNs, ValueNs}, - signatures::TraitFlags, - type_ref::{TypeRef, TypeRefId}, -}; -use hir_expand::name::Name; -use rustc_type_ir::{ - AliasTerm, AliasTy, AliasTyKind, - inherent::{GenericArgs as _, Region as _, SliceLike, Ty as _}, -}; -use smallvec::SmallVec; -use stdx::never; - -use crate::{ - GenericArgsProhibitedReason, IncorrectGenericsLenKind, PathGenericsSource, - PathLoweringDiagnostic, TyDefId, ValueTyDefId, - consteval::{unknown_const, unknown_const_as_generic}, - db::HirDatabase, - generics::{Generics, generics}, - lower::PathDiagnosticCallbackData, - lower_nextsolver::{LifetimeElisionKind, named_associated_type_shorthand_candidates}, - next_solver::{ - Binder, Clause, Const, DbInterner, ErrorGuaranteed, Predicate, ProjectionPredicate, Region, - TraitRef, Ty, - mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, - }, -}; - -use super::{ - ImplTraitLoweringMode, TyLoweringContext, associated_type_by_name_including_super_traits, - const_param_ty_query, ty_query, -}; - -type CallbackData<'a, 'db> = Either< - PathDiagnosticCallbackData, - crate::infer::diagnostics::PathDiagnosticCallbackData<'a, 'db>, ->; - -// We cannot use `&mut dyn FnMut()` because of lifetime issues, and we don't want to use `Box` -// because of the allocation, so we create a lifetime-less callback, tailored for our needs. -pub(crate) struct PathDiagnosticCallback<'a, 'db> { - pub(crate) data: CallbackData<'a, 'db>, - pub(crate) callback: - fn(&CallbackData<'_, 'db>, &mut TyLoweringContext<'db, '_>, PathLoweringDiagnostic), -} - -pub(crate) struct PathLoweringContext<'a, 'b, 'db> { - ctx: &'a mut TyLoweringContext<'db, 'b>, - on_diagnostic: PathDiagnosticCallback<'a, 'db>, - path: &'a Path, - segments: PathSegments<'a>, - current_segment_idx: usize, - /// Contains the previous segment if `current_segment_idx == segments.len()` - current_or_prev_segment: PathSegment<'a>, -} - -impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { - #[inline] - pub(crate) fn new( - ctx: &'a mut TyLoweringContext<'db, 'b>, - on_diagnostic: PathDiagnosticCallback<'a, 'db>, - path: &'a Path, - ) -> Self { - let segments = path.segments(); - let first_segment = segments.first().unwrap_or(PathSegment::MISSING); - Self { - ctx, - on_diagnostic, - path, - segments, - current_segment_idx: 0, - current_or_prev_segment: first_segment, - } - } - - #[inline] - #[cold] - fn on_diagnostic(&mut self, diag: PathLoweringDiagnostic) { - (self.on_diagnostic.callback)(&self.on_diagnostic.data, self.ctx, diag); - } - - #[inline] - pub(crate) fn ty_ctx(&mut self) -> &mut TyLoweringContext<'db, 'b> { - self.ctx - } - - #[inline] - fn current_segment_u32(&self) -> u32 { - self.current_segment_idx as u32 - } - - #[inline] - fn skip_resolved_segment(&mut self) { - if !matches!(self.path, Path::LangItem(..)) { - // In lang items, the resolved "segment" is not one of the segments. Perhaps we should've put it - // point at -1, but I don't feel this is clearer. - self.current_segment_idx += 1; - } - self.update_current_segment(); - } - - #[inline] - fn update_current_segment(&mut self) { - self.current_or_prev_segment = - self.segments.get(self.current_segment_idx).unwrap_or(self.current_or_prev_segment); - } - - #[inline] - pub(crate) fn ignore_last_segment(&mut self) { - self.segments = self.segments.strip_last(); - } - - #[inline] - pub(crate) fn set_current_segment(&mut self, segment: usize) { - self.current_segment_idx = segment; - self.current_or_prev_segment = self - .segments - .get(segment) - .expect("invalid segment passed to PathLoweringContext::set_current_segment()"); - } - - #[inline] - fn with_lifetime_elision( - &mut self, - lifetime_elision: LifetimeElisionKind<'db>, - f: impl FnOnce(&mut PathLoweringContext<'_, '_, 'db>) -> T, - ) -> T { - let old_lifetime_elision = - std::mem::replace(&mut self.ctx.lifetime_elision, lifetime_elision); - let result = f(self); - self.ctx.lifetime_elision = old_lifetime_elision; - result - } - - pub(crate) fn lower_ty_relative_path( - &mut self, - ty: Ty<'db>, - // We need the original resolution to lower `Self::AssocTy` correctly - res: Option, - infer_args: bool, - ) -> (Ty<'db>, Option) { - let remaining_segments = self.segments.len() - self.current_segment_idx; - match remaining_segments { - 0 => (ty, res), - 1 => { - // resolve unselected assoc types - (self.select_associated_type(res, infer_args), None) - } - _ => { - // FIXME report error (ambiguous associated type) - (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None) - } - } - } - - // When calling this, the current segment is the resolved segment (we don't advance it yet). - pub(crate) fn lower_partly_resolved_path( - &mut self, - resolution: TypeNs, - infer_args: bool, - ) -> (Ty<'db>, Option) { - let remaining_segments = self.segments.skip(self.current_segment_idx + 1); - tracing::debug!(?remaining_segments); - let rem_seg_len = remaining_segments.len(); - tracing::debug!(?rem_seg_len); - - let ty = match resolution { - TypeNs::TraitId(trait_) => { - let ty = match remaining_segments.len() { - 1 => { - let trait_ref = self.lower_trait_ref_from_resolved_path( - trait_, - Ty::new_error(self.ctx.interner, ErrorGuaranteed), - false, - ); - tracing::debug!(?trait_ref); - self.skip_resolved_segment(); - let segment = self.current_or_prev_segment; - let trait_id = trait_ref.def_id.0; - let found = - trait_id.trait_items(self.ctx.db).associated_type_by_name(segment.name); - - tracing::debug!(?found); - match found { - Some(associated_ty) => { - // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent - // generic params. It's inefficient to splice the `Substitution`s, so we may want - // that method to optionally take parent `Substitution` as we already know them at - // this point (`trait_ref.substitution`). - let substitution = self.substs_from_path_segment( - associated_ty.into(), - false, - None, - true, - ); - let args = crate::next_solver::GenericArgs::new_from_iter( - self.ctx.interner, - trait_ref - .args - .iter() - .chain(substitution.iter().skip(trait_ref.args.len())), - ); - Ty::new_alias( - self.ctx.interner, - AliasTyKind::Projection, - AliasTy::new_from_args( - self.ctx.interner, - associated_ty.into(), - args, - ), - ) - } - None => { - // FIXME: report error (associated type not found) - Ty::new_error(self.ctx.interner, ErrorGuaranteed) - } - } - } - 0 => { - // Trait object type without dyn; this should be handled in upstream. See - // `lower_path()`. - stdx::never!("unexpected fully resolved trait path"); - Ty::new_error(self.ctx.interner, ErrorGuaranteed) - } - _ => { - // FIXME report error (ambiguous associated type) - Ty::new_error(self.ctx.interner, ErrorGuaranteed) - } - }; - return (ty, None); - } - 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) - } - } - } - TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty(impl_id).skip_binder(), - TypeNs::AdtSelfType(adt) => { - let args = crate::next_solver::GenericArgs::identity_for_item( - self.ctx.interner, - adt.into(), - ); - Ty::new_adt(self.ctx.interner, adt, args) - } - - TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args), - TypeNs::BuiltinType(it) => self.lower_path_inner(it.into(), infer_args), - TypeNs::TypeAliasId(it) => self.lower_path_inner(it.into(), infer_args), - // FIXME: report error - TypeNs::EnumVariantId(_) | TypeNs::ModuleId(_) => { - return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None); - } - }; - - tracing::debug!(?ty); - - self.skip_resolved_segment(); - self.lower_ty_relative_path(ty, Some(resolution), infer_args) - } - - /// This returns whether to keep the resolution (`true`) of throw it (`false`). - #[must_use] - fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) -> bool { - let mut prohibit_generics_on_resolved = |reason| { - if self.current_or_prev_segment.args_and_bindings.is_some() { - let segment = self.current_segment_u32(); - self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { - segment, - reason, - }); - } - }; - - match resolution { - TypeNs::SelfType(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) - } - TypeNs::GenericParam(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam) - } - TypeNs::AdtSelfType(_) => { - 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. - return false; - } - } - TypeNs::BuiltinType(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy) - } - TypeNs::ModuleId(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::Module) - } - TypeNs::AdtId(_) - | TypeNs::EnumVariantId(_) - | TypeNs::TypeAliasId(_) - | TypeNs::TraitId(_) => {} - } - - true - } - - pub(crate) fn resolve_path_in_type_ns_fully(&mut self) -> Option { - let (res, unresolved) = self.resolve_path_in_type_ns()?; - if unresolved.is_some() { - return None; - } - Some(res) - } - - #[tracing::instrument(skip(self), ret)] - pub(crate) fn resolve_path_in_type_ns(&mut self) -> Option<(TypeNs, Option)> { - let (resolution, remaining_index, _, prefix_info) = - self.ctx.resolver.resolve_path_in_type_ns_with_prefix_info(self.ctx.db, self.path)?; - - let segments = self.segments; - if segments.is_empty() || matches!(self.path, Path::LangItem(..)) { - // `segments.is_empty()` can occur with `self`. - return Some((resolution, remaining_index)); - } - - let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index { - None if prefix_info.enum_variant => { - (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2)) - } - None => (segments.strip_last(), segments.len() - 1, None), - Some(i) => (segments.take(i - 1), i - 1, None), - }; - - self.current_segment_idx = resolved_segment_idx; - self.current_or_prev_segment = - segments.get(resolved_segment_idx).expect("should have resolved segment"); - - for (i, mod_segment) in module_segments.iter().enumerate() { - if mod_segment.args_and_bindings.is_some() { - self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { - segment: i as u32, - reason: GenericArgsProhibitedReason::Module, - }); - } - } - - if let Some(enum_segment) = enum_segment - && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) - && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) - { - self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { - segment: (enum_segment + 1) as u32, - reason: GenericArgsProhibitedReason::EnumVariant, - }); - } - - if !self.handle_type_ns_resolution(&resolution) { - return None; - } - - Some((resolution, remaining_index)) - } - - pub(crate) fn resolve_path_in_value_ns( - &mut self, - hygiene_id: HygieneId, - ) -> Option { - let (res, prefix_info) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info( - self.ctx.db, - self.path, - hygiene_id, - )?; - - let segments = self.segments; - if segments.is_empty() || matches!(self.path, Path::LangItem(..)) { - // `segments.is_empty()` can occur with `self`. - return Some(res); - } - - let (mod_segments, enum_segment, resolved_segment_idx) = match res { - ResolveValueResult::Partial(_, unresolved_segment, _) => { - (segments.take(unresolved_segment - 1), None, unresolved_segment - 1) - } - ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _) - if prefix_info.enum_variant => - { - (segments.strip_last_two(), segments.len().checked_sub(2), segments.len() - 1) - } - ResolveValueResult::ValueNs(..) => (segments.strip_last(), None, segments.len() - 1), - }; - - self.current_segment_idx = resolved_segment_idx; - self.current_or_prev_segment = - segments.get(resolved_segment_idx).expect("should have resolved segment"); - - for (i, mod_segment) in mod_segments.iter().enumerate() { - if mod_segment.args_and_bindings.is_some() { - self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { - segment: i as u32, - reason: GenericArgsProhibitedReason::Module, - }); - } - } - - if let Some(enum_segment) = enum_segment - && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) - && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) - { - self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { - segment: (enum_segment + 1) as u32, - reason: GenericArgsProhibitedReason::EnumVariant, - }); - } - - match &res { - ResolveValueResult::ValueNs(resolution, _) => { - let resolved_segment_idx = self.current_segment_u32(); - let resolved_segment = self.current_or_prev_segment; - - let mut prohibit_generics_on_resolved = |reason| { - if resolved_segment.args_and_bindings.is_some() { - self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { - segment: resolved_segment_idx, - reason, - }); - } - }; - - match resolution { - ValueNs::ImplSelf(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy); - } - // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not - // E0109 (generic arguments provided for a type that doesn't accept them) for - // consts and statics, presumably as a defense against future in which consts - // and statics can be generic, or just because it was easier for rustc implementors. - // That means we'll show the wrong error code. Because of us it's easier to do it - // this way :) - ValueNs::GenericParam(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const) - } - ValueNs::StaticId(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static) - } - ValueNs::LocalBinding(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::LocalVariable) - } - ValueNs::FunctionId(_) - | ValueNs::StructId(_) - | ValueNs::EnumVariantId(_) - | ValueNs::ConstId(_) => {} - } - } - ResolveValueResult::Partial(resolution, _, _) => { - if !self.handle_type_ns_resolution(resolution) { - return None; - } - } - }; - Some(res) - } - - #[tracing::instrument(skip(self), ret)] - fn select_associated_type(&mut self, res: Option, infer_args: bool) -> Ty<'db> { - let interner = self.ctx.interner; - let Some(res) = res else { - return Ty::new_error(self.ctx.interner, ErrorGuaranteed); - }; - let def = self.ctx.def; - let segment = self.current_or_prev_segment; - let assoc_name = segment.name; - let check_alias = |name: &Name, t: TraitRef<'db>, associated_ty: TypeAliasId| { - if name != assoc_name { - return None; - } - - // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent - // generic params. It's inefficient to splice the `Substitution`s, so we may want - // that method to optionally take parent `Substitution` as we already know them at - // this point (`t.substitution`). - let substs = - self.substs_from_path_segment(associated_ty.into(), infer_args, None, true); - - let substs = crate::next_solver::GenericArgs::new_from_iter( - interner, - t.args.iter().chain(substs.iter().skip(t.args.len())), - ); - - Some(Ty::new_alias( - interner, - AliasTyKind::Projection, - AliasTy::new(interner, associated_ty.into(), substs), - )) - }; - named_associated_type_shorthand_candidates( - interner, - def, - res, - Some(assoc_name.clone()), - check_alias, - ) - .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) - } - - fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> { - let generic_def = match typeable { - TyDefId::BuiltinType(builtinty) => { - return Ty::from_builtin_type(self.ctx.interner, builtinty); - } - TyDefId::AdtId(it) => it.into(), - TyDefId::TypeAliasId(it) => it.into(), - }; - let args = self.substs_from_path_segment(generic_def, infer_args, None, false); - let ty = ty_query(self.ctx.db, typeable); - ty.instantiate(self.ctx.interner, args) - } - - /// Collect generic arguments from a path into a `Substs`. See also - /// `create_substs_for_ast_path` and `def_to_ty` in rustc. - pub(crate) fn substs_from_path( - &mut self, - // Note that we don't call `db.value_type(resolved)` here, - // `ValueTyDefId` is just a convenient way to pass generics and - // special-case enum variants - resolved: ValueTyDefId, - infer_args: bool, - lowering_assoc_type_generics: bool, - ) -> crate::next_solver::GenericArgs<'db> { - let interner = self.ctx.interner; - let prev_current_segment_idx = self.current_segment_idx; - let prev_current_segment = self.current_or_prev_segment; - - let generic_def = match resolved { - ValueTyDefId::FunctionId(it) => it.into(), - ValueTyDefId::StructId(it) => it.into(), - ValueTyDefId::UnionId(it) => it.into(), - ValueTyDefId::ConstId(it) => it.into(), - ValueTyDefId::StaticId(_) => { - return crate::next_solver::GenericArgs::new_from_iter(interner, []); - } - ValueTyDefId::EnumVariantId(var) => { - // the generic args for an enum variant may be either specified - // on the segment referring to the enum, or on the segment - // referring to the variant. So `Option::::None` and - // `Option::None::` are both allowed (though the former is - // FIXME: This isn't strictly correct, enum variants may be used not through the enum - // (via `use Enum::Variant`). The resolver returns whether they were, but we don't have its result - // available here. The worst that can happen is that we will show some confusing diagnostics to the user, - // if generics exist on the module and they don't match with the variant. - // preferred). See also `def_ids_for_path_segments` in rustc. - // - // `wrapping_sub(1)` will return a number which `get` will return None for if current_segment_idx<2. - // This simplifies the code a bit. - let penultimate_idx = self.current_segment_idx.wrapping_sub(1); - let penultimate = self.segments.get(penultimate_idx); - if let Some(penultimate) = penultimate - && self.current_or_prev_segment.args_and_bindings.is_none() - && penultimate.args_and_bindings.is_some() - { - self.current_segment_idx = penultimate_idx; - self.current_or_prev_segment = penultimate; - } - var.lookup(self.ctx.db).parent.into() - } - }; - let result = self.substs_from_path_segment( - generic_def, - infer_args, - None, - lowering_assoc_type_generics, - ); - self.current_segment_idx = prev_current_segment_idx; - self.current_or_prev_segment = prev_current_segment; - result - } - - pub(crate) fn substs_from_path_segment( - &mut self, - def: GenericDefId, - infer_args: bool, - explicit_self_ty: Option>, - lowering_assoc_type_generics: bool, - ) -> crate::next_solver::GenericArgs<'db> { - let old_lifetime_elision = self.ctx.lifetime_elision.clone(); - - if let Some(args) = self.current_or_prev_segment.args_and_bindings - && args.parenthesized != GenericArgsParentheses::No - { - let prohibit_parens = match def { - GenericDefId::TraitId(trait_) => { - // RTN is prohibited anyways if we got here. - let is_rtn = args.parenthesized == GenericArgsParentheses::ReturnTypeNotation; - let is_fn_trait = self - .ctx - .db - .trait_signature(trait_) - .flags - .contains(TraitFlags::RUSTC_PAREN_SUGAR); - is_rtn || !is_fn_trait - } - _ => true, - }; - - if prohibit_parens { - let segment = self.current_segment_u32(); - self.on_diagnostic( - PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, - ); - - return unknown_subst(self.ctx.interner, def); - } - - // `Fn()`-style generics are treated like functions for the purpose of lifetime elision. - self.ctx.lifetime_elision = - LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }; - } - - let result = self.substs_from_args_and_bindings( - self.current_or_prev_segment.args_and_bindings, - def, - infer_args, - explicit_self_ty, - PathGenericsSource::Segment(self.current_segment_u32()), - lowering_assoc_type_generics, - self.ctx.lifetime_elision.clone(), - ); - self.ctx.lifetime_elision = old_lifetime_elision; - result - } - - pub(super) fn substs_from_args_and_bindings( - &mut self, - args_and_bindings: Option<&GenericArgs>, - def: GenericDefId, - infer_args: bool, - explicit_self_ty: Option>, - generics_source: PathGenericsSource, - lowering_assoc_type_generics: bool, - lifetime_elision: LifetimeElisionKind<'db>, - ) -> crate::next_solver::GenericArgs<'db> { - struct LowererCtx<'a, 'b, 'c, 'db> { - ctx: &'a mut PathLoweringContext<'b, 'c, 'db>, - generics_source: PathGenericsSource, - } - - impl<'db> GenericArgsLowerer<'db> for LowererCtx<'_, '_, '_, 'db> { - fn report_len_mismatch( - &mut self, - def: GenericDefId, - provided_count: u32, - expected_count: u32, - kind: IncorrectGenericsLenKind, - ) { - self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsLen { - generics_source: self.generics_source, - provided_count, - expected_count, - kind, - def, - }); - } - - fn report_arg_mismatch( - &mut self, - param_id: GenericParamId, - arg_idx: u32, - has_self_arg: bool, - ) { - self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsOrder { - generics_source: self.generics_source, - param_id, - arg_idx, - has_self_arg, - }); - } - - fn provided_kind( - &mut self, - param_id: GenericParamId, - param: GenericParamDataRef<'_>, - arg: &GenericArg, - ) -> crate::next_solver::GenericArg<'db> { - match (param, *arg) { - (GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => { - self.ctx.ctx.lower_lifetime(lifetime).into() - } - (GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => { - self.ctx.ctx.lower_ty(type_ref).into() - } - (GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => { - let GenericParamId::ConstParamId(const_id) = param_id else { - unreachable!("non-const param ID for const param"); - }; - self.ctx - .ctx - .lower_const(konst, const_param_ty_query(self.ctx.ctx.db, const_id)) - .into() - } - _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), - } - } - - fn provided_type_like_const( - &mut self, - const_ty: Ty<'db>, - arg: TypeLikeConst<'_>, - ) -> crate::next_solver::Const<'db> { - match arg { - TypeLikeConst::Path(path) => self.ctx.ctx.lower_path_as_const(path, const_ty), - TypeLikeConst::Infer => unknown_const(const_ty), - } - } - - fn inferred_kind( - &mut self, - def: GenericDefId, - param_id: GenericParamId, - param: GenericParamDataRef<'_>, - infer_args: bool, - preceding_args: &[crate::next_solver::GenericArg<'db>], - ) -> crate::next_solver::GenericArg<'db> { - let default = || { - self.ctx.ctx.db.generic_defaults(def).get(preceding_args.len()).map(|default| { - convert_binder_to_early_binder( - self.ctx.ctx.interner, - def, - default.to_nextsolver(self.ctx.ctx.interner), - ) - .instantiate(self.ctx.ctx.interner, preceding_args) - }) - }; - match param { - GenericParamDataRef::LifetimeParamData(_) => { - Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed)) - .into() - } - GenericParamDataRef::TypeParamData(param) => { - if !infer_args - && param.default.is_some() - && let Some(default) = default() - { - return default; - } - Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() - } - GenericParamDataRef::ConstParamData(param) => { - if !infer_args - && param.default.is_some() - && let Some(default) = default() - { - return default; - } - let GenericParamId::ConstParamId(const_id) = param_id else { - unreachable!("non-const param ID for const param"); - }; - unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id)) - } - } - } - - fn parent_arg( - &mut self, - param_id: GenericParamId, - ) -> crate::next_solver::GenericArg<'db> { - match param_id { - GenericParamId::TypeParamId(_) => { - Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() - } - GenericParamId::ConstParamId(const_id) => { - unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id)) - } - GenericParamId::LifetimeParamId(_) => { - Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed)) - .into() - } - } - } - - fn report_elided_lifetimes_in_path( - &mut self, - def: GenericDefId, - expected_count: u32, - hard_error: bool, - ) { - self.ctx.on_diagnostic(PathLoweringDiagnostic::ElidedLifetimesInPath { - generics_source: self.generics_source, - def, - expected_count, - hard_error, - }); - } - - fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32) { - self.ctx.on_diagnostic(PathLoweringDiagnostic::ElisionFailure { - generics_source: self.generics_source, - def, - expected_count, - }); - } - - fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32) { - self.ctx.on_diagnostic(PathLoweringDiagnostic::MissingLifetime { - generics_source: self.generics_source, - def, - expected_count, - }); - } - } - - substs_from_args_and_bindings( - self.ctx.db, - self.ctx.store, - args_and_bindings, - def, - infer_args, - lifetime_elision, - lowering_assoc_type_generics, - explicit_self_ty, - &mut LowererCtx { ctx: self, generics_source }, - ) - } - - pub(crate) fn lower_trait_ref_from_resolved_path( - &mut self, - resolved: TraitId, - explicit_self_ty: Ty<'db>, - infer_args: bool, - ) -> TraitRef<'db> { - let args = self.trait_ref_substs_from_path(resolved, explicit_self_ty, infer_args); - TraitRef::new_from_args(self.ctx.interner, resolved.into(), args) - } - - fn trait_ref_substs_from_path( - &mut self, - resolved: TraitId, - explicit_self_ty: Ty<'db>, - infer_args: bool, - ) -> crate::next_solver::GenericArgs<'db> { - self.substs_from_path_segment(resolved.into(), infer_args, Some(explicit_self_ty), false) - } - - pub(super) fn assoc_type_bindings_from_type_bound<'c>( - mut self, - trait_ref: TraitRef<'db>, - ) -> Option> + use<'a, 'b, 'c, 'db>> { - let interner = self.ctx.interner; - self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| { - args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| { - let found = associated_type_by_name_including_super_traits( - self.ctx.db, - trait_ref, - &binding.name, - ); - let (super_trait_ref, associated_ty) = match found { - None => return SmallVec::new(), - Some(t) => t, - }; - let args = - self.with_lifetime_elision(LifetimeElisionKind::AnonymousReportError, |this| { - // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent - // generic params. It's inefficient to splice the `Substitution`s, so we may want - // that method to optionally take parent `Substitution` as we already know them at - // this point (`super_trait_ref.substitution`). - this.substs_from_args_and_bindings( - binding.args.as_ref(), - associated_ty.into(), - false, // this is not relevant - Some(super_trait_ref.self_ty()), - PathGenericsSource::AssocType { - segment: this.current_segment_u32(), - assoc_type: binding_idx as u32, - }, - false, - this.ctx.lifetime_elision.clone(), - ) - }); - let args = crate::next_solver::GenericArgs::new_from_iter( - interner, - super_trait_ref.args.iter().chain(args.iter().skip(super_trait_ref.args.len())), - ); - let projection_term = - AliasTerm::new_from_args(interner, associated_ty.into(), args); - let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity( - binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), - ); - if let Some(type_ref) = binding.type_ref { - let lifetime_elision = - if args_and_bindings.parenthesized == GenericArgsParentheses::ParenSugar { - // `Fn()`-style generics are elided like functions. This is `Output` (we lower to it in hir-def). - LifetimeElisionKind::for_fn_ret(self.ctx.interner) - } else { - self.ctx.lifetime_elision.clone() - }; - self.with_lifetime_elision(lifetime_elision, |this| { - match (&this.ctx.store[type_ref], this.ctx.impl_trait_mode.mode) { - (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (), - ( - _, - ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque, - ) => { - let ty = this.ctx.lower_ty(type_ref); - let pred = Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Projection( - ProjectionPredicate { - projection_term, - term: ty.into(), - }, - ), - )), - )); - predicates.push(pred); - } - } - }) - } - for bound in binding.bounds.iter() { - predicates.extend(self.ctx.lower_type_bound( - bound, - Ty::new_alias( - self.ctx.interner, - AliasTyKind::Projection, - AliasTy::new_from_args(self.ctx.interner, associated_ty.into(), args), - ), - false, - )); - } - predicates - }) - }) - } -} - -/// A const that were parsed like a type. -pub(crate) enum TypeLikeConst<'a> { - Infer, - Path(&'a Path), -} - -pub(crate) trait GenericArgsLowerer<'db> { - fn report_elided_lifetimes_in_path( - &mut self, - def: GenericDefId, - expected_count: u32, - hard_error: bool, - ); - - fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32); - - fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32); - - fn report_len_mismatch( - &mut self, - def: GenericDefId, - provided_count: u32, - expected_count: u32, - kind: IncorrectGenericsLenKind, - ); - - fn report_arg_mismatch(&mut self, param_id: GenericParamId, arg_idx: u32, has_self_arg: bool); - - fn provided_kind( - &mut self, - param_id: GenericParamId, - param: GenericParamDataRef<'_>, - arg: &GenericArg, - ) -> crate::next_solver::GenericArg<'db>; - - fn provided_type_like_const(&mut self, const_ty: Ty<'db>, arg: TypeLikeConst<'_>) - -> Const<'db>; - - fn inferred_kind( - &mut self, - def: GenericDefId, - param_id: GenericParamId, - param: GenericParamDataRef<'_>, - infer_args: bool, - preceding_args: &[crate::next_solver::GenericArg<'db>], - ) -> crate::next_solver::GenericArg<'db>; - - fn parent_arg(&mut self, param_id: GenericParamId) -> crate::next_solver::GenericArg<'db>; -} - -/// Returns true if there was an error. -fn check_generic_args_len<'db>( - args_and_bindings: Option<&GenericArgs>, - def: GenericDefId, - def_generics: &Generics, - infer_args: bool, - lifetime_elision: &LifetimeElisionKind<'db>, - lowering_assoc_type_generics: bool, - ctx: &mut impl GenericArgsLowerer<'db>, -) -> bool { - let mut had_error = false; - - let (mut provided_lifetimes_count, mut provided_types_and_consts_count) = (0usize, 0usize); - if let Some(args_and_bindings) = args_and_bindings { - let args_no_self = &args_and_bindings.args[usize::from(args_and_bindings.has_self_type)..]; - for arg in args_no_self { - match arg { - GenericArg::Lifetime(_) => provided_lifetimes_count += 1, - GenericArg::Type(_) | GenericArg::Const(_) => provided_types_and_consts_count += 1, - } - } - } - - let lifetime_args_len = def_generics.len_lifetimes_self(); - if provided_lifetimes_count == 0 - && lifetime_args_len > 0 - && (!lowering_assoc_type_generics || infer_args) - { - // In generic associated types, we never allow inferring the lifetimes, but only in type context, that is - // when `infer_args == false`. In expression/pattern context we always allow inferring them, even for GATs. - match lifetime_elision { - &LifetimeElisionKind::AnonymousCreateParameter { report_in_path } => { - ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, report_in_path); - had_error |= report_in_path; - } - LifetimeElisionKind::AnonymousReportError => { - ctx.report_missing_lifetime(def, lifetime_args_len as u32); - had_error = true - } - LifetimeElisionKind::ElisionFailure => { - ctx.report_elision_failure(def, lifetime_args_len as u32); - had_error = true; - } - LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => { - // FIXME: Check there are other lifetimes in scope, and error/lint. - } - LifetimeElisionKind::Elided(_) => { - ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, false); - } - LifetimeElisionKind::Infer => { - // Allow eliding lifetimes. - } - } - } else if lifetime_args_len != provided_lifetimes_count { - ctx.report_len_mismatch( - def, - provided_lifetimes_count as u32, - lifetime_args_len as u32, - IncorrectGenericsLenKind::Lifetimes, - ); - had_error = true; - } - - let defaults_count = - def_generics.iter_self_type_or_consts().filter(|(_, param)| param.has_default()).count(); - let named_type_and_const_params_count = def_generics - .iter_self_type_or_consts() - .filter(|(_, param)| match param { - TypeOrConstParamData::TypeParamData(param) => { - param.provenance == TypeParamProvenance::TypeParamList - } - TypeOrConstParamData::ConstParamData(_) => true, - }) - .count(); - let expected_max = named_type_and_const_params_count; - let expected_min = - if infer_args { 0 } else { named_type_and_const_params_count - defaults_count }; - if provided_types_and_consts_count < expected_min - || expected_max < provided_types_and_consts_count - { - ctx.report_len_mismatch( - def, - provided_types_and_consts_count as u32, - named_type_and_const_params_count as u32, - IncorrectGenericsLenKind::TypesAndConsts, - ); - had_error = true; - } - - had_error -} - -pub(crate) fn substs_from_args_and_bindings<'db>( - db: &'db dyn HirDatabase, - store: &ExpressionStore, - args_and_bindings: Option<&GenericArgs>, - def: GenericDefId, - mut infer_args: bool, - lifetime_elision: LifetimeElisionKind<'db>, - lowering_assoc_type_generics: bool, - explicit_self_ty: Option>, - ctx: &mut impl GenericArgsLowerer<'db>, -) -> crate::next_solver::GenericArgs<'db> { - let interner = DbInterner::new_with(db, None, None); - - tracing::debug!(?args_and_bindings); - - // Order is - // - Parent parameters - // - Optional Self parameter - // - Lifetime parameters - // - Type or Const parameters - let def_generics = generics(db, def); - let args_slice = args_and_bindings.map(|it| &*it.args).unwrap_or_default(); - - // We do not allow inference if there are specified args, i.e. we do not allow partial inference. - let has_non_lifetime_args = - args_slice.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))); - infer_args &= !has_non_lifetime_args; - - let had_count_error = check_generic_args_len( - args_and_bindings, - def, - &def_generics, - infer_args, - &lifetime_elision, - lowering_assoc_type_generics, - ctx, - ); - - let mut substs = Vec::with_capacity(def_generics.len()); - - substs.extend(def_generics.iter_parent_id().map(|id| ctx.parent_arg(id))); - - let mut args = args_slice.iter().enumerate().peekable(); - let mut params = def_generics.iter_self().peekable(); - - // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. - // If we later encounter a lifetime, we know that the arguments were provided in the - // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be - // inferred, so we can use it for diagnostics later. - let mut force_infer_lt = None; - - let has_self_arg = args_and_bindings.is_some_and(|it| it.has_self_type); - // First, handle `Self` parameter. Consume it from the args if provided, otherwise from `explicit_self_ty`, - // and lastly infer it. - if let Some(&( - self_param_id, - self_param @ GenericParamDataRef::TypeParamData(TypeParamData { - provenance: TypeParamProvenance::TraitSelf, - .. - }), - )) = params.peek() - { - let self_ty = if has_self_arg { - let (_, self_ty) = args.next().expect("has_self_type=true, should have Self type"); - ctx.provided_kind(self_param_id, self_param, self_ty) - } else { - explicit_self_ty.map(|it| it.into()).unwrap_or_else(|| { - ctx.inferred_kind(def, self_param_id, self_param, infer_args, &substs) - }) - }; - params.next(); - substs.push(self_ty); - } - - loop { - // We're going to iterate through the generic arguments that the user - // provided, matching them with the generic parameters we expect. - // Mismatches can occur as a result of elided lifetimes, or for malformed - // input. We try to handle both sensibly. - match (args.peek(), params.peek()) { - (Some(&(arg_idx, arg)), Some(&(param_id, param))) => match (arg, param) { - (GenericArg::Type(_), GenericParamDataRef::TypeParamData(type_param)) - if type_param.provenance == TypeParamProvenance::ArgumentImplTrait => - { - // Do not allow specifying `impl Trait` explicitly. We already err at that, but if we won't handle it here - // we will handle it as if it was specified, instead of inferring it. - substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs)); - params.next(); - } - (GenericArg::Lifetime(_), GenericParamDataRef::LifetimeParamData(_)) - | (GenericArg::Type(_), GenericParamDataRef::TypeParamData(_)) - | (GenericArg::Const(_), GenericParamDataRef::ConstParamData(_)) => { - substs.push(ctx.provided_kind(param_id, param, arg)); - args.next(); - params.next(); - } - ( - GenericArg::Type(_) | GenericArg::Const(_), - GenericParamDataRef::LifetimeParamData(_), - ) => { - // We expected a lifetime argument, but got a type or const - // argument. That means we're inferring the lifetime. - substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs)); - params.next(); - force_infer_lt = Some((arg_idx as u32, param_id)); - } - (GenericArg::Type(type_ref), GenericParamDataRef::ConstParamData(_)) => { - if let Some(konst) = type_looks_like_const(store, *type_ref) { - let GenericParamId::ConstParamId(param_id) = param_id else { - 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()); - args.next(); - params.next(); - } else { - // See the `_ => { ... }` branch. - if !had_count_error { - ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg); - } - while args.next().is_some() {} - } - } - _ => { - // We expected one kind of parameter, but the user provided - // another. This is an error. However, if we already know that - // the arguments don't match up with the parameters, we won't issue - // an additional error, as the user already knows what's wrong. - if !had_count_error { - ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg); - } - - // We've reported the error, but we want to make sure that this - // problem doesn't bubble down and create additional, irrelevant - // errors. In this case, we're simply going to ignore the argument - // and any following arguments. The rest of the parameters will be - // inferred. - while args.next().is_some() {} - } - }, - - (Some(&(_, arg)), None) => { - // We should never be able to reach this point with well-formed input. - // There are two situations in which we can encounter this issue. - // - // 1. The number of arguments is incorrect. In this case, an error - // will already have been emitted, and we can ignore it. - // 2. We've inferred some lifetimes, which have been provided later (i.e. - // after a type or const). We want to throw an error in this case. - if !had_count_error { - assert!( - matches!(arg, GenericArg::Lifetime(_)), - "the only possible situation here is incorrect lifetime order" - ); - let (provided_arg_idx, param_id) = - force_infer_lt.expect("lifetimes ought to have been inferred"); - ctx.report_arg_mismatch(param_id, provided_arg_idx, has_self_arg); - } - - break; - } - - (None, Some(&(param_id, param))) => { - // If there are fewer arguments than parameters, it means we're inferring the remaining arguments. - let param = if let GenericParamId::LifetimeParamId(_) = param_id { - match &lifetime_elision { - LifetimeElisionKind::ElisionFailure - | LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true } - | LifetimeElisionKind::AnonymousReportError => { - assert!(had_count_error); - ctx.inferred_kind(def, param_id, param, infer_args, &substs) - } - LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => { - Region::new_static(interner).into() - } - LifetimeElisionKind::Elided(lifetime) => (*lifetime).into(), - LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false } - | LifetimeElisionKind::Infer => { - // FIXME: With `AnonymousCreateParameter`, we need to create a new lifetime parameter here - // (but this will probably be done in hir-def lowering instead). - ctx.inferred_kind(def, param_id, param, infer_args, &substs) - } - } - } else { - ctx.inferred_kind(def, param_id, param, infer_args, &substs) - }; - substs.push(param); - params.next(); - } - - (None, None) => break, - } - } - - crate::next_solver::GenericArgs::new_from_iter(interner, substs) -} - -fn type_looks_like_const( - store: &ExpressionStore, - type_ref: TypeRefId, -) -> Option> { - // A path/`_` const will be parsed as a type, instead of a const, because when parsing/lowering - // in hir-def we don't yet know the expected argument kind. rustc does this a bit differently, - // when lowering to HIR it resolves the path, and if it doesn't resolve to the type namespace - // it is lowered as a const. Our behavior could deviate from rustc when the value is resolvable - // in both the type and value namespaces, but I believe we only allow more code. - let type_ref = &store[type_ref]; - match type_ref { - TypeRef::Path(path) => Some(TypeLikeConst::Path(path)), - TypeRef::Placeholder => Some(TypeLikeConst::Infer), - _ => None, - } -} - -fn unknown_subst<'db>( - interner: DbInterner<'db>, - def: impl Into, -) -> crate::next_solver::GenericArgs<'db> { - let params = generics(interner.db(), def.into()); - crate::next_solver::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(_) => { - crate::next_solver::Region::error(interner).into() - } - }), - ) -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs deleted file mode 100644 index 5125a38825cb..000000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs +++ /dev/null @@ -1,169 +0,0 @@ -//! This module contains the implementations of the `ToChalk` trait, which -//! handles conversion between our data types and their corresponding types in -//! Chalk (in both directions); plus some helper functions for more specialized -//! conversions. - -use hir_def::{LifetimeParamId, TraitId, TypeAliasId, TypeOrConstParamId}; -use salsa::{ - Id, - plumbing::{AsId, FromId}, -}; - -use crate::{ - AssocTypeId, CallableDefId, ChalkTraitId, FnDefId, ForeignDefId, Interner, OpaqueTyId, - PlaceholderIndex, chalk_db, - db::{HirDatabase, InternedLifetimeParamId, InternedTypeOrConstParamId}, -}; - -pub trait ToChalk { - type Chalk; - fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk; - fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self; -} - -pub(crate) fn from_chalk(db: &dyn HirDatabase, chalk: ChalkT) -> T -where - T: ToChalk, -{ - T::from_chalk(db, chalk) -} - -impl ToChalk for hir_def::ImplId { - type Chalk = chalk_db::ImplId; - - fn to_chalk(self, _db: &dyn HirDatabase) -> chalk_db::ImplId { - chalk_ir::ImplId(self.as_id()) - } - - fn from_chalk(_db: &dyn HirDatabase, impl_id: chalk_db::ImplId) -> hir_def::ImplId { - FromId::from_id(impl_id.0.as_id()) - } -} - -impl ToChalk for CallableDefId { - type Chalk = FnDefId; - - fn to_chalk(self, _db: &dyn HirDatabase) -> FnDefId { - chalk_ir::FnDefId(salsa::plumbing::AsId::as_id(&self)) - } - - fn from_chalk(db: &dyn HirDatabase, fn_def_id: FnDefId) -> CallableDefId { - salsa::plumbing::FromIdWithDb::from_id(fn_def_id.0, db.zalsa()) - } -} - -impl From for crate::db::InternedOpaqueTyId { - fn from(id: OpaqueTyId) -> Self { - FromId::from_id(id.0) - } -} - -impl From for OpaqueTyId { - fn from(id: crate::db::InternedOpaqueTyId) -> Self { - chalk_ir::OpaqueTyId(id.as_id()) - } -} - -impl From> for crate::db::InternedClosureId { - fn from(id: chalk_ir::ClosureId) -> Self { - FromId::from_id(id.0) - } -} - -impl From for chalk_ir::ClosureId { - fn from(id: crate::db::InternedClosureId) -> Self { - chalk_ir::ClosureId(id.as_id()) - } -} - -impl From> for crate::db::InternedCoroutineId { - fn from(id: chalk_ir::CoroutineId) -> Self { - Self::from_id(id.0) - } -} - -impl From for chalk_ir::CoroutineId { - fn from(id: crate::db::InternedCoroutineId) -> Self { - chalk_ir::CoroutineId(id.as_id()) - } -} - -pub fn to_foreign_def_id(id: TypeAliasId) -> ForeignDefId { - chalk_ir::ForeignDefId(id.as_id()) -} - -pub fn from_foreign_def_id(id: ForeignDefId) -> TypeAliasId { - FromId::from_id(id.0) -} - -pub fn to_assoc_type_id(id: TypeAliasId) -> AssocTypeId { - chalk_ir::AssocTypeId(id.as_id()) -} - -pub fn from_assoc_type_id(id: AssocTypeId) -> TypeAliasId { - FromId::from_id(id.0) -} - -pub fn from_placeholder_idx( - db: &dyn HirDatabase, - idx: PlaceholderIndex, -) -> (TypeOrConstParamId, u32) { - assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); - // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. - let interned_id = - InternedTypeOrConstParamId::from_id(unsafe { Id::from_index(idx.idx.try_into().unwrap()) }); - interned_id.loc(db) -} - -pub fn to_placeholder_idx( - db: &dyn HirDatabase, - id: TypeOrConstParamId, - idx: u32, -) -> PlaceholderIndex { - let interned_id = InternedTypeOrConstParamId::new(db, (id, idx)); - PlaceholderIndex { - ui: chalk_ir::UniverseIndex::ROOT, - idx: interned_id.as_id().index() as usize, - } -} - -pub fn to_placeholder_idx_no_index( - db: &dyn HirDatabase, - id: TypeOrConstParamId, -) -> PlaceholderIndex { - let index = crate::generics::generics(db, id.parent) - .type_or_const_param_idx(id) - .expect("param not found"); - to_placeholder_idx(db, id, index as u32) -} - -pub fn lt_from_placeholder_idx( - db: &dyn HirDatabase, - idx: PlaceholderIndex, -) -> (LifetimeParamId, u32) { - assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); - // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. - let interned_id = - InternedLifetimeParamId::from_id(unsafe { Id::from_index(idx.idx.try_into().unwrap()) }); - interned_id.loc(db) -} - -pub fn lt_to_placeholder_idx( - db: &dyn HirDatabase, - id: LifetimeParamId, - idx: u32, -) -> PlaceholderIndex { - let interned_id = InternedLifetimeParamId::new(db, (id, idx)); - PlaceholderIndex { - ui: chalk_ir::UniverseIndex::ROOT, - idx: interned_id.as_id().index() as usize, - } -} - -pub fn to_chalk_trait_id(id: TraitId) -> ChalkTraitId { - chalk_ir::TraitId(id.as_id()) -} - -pub fn from_chalk_trait_id(id: ChalkTraitId) -> TraitId { - FromId::from_id(id.0) -} 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 bce17905037c..cec63566338f 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,10 +25,8 @@ use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; -use crate::next_solver::infer::InferCtxt; -use crate::next_solver::infer::select::ImplSource; use crate::{ - TraitEnvironment, TyBuilder, + TraitEnvironment, autoderef::{self, AutoderefKind}, db::HirDatabase, infer::{Adjust, Adjustment, OverloadedDeref, PointerCast, unify::InferenceTable}, @@ -37,7 +35,8 @@ use crate::{ Canonical, DbInterner, ErrorGuaranteed, GenericArgs, Goal, Predicate, Region, SolverDefId, TraitRef, Ty, TyKind, TypingMode, infer::{ - DbInternerInferExt, + DbInternerInferExt, InferCtxt, + select::ImplSource, traits::{Obligation, ObligationCause, PredicateObligation}, }, obligation_ctxt::ObligationCtxt, @@ -1597,9 +1596,9 @@ fn is_valid_impl_method_candidate<'db>( return IsValidCandidate::NotVisible; } let self_ty_matches = table.run_in_snapshot(|table| { - let expected_self_ty = TyBuilder::impl_self_ty(db, impl_id) - .fill_with_inference_vars(table) - .build(table.interner()); + let impl_args = table.fresh_args_for_item(impl_id.into()); + let expected_self_ty = + db.impl_self_ty(impl_id).instantiate(table.interner(), impl_args); table.unify(expected_self_ty, self_ty) }); if !self_ty_matches { @@ -1727,7 +1726,7 @@ fn is_valid_impl_fn_candidate<'db>( // We need to consider the bounds on the impl to distinguish functions of the same name // for a type. - let predicates = db.generic_predicates_ns(impl_id.into()); + let predicates = db.generic_predicates(impl_id.into()); let Some(predicates) = predicates.instantiate(table.interner(), impl_subst) else { return IsValidCandidate::Yes; }; 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 936895fb7fd3..7aebe17e5b4e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -196,7 +196,7 @@ impl ProjectionElem { }, ProjectionElem::Field(Either::Left(f)) => match base.kind() { TyKind::Adt(_, subst) => { - db.field_types_ns(f.parent)[f.local_id].instantiate(interner, subst) + db.field_types(f.parent)[f.local_id].instantiate(interner, subst) } ty => { never!("Only adt has field, found {:?}", ty); 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 444336ca3f9b..6e62bcbbddef 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 @@ -1696,7 +1696,7 @@ impl<'db> Evaluator<'db> { if let TyKind::Adt(adt_ef, subst) = kind && let AdtId::StructId(struct_id) = adt_ef.def_id().0 { - let field_types = self.db.field_types_ns(struct_id.into()); + let field_types = self.db.field_types(struct_id.into()); if let Some(ty) = field_types.iter().last().map(|it| it.1.instantiate(self.interner(), subst)) { @@ -1775,9 +1775,9 @@ impl<'db> Evaluator<'db> { else { not_supported!("unsizing struct without field"); }; - let target_last_field = self.db.field_types_ns(id.into())[last_field] + let target_last_field = self.db.field_types(id.into())[last_field] .instantiate(self.interner(), target_subst); - let current_last_field = self.db.field_types_ns(id.into())[last_field] + let current_last_field = self.db.field_types(id.into())[last_field] .instantiate(self.interner(), current_subst); return self.unsizing_ptr_from_addr( target_last_field, @@ -2268,7 +2268,7 @@ impl<'db> Evaluator<'db> { AdtId::StructId(s) => { let data = s.fields(this.db); let layout = this.layout(ty)?; - let field_types = this.db.field_types_ns(s.into()); + let field_types = this.db.field_types(s.into()); for (f, _) in data.fields().iter() { let offset = layout .fields @@ -2296,7 +2296,7 @@ impl<'db> Evaluator<'db> { e, ) { let data = v.fields(this.db); - let field_types = this.db.field_types_ns(v.into()); + let field_types = this.db.field_types(v.into()); for (f, _) in data.fields().iter() { let offset = l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize(); @@ -2373,7 +2373,7 @@ impl<'db> Evaluator<'db> { } TyKind::Adt(id, args) => match id.def_id().0 { AdtId::StructId(s) => { - for (i, (_, ty)) in self.db.field_types_ns(s.into()).iter().enumerate() { + for (i, (_, ty)) in self.db.field_types(s.into()).iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); let ty = ty.instantiate(self.interner(), args); self.patch_addresses( @@ -2394,7 +2394,7 @@ impl<'db> Evaluator<'db> { self.read_memory(addr, layout.size.bytes_usize())?, e, ) { - for (i, (_, ty)) in self.db.field_types_ns(ev.into()).iter().enumerate() { + for (i, (_, ty)) in self.db.field_types(ev.into()).iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); let ty = ty.instantiate(self.interner(), args); self.patch_addresses( @@ -2895,7 +2895,7 @@ impl<'db> Evaluator<'db> { let variant_fields = s.fields(self.db); match variant_fields.shape { FieldsShape::Record | FieldsShape::Tuple => { - let field_types = self.db.field_types_ns(s.into()); + let field_types = self.db.field_types(s.into()); for (field, _) in variant_fields.fields().iter() { let offset = layout .fields 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 c45ae9dcc3d3..4b1adecf8c87 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 @@ -1383,7 +1383,7 @@ impl<'db> Evaluator<'db> { AdtId::StructId(s) => s, _ => not_supported!("unsized enum or union"), }; - let field_types = self.db.field_types_ns(id.into()); + let field_types = self.db.field_types(id.into()); let last_field_ty = field_types.iter().next_back().unwrap().1.instantiate(self.interner(), subst); let sized_part_size = 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 ade94b94c0ee..4c64a70a7a62 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 @@ -34,7 +34,7 @@ impl<'db> Evaluator<'db> { let Some((first_field, _)) = fields.iter().next() else { not_supported!("simd type with no field"); }; - let field_ty = self.db.field_types_ns(id.into())[first_field] + let field_ty = self.db.field_types(id.into())[first_field] .instantiate(self.interner(), subst); return Ok((fields.len(), field_ty)); } 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 f5b4fa1e2a00..1fb9a82ac9e0 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 @@ -32,7 +32,7 @@ pub use region::*; pub use solver::*; pub use ty::*; -pub use crate::lower_nextsolver::ImplTraitIdx; +pub use crate::lower::ImplTraitIdx; pub use rustc_ast_ir::Mutability; pub type Binder<'db, T> = rustc_type_ir::Binder, T>; 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 8d81a382c362..c28af948bfc8 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 @@ -14,8 +14,7 @@ use rustc_type_ir::{ use crate::{ MemoryMap, - interner::InternedWrapperNoDebug, - next_solver::{ClauseKind, ParamEnv}, + next_solver::{ClauseKind, ParamEnv, interner::InternedWrapperNoDebug}, }; use super::{BoundVarKind, DbInterner, ErrorGuaranteed, GenericArgs, Placeholder, Ty}; 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 b2632ba63709..24f22bcb0c3e 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 @@ -207,7 +207,7 @@ impl<'db> GenericArgs<'db> { where F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { - let defaults = interner.db.generic_defaults_ns(def_id); + let defaults = interner.db.generic_defaults(def_id); Self::for_item(interner, def_id.into(), |idx, id, prev| match defaults.get(idx as usize) { Some(default) => default.instantiate(interner, prev), None => fallback(idx, id, prev), @@ -240,7 +240,7 @@ impl<'db> GenericArgs<'db> { where F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { - let defaults = interner.db.generic_defaults_ns(def_id); + let defaults = interner.db.generic_defaults(def_id); Self::fill_rest(interner, def_id.into(), first, |idx, id, prev| { defaults .get(idx as usize) 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 331bcdcb26d3..42f1d926d7db 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 @@ -32,8 +32,8 @@ use crate::{ method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint}, next_solver::{ AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, - CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, ImplIdWrapper, InternedWrapperNoDebug, - RegionAssumptions, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, + CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, ImplIdWrapper, RegionAssumptions, + SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls}, }, }; @@ -53,6 +53,9 @@ use super::{ util::sizedness_constraint_for_ty, }; +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] +pub struct InternedWrapperNoDebug(pub(crate) T); + #[macro_export] #[doc(hidden)] macro_rules! _interned_vec_nolifetime_salsa { @@ -611,7 +614,7 @@ impl<'db> inherent::AdtDef> for AdtDef { return None; }; let id: VariantId = struct_id.into(); - let field_types = interner.db().field_types_ns(id); + let field_types = interner.db().field_types(id); field_types.iter().last().map(|f| *f.1) } @@ -623,7 +626,7 @@ impl<'db> inherent::AdtDef> for AdtDef { let db = interner.db(); // FIXME: this is disabled just to match the behavior with chalk right now let _field_tys = |id: VariantId| { - db.field_types_ns(id).iter().map(|(_, ty)| ty.skip_binder()).collect::>() + db.field_types(id).iter().map(|(_, ty)| ty.skip_binder()).collect::>() }; let field_tys = |_id: VariantId| vec![]; let tys: Vec<_> = match self.inner().id { @@ -1284,7 +1287,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, def_id: Self::DefId, ) -> EarlyBinder> { - let predicates = self.db().generic_predicates_ns(def_id.try_into().unwrap()); + let predicates = self.db().generic_predicates(def_id.try_into().unwrap()); let predicates: Vec<_> = predicates.iter().cloned().collect(); EarlyBinder::bind(predicates.into_iter()) } @@ -1311,7 +1314,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { let predicates: Vec<(Clause<'db>, Span)> = self .db() - .generic_predicates_ns(def_id.0.into()) + .generic_predicates(def_id.0.into()) .iter() .filter(|p| match p.kind().skip_binder() { // rustc has the following assertion: @@ -1345,7 +1348,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { let predicates: Vec<(Clause<'db>, Span)> = self .db() - .generic_predicates_ns(def_id.try_into().unwrap()) + .generic_predicates(def_id.try_into().unwrap()) .iter() .filter(|p| match p.kind().skip_binder() { rustc_type_ir::ClauseKind::Trait(it) => is_self_or_assoc(it.self_ty()), @@ -1765,7 +1768,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { return UnsizingParams(DenseBitSet::new_empty(num_params)); }; - let field_types = self.db().field_types_ns(variant.id()); + let field_types = self.db().field_types(variant.id()); let mut unsizing_params = DenseBitSet::new_empty(num_params); let ty = field_types[tail_field.0]; for arg in ty.instantiate_identity().walk() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 671f06f1b88a..2b29561393ee 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -1,1736 +1,13 @@ //! Things useful for mapping to/from Chalk and next-trait-solver types. -use chalk_ir::{ - InferenceVar, Substitution, TyVariableKind, WellFormed, cast::Cast, fold::Shift, - interner::HasInterner, -}; -use hir_def::{CallableDefId, ConstParamId, GeneralConstId, TypeParamId, signatures::TraitFlags}; -use hir_def::{GenericDefId, GenericParamId}; -use rustc_type_ir::{ - AliasTerm, BoundVar, DebruijnIndex, ExistentialProjection, ExistentialTraitRef, - OutlivesPredicate, ProjectionPredicate, TypeFoldable, TypeSuperFoldable, TypeVisitable, - UniverseIndex, elaborate, - inherent::{BoundVarLike, IntoKind, SliceLike, Ty as _}, - shift_vars, - solve::Goal, -}; +use crate::next_solver::interner::DbInterner; -use crate::next_solver::BoundConst; -use crate::{ - ConstScalar, Interner, MemoryMap, - db::{InternedClosureId, InternedCoroutineId, InternedOpaqueTyId}, - from_assoc_type_id, from_chalk_trait_id, - mapping::ToChalk, - next_solver::{ - Binder, ClauseKind, ConstBytes, TraitPredicate, UnevaluatedConst, - interner::{AdtDef, BoundVarKind, BoundVarKinds, DbInterner}, - }, - to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id, -}; -use crate::{ - from_placeholder_idx, lt_from_placeholder_idx, lt_to_placeholder_idx, to_placeholder_idx, -}; - -use super::{ - BoundExistentialPredicates, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, Canonical, - CanonicalVars, Clause, Clauses, Const, EarlyParamRegion, ErrorGuaranteed, ExistentialPredicate, - GenericArg, GenericArgs, ParamConst, ParamEnv, ParamTy, Predicate, PredicateKind, Region, - SolverDefId, SubtypePredicate, Term, TraitRef, Ty, Tys, ValueConst, -}; - -// FIXME: This should urgently go (as soon as we finish the migration off Chalk, that is). -pub fn convert_binder_to_early_binder<'db, T: rustc_type_ir::TypeFoldable>>( - interner: DbInterner<'db>, - def: GenericDefId, - binder: rustc_type_ir::Binder, T>, -) -> rustc_type_ir::EarlyBinder, T> { - let mut folder = BinderToEarlyBinder { - interner, - debruijn: rustc_type_ir::DebruijnIndex::ZERO, - params: crate::generics::generics(interner.db, def).iter_id().collect(), - }; - rustc_type_ir::EarlyBinder::bind(binder.skip_binder().fold_with(&mut folder)) -} - -struct BinderToEarlyBinder<'db> { - interner: DbInterner<'db>, - debruijn: rustc_type_ir::DebruijnIndex, - params: Vec, -} - -impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db> { - fn cx(&self) -> DbInterner<'db> { - self.interner - } - - fn fold_binder( - &mut self, - t: rustc_type_ir::Binder, T>, - ) -> rustc_type_ir::Binder, T> - where - T: TypeFoldable>, - { - self.debruijn.shift_in(1); - let result = t.super_fold_with(self); - self.debruijn.shift_out(1); - result - } - - fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { - match t.kind() { - rustc_type_ir::TyKind::Bound(debruijn, bound_ty) if self.debruijn == debruijn => { - let var: rustc_type_ir::BoundVar = bound_ty.var(); - let GenericParamId::TypeParamId(id) = self.params[bound_ty.var.as_usize()] else { - unreachable!() - }; - Ty::new( - self.cx(), - rustc_type_ir::TyKind::Param(ParamTy { index: var.as_u32(), id }), - ) - } - _ => t.super_fold_with(self), - } - } - - fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { - match r.kind() { - rustc_type_ir::ReBound(debruijn, bound_region) if self.debruijn == debruijn => { - let var: rustc_type_ir::BoundVar = bound_region.var(); - let GenericParamId::LifetimeParamId(id) = self.params[bound_region.var.as_usize()] - else { - unreachable!() - }; - Region::new( - self.cx(), - rustc_type_ir::RegionKind::ReEarlyParam(EarlyParamRegion { - index: var.as_u32(), - id, - }), - ) - } - _ => r, - } - } - - fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { - match c.kind() { - rustc_type_ir::ConstKind::Bound(debruijn, var) if self.debruijn == debruijn => { - let GenericParamId::ConstParamId(id) = self.params[var.var.as_usize()] else { - unreachable!() - }; - Const::new( - self.cx(), - rustc_type_ir::ConstKind::Param(ParamConst { index: var.var.as_u32(), id }), - ) - } - _ => c.super_fold_with(self), - } - } -} - -pub trait ChalkToNextSolver<'db, Out> { +pub(crate) trait ChalkToNextSolver<'db, Out> { fn to_nextsolver(&self, interner: DbInterner<'db>) -> Out; } -impl<'db, A, OutA, B, OutB> ChalkToNextSolver<'db, (OutA, OutB)> for (A, B) -where - A: ChalkToNextSolver<'db, OutA>, - B: ChalkToNextSolver<'db, OutB>, -{ - fn to_nextsolver(&self, interner: DbInterner<'db>) -> (OutA, OutB) { - (self.0.to_nextsolver(interner), self.1.to_nextsolver(interner)) - } -} - -pub trait NextSolverToChalk<'db, Out> { - fn to_chalk(self, interner: DbInterner<'db>) -> Out; -} - -impl<'db, T, Out> NextSolverToChalk<'db, Option> for Option -where - T: NextSolverToChalk<'db, Out>, -{ - fn to_chalk(self, interner: DbInterner<'db>) -> Option { - self.map(|it| it.to_chalk(interner)) - } -} - -impl NextSolverToChalk<'_, chalk_ir::Mutability> for rustc_ast_ir::Mutability { - fn to_chalk(self, _interner: DbInterner<'_>) -> chalk_ir::Mutability { - match self { - rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, - rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, - } - } -} - -impl NextSolverToChalk<'_, chalk_ir::Safety> for crate::next_solver::abi::Safety { - fn to_chalk(self, _interner: DbInterner<'_>) -> chalk_ir::Safety { - match self { - crate::next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, - crate::next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, - } - } -} - -impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> Ty<'db> { - Ty::new( - interner, - match self.kind(Interner) { - chalk_ir::TyKind::Adt(adt_id, substitution) => { - let def = AdtDef::new(adt_id.0, interner); - let args = substitution.to_nextsolver(interner); - rustc_type_ir::TyKind::Adt(def, args) - } - chalk_ir::TyKind::AssociatedType(assoc_type_id, substitution) => { - let def_id = SolverDefId::TypeAliasId(from_assoc_type_id(*assoc_type_id)); - let args: GenericArgs<'db> = substitution.to_nextsolver(interner); - let alias_ty = rustc_type_ir::AliasTy::new(interner, def_id, args.iter()); - rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Projection, alias_ty) - } - chalk_ir::TyKind::Scalar(scalar) => match scalar { - chalk_ir::Scalar::Bool => rustc_type_ir::TyKind::Bool, - chalk_ir::Scalar::Char => rustc_type_ir::TyKind::Char, - chalk_ir::Scalar::Int(chalk_ir::IntTy::Isize) => { - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::Isize) - } - chalk_ir::Scalar::Int(chalk_ir::IntTy::I8) => { - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I8) - } - chalk_ir::Scalar::Int(chalk_ir::IntTy::I16) => { - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I16) - } - chalk_ir::Scalar::Int(chalk_ir::IntTy::I32) => { - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I32) - } - chalk_ir::Scalar::Int(chalk_ir::IntTy::I64) => { - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I64) - } - chalk_ir::Scalar::Int(chalk_ir::IntTy::I128) => { - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I128) - } - chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize) => { - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::Usize) - } - chalk_ir::Scalar::Uint(chalk_ir::UintTy::U8) => { - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U8) - } - chalk_ir::Scalar::Uint(chalk_ir::UintTy::U16) => { - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U16) - } - chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32) => { - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U32) - } - chalk_ir::Scalar::Uint(chalk_ir::UintTy::U64) => { - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U64) - } - chalk_ir::Scalar::Uint(chalk_ir::UintTy::U128) => { - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U128) - } - chalk_ir::Scalar::Float(chalk_ir::FloatTy::F16) => { - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F16) - } - chalk_ir::Scalar::Float(chalk_ir::FloatTy::F32) => { - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F32) - } - chalk_ir::Scalar::Float(chalk_ir::FloatTy::F64) => { - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F64) - } - chalk_ir::Scalar::Float(chalk_ir::FloatTy::F128) => { - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F128) - } - }, - chalk_ir::TyKind::Tuple(_, substitution) => { - let args = substitution.to_nextsolver(interner); - rustc_type_ir::TyKind::Tuple(args) - } - chalk_ir::TyKind::Array(ty, len) => rustc_type_ir::TyKind::Array( - ty.to_nextsolver(interner), - len.to_nextsolver(interner), - ), - chalk_ir::TyKind::Slice(ty) => { - rustc_type_ir::TyKind::Slice(ty.to_nextsolver(interner)) - } - chalk_ir::TyKind::Raw(mutability, ty) => rustc_type_ir::RawPtr( - ty.to_nextsolver(interner), - mutability.to_nextsolver(interner), - ), - chalk_ir::TyKind::Ref(mutability, lifetime, ty) => rustc_type_ir::TyKind::Ref( - lifetime.to_nextsolver(interner), - ty.to_nextsolver(interner), - mutability.to_nextsolver(interner), - ), - chalk_ir::TyKind::OpaqueType(def_id, substitution) => { - let id: InternedOpaqueTyId = (*def_id).into(); - let args: GenericArgs<'db> = substitution.to_nextsolver(interner); - let alias_ty = rustc_type_ir::AliasTy::new(interner, id.into(), args); - rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Opaque, alias_ty) - } - chalk_ir::TyKind::FnDef(fn_def_id, substitution) => { - let def_id = CallableDefId::from_chalk(interner.db(), *fn_def_id); - rustc_type_ir::TyKind::FnDef( - def_id.into(), - substitution.to_nextsolver(interner), - ) - } - chalk_ir::TyKind::Str => rustc_type_ir::TyKind::Str, - chalk_ir::TyKind::Never => rustc_type_ir::TyKind::Never, - chalk_ir::TyKind::Closure(closure_id, substitution) => { - let id: InternedClosureId = (*closure_id).into(); - rustc_type_ir::TyKind::Closure(id.into(), substitution.to_nextsolver(interner)) - } - chalk_ir::TyKind::Coroutine(coroutine_id, substitution) => { - let id: InternedCoroutineId = (*coroutine_id).into(); - rustc_type_ir::TyKind::Coroutine( - id.into(), - substitution.to_nextsolver(interner), - ) - } - chalk_ir::TyKind::CoroutineWitness(coroutine_id, substitution) => { - let id: InternedCoroutineId = (*coroutine_id).into(); - rustc_type_ir::TyKind::CoroutineWitness( - id.into(), - substitution.to_nextsolver(interner), - ) - } - chalk_ir::TyKind::Foreign(foreign_def_id) => rustc_type_ir::TyKind::Foreign( - crate::from_foreign_def_id(*foreign_def_id).into(), - ), - chalk_ir::TyKind::Error => rustc_type_ir::TyKind::Error(ErrorGuaranteed), - chalk_ir::TyKind::Dyn(dyn_ty) => { - // exists { for<...> ^1.0: ... } - let bounds = BoundExistentialPredicates::new_from_iter( - interner, - dyn_ty.bounds.skip_binders().iter(Interner).filter_map(|pred| { - // for<...> ^1.0: ... - let (val, binders) = pred.clone().into_value_and_skipped_binders(); - let bound_vars = binders.to_nextsolver(interner); - let clause = match val { - chalk_ir::WhereClause::Implemented(trait_ref) => { - let trait_id = from_chalk_trait_id(trait_ref.trait_id); - if interner - .db() - .trait_signature(trait_id) - .flags - .contains(TraitFlags::AUTO) - { - ExistentialPredicate::AutoTrait(trait_id.into()) - } else { - let args = GenericArgs::new_from_iter( - interner, - trait_ref - .substitution - .iter(Interner) - .skip(1) - .map(|a| a.clone().shifted_out(Interner).unwrap()) - .map(|a| a.to_nextsolver(interner)), - ); - let trait_ref = ExistentialTraitRef::new_from_args( - interner, trait_id.into(), args, - ); - ExistentialPredicate::Trait(trait_ref) - } - } - chalk_ir::WhereClause::AliasEq(alias_eq) => { - let (def_id, args) = match &alias_eq.alias { - chalk_ir::AliasTy::Projection(projection) => { - let id = - from_assoc_type_id(projection.associated_ty_id); - let def_id = SolverDefId::TypeAliasId(id); - let substs = projection.substitution.iter(Interner).skip(1); - - let args = GenericArgs::new_from_iter( - interner, - substs - .map(|a| { - a.clone().shifted_out(Interner).unwrap() - }) - .map(|a| a.to_nextsolver(interner)), - ); - (def_id, args) - } - chalk_ir::AliasTy::Opaque(_opaque_ty) => { - panic!("Invalid ExistentialPredicate (opaques can't be named)."); - } - }; - let term = alias_eq - .ty - .clone() - .shifted_out(Interner) - .unwrap() - .to_nextsolver(interner) - .into(); - let projection = ExistentialProjection::new_from_args( - interner, def_id, args, term, - ); - ExistentialPredicate::Projection(projection) - } - chalk_ir::WhereClause::LifetimeOutlives(_lifetime_outlives) => { - return None; - } - chalk_ir::WhereClause::TypeOutlives(_type_outlives) => return None, - }; - - Some(Binder::bind_with_vars(clause, bound_vars)) - }), - ); - let region = dyn_ty.lifetime.to_nextsolver(interner); - rustc_type_ir::TyKind::Dynamic(bounds, region) - } - chalk_ir::TyKind::Alias(alias_ty) => match alias_ty { - chalk_ir::AliasTy::Projection(projection_ty) => { - let def_id = SolverDefId::TypeAliasId(from_assoc_type_id( - projection_ty.associated_ty_id, - )); - let alias_ty = rustc_type_ir::AliasTy::new_from_args( - interner, - def_id, - projection_ty.substitution.to_nextsolver(interner), - ); - rustc_type_ir::TyKind::Alias( - rustc_type_ir::AliasTyKind::Projection, - alias_ty, - ) - } - chalk_ir::AliasTy::Opaque(opaque_ty) => { - let id: InternedOpaqueTyId = opaque_ty.opaque_ty_id.into(); - let def_id = SolverDefId::InternedOpaqueTyId(id); - let alias_ty = rustc_type_ir::AliasTy::new_from_args( - interner, - def_id, - opaque_ty.substitution.to_nextsolver(interner), - ); - rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Opaque, alias_ty) - } - }, - chalk_ir::TyKind::Function(fn_pointer) => { - let sig_tys = fn_pointer.clone().into_binders(Interner).to_nextsolver(interner); - let header = rustc_type_ir::FnHeader { - abi: fn_pointer.sig.abi, - c_variadic: fn_pointer.sig.variadic, - safety: match fn_pointer.sig.safety { - chalk_ir::Safety::Safe => super::abi::Safety::Safe, - chalk_ir::Safety::Unsafe => super::abi::Safety::Unsafe, - }, - }; - - rustc_type_ir::TyKind::FnPtr(sig_tys, header) - } - // The schema here is quite confusing. - // The new solver, like rustc, uses `Param` and `EarlyBinder` for generic params. It uses `BoundVar` - // and `Placeholder` together with `Binder` for HRTB, which we mostly don't handle. - // Chalk uses `Placeholder` for generic params and `BoundVar` quite liberally, and this is quite a - // problem. `chalk_ir::TyKind::BoundVar` can represent either HRTB or generic params, depending on the - // context. When returned from signature queries, the outer `Binders` represent the generic params. - // But there are also inner `Binders` for HRTB. - // AFAIK there is no way to tell which of the meanings is relevant, so we just use `rustc_type_ir::Bound` - // here, and hope for the best. If you are working with new solver types, therefore, use the new solver - // lower queries. - // Hopefully sooner than later Chalk will be ripped from the codebase and we can avoid that problem. - // For details about the rustc setup, read: https://rustc-dev-guide.rust-lang.org/generic_parameters_summary.html - // and the following chapters. - chalk_ir::TyKind::Placeholder(placeholder_index) => { - let (id, index) = from_placeholder_idx(interner.db, *placeholder_index); - rustc_type_ir::TyKind::Param(ParamTy { - id: TypeParamId::from_unchecked(id), - index, - }) - } - chalk_ir::TyKind::BoundVar(bound_var) => rustc_type_ir::TyKind::Bound( - bound_var.debruijn.to_nextsolver(interner), - BoundTy { - var: rustc_type_ir::BoundVar::from_usize(bound_var.index), - kind: BoundTyKind::Anon, - }, - ), - chalk_ir::TyKind::InferenceVar(inference_var, ty_variable_kind) => { - rustc_type_ir::TyKind::Infer( - (*inference_var, *ty_variable_kind).to_nextsolver(interner), - ) - } - }, - ) - } -} - -impl<'db> NextSolverToChalk<'db, chalk_ir::Ty> for Ty<'db> { - fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Ty { - convert_ty_for_result(interner, self) - } -} - -impl<'db> ChalkToNextSolver<'db, Region<'db>> for chalk_ir::Lifetime { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> Region<'db> { - Region::new( - interner, - match self.data(Interner) { - chalk_ir::LifetimeData::BoundVar(bound_var) => rustc_type_ir::RegionKind::ReBound( - bound_var.debruijn.to_nextsolver(interner), - BoundRegion { - var: rustc_type_ir::BoundVar::from_u32(bound_var.index as u32), - kind: BoundRegionKind::Anon, - }, - ), - chalk_ir::LifetimeData::InferenceVar(inference_var) => { - rustc_type_ir::RegionKind::ReVar(rustc_type_ir::RegionVid::from_u32( - inference_var.index(), - )) - } - chalk_ir::LifetimeData::Placeholder(placeholder_index) => { - let (id, index) = lt_from_placeholder_idx(interner.db, *placeholder_index); - rustc_type_ir::RegionKind::ReEarlyParam(EarlyParamRegion { id, index }) - } - chalk_ir::LifetimeData::Static => rustc_type_ir::RegionKind::ReStatic, - chalk_ir::LifetimeData::Erased => rustc_type_ir::RegionKind::ReErased, - chalk_ir::LifetimeData::Phantom(_, _) => { - unreachable!() - } - chalk_ir::LifetimeData::Error => { - rustc_type_ir::RegionKind::ReError(ErrorGuaranteed) - } - }, - ) - } -} - -impl<'db> NextSolverToChalk<'db, chalk_ir::Lifetime> for Region<'db> { - fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Lifetime { - convert_region_for_result(interner, self) - } -} - -impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> Const<'db> { - let data = self.data(Interner); - Const::new( - interner, - match &data.value { - chalk_ir::ConstValue::BoundVar(bound_var) => rustc_type_ir::ConstKind::Bound( - bound_var.debruijn.to_nextsolver(interner), - BoundConst { var: rustc_type_ir::BoundVar::from_usize(bound_var.index) }, - ), - chalk_ir::ConstValue::InferenceVar(inference_var) => { - rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var( - rustc_type_ir::ConstVid::from_u32(inference_var.index()), - )) - } - chalk_ir::ConstValue::Placeholder(placeholder_index) => { - let (id, index) = from_placeholder_idx(interner.db, *placeholder_index); - rustc_type_ir::ConstKind::Param(ParamConst { - id: ConstParamId::from_unchecked(id), - index, - }) - } - chalk_ir::ConstValue::Concrete(concrete_const) => match &concrete_const.interned { - ConstScalar::Bytes(bytes, memory) => { - rustc_type_ir::ConstKind::Value(ValueConst::new( - data.ty.to_nextsolver(interner), - ConstBytes { memory: bytes.clone(), memory_map: memory.clone() }, - )) - } - ConstScalar::UnevaluatedConst(c, subst) => { - let def = match *c { - GeneralConstId::ConstId(id) => SolverDefId::ConstId(id), - GeneralConstId::StaticId(id) => SolverDefId::StaticId(id), - }; - let args = subst.to_nextsolver(interner); - rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new(def, args)) - } - ConstScalar::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), - }, - }, - ) - } -} - -impl<'db> NextSolverToChalk<'db, chalk_ir::Const> for Const<'db> { - fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Const { - convert_const_for_result(interner, self) - } -} - -impl<'db> ChalkToNextSolver<'db, rustc_type_ir::FnSigTys>> - for chalk_ir::FnSubst -{ - fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::FnSigTys> { - rustc_type_ir::FnSigTys { - inputs_and_output: Tys::new_from_iter( - interner, - self.0.iter(Interner).map(|g| g.assert_ty_ref(Interner).to_nextsolver(interner)), - ), - } - } -} - -impl< - 'db, - U: TypeVisitable>, - T: Clone + ChalkToNextSolver<'db, U> + HasInterner, -> ChalkToNextSolver<'db, rustc_type_ir::Binder, U>> for chalk_ir::Binders -{ - fn to_nextsolver( - &self, - interner: DbInterner<'db>, - ) -> rustc_type_ir::Binder, U> { - let (val, binders) = self.clone().into_value_and_skipped_binders(); - rustc_type_ir::Binder::bind_with_vars( - val.to_nextsolver(interner), - binders.to_nextsolver(interner), - ) - } -} - -impl<'db, T: NextSolverToChalk<'db, U>, U: HasInterner> - NextSolverToChalk<'db, chalk_ir::Binders> for rustc_type_ir::Binder, T> -{ - fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Binders { - chalk_ir::Binders::new( - self.bound_vars().to_chalk(interner), - self.skip_binder().to_chalk(interner), - ) - } -} - -impl<'db> ChalkToNextSolver<'db, BoundVarKinds<'db>> for chalk_ir::VariableKinds { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKinds<'db> { - BoundVarKinds::new_from_iter( - interner, - self.iter(Interner).map(|v| v.to_nextsolver(interner)), - ) - } -} - -impl<'db> NextSolverToChalk<'db, chalk_ir::VariableKinds> for BoundVarKinds<'db> { - fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::VariableKinds { - chalk_ir::VariableKinds::from_iter(Interner, self.iter().map(|v| v.to_chalk(interner))) - } -} - -impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind { - fn to_nextsolver(&self, _interner: DbInterner<'db>) -> BoundVarKind { - match self { - chalk_ir::VariableKind::Ty(_ty_variable_kind) => BoundVarKind::Ty(BoundTyKind::Anon), - chalk_ir::VariableKind::Lifetime => BoundVarKind::Region(BoundRegionKind::Anon), - chalk_ir::VariableKind::Const(_ty) => BoundVarKind::Const, - } - } -} - -impl<'db> NextSolverToChalk<'db, chalk_ir::VariableKind> for BoundVarKind { - fn to_chalk(self, _interner: DbInterner<'db>) -> chalk_ir::VariableKind { - match self { - BoundVarKind::Ty(_) => chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), - BoundVarKind::Region(_) => chalk_ir::VariableKind::Lifetime, - BoundVarKind::Const => { - chalk_ir::VariableKind::Const(chalk_ir::TyKind::Error.intern(Interner)) - } - } - } -} - -impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArg<'db> { - match self.data(Interner) { - chalk_ir::GenericArgData::Ty(ty) => ty.to_nextsolver(interner).into(), - chalk_ir::GenericArgData::Lifetime(lifetime) => lifetime.to_nextsolver(interner).into(), - chalk_ir::GenericArgData::Const(const_) => const_.to_nextsolver(interner).into(), - } - } -} - -impl<'db> NextSolverToChalk<'db, crate::GenericArg> for GenericArg<'db> { - fn to_chalk(self, interner: DbInterner<'db>) -> crate::GenericArg { - match self { - GenericArg::Ty(ty) => ty.to_chalk(interner).cast(Interner), - GenericArg::Lifetime(region) => region.to_chalk(interner).cast(Interner), - GenericArg::Const(konst) => konst.to_chalk(interner).cast(Interner), - } - } -} - -impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArgs<'db> { - GenericArgs::new_from_iter( - interner, - self.iter(Interner).map(|arg| -> GenericArg<'db> { arg.to_nextsolver(interner) }), - ) - } -} - -impl<'db> ChalkToNextSolver<'db, crate::lower_nextsolver::ImplTraitIdx<'db>> - for crate::ImplTraitIdx -{ - fn to_nextsolver( - &self, - _interner: DbInterner<'db>, - ) -> crate::lower_nextsolver::ImplTraitIdx<'db> { - crate::lower_nextsolver::ImplTraitIdx::from_raw(self.into_raw()) - } -} - -impl<'db> NextSolverToChalk<'db, chalk_ir::Substitution> for GenericArgs<'db> { - fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Substitution { - convert_args_for_result(interner, self.as_slice()) - } -} - -impl<'db> ChalkToNextSolver<'db, Tys<'db>> for chalk_ir::Substitution { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> Tys<'db> { - Tys::new_from_iter( - interner, - self.iter(Interner).map(|arg| -> Ty<'db> { - match arg.data(Interner) { - chalk_ir::GenericArgData::Ty(ty) => ty.to_nextsolver(interner), - chalk_ir::GenericArgData::Lifetime(_) => unreachable!(), - chalk_ir::GenericArgData::Const(_) => unreachable!(), - } - }), - ) - } -} - -impl<'db> NextSolverToChalk<'db, crate::Substitution> for Tys<'db> { - fn to_chalk(self, interner: DbInterner<'db>) -> crate::Substitution { - Substitution::from_iter( - Interner, - self.inner().iter().map(|ty| ty.to_chalk(interner).cast(Interner)), - ) - } -} - -impl<'db> ChalkToNextSolver<'db, rustc_type_ir::DebruijnIndex> for chalk_ir::DebruijnIndex { - fn to_nextsolver(&self, _interner: DbInterner<'db>) -> rustc_type_ir::DebruijnIndex { - rustc_type_ir::DebruijnIndex::from_u32(self.depth()) - } -} - -impl<'db> NextSolverToChalk<'db, chalk_ir::DebruijnIndex> for rustc_type_ir::DebruijnIndex { - fn to_chalk(self, _interner: DbInterner<'db>) -> chalk_ir::DebruijnIndex { - chalk_ir::DebruijnIndex::new(self.index() as u32) - } -} - -impl<'db> ChalkToNextSolver<'db, rustc_type_ir::UniverseIndex> for chalk_ir::UniverseIndex { - fn to_nextsolver(&self, _interner: DbInterner<'db>) -> rustc_type_ir::UniverseIndex { - rustc_type_ir::UniverseIndex::from_u32(self.counter as u32) - } -} - -impl<'db> NextSolverToChalk<'db, chalk_ir::UniverseIndex> for rustc_type_ir::UniverseIndex { - fn to_chalk(self, _interner: DbInterner<'db>) -> chalk_ir::UniverseIndex { - chalk_ir::UniverseIndex { counter: self.index() } - } -} - -impl<'db> ChalkToNextSolver<'db, rustc_type_ir::InferTy> - for (chalk_ir::InferenceVar, chalk_ir::TyVariableKind) -{ - fn to_nextsolver(&self, _interner: DbInterner<'db>) -> rustc_type_ir::InferTy { - match self.1 { - chalk_ir::TyVariableKind::General => { - rustc_type_ir::InferTy::TyVar(rustc_type_ir::TyVid::from_u32(self.0.index())) - } - chalk_ir::TyVariableKind::Integer => { - rustc_type_ir::InferTy::IntVar(rustc_type_ir::IntVid::from_u32(self.0.index())) - } - chalk_ir::TyVariableKind::Float => { - rustc_type_ir::InferTy::FloatVar(rustc_type_ir::FloatVid::from_u32(self.0.index())) - } - } - } -} - -impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutability { - fn to_nextsolver(&self, _interner: DbInterner<'db>) -> rustc_ast_ir::Mutability { - match self { - chalk_ir::Mutability::Mut => rustc_ast_ir::Mutability::Mut, - chalk_ir::Mutability::Not => rustc_ast_ir::Mutability::Not, - } - } -} - -impl<'db> ChalkToNextSolver<'db, Goal, Predicate<'db>>> - for chalk_ir::InEnvironment> -{ - fn to_nextsolver(&self, interner: DbInterner<'db>) -> Goal, Predicate<'db>> { - Goal::new( - interner, - self.environment.to_nextsolver(interner), - self.goal.to_nextsolver(interner), - ) - } -} - -impl<'db> NextSolverToChalk<'db, chalk_ir::InEnvironment>> - for Goal, Predicate<'db>> -{ - fn to_chalk( - self, - interner: DbInterner<'db>, - ) -> chalk_ir::InEnvironment> { - chalk_ir::InEnvironment { - environment: self.param_env.to_chalk(interner), - goal: self.predicate.to_chalk(interner), - } - } -} - -impl<'db, T: HasInterner + ChalkToNextSolver<'db, U>, U> - ChalkToNextSolver<'db, Canonical<'db, U>> for chalk_ir::Canonical -{ - fn to_nextsolver(&self, interner: DbInterner<'db>) -> Canonical<'db, U> { - let variables = CanonicalVars::new_from_iter( - interner, - self.binders.iter(Interner).map(|k| match &k.kind { - chalk_ir::VariableKind::Ty(ty_variable_kind) => match ty_variable_kind { - // FIXME(next-solver): the info is incorrect, but we have no way to store the information in Chalk. - TyVariableKind::General => rustc_type_ir::CanonicalVarKind::Ty { - ui: UniverseIndex::ROOT, - sub_root: BoundVar::from_u32(0), - }, - TyVariableKind::Integer => rustc_type_ir::CanonicalVarKind::Int, - TyVariableKind::Float => rustc_type_ir::CanonicalVarKind::Float, - }, - chalk_ir::VariableKind::Lifetime => { - rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ROOT) - } - chalk_ir::VariableKind::Const(_ty) => { - rustc_type_ir::CanonicalVarKind::Const(UniverseIndex::ROOT) - } - }), - ); - Canonical { - max_universe: UniverseIndex::ROOT, - value: self.value.to_nextsolver(interner), - variables, - } - } -} - -impl<'db, T: NextSolverToChalk<'db, U>, U: HasInterner> - NextSolverToChalk<'db, chalk_ir::Canonical> for Canonical<'db, T> -{ - fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Canonical { - let binders = chalk_ir::CanonicalVarKinds::from_iter( - Interner, - self.variables.iter().map(|v| match v { - rustc_type_ir::CanonicalVarKind::Ty { ui, sub_root: _ } => { - chalk_ir::CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::General), - chalk_ir::UniverseIndex { counter: ui.as_usize() }, - ) - } - rustc_type_ir::CanonicalVarKind::Int => chalk_ir::CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::Integer), - chalk_ir::UniverseIndex::root(), - ), - rustc_type_ir::CanonicalVarKind::Float => chalk_ir::CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::Float), - chalk_ir::UniverseIndex::root(), - ), - rustc_type_ir::CanonicalVarKind::Region(ui) => chalk_ir::CanonicalVarKind::new( - chalk_ir::VariableKind::Lifetime, - chalk_ir::UniverseIndex { counter: ui.as_usize() }, - ), - rustc_type_ir::CanonicalVarKind::Const(ui) => chalk_ir::CanonicalVarKind::new( - chalk_ir::VariableKind::Const(chalk_ir::TyKind::Error.intern(Interner)), - chalk_ir::UniverseIndex { counter: ui.as_usize() }, - ), - rustc_type_ir::CanonicalVarKind::PlaceholderTy(_) => unimplemented!(), - rustc_type_ir::CanonicalVarKind::PlaceholderRegion(_) => unimplemented!(), - rustc_type_ir::CanonicalVarKind::PlaceholderConst(_) => unimplemented!(), - }), - ); - let value = self.value.to_chalk(interner); - chalk_ir::Canonical { binders, value } - } -} - -impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> Predicate<'db> { - match self.data(Interner) { - chalk_ir::GoalData::Quantified(_quantifier_kind, binders) => { - if !binders.binders.is_empty(Interner) { - panic!("Should not be constructed."); - } - let (val, _) = binders.clone().into_value_and_skipped_binders(); - val.shifted_out(Interner).unwrap().to_nextsolver(interner) - } - chalk_ir::GoalData::Implies(_program_clauses, _goal) => { - panic!("Should not be constructed.") - } - chalk_ir::GoalData::All(_goals) => panic!("Should not be constructed."), - chalk_ir::GoalData::Not(_goal) => panic!("Should not be constructed."), - chalk_ir::GoalData::EqGoal(eq_goal) => { - let arg_to_term = |g: &chalk_ir::GenericArg| match g.data(Interner) { - chalk_ir::GenericArgData::Ty(ty) => Term::Ty(ty.to_nextsolver(interner)), - chalk_ir::GenericArgData::Const(const_) => { - Term::Const(const_.to_nextsolver(interner)) - } - chalk_ir::GenericArgData::Lifetime(_lifetime) => unreachable!(), - }; - let pred_kind = PredicateKind::AliasRelate( - arg_to_term(&eq_goal.a), - arg_to_term(&eq_goal.b), - rustc_type_ir::AliasRelationDirection::Equate, - ); - let pred_kind = - Binder::bind_with_vars(pred_kind, BoundVarKinds::new_from_iter(interner, [])); - Predicate::new(interner, pred_kind) - } - chalk_ir::GoalData::SubtypeGoal(subtype_goal) => { - let subtype_predicate = SubtypePredicate { - a: subtype_goal.a.to_nextsolver(interner), - b: subtype_goal.b.to_nextsolver(interner), - a_is_expected: true, - }; - let pred_kind = PredicateKind::Subtype(subtype_predicate); - let pred_kind = Binder::bind_with_vars( - shift_vars(interner, pred_kind, 1), - BoundVarKinds::new_from_iter(interner, []), - ); - Predicate::new(interner, pred_kind) - } - chalk_ir::GoalData::DomainGoal(domain_goal) => { - let pred_kind = domain_goal.to_nextsolver(interner); - let pred_kind = Binder::bind_with_vars( - shift_vars(interner, pred_kind, 1), - BoundVarKinds::new_from_iter(interner, []), - ); - Predicate::new(interner, pred_kind) - } - chalk_ir::GoalData::CannotProve => panic!("Should not be constructed."), - } - } -} - -impl<'db> NextSolverToChalk<'db, chalk_ir::Goal> for Predicate<'db> { - fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Goal { - chalk_ir::Goal::new(Interner, self.kind().skip_binder().to_chalk(interner)) - } -} - -impl<'db> NextSolverToChalk<'db, crate::ProjectionTy> for crate::next_solver::AliasTy<'db> { - fn to_chalk(self, interner: DbInterner<'db>) -> crate::ProjectionTy { - let SolverDefId::TypeAliasId(assoc_id) = self.def_id else { unreachable!() }; - crate::ProjectionTy { - associated_ty_id: to_assoc_type_id(assoc_id), - substitution: self.args.to_chalk(interner), - } - } -} - -impl<'db> ChalkToNextSolver<'db, ParamEnv<'db>> for chalk_ir::Environment { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> ParamEnv<'db> { - let clauses = Clauses::new_from_iter( - interner, - self.clauses.iter(Interner).map(|c| c.to_nextsolver(interner)), - ); - let clauses = - Clauses::new_from_iter(interner, elaborate::elaborate(interner, clauses.iter())); - ParamEnv { clauses } - } -} - -impl<'db> NextSolverToChalk<'db, chalk_ir::Environment> for ParamEnv<'db> { - fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Environment { - let clauses = chalk_ir::ProgramClauses::from_iter( - Interner, - self.clauses.iter().filter_map(|c| -> Option> { - c.to_chalk(interner) - }), - ); - chalk_ir::Environment { clauses } - } -} - -impl<'db> ChalkToNextSolver<'db, Clause<'db>> for chalk_ir::ProgramClause { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> Clause<'db> { - Clause(Predicate::new(interner, self.data(Interner).0.to_nextsolver(interner))) - } -} - -impl<'db> NextSolverToChalk<'db, Option>> for Clause<'db> { - fn to_chalk(self, interner: DbInterner<'db>) -> Option> { - let value: chalk_ir::ProgramClauseImplication = - as NextSolverToChalk< - 'db, - Option>, - >>::to_chalk(self.0.kind().skip_binder(), interner)?; - Some(chalk_ir::ProgramClause::new( - Interner, - chalk_ir::ProgramClauseData(chalk_ir::Binders::empty(Interner, value)), - )) - } -} - -impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> - for chalk_ir::ProgramClauseImplication -{ - fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> { - assert!(self.conditions.is_empty(Interner)); - assert!(self.constraints.is_empty(Interner)); - self.consequence.to_nextsolver(interner) - } -} - -impl<'db> NextSolverToChalk<'db, Option>> - for PredicateKind<'db> -{ - fn to_chalk( - self, - interner: DbInterner<'db>, - ) -> Option> { - let chalk_ir::GoalData::DomainGoal(consequence) = self.to_chalk(interner) else { - return None; - }; - - Some(chalk_ir::ProgramClauseImplication { - consequence, - conditions: chalk_ir::Goals::empty(Interner), - constraints: chalk_ir::Constraints::empty(Interner), - priority: chalk_ir::ClausePriority::High, - }) - } -} - -impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> { - match self { - chalk_ir::DomainGoal::Holds(where_clause) => match where_clause { - chalk_ir::WhereClause::Implemented(trait_ref) => { - let predicate = TraitPredicate { - trait_ref: trait_ref.to_nextsolver(interner), - polarity: rustc_type_ir::PredicatePolarity::Positive, - }; - PredicateKind::Clause(ClauseKind::Trait(predicate)) - } - chalk_ir::WhereClause::AliasEq(alias_eq) => match &alias_eq.alias { - chalk_ir::AliasTy::Projection(p) => { - let def_id = - SolverDefId::TypeAliasId(from_assoc_type_id(p.associated_ty_id)); - let args = p.substitution.to_nextsolver(interner); - let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner); - let term: Term<'db> = term.into(); - let predicate = ProjectionPredicate { - projection_term: AliasTerm::new_from_args(interner, def_id, args), - term, - }; - PredicateKind::Clause(ClauseKind::Projection(predicate)) - } - chalk_ir::AliasTy::Opaque(opaque) => { - let id: InternedOpaqueTyId = opaque.opaque_ty_id.into(); - let def_id = SolverDefId::InternedOpaqueTyId(id); - let args = opaque.substitution.to_nextsolver(interner); - let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner); - let term: Term<'db> = term.into(); - let opaque_ty = Ty::new( - interner, - rustc_type_ir::TyKind::Alias( - rustc_type_ir::AliasTyKind::Opaque, - rustc_type_ir::AliasTy::new_from_args(interner, def_id, args), - ), - ) - .into(); - PredicateKind::AliasRelate( - opaque_ty, - term, - rustc_type_ir::AliasRelationDirection::Equate, - ) - } - }, - chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => { - let predicate = OutlivesPredicate( - lifetime_outlives.a.to_nextsolver(interner), - lifetime_outlives.b.to_nextsolver(interner), - ); - PredicateKind::Clause(ClauseKind::RegionOutlives(predicate)) - } - chalk_ir::WhereClause::TypeOutlives(type_outlives) => { - let predicate = OutlivesPredicate( - type_outlives.ty.to_nextsolver(interner), - type_outlives.lifetime.to_nextsolver(interner), - ); - PredicateKind::Clause(ClauseKind::TypeOutlives(predicate)) - } - }, - chalk_ir::DomainGoal::Normalize(normalize) => { - let proj_ty = match &normalize.alias { - chalk_ir::AliasTy::Projection(proj) => proj, - _ => unimplemented!(), - }; - let args: GenericArgs<'db> = proj_ty.substitution.to_nextsolver(interner); - let alias = Ty::new( - interner, - rustc_type_ir::TyKind::Alias( - rustc_type_ir::AliasTyKind::Projection, - rustc_type_ir::AliasTy::new( - interner, - from_assoc_type_id(proj_ty.associated_ty_id).into(), - args, - ), - ), - ) - .into(); - let term = normalize.ty.to_nextsolver(interner).into(); - PredicateKind::AliasRelate( - alias, - term, - rustc_type_ir::AliasRelationDirection::Equate, - ) - } - chalk_ir::DomainGoal::WellFormed(well_formed) => { - let term = match well_formed { - WellFormed::Trait(_) => panic!("Should not be constructed."), - WellFormed::Ty(ty) => Term::Ty(ty.to_nextsolver(interner)), - }; - PredicateKind::Clause(rustc_type_ir::ClauseKind::WellFormed(term)) - } - chalk_ir::DomainGoal::FromEnv(from_env) => match from_env { - chalk_ir::FromEnv::Trait(trait_ref) => { - let predicate = TraitPredicate { - trait_ref: trait_ref.to_nextsolver(interner), - polarity: rustc_type_ir::PredicatePolarity::Positive, - }; - PredicateKind::Clause(ClauseKind::Trait(predicate)) - } - chalk_ir::FromEnv::Ty(ty) => PredicateKind::Clause(ClauseKind::WellFormed( - Term::Ty(ty.to_nextsolver(interner)), - )), - }, - chalk_ir::DomainGoal::IsLocal(_ty) => panic!("Should not be constructed."), - chalk_ir::DomainGoal::IsUpstream(_ty) => panic!("Should not be constructed."), - chalk_ir::DomainGoal::IsFullyVisible(_ty) => panic!("Should not be constructed."), - chalk_ir::DomainGoal::LocalImplAllowed(_trait_ref) => { - panic!("Should not be constructed.") - } - chalk_ir::DomainGoal::Compatible => panic!("Should not be constructed."), - chalk_ir::DomainGoal::DownstreamType(_ty) => panic!("Should not be constructed."), - chalk_ir::DomainGoal::Reveal => panic!("Should not be constructed."), - chalk_ir::DomainGoal::ObjectSafe(_trait_id) => panic!("Should not be constructed."), - } - } -} - -impl<'db> NextSolverToChalk<'db, chalk_ir::GoalData> for PredicateKind<'db> { - fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::GoalData { - match self { - rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait(trait_pred)) => { - let trait_ref = trait_pred.trait_ref.to_chalk(interner); - let where_clause = chalk_ir::WhereClause::Implemented(trait_ref); - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) - } - rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Projection( - proj_predicate, - )) => { - let associated_ty_id = match proj_predicate.def_id() { - SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), - _ => unreachable!(), - }; - let substitution = proj_predicate.projection_term.args.to_chalk(interner); - let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { - associated_ty_id, - substitution, - }); - let ty = match proj_predicate.term.kind() { - rustc_type_ir::TermKind::Ty(ty) => ty, - rustc_type_ir::TermKind::Const(_) => unimplemented!(), - }; - let ty = ty.to_chalk(interner); - let alias_eq = chalk_ir::AliasEq { alias, ty }; - let where_clause = chalk_ir::WhereClause::AliasEq(alias_eq); - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) - } - rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::TypeOutlives( - outlives, - )) => { - let lifetime = outlives.1.to_chalk(interner); - let ty = outlives.0.to_chalk(interner); - let where_clause = - chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { lifetime, ty }); - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) - } - rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::RegionOutlives( - outlives, - )) => { - let a = outlives.0.to_chalk(interner); - let b = outlives.1.to_chalk(interner); - let where_clause = - chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { a, b }); - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) - } - rustc_type_ir::PredicateKind::AliasRelate( - alias_term, - target_term, - _alias_relation_direction, - ) => { - let term_to_generic_arg = |term: Term<'db>| match term { - Term::Ty(ty) => chalk_ir::GenericArg::new( - Interner, - chalk_ir::GenericArgData::Ty(ty.to_chalk(interner)), - ), - Term::Const(const_) => chalk_ir::GenericArg::new( - Interner, - chalk_ir::GenericArgData::Const(const_.to_chalk(interner)), - ), - }; - - chalk_ir::GoalData::EqGoal(chalk_ir::EqGoal { - a: term_to_generic_arg(alias_term), - b: term_to_generic_arg(target_term), - }) - } - rustc_type_ir::PredicateKind::Clause(_) => unimplemented!(), - rustc_type_ir::PredicateKind::DynCompatible(_) => unimplemented!(), - rustc_type_ir::PredicateKind::Subtype(_) => unimplemented!(), - rustc_type_ir::PredicateKind::Coerce(_) => unimplemented!(), - rustc_type_ir::PredicateKind::ConstEquate(_, _) => unimplemented!(), - rustc_type_ir::PredicateKind::Ambiguous => unimplemented!(), - rustc_type_ir::PredicateKind::NormalizesTo(_) => unimplemented!(), - } - } -} - -impl<'db> ChalkToNextSolver<'db, TraitRef<'db>> for chalk_ir::TraitRef { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> TraitRef<'db> { - let args = self.substitution.to_nextsolver(interner); - TraitRef::new_from_args(interner, from_chalk_trait_id(self.trait_id).into(), args) - } -} - -impl<'db> NextSolverToChalk<'db, chalk_ir::TraitRef> for TraitRef<'db> { - fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::TraitRef { - let trait_id = to_chalk_trait_id(self.def_id.0); - let substitution = self.args.to_chalk(interner); - chalk_ir::TraitRef { trait_id, substitution } - } -} - -impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::WhereClause { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> { - match self { - chalk_ir::WhereClause::Implemented(trait_ref) => { - let predicate = TraitPredicate { - trait_ref: trait_ref.to_nextsolver(interner), - polarity: rustc_type_ir::PredicatePolarity::Positive, - }; - PredicateKind::Clause(ClauseKind::Trait(predicate)) - } - chalk_ir::WhereClause::AliasEq(alias_eq) => { - let projection = match &alias_eq.alias { - chalk_ir::AliasTy::Projection(p) => p, - _ => unimplemented!(), - }; - let def_id = - SolverDefId::TypeAliasId(from_assoc_type_id(projection.associated_ty_id)); - let args = projection.substitution.to_nextsolver(interner); - let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner); - let term: Term<'db> = term.into(); - let predicate = ProjectionPredicate { - projection_term: AliasTerm::new_from_args(interner, def_id, args), - term, - }; - PredicateKind::Clause(ClauseKind::Projection(predicate)) - } - chalk_ir::WhereClause::TypeOutlives(type_outlives) => { - let ty = type_outlives.ty.to_nextsolver(interner); - let r = type_outlives.lifetime.to_nextsolver(interner); - PredicateKind::Clause(ClauseKind::TypeOutlives(OutlivesPredicate(ty, r))) - } - chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => { - let a = lifetime_outlives.a.to_nextsolver(interner); - let b = lifetime_outlives.b.to_nextsolver(interner); - PredicateKind::Clause(ClauseKind::RegionOutlives(OutlivesPredicate(a, b))) - } - } - } -} - -impl<'db, I> NextSolverToChalk<'db, chalk_ir::ConstrainedSubst> for I -where - I: IntoIterator>, -{ - fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::ConstrainedSubst { - chalk_ir::ConstrainedSubst { - constraints: chalk_ir::Constraints::empty(Interner), - subst: GenericArgs::new_from_iter(interner, self).to_chalk(interner), - } - } -} - -impl<'db> NextSolverToChalk<'db, crate::CallableSig> for rustc_type_ir::FnSig> { - fn to_chalk(self, interner: DbInterner<'db>) -> crate::CallableSig { - crate::CallableSig { - abi: self.abi, - is_varargs: self.c_variadic, - safety: match self.safety { - super::abi::Safety::Safe => chalk_ir::Safety::Safe, - super::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, - }, - params_and_return: triomphe::Arc::from_iter( - self.inputs_and_output.iter().map(|ty| convert_ty_for_result(interner, ty)), - ), - } - } -} - -pub fn convert_canonical_args_for_result<'db>( - interner: DbInterner<'db>, - args: Canonical<'db, Vec>>, -) -> chalk_ir::Canonical> { - args.to_chalk(interner) -} - -pub fn convert_args_for_result<'db>( - interner: DbInterner<'db>, - args: &[GenericArg<'db>], -) -> crate::Substitution { - let mut substs = Vec::with_capacity(args.len()); - for arg in args { - match (*arg).kind() { - rustc_type_ir::GenericArgKind::Type(ty) => { - let ty = convert_ty_for_result(interner, ty); - substs.push(chalk_ir::GenericArgData::Ty(ty).intern(Interner)); - } - rustc_type_ir::GenericArgKind::Lifetime(region) => { - let lifetime = convert_region_for_result(interner, region); - substs.push(chalk_ir::GenericArgData::Lifetime(lifetime).intern(Interner)); - } - rustc_type_ir::GenericArgKind::Const(const_) => { - substs.push( - chalk_ir::GenericArgData::Const(convert_const_for_result(interner, const_)) - .intern(Interner), - ); - } - } - } - Substitution::from_iter(Interner, substs) -} - -pub fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) -> crate::Ty { - use crate::{Scalar, TyKind}; - use chalk_ir::{FloatTy, IntTy, UintTy}; - match ty.kind() { - rustc_type_ir::TyKind::Bool => TyKind::Scalar(Scalar::Bool), - rustc_type_ir::TyKind::Char => TyKind::Scalar(Scalar::Char), - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I8) => { - TyKind::Scalar(Scalar::Int(IntTy::I8)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I16) => { - TyKind::Scalar(Scalar::Int(IntTy::I16)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I32) => { - TyKind::Scalar(Scalar::Int(IntTy::I32)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I64) => { - TyKind::Scalar(Scalar::Int(IntTy::I64)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I128) => { - TyKind::Scalar(Scalar::Int(IntTy::I128)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::Isize) => { - TyKind::Scalar(Scalar::Int(IntTy::Isize)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U8) => { - TyKind::Scalar(Scalar::Uint(UintTy::U8)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U16) => { - TyKind::Scalar(Scalar::Uint(UintTy::U16)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U32) => { - TyKind::Scalar(Scalar::Uint(UintTy::U32)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U64) => { - TyKind::Scalar(Scalar::Uint(UintTy::U64)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U128) => { - TyKind::Scalar(Scalar::Uint(UintTy::U128)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::Usize) => { - TyKind::Scalar(Scalar::Uint(UintTy::Usize)) - } - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F16) => { - TyKind::Scalar(Scalar::Float(FloatTy::F16)) - } - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F32) => { - TyKind::Scalar(Scalar::Float(FloatTy::F32)) - } - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F64) => { - TyKind::Scalar(Scalar::Float(FloatTy::F64)) - } - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F128) => { - TyKind::Scalar(Scalar::Float(FloatTy::F128)) - } - rustc_type_ir::TyKind::Str => TyKind::Str, - rustc_type_ir::TyKind::Error(_) => TyKind::Error, - rustc_type_ir::TyKind::Never => TyKind::Never, - - rustc_type_ir::TyKind::Adt(def, args) => { - let adt_id = def.inner().id; - let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::Adt(chalk_ir::AdtId(adt_id), subst) - } - - rustc_type_ir::TyKind::Infer(infer_ty) => { - let (var, kind) = match infer_ty { - rustc_type_ir::InferTy::TyVar(var) => { - (InferenceVar::from(var.as_u32()), TyVariableKind::General) - } - rustc_type_ir::InferTy::IntVar(var) => { - (InferenceVar::from(var.as_u32()), TyVariableKind::Integer) - } - rustc_type_ir::InferTy::FloatVar(var) => { - (InferenceVar::from(var.as_u32()), TyVariableKind::Float) - } - rustc_type_ir::InferTy::FreshFloatTy(..) - | rustc_type_ir::InferTy::FreshIntTy(..) - | rustc_type_ir::InferTy::FreshTy(..) => { - panic!("Freshening shouldn't happen.") - } - }; - TyKind::InferenceVar(var, kind) - } - - rustc_type_ir::TyKind::Ref(r, ty, mutability) => { - let mutability = match mutability { - rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, - rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, - }; - let r = convert_region_for_result(interner, r); - let ty = convert_ty_for_result(interner, ty); - TyKind::Ref(mutability, r, ty) - } - - rustc_type_ir::TyKind::Tuple(tys) => { - let size = tys.len(); - let subst = Substitution::from_iter( - Interner, - tys.iter().map(|ty| { - chalk_ir::GenericArgData::Ty(convert_ty_for_result(interner, ty)) - .intern(Interner) - }), - ); - TyKind::Tuple(size, subst) - } - - rustc_type_ir::TyKind::Array(ty, const_) => { - let ty = convert_ty_for_result(interner, ty); - let const_ = convert_const_for_result(interner, const_); - TyKind::Array(ty, const_) - } - - rustc_type_ir::TyKind::Alias(alias_ty_kind, alias_ty) => match alias_ty_kind { - rustc_type_ir::AliasTyKind::Projection => { - let assoc_ty_id = match alias_ty.def_id { - SolverDefId::TypeAliasId(id) => id, - _ => unreachable!(), - }; - let associated_ty_id = to_assoc_type_id(assoc_ty_id); - let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); - TyKind::Alias(crate::AliasTy::Projection(crate::ProjectionTy { - associated_ty_id, - substitution, - })) - } - rustc_type_ir::AliasTyKind::Opaque => { - let opaque_ty_id = match alias_ty.def_id { - SolverDefId::InternedOpaqueTyId(id) => id, - _ => unreachable!(), - }; - let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); - TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { - opaque_ty_id: opaque_ty_id.into(), - substitution, - })) - } - rustc_type_ir::AliasTyKind::Inherent => unimplemented!(), - rustc_type_ir::AliasTyKind::Free => unimplemented!(), - }, - - // For `Placeholder`, `Bound` and `Param`, see the comment on the reverse conversion. - rustc_type_ir::TyKind::Placeholder(_placeholder) => { - unimplemented!( - "A `rustc_type_ir::TyKind::Placeholder` doesn't have a direct \ - correspondence in Chalk, as it represents a universally instantiated `Bound`.\n\ - It therefore feels safer to leave it panicking, but if you hit this panic \ - feel free to do the same as in `rustc_type_ir::TyKind::Bound` here." - ) - } - rustc_type_ir::TyKind::Bound(debruijn_index, ty) => TyKind::BoundVar(chalk_ir::BoundVar { - debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), - index: ty.var.as_usize(), - }), - rustc_type_ir::TyKind::Param(param) => { - let placeholder = to_placeholder_idx(interner.db, param.id.into(), param.index); - TyKind::Placeholder(placeholder) - } - - rustc_type_ir::TyKind::FnPtr(bound_sig, fn_header) => { - let num_binders = bound_sig.bound_vars().len(); - let sig = chalk_ir::FnSig { - abi: fn_header.abi, - safety: match fn_header.safety { - crate::next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, - crate::next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, - }, - variadic: fn_header.c_variadic, - }; - let args = GenericArgs::new_from_iter( - interner, - bound_sig.skip_binder().inputs_and_output.iter().map(|a| a.into()), - ); - let substitution = convert_args_for_result(interner, args.as_slice()); - let substitution = chalk_ir::FnSubst(substitution); - let fnptr = chalk_ir::FnPointer { num_binders, sig, substitution }; - TyKind::Function(fnptr) - } - - rustc_type_ir::TyKind::Dynamic(preds, region) => { - let self_ty = Ty::new_bound( - interner, - DebruijnIndex::from_u32(1), - BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_u32(0) }, - ); - let bounds = chalk_ir::QuantifiedWhereClauses::from_iter( - Interner, - preds.iter().map(|p| { - let binders = chalk_ir::VariableKinds::from_iter( - Interner, - p.bound_vars().iter().map(|b| match b { - BoundVarKind::Ty(_kind) => { - chalk_ir::VariableKind::Ty(TyVariableKind::General) - } - BoundVarKind::Region(_kind) => chalk_ir::VariableKind::Lifetime, - BoundVarKind::Const => { - chalk_ir::VariableKind::Const(crate::TyKind::Error.intern(Interner)) - } - }), - ); - - // Rust and chalk have slightly different - // representation for trait objects. - // - // Chalk uses `for for<'a> T0: Trait<'a>` while rustc - // uses `ExistentialPredicate`s, which do not have a self ty. - // We need to shift escaping bound vars by 1 to accommodate - // the newly introduced `for` binder. - let p = shift_vars(interner, p, 1); - - let where_clause = match p.skip_binder() { - rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { - let trait_ref = TraitRef::new( - interner, - trait_ref.def_id, - [self_ty.into()].into_iter().chain(trait_ref.args.iter()), - ); - let trait_id = to_chalk_trait_id(trait_ref.def_id.0); - let substitution = - convert_args_for_result(interner, trait_ref.args.as_slice()); - let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; - chalk_ir::WhereClause::Implemented(trait_ref) - } - rustc_type_ir::ExistentialPredicate::AutoTrait(trait_) => { - let trait_id = to_chalk_trait_id(trait_.0); - let substitution = chalk_ir::Substitution::from1( - Interner, - convert_ty_for_result(interner, self_ty), - ); - let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; - chalk_ir::WhereClause::Implemented(trait_ref) - } - rustc_type_ir::ExistentialPredicate::Projection(existential_projection) => { - let projection = ProjectionPredicate { - projection_term: AliasTerm::new( - interner, - existential_projection.def_id, - [self_ty.into()] - .iter() - .chain(existential_projection.args.iter()), - ), - term: existential_projection.term, - }; - let associated_ty_id = match projection.projection_term.def_id { - SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), - _ => unreachable!(), - }; - let substitution = convert_args_for_result( - interner, - projection.projection_term.args.as_slice(), - ); - let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { - associated_ty_id, - substitution, - }); - let ty = match projection.term { - Term::Ty(ty) => ty, - _ => unreachable!(), - }; - let ty = convert_ty_for_result(interner, ty); - let alias_eq = chalk_ir::AliasEq { alias, ty }; - chalk_ir::WhereClause::AliasEq(alias_eq) - } - }; - chalk_ir::Binders::new(binders, where_clause) - }), - ); - let binders = chalk_ir::VariableKinds::from1( - Interner, - chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), - ); - let bounds = chalk_ir::Binders::new(binders, bounds); - let dyn_ty = - chalk_ir::DynTy { bounds, lifetime: convert_region_for_result(interner, region) }; - TyKind::Dyn(dyn_ty) - } - - rustc_type_ir::TyKind::Slice(ty) => { - let ty = convert_ty_for_result(interner, ty); - TyKind::Slice(ty) - } - - rustc_type_ir::TyKind::Foreign(foreign) => TyKind::Foreign(to_foreign_def_id(foreign.0)), - rustc_type_ir::TyKind::Pat(_, _) => unimplemented!(), - rustc_type_ir::TyKind::RawPtr(ty, mutability) => { - let mutability = match mutability { - rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, - rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, - }; - let ty = convert_ty_for_result(interner, ty); - TyKind::Raw(mutability, ty) - } - rustc_type_ir::TyKind::FnDef(def_id, args) => { - let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::FnDef(def_id.0.to_chalk(interner.db()), subst) - } - - rustc_type_ir::TyKind::Closure(def_id, args) => { - let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::Closure(def_id.0.into(), subst) - } - rustc_type_ir::TyKind::CoroutineClosure(_, _) => unimplemented!(), - rustc_type_ir::TyKind::Coroutine(def_id, args) => { - let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::Coroutine(def_id.0.into(), subst) - } - rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => { - let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::CoroutineWitness(def_id.0.into(), subst) - } - - rustc_type_ir::TyKind::UnsafeBinder(_) => unimplemented!(), - } - .intern(Interner) -} - -pub fn convert_const_for_result<'db>( - interner: DbInterner<'db>, - const_: Const<'db>, -) -> crate::Const { - let value: chalk_ir::ConstValue = match const_.kind() { - rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(var)) => { - chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(var.as_u32())) - } - rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Fresh(_fresh)) => { - panic!("Vars should not be freshened.") - } - rustc_type_ir::ConstKind::Param(param) => { - let placeholder = to_placeholder_idx(interner.db, param.id.into(), param.index); - chalk_ir::ConstValue::Placeholder(placeholder) - } - rustc_type_ir::ConstKind::Bound(debruijn_index, var) => { - chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( - chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), - var.var.index(), - )) - } - rustc_type_ir::ConstKind::Placeholder(_placeholder_const) => { - unimplemented!( - "A `rustc_type_ir::ConstKind::Placeholder` doesn't have a direct \ - correspondence in Chalk, as it represents a universally instantiated `Bound`.\n\ - It therefore feels safer to leave it panicking, but if you hit this panic \ - feel free to do the same as in `rustc_type_ir::ConstKind::Bound` here." - ) - } - rustc_type_ir::ConstKind::Unevaluated(unevaluated_const) => { - let id = match unevaluated_const.def { - SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), - SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), - _ => unreachable!(), - }; - let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); - chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { - interned: ConstScalar::UnevaluatedConst(id, subst), - }) - } - rustc_type_ir::ConstKind::Value(value_const) => { - let bytes = value_const.value.inner(); - let value = chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { - // SAFETY: we will never actually use this without a database - interned: ConstScalar::Bytes(bytes.memory.clone(), unsafe { - std::mem::transmute::, MemoryMap<'static>>( - bytes.memory_map.clone(), - ) - }), - }); - return chalk_ir::ConstData { - ty: convert_ty_for_result(interner, value_const.ty), - value, - } - .intern(Interner); - } - rustc_type_ir::ConstKind::Error(_) => { - chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { - interned: ConstScalar::Unknown, - }) - } - rustc_type_ir::ConstKind::Expr(_) => unimplemented!(), - }; - chalk_ir::ConstData { ty: crate::TyKind::Error.intern(Interner), value }.intern(Interner) -} - -pub fn convert_region_for_result<'db>( - interner: DbInterner<'db>, - region: Region<'db>, -) -> crate::Lifetime { - let lifetime = match region.kind() { - rustc_type_ir::RegionKind::ReEarlyParam(early) => { - let placeholder = lt_to_placeholder_idx(interner.db, early.id, early.index); - chalk_ir::LifetimeData::Placeholder(placeholder) - } - rustc_type_ir::RegionKind::ReBound(db, bound) => { - chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( - chalk_ir::DebruijnIndex::new(db.as_u32()), - bound.var.as_usize(), - )) - } - rustc_type_ir::RegionKind::RePlaceholder(_placeholder) => unimplemented!( - "A `rustc_type_ir::RegionKind::RePlaceholder` doesn't have a direct \ - correspondence in Chalk, as it represents a universally instantiated `Bound`.\n\ - It therefore feels safer to leave it panicking, but if you hit this panic \ - feel free to do the same as in `rustc_type_ir::RegionKind::ReBound` here." - ), - rustc_type_ir::RegionKind::ReLateParam(_) => unimplemented!(), - rustc_type_ir::RegionKind::ReStatic => chalk_ir::LifetimeData::Static, - rustc_type_ir::RegionKind::ReVar(vid) => { - chalk_ir::LifetimeData::InferenceVar(chalk_ir::InferenceVar::from(vid.as_u32())) - } - rustc_type_ir::RegionKind::ReErased => chalk_ir::LifetimeData::Erased, - rustc_type_ir::RegionKind::ReError(_) => chalk_ir::LifetimeData::Error, - }; - chalk_ir::Lifetime::new(Interner, lifetime) -} - -pub trait InferenceVarExt { - fn to_vid(self) -> rustc_type_ir::TyVid; - fn from_vid(vid: rustc_type_ir::TyVid) -> InferenceVar; -} - -impl InferenceVarExt for InferenceVar { - fn to_vid(self) -> rustc_type_ir::TyVid { - rustc_type_ir::TyVid::from_u32(self.index()) - } - fn from_vid(vid: rustc_type_ir::TyVid) -> InferenceVar { - InferenceVar::from(vid.as_u32()) +impl<'db> ChalkToNextSolver<'db, crate::lower::ImplTraitIdx<'db>> for crate::ImplTraitIdx { + fn to_nextsolver(&self, _interner: DbInterner<'db>) -> crate::lower::ImplTraitIdx<'db> { + crate::lower::ImplTraitIdx::from_raw(self.into_raw()) } } 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 70b6f20ede04..3438b755fb9e 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 @@ -13,7 +13,7 @@ use rustc_type_ir::{ }; use smallvec::SmallVec; -use crate::next_solver::TraitIdWrapper; +use crate::next_solver::{InternedWrapperNoDebug, TraitIdWrapper}; use super::{Binder, BoundVarKinds, DbInterner, Region, Ty, interned_vec_db}; @@ -171,9 +171,6 @@ impl<'db> rustc_type_ir::relate::Relate> for BoundExistentialPre } } -#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] -pub struct InternedWrapperNoDebug(pub(crate) T); - #[salsa::interned(constructor = new_)] pub struct Predicate<'db> { #[returns(ref)] 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 8932f519785c..7cf23b82f63d 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 @@ -26,11 +26,11 @@ use rustc_type_ir::{ use crate::{ ImplTraitId, db::HirDatabase, - interner::InternedWrapperNoDebug, next_solver::{ AdtDef, Binder, CallableIdWrapper, Clause, ClauseKind, ClosureIdWrapper, Const, CoroutineIdWrapper, FnSig, GenericArg, PolyFnSig, Region, TraitRef, TypeAliasIdWrapper, abi::Safety, + interner::InternedWrapperNoDebug, mapping::ChalkToNextSolver, util::{CoroutineArgsExt, IntegerTypeExt}, }, @@ -531,7 +531,7 @@ impl<'db> Ty<'db> { TyKind::Alias(AliasTyKind::Opaque, opaque_ty) => { match db.lookup_intern_impl_trait_id(opaque_ty.def_id.expect_opaque_ty()) { ImplTraitId::ReturnTypeImplTrait(func, idx) => { - db.return_type_impl_traits_ns(func).map(|it| { + db.return_type_impl_traits(func).map(|it| { let data = (*it).as_ref().map_bound(|rpit| { &rpit.impl_traits[idx.to_nextsolver(interner)].predicates }); @@ -540,7 +540,7 @@ impl<'db> Ty<'db> { }) } ImplTraitId::TypeAliasImplTrait(alias, idx) => { - db.type_alias_impl_traits_ns(alias).map(|it| { + db.type_alias_impl_traits(alias).map(|it| { let data = (*it).as_ref().map_bound(|rpit| { &rpit.impl_traits[idx.to_nextsolver(interner)].predicates }); @@ -575,7 +575,7 @@ impl<'db> Ty<'db> { TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeParamProvenance::ArgumentImplTrait => { let predicates = db - .generic_predicates_ns(param.id.parent()) + .generic_predicates(param.id.parent()) .instantiate_identity() .into_iter() .flatten() 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 97f536305805..bb0d0552c710 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 @@ -1,46 +1,42 @@ //! Various utilities for the next-trait-solver. -use std::iter; -use std::ops::{self, ControlFlow}; +use std::{ + iter, + ops::{self, ControlFlow}, +}; use base_db::Crate; -use hir_def::lang_item::LangItem; -use hir_def::{BlockId, HasModule}; +use hir_def::{BlockId, HasModule, lang_item::LangItem}; use intern::sym; use la_arena::Idx; use rustc_abi::{Float, HasDataLayout, Integer, IntegerType, Primitive, ReprOptions}; -use rustc_type_ir::data_structures::IndexMap; -use rustc_type_ir::inherent::{ - AdtDef, GenericArg as _, GenericArgs as _, ParamEnv as _, SliceLike, Ty as _, -}; -use rustc_type_ir::lang_items::SolverTraitLangItem; -use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ - BoundVar, Canonical, DebruijnIndex, GenericArgKind, INNERMOST, Interner, PredicatePolarity, - TypeVisitableExt, + ConstKind, CoroutineArgs, DebruijnIndex, FloatTy, GenericArgKind, INNERMOST, IntTy, Interner, + PredicatePolarity, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, + TypeVisitableExt, TypeVisitor, UintTy, UniverseIndex, + inherent::{ + AdtDef, GenericArg as _, GenericArgs as _, IntoKind, ParamEnv as _, SliceLike, Ty as _, + }, + lang_items::SolverTraitLangItem, + solve::SizedTraitKind, }; -use rustc_type_ir::{ - ConstKind, CoroutineArgs, FloatTy, IntTy, RegionKind, TypeFolder, TypeSuperFoldable, - TypeSuperVisitable, TypeVisitor, UintTy, UniverseIndex, inherent::IntoKind, -}; -use rustc_type_ir::{InferCtxtLike, TypeFoldable}; -use crate::lower_nextsolver::{LifetimeElisionKind, TyLoweringContext}; -use crate::next_solver::infer::InferCtxt; -use crate::next_solver::{ - BoundConst, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion, -}; use crate::{ db::HirDatabase, + lower::{LifetimeElisionKind, TyLoweringContext}, method_resolution::{TraitImpls, TyFingerprint}, + next_solver::{ + BoundConst, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion, + infer::InferCtxt, + }, }; -use super::fold::{BoundVarReplacer, FnMutDelegate}; use super::{ AliasTerm, AliasTy, Binder, BoundRegion, BoundTy, BoundTyKind, BoundVarKind, BoundVarKinds, - CanonicalVars, Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder, GenericArg, - GenericArgs, Predicate, PredicateKind, ProjectionPredicate, Region, SolverContext, SolverDefId, - Term, TraitPredicate, TraitRef, Ty, TyKind, + Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder, GenericArgs, Predicate, + PredicateKind, ProjectionPredicate, Region, SolverDefId, Term, TraitPredicate, TraitRef, Ty, + TyKind, + fold::{BoundVarReplacer, FnMutDelegate}, }; #[derive(Clone, Debug)] @@ -510,151 +506,6 @@ pub fn apply_args_to_binder<'db, T: TypeFoldable>>( b.skip_binder().fold_with(&mut instantiate) } -pub(crate) fn mini_canonicalize<'db, T: TypeFoldable>>( - mut context: SolverContext<'db>, - val: T, -) -> Canonical, T> { - let mut canon = MiniCanonicalizer { - context: &mut context, - db: DebruijnIndex::ZERO, - vars: IndexMap::default(), - }; - let canon_val = val.fold_with(&mut canon); - let vars = canon.vars; - Canonical { - value: canon_val, - max_universe: UniverseIndex::from_u32(1), - variables: CanonicalVars::new_from_iter( - context.cx(), - vars.iter().enumerate().map(|(idx, (k, _v))| match (*k).kind() { - GenericArgKind::Type(ty) => match ty.kind() { - TyKind::Int(..) | TyKind::Uint(..) => rustc_type_ir::CanonicalVarKind::Int, - TyKind::Float(..) => rustc_type_ir::CanonicalVarKind::Float, - _ => rustc_type_ir::CanonicalVarKind::Ty { - ui: UniverseIndex::ZERO, - sub_root: BoundVar::from_usize(idx), - }, - }, - GenericArgKind::Lifetime(_) => { - rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ZERO) - } - GenericArgKind::Const(_) => { - rustc_type_ir::CanonicalVarKind::Const(UniverseIndex::ZERO) - } - }), - ), - } -} - -struct MiniCanonicalizer<'a, 'db> { - context: &'a mut SolverContext<'db>, - db: DebruijnIndex, - vars: IndexMap, usize>, -} - -impl<'db> TypeFolder> for MiniCanonicalizer<'_, 'db> { - fn cx(&self) -> DbInterner<'db> { - self.context.cx() - } - - fn fold_binder>>( - &mut self, - t: rustc_type_ir::Binder, T>, - ) -> rustc_type_ir::Binder, T> { - self.db.shift_in(1); - let res = t.map_bound(|t| t.fold_with(self)); - self.db.shift_out(1); - res - } - - fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { - match t.kind() { - rustc_type_ir::TyKind::Bound(db, _) => { - if db >= self.db { - panic!("Unexpected bound var"); - } - t - } - rustc_type_ir::TyKind::Infer(infer) => { - let t = match infer { - rustc_type_ir::InferTy::TyVar(vid) => { - self.context.opportunistic_resolve_ty_var(vid) - } - rustc_type_ir::InferTy::IntVar(vid) => { - self.context.opportunistic_resolve_int_var(vid) - } - rustc_type_ir::InferTy::FloatVar(vid) => { - self.context.opportunistic_resolve_float_var(vid) - } - _ => t, - }; - let len = self.vars.len(); - let var = *self.vars.entry(t.into()).or_insert(len); - Ty::new( - self.cx(), - TyKind::Bound( - self.db, - BoundTy { kind: super::BoundTyKind::Anon, var: BoundVar::from_usize(var) }, - ), - ) - } - _ => t.super_fold_with(self), - } - } - - fn fold_region( - &mut self, - r: as rustc_type_ir::Interner>::Region, - ) -> as rustc_type_ir::Interner>::Region { - match r.kind() { - RegionKind::ReBound(db, _) => { - if db >= self.db { - panic!("Unexpected bound var"); - } - r - } - RegionKind::ReVar(_vid) => { - let len = self.vars.len(); - let var = *self.vars.entry(r.into()).or_insert(len); - Region::new( - self.cx(), - RegionKind::ReBound( - self.db, - BoundRegion { - kind: super::BoundRegionKind::Anon, - var: BoundVar::from_usize(var), - }, - ), - ) - } - _ => r, - } - } - - fn fold_const( - &mut self, - c: as rustc_type_ir::Interner>::Const, - ) -> as rustc_type_ir::Interner>::Const { - match c.kind() { - ConstKind::Bound(db, _) => { - if db >= self.db { - panic!("Unexpected bound var"); - } - c - } - ConstKind::Infer(_infer) => { - let len = self.vars.len(); - let var = *self.vars.entry(c.into()).or_insert(len); - Const::new( - self.cx(), - ConstKind::Bound(self.db, BoundConst { var: BoundVar::from_usize(var) }), - ) - } - _ => c.super_fold_with(self), - } - } -} - pub fn explicit_item_bounds<'db>( interner: DbInterner<'db>, def_id: SolverDefId, @@ -713,7 +564,7 @@ pub fn explicit_item_bounds<'db>( match full_id { crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { let datas = db - .return_type_impl_traits_ns(func) + .return_type_impl_traits(func) .expect("impl trait id without impl traits"); let datas = (*datas).as_ref().skip_binder(); let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())]; @@ -721,7 +572,7 @@ pub fn explicit_item_bounds<'db>( } crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => { let datas = db - .type_alias_impl_traits_ns(alias) + .type_alias_impl_traits(alias) .expect("impl trait id without impl traits"); let datas = (*datas).as_ref().skip_binder(); let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())]; 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 76cd5f7ab330..bc4701970c76 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 @@ -44,7 +44,7 @@ fn foo() -> i32 { "body_shim", "body_with_source_map_shim", "trait_environment_shim", - "return_type_impl_traits_ns_shim", + "return_type_impl_traits_shim", "expr_scopes_shim", "lang_item", "crate_lang_items", @@ -131,7 +131,7 @@ fn baz() -> i32 { "body_shim", "body_with_source_map_shim", "trait_environment_shim", - "return_type_impl_traits_ns_shim", + "return_type_impl_traits_shim", "expr_scopes_shim", "lang_item", "crate_lang_items", @@ -143,7 +143,7 @@ fn baz() -> i32 { "body_shim", "body_with_source_map_shim", "trait_environment_shim", - "return_type_impl_traits_ns_shim", + "return_type_impl_traits_shim", "expr_scopes_shim", "infer_shim", "function_signature_shim", @@ -151,7 +151,7 @@ fn baz() -> i32 { "body_shim", "body_with_source_map_shim", "trait_environment_shim", - "return_type_impl_traits_ns_shim", + "return_type_impl_traits_shim", "expr_scopes_shim", ] "#]], @@ -585,8 +585,8 @@ fn main() { "crate_lang_items", "attrs_shim", "attrs_shim", - "generic_predicates_ns_shim", - "return_type_impl_traits_ns_shim", + "generic_predicates_shim", + "return_type_impl_traits_shim", "infer_shim", "function_signature_shim", "function_signature_with_source_map_shim", @@ -594,7 +594,7 @@ fn main() { "expr_scopes_shim", "struct_signature_shim", "struct_signature_with_source_map_shim", - "generic_predicates_ns_shim", + "generic_predicates_shim", "value_ty_shim", "VariantFields::firewall_", "VariantFields::query_", @@ -608,9 +608,9 @@ fn main() { "trait_impls_in_crate_shim", "impl_trait_with_diagnostics_shim", "impl_self_ty_with_diagnostics_shim", - "generic_predicates_ns_shim", + "generic_predicates_shim", "value_ty_shim", - "generic_predicates_ns_shim", + "generic_predicates_shim", ] "#]], ); @@ -682,13 +682,13 @@ fn main() { "attrs_shim", "attrs_shim", "attrs_shim", - "generic_predicates_ns_shim", - "return_type_impl_traits_ns_shim", + "generic_predicates_shim", + "return_type_impl_traits_shim", "infer_shim", "function_signature_with_source_map_shim", "expr_scopes_shim", "struct_signature_with_source_map_shim", - "generic_predicates_ns_shim", + "generic_predicates_shim", "VariantFields::query_", "inherent_impls_in_crate_shim", "impl_signature_with_source_map_shim", @@ -697,8 +697,8 @@ fn main() { "trait_impls_in_crate_shim", "impl_trait_with_diagnostics_shim", "impl_self_ty_with_diagnostics_shim", - "generic_predicates_ns_shim", - "generic_predicates_ns_shim", + "generic_predicates_shim", + "generic_predicates_shim", ] "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs deleted file mode 100644 index fe4cf7a3da52..000000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs +++ /dev/null @@ -1,155 +0,0 @@ -//! Implementation of Chalk debug helper functions using TLS. -use std::fmt::{self, Display}; - -use itertools::Itertools; -use span::Edition; - -use crate::{ - CallableDefId, Interner, ProjectionTyExt, chalk_db, db::HirDatabase, from_assoc_type_id, - from_chalk_trait_id, mapping::from_chalk, -}; -use hir_def::{AdtId, ItemContainerId, Lookup, TypeAliasId}; - -#[allow(unused)] -pub(crate) use unsafe_tls::{set_current_program, with_current_program}; - -pub(crate) struct DebugContext<'a>(&'a dyn HirDatabase); - -impl DebugContext<'_> { - pub(crate) fn debug_struct_id( - &self, - id: chalk_db::AdtId, - f: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - let name = match id.0 { - AdtId::StructId(it) => self.0.struct_signature(it).name.clone(), - AdtId::UnionId(it) => self.0.union_signature(it).name.clone(), - AdtId::EnumId(it) => self.0.enum_signature(it).name.clone(), - }; - name.display(self.0, Edition::LATEST).fmt(f)?; - Ok(()) - } - - pub(crate) fn debug_trait_id( - &self, - id: chalk_db::TraitId, - f: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - let trait_: hir_def::TraitId = from_chalk_trait_id(id); - let trait_data = self.0.trait_signature(trait_); - trait_data.name.display(self.0, Edition::LATEST).fmt(f)?; - Ok(()) - } - - pub(crate) fn debug_assoc_type_id( - &self, - id: chalk_db::AssocTypeId, - fmt: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - let type_alias: TypeAliasId = from_assoc_type_id(id); - let type_alias_data = self.0.type_alias_signature(type_alias); - let trait_ = match type_alias.lookup(self.0).container { - ItemContainerId::TraitId(t) => t, - _ => panic!("associated type not in trait"), - }; - let trait_data = self.0.trait_signature(trait_); - write!( - fmt, - "{}::{}", - trait_data.name.display(self.0, Edition::LATEST), - type_alias_data.name.display(self.0, Edition::LATEST) - )?; - Ok(()) - } - - pub(crate) fn debug_projection_ty( - &self, - projection_ty: &chalk_ir::ProjectionTy, - fmt: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - let type_alias = from_assoc_type_id(projection_ty.associated_ty_id); - let type_alias_data = self.0.type_alias_signature(type_alias); - let trait_ = match type_alias.lookup(self.0).container { - ItemContainerId::TraitId(t) => t, - _ => panic!("associated type not in trait"), - }; - let trait_name = &self.0.trait_signature(trait_).name; - let trait_ref = projection_ty.trait_ref(self.0); - let trait_params = trait_ref.substitution.as_slice(Interner); - let self_ty = trait_ref.self_type_parameter(Interner); - write!(fmt, "<{self_ty:?} as {}", trait_name.display(self.0, Edition::LATEST))?; - if trait_params.len() > 1 { - write!( - fmt, - "<{}>", - trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{x:?}"))), - )?; - } - write!(fmt, ">::{}", type_alias_data.name.display(self.0, Edition::LATEST))?; - - let proj_params = &projection_ty.substitution.as_slice(Interner)[trait_params.len()..]; - if !proj_params.is_empty() { - write!( - fmt, - "<{}>", - proj_params.iter().format_with(", ", |x, f| f(&format_args!("{x:?}"))), - )?; - } - - Ok(()) - } - - pub(crate) fn debug_fn_def_id( - &self, - fn_def_id: chalk_ir::FnDefId, - fmt: &mut fmt::Formatter<'_>, - ) -> Result<(), fmt::Error> { - let def: CallableDefId = from_chalk(self.0, fn_def_id); - let name = match def { - CallableDefId::FunctionId(ff) => self.0.function_signature(ff).name.clone(), - CallableDefId::StructId(s) => self.0.struct_signature(s).name.clone(), - CallableDefId::EnumVariantId(e) => { - let loc = e.lookup(self.0); - loc.parent.enum_variants(self.0).variants[loc.index as usize].1.clone() - } - }; - match def { - CallableDefId::FunctionId(_) => { - write!(fmt, "{{fn {}}}", name.display(self.0, Edition::LATEST)) - } - CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => { - write!(fmt, "{{ctor {}}}", name.display(self.0, Edition::LATEST)) - } - } - } -} - -mod unsafe_tls { - use super::DebugContext; - use crate::db::HirDatabase; - use scoped_tls::scoped_thread_local; - - scoped_thread_local!(static PROGRAM: DebugContext<'_>); - - pub(crate) fn with_current_program( - op: impl for<'a> FnOnce(Option<&'a DebugContext<'a>>) -> R, - ) -> R { - if PROGRAM.is_set() { PROGRAM.with(|prog| op(Some(prog))) } else { op(None) } - } - - #[allow(dead_code)] - pub(crate) fn set_current_program(p: &dyn HirDatabase, op: OP) -> R - where - OP: FnOnce() -> R, - { - let ctx = DebugContext(p); - // we're transmuting the lifetime in the DebugContext to static. This is - // fine because we only keep the reference for the lifetime of this - // function, *and* the only way to access the context is through - // `with_current_program`, which hides the lifetime through the `for` - // type. - let static_p: &DebugContext<'static> = - unsafe { std::mem::transmute::<&DebugContext<'_>, &DebugContext<'static>>(&ctx) }; - PROGRAM.set(static_p, op) - } -} 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 35c8a197f52c..7f6d4ff17f9f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -3,33 +3,26 @@ use core::fmt; use std::hash::Hash; -use chalk_ir::{DebruijnIndex, GoalData, fold::TypeFoldable}; - use base_db::Crate; use hir_def::{BlockId, TraitId, lang_item::LangItem}; use hir_expand::name::Name; use intern::sym; use rustc_next_trait_solver::solve::{HasChanged, SolverDelegateEvalExt}; use rustc_type_ir::{ - InferCtxtLike, TypingMode, - inherent::{IntoKind, SliceLike, Span as _}, + TypingMode, + inherent::{IntoKind, Span as _}, solve::Certainty, }; -use span::Edition; use triomphe::Arc; use crate::{ - AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTyExt, - TraitRefExt, TyKind, WhereClause, db::HirDatabase, next_solver::{ - DbInterner, GenericArg, ParamEnv, Predicate, SolverContext, Span, + Canonical, DbInterner, GenericArgs, Goal, ParamEnv, Predicate, SolverContext, Span, Ty, + TyKind, infer::{DbInternerInferExt, InferCtxt, traits::ObligationCause}, - mapping::{ChalkToNextSolver, convert_canonical_args_for_result}, obligation_ctxt::ObligationCtxt, - util::mini_canonicalize, }, - utils::UnevaluatedConstEvaluatorFolder, }; /// A set of clauses that we assume to be true. E.g. if we are inside this function: @@ -42,7 +35,7 @@ pub struct TraitEnvironment<'db> { pub krate: Crate, pub block: Option, // FIXME make this a BTreeMap - traits_from_clauses: Box<[(crate::next_solver::Ty<'db>, TraitId)]>, + traits_from_clauses: Box<[(Ty<'db>, TraitId)]>, pub env: ParamEnv<'db>, } @@ -59,7 +52,7 @@ impl<'db> TraitEnvironment<'db> { pub fn new( krate: Crate, block: Option, - traits_from_clauses: Box<[(crate::next_solver::Ty<'db>, TraitId)]>, + traits_from_clauses: Box<[(Ty<'db>, TraitId)]>, env: ParamEnv<'db>, ) -> Arc { Arc::new(TraitEnvironment { krate, block, traits_from_clauses, env }) @@ -70,10 +63,7 @@ impl<'db> TraitEnvironment<'db> { Arc::make_mut(this).block = Some(block); } - pub fn traits_in_scope_from_clauses( - &self, - ty: crate::next_solver::Ty<'db>, - ) -> impl Iterator + '_ { + pub fn traits_in_scope_from_clauses(&self, ty: Ty<'db>) -> impl Iterator + '_ { self.traits_from_clauses .iter() .filter_map(move |(self_ty, trait_id)| (*self_ty == ty).then_some(*trait_id)) @@ -83,92 +73,19 @@ impl<'db> TraitEnvironment<'db> { /// This should be used in `hir` only. pub fn structurally_normalize_ty<'db>( infcx: &InferCtxt<'db>, - ty: crate::next_solver::Ty<'db>, + ty: Ty<'db>, env: Arc>, -) -> crate::next_solver::Ty<'db> { - let crate::next_solver::TyKind::Alias(..) = ty.kind() else { return ty }; +) -> Ty<'db> { + let TyKind::Alias(..) = ty.kind() else { return ty }; let mut ocx = ObligationCtxt::new(infcx); let ty = ocx.structurally_normalize_ty(&ObligationCause::dummy(), env.env, ty).unwrap_or(ty); ty.replace_infer_with_error(infcx.interner) } -fn identity_subst( - binders: chalk_ir::CanonicalVarKinds, -) -> chalk_ir::Canonical> { - let identity_subst = chalk_ir::Substitution::from_iter( - Interner, - binders.iter(Interner).enumerate().map(|(index, c)| { - let index_db = chalk_ir::BoundVar::new(DebruijnIndex::INNERMOST, index); - match &c.kind { - chalk_ir::VariableKind::Ty(_) => { - chalk_ir::GenericArgData::Ty(TyKind::BoundVar(index_db).intern(Interner)) - .intern(Interner) - } - chalk_ir::VariableKind::Lifetime => chalk_ir::GenericArgData::Lifetime( - chalk_ir::LifetimeData::BoundVar(index_db).intern(Interner), - ) - .intern(Interner), - chalk_ir::VariableKind::Const(ty) => chalk_ir::GenericArgData::Const( - chalk_ir::ConstData { - ty: ty.clone(), - value: chalk_ir::ConstValue::BoundVar(index_db), - } - .intern(Interner), - ) - .intern(Interner), - } - }), - ); - chalk_ir::Canonical { binders, value: identity_subst } -} - -fn solve_nextsolver<'db>( - db: &'db dyn HirDatabase, - krate: Crate, - block: Option, - goal: &chalk_ir::UCanonical>>, -) -> Result< - (HasChanged, Certainty, rustc_type_ir::Canonical, Vec>>), - rustc_type_ir::solve::NoSolution, -> { - // FIXME: should use analysis_in_body, but that needs GenericDefId::Block - let context = SolverContext( - DbInterner::new_with(db, Some(krate), block) - .infer_ctxt() - .build(TypingMode::non_body_analysis()), - ); - - match goal.canonical.value.goal.data(Interner) { - // FIXME: args here should be...what? not empty - GoalData::All(goals) if goals.is_empty(Interner) => { - return Ok((HasChanged::No, Certainty::Yes, mini_canonicalize(context, vec![]))); - } - _ => {} - } - - let goal = goal.canonical.to_nextsolver(context.cx()); - 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 vars = - var_values.var_values.iter().map(|g| context.0.resolve_vars_if_possible(g)).collect(); - let canonical_var_values = mini_canonicalize(context, vars); - - let res = res.map(|r| (r.has_changed, r.certainty, canonical_var_values)); - - tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); - - res -} - #[derive(Clone, Debug, PartialEq)] pub enum NextTraitSolveResult { - Certain(chalk_ir::Canonical>), - Uncertain(chalk_ir::Canonical>), + Certain, + Uncertain, NoSolution, } @@ -178,75 +95,17 @@ impl NextTraitSolveResult { } pub fn certain(&self) -> bool { - matches!(self, NextTraitSolveResult::Certain(..)) + matches!(self, NextTraitSolveResult::Certain) } pub fn uncertain(&self) -> bool { - matches!(self, NextTraitSolveResult::Uncertain(..)) - } -} - -pub fn next_trait_solve( - db: &dyn HirDatabase, - krate: Crate, - block: Option, - goal: Canonical>, -) -> NextTraitSolveResult { - let detail = match &goal.value.goal.data(Interner) { - GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => { - db.trait_signature(it.hir_trait_id()).name.display(db, Edition::LATEST).to_string() - } - GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_owned(), - _ => "??".to_owned(), - }; - let _p = tracing::info_span!("next_trait_solve", ?detail).entered(); - tracing::info!("next_trait_solve({:?})", goal.value.goal); - - if let GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(projection_ty), - .. - }))) = &goal.value.goal.data(Interner) - && let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner) - { - // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible - // FIXME - return NextTraitSolveResult::Uncertain(identity_subst(goal.binders.clone())); - } - - // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So - // we should get rid of it when talking to chalk. - let goal = goal - .try_fold_with(&mut UnevaluatedConstEvaluatorFolder { db }, DebruijnIndex::INNERMOST) - .unwrap(); - - // We currently don't deal with universes (I think / hope they're not yet - // relevant for our use cases?) - let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 }; - tracing::info!(?u_canonical); - - let next_solver_res = solve_nextsolver(db, krate, block, &u_canonical); - - match next_solver_res { - Err(_) => NextTraitSolveResult::NoSolution, - Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( - convert_canonical_args_for_result(DbInterner::new_with(db, Some(krate), block), args), - ), - Ok((_, Certainty::Maybe { .. }, args)) => { - let subst = convert_canonical_args_for_result( - DbInterner::new_with(db, Some(krate), block), - args, - ); - NextTraitSolveResult::Uncertain(chalk_ir::Canonical { - binders: subst.binders, - value: subst.value.subst, - }) - } + matches!(self, NextTraitSolveResult::Uncertain) } } pub fn next_trait_solve_canonical_in_ctxt<'db>( infer_ctxt: &InferCtxt<'db>, - goal: crate::next_solver::Canonical<'db, crate::next_solver::Goal<'db, Predicate<'db>>>, + goal: Canonical<'db, Goal<'db, Predicate<'db>>>, ) -> NextTraitSolveResult { let context = SolverContext(infer_ctxt.clone()); @@ -257,33 +116,21 @@ pub fn next_trait_solve_canonical_in_ctxt<'db>( let res = context.evaluate_root_goal(goal, Span::dummy(), None); - let vars = - var_values.var_values.iter().map(|g| context.0.resolve_vars_if_possible(g)).collect(); - let canonical_var_values = mini_canonicalize(context, vars); - - let res = res.map(|r| (r.has_changed, r.certainty, canonical_var_values)); + let res = res.map(|r| (r.has_changed, r.certainty)); tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); match res { Err(_) => NextTraitSolveResult::NoSolution, - Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( - convert_canonical_args_for_result(infer_ctxt.interner, args), - ), - Ok((_, Certainty::Maybe { .. }, args)) => { - let subst = convert_canonical_args_for_result(infer_ctxt.interner, args); - NextTraitSolveResult::Uncertain(chalk_ir::Canonical { - binders: subst.binders, - value: subst.value.subst, - }) - } + 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: crate::next_solver::Goal<'db, crate::next_solver::Predicate<'db>>, + goal: Goal<'db, Predicate<'db>>, ) -> Result<(HasChanged, Certainty), rustc_type_ir::solve::NoSolution> { tracing::info!(?goal); @@ -377,7 +224,7 @@ impl FnTrait { /// This should not be used in `hir-ty`, only in `hir`. pub fn implements_trait_unique<'db>( - ty: crate::next_solver::Ty<'db>, + ty: Ty<'db>, db: &'db dyn HirDatabase, env: Arc>, trait_: TraitId, @@ -392,7 +239,7 @@ pub fn implements_trait_unique_with_args<'db>( db: &'db dyn HirDatabase, env: Arc>, trait_: TraitId, - args: crate::next_solver::GenericArgs<'db>, + args: GenericArgs<'db>, ) -> bool { implements_trait_unique_impl(db, env, trait_, &mut |_| args) } @@ -401,7 +248,7 @@ fn implements_trait_unique_impl<'db>( db: &'db dyn HirDatabase, env: Arc>, trait_: TraitId, - create_args: &mut dyn FnMut(&InferCtxt<'db>) -> crate::next_solver::GenericArgs<'db>, + create_args: &mut dyn FnMut(&InferCtxt<'db>) -> GenericArgs<'db>, ) -> bool { let interner = DbInterner::new_with(db, Some(env.krate), env.block); // FIXME(next-solver): I believe this should be `PostAnalysis`. @@ -409,7 +256,7 @@ 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 = crate::next_solver::Goal::new(interner, env.env, trait_ref); + let goal = Goal::new(interner, env.env, trait_ref); let result = crate::traits::next_trait_solve_in_ctxt(&infcx, goal); matches!(result, Ok((_, Certainty::Yes))) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 15359922c80e..ca5e33fe6ad0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -1,40 +1,30 @@ //! Helper functions for working with def, which don't need to be a separate //! query, but can't be computed directly from `*Data` (ie, which need a `db`). -use std::{cell::LazyCell, iter}; +use std::cell::LazyCell; use base_db::{ Crate, target::{self, TargetData}, }; -use chalk_ir::{DebruijnIndex, fold::FallibleTypeFolder}; use hir_def::{ - EnumId, EnumVariantId, FunctionId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId, + EnumId, EnumVariantId, FunctionId, Lookup, TraitId, db::DefDatabase, hir::generics::WherePredicate, lang_item::LangItem, resolver::{HasResolver, TypeNs}, type_ref::{TraitBoundModifier, TypeRef}, }; -use hir_expand::name::Name; use intern::sym; use rustc_abi::TargetDataLayout; -use rustc_hash::FxHashSet; -use rustc_type_ir::inherent::{IntoKind, SliceLike}; use smallvec::{SmallVec, smallvec}; use span::Edition; use crate::{ - ChalkTraitId, Const, ConstScalar, Interner, TargetFeatures, TraitRef, TraitRefExt, - consteval::unknown_const, + TargetFeatures, db::HirDatabase, layout::{Layout, TagEncoding}, mir::pad16, - next_solver::{ - DbInterner, - mapping::{ChalkToNextSolver, NextSolverToChalk, convert_args_for_result}, - }, - to_chalk_trait_id, }; pub(crate) fn fn_traits(db: &dyn DefDatabase, krate: Crate) -> impl Iterator + '_ { @@ -75,49 +65,6 @@ pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[Trai result } -/// Given a trait ref (`Self: Trait`), builds all the implied trait refs for -/// super traits. The original trait ref will be included. So the difference to -/// `all_super_traits` is that we keep track of type parameters; for example if -/// we have `Self: Trait` and `Trait: OtherTrait` we'll get -/// `Self: OtherTrait`. -pub(super) fn all_super_trait_refs( - db: &dyn HirDatabase, - trait_ref: TraitRef, - cb: impl FnMut(TraitRef) -> Option, -) -> Option { - let seen = iter::once(trait_ref.trait_id).collect(); - SuperTraits { db, seen, stack: vec![trait_ref] }.find_map(cb) -} - -struct SuperTraits<'a> { - db: &'a dyn HirDatabase, - stack: Vec, - seen: FxHashSet, -} - -impl SuperTraits<'_> { - fn elaborate(&mut self, trait_ref: &TraitRef) { - direct_super_trait_refs(self.db, trait_ref, |trait_ref| { - if !self.seen.contains(&trait_ref.trait_id) { - self.stack.push(trait_ref); - } - }); - } -} - -impl Iterator for SuperTraits<'_> { - type Item = TraitRef; - - fn next(&mut self) -> Option { - if let Some(next) = self.stack.pop() { - self.elaborate(&next); - Some(next) - } else { - None - } - } -} - fn direct_super_traits_cb(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(TraitId)) { let resolver = LazyCell::new(|| trait_.resolver(db)); let (generic_params, store) = db.generic_params_and_store(trait_.into()); @@ -148,49 +95,6 @@ fn direct_super_traits_cb(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut( .for_each(cb); } -fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef, cb: impl FnMut(TraitRef)) { - let interner = DbInterner::new_with(db, None, None); - let generic_params = db.generic_params(trait_ref.hir_trait_id().into()); - let trait_self = match generic_params.trait_self_param() { - Some(p) => TypeOrConstParamId { parent: trait_ref.hir_trait_id().into(), local_id: p }, - None => return, - }; - let trait_ref_args: crate::next_solver::GenericArgs<'_> = - trait_ref.substitution.to_nextsolver(interner); - db.generic_predicates_for_param_ns(trait_self.parent, trait_self, None) - .iter() - .filter_map(|pred| { - let pred = pred.kind(); - // FIXME: how to correctly handle higher-ranked bounds here? - let pred = pred.no_bound_vars().expect("FIXME unexpected higher-ranked trait bound"); - match pred { - rustc_type_ir::ClauseKind::Trait(t) => { - let t = - rustc_type_ir::EarlyBinder::bind(t).instantiate(interner, trait_ref_args); - let trait_id = to_chalk_trait_id(t.def_id().0); - - let substitution = - convert_args_for_result(interner, t.trait_ref.args.as_slice()); - let tr = chalk_ir::TraitRef { trait_id, substitution }; - Some(tr) - } - _ => None, - } - }) - .for_each(cb); -} - -pub(super) fn associated_type_by_name_including_super_traits( - db: &dyn HirDatabase, - trait_ref: TraitRef, - name: &Name, -) -> Option<(TraitRef, TypeAliasId)> { - all_super_trait_refs(db, trait_ref, |t| { - let assoc_type = t.hir_trait_id().trait_items(db).associated_type_by_name(name)?; - Some((t, assoc_type)) - }) -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Unsafety { Safe, @@ -263,41 +167,6 @@ pub fn is_fn_unsafe_to_call( } } -pub(crate) struct UnevaluatedConstEvaluatorFolder<'a> { - pub(crate) db: &'a dyn HirDatabase, -} - -impl FallibleTypeFolder for UnevaluatedConstEvaluatorFolder<'_> { - type Error = (); - - fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { - self - } - - fn interner(&self) -> Interner { - Interner - } - - fn try_fold_const( - &mut self, - constant: Const, - _outer_binder: DebruijnIndex, - ) -> Result { - if let chalk_ir::ConstValue::Concrete(c) = &constant.data(Interner).value - && let ConstScalar::UnevaluatedConst(id, subst) = &c.interned - { - let interner = DbInterner::conjure(); - if let Ok(eval) = self.db.const_eval(*id, subst.to_nextsolver(interner), None) { - return Ok(eval.to_chalk(interner)); - } else { - return Ok(unknown_const(constant.data(Interner).ty.to_nextsolver(interner)) - .to_chalk(interner)); - } - } - Ok(constant) - } -} - pub(crate) fn detect_variant_from_bytes<'a>( layout: &'a Layout, db: &dyn HirDatabase, 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 46898ddeec12..b57bf03f2472 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -128,7 +128,7 @@ impl<'db> Context<'db> { GenericDefId::AdtId(adt) => { let db = self.db; let mut add_constraints_from_variant = |variant| { - for (_, field) in db.field_types_ns(variant).iter() { + for (_, field) in db.field_types(variant).iter() { self.add_constraints_from_ty( field.instantiate_identity(), Variance::Covariant, diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index b31bb248e839..d61c2eca8347 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -484,7 +484,7 @@ impl<'db> HirDisplay<'db> for TypeParam { let param_data = ¶ms[self.id.local_id()]; let krate = self.id.parent().krate(f.db).id; let ty = self.ty(f.db).ty; - let predicates = f.db.generic_predicates_ns(self.id.parent()); + let predicates = f.db.generic_predicates(self.id.parent()); let predicates = predicates .instantiate_identity() .into_iter() diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 78b4533a94b0..48eafb0bd4c6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1271,7 +1271,7 @@ impl<'db> InstantiatedField<'db> { let interner = DbInterner::new_with(db, Some(krate.base()), None); let var_id = self.inner.parent.into(); - let field = db.field_types_ns(var_id)[self.inner.id]; + let field = db.field_types(var_id)[self.inner.id]; let ty = field.instantiate(interner, self.args); TypeNs::new(db, var_id, ty) } @@ -1350,7 +1350,7 @@ impl Field { /// context of the field definition. pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> { let var_id = self.parent.into(); - let ty = db.field_types_ns(var_id)[self.id].skip_binder(); + let ty = db.field_types(var_id)[self.id].skip_binder(); TypeNs::new(db, var_id, ty) } @@ -1368,7 +1368,7 @@ impl Field { }; let interner = DbInterner::new_with(db, None, None); let args = generic_args_from_tys(interner, def_id.into(), generics.map(|ty| ty.ty)); - let ty = db.field_types_ns(var_id)[self.id].instantiate(interner, args); + let ty = db.field_types(var_id)[self.id].instantiate(interner, args); Type::new(db, var_id, ty) } @@ -3693,7 +3693,7 @@ impl GenericDef { }; expr_store_diagnostics(db, acc, &source_map); - push_ty_diagnostics(db, acc, db.generic_defaults_ns_with_diagnostics(def).1, &source_map); + push_ty_diagnostics(db, acc, db.generic_defaults_with_diagnostics(def).1, &source_map); push_ty_diagnostics( db, acc, @@ -4192,7 +4192,7 @@ impl TypeParam { /// parameter, not additional bounds that might be added e.g. by a method if /// the parameter comes from an impl! pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec { - db.generic_predicates_for_param_ns(self.id.parent(), self.id.into(), None) + db.generic_predicates_for_param(self.id.parent(), self.id.into(), None) .iter() .filter_map(|pred| match &pred.kind().skip_binder() { ClauseKind::Trait(trait_ref) => Some(Trait::from(trait_ref.def_id().0)), @@ -4282,7 +4282,7 @@ impl ConstParam { fn generic_arg_from_param(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option> { let local_idx = hir_ty::param_idx(db, id)?; - let defaults = db.generic_defaults_ns(id.parent); + let defaults = db.generic_defaults(id.parent); let ty = defaults.get(local_idx)?; // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. Some(ty.instantiate_identity()) @@ -4883,7 +4883,7 @@ impl<'db> Type<'db> { if variant_data.fields().is_empty() { vec![] } else { - let field_types = self.interner.db().field_types_ns(id); + let field_types = self.interner.db().field_types(id); variant_data .fields() .iter() @@ -5216,7 +5216,7 @@ impl<'db> Type<'db> { _ => return Vec::new(), }; - db.field_types_ns(variant_id) + db.field_types(variant_id) .iter() .map(|(local_id, ty)| { let def = Field { parent: variant_id.into(), id: local_id }; @@ -6450,7 +6450,7 @@ fn generic_args_from_tys<'db>( fn has_non_default_type_params(db: &dyn HirDatabase, generic_def: GenericDefId) -> bool { let params = db.generic_params(generic_def); - let defaults = db.generic_defaults_ns(generic_def); + let defaults = db.generic_defaults(generic_def); params .iter_type_or_consts() .filter(|(_, param)| matches!(param, TypeOrConstParamData::TypeParamData(_))) 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 8d2ba7e604e7..15eab14b88df 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -712,8 +712,7 @@ impl<'db> SourceAnalyzer<'db> { let variant = self.infer()?.variant_resolution_for_expr_or_pat(expr_id)?; let variant_data = variant.fields(db); let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? }; - let field_ty = - (*db.field_types_ns(variant).get(field.local_id)?).instantiate(interner, subst); + let field_ty = (*db.field_types(variant).get(field.local_id)?).instantiate(interner, subst); Some(( field.into(), local, @@ -735,8 +734,7 @@ impl<'db> SourceAnalyzer<'db> { let variant_data = variant.fields(db); let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? }; let (adt, subst) = self.infer()?[pat_id.as_pat()?].as_adt()?; - let field_ty = - (*db.field_types_ns(variant).get(field.local_id)?).instantiate(interner, subst); + let field_ty = (*db.field_types(variant).get(field.local_id)?).instantiate(interner, subst); Some(( field.into(), Type::new_with_resolver(db, &self.resolver, field_ty), @@ -802,7 +800,7 @@ impl<'db> SourceAnalyzer<'db> { |variant: VariantId, subst: GenericArgs<'db>, container: &mut _| { let fields = variant.fields(db); let field = fields.field(&field_name.as_name())?; - let field_types = db.field_types_ns(variant); + let field_types = db.field_types(variant); *container = Either::Right(field_types[field].instantiate(interner, subst)); let generic_def = match variant { VariantId::EnumVariantId(it) => it.loc(db).parent.into(), @@ -1255,7 +1253,7 @@ impl<'db> SourceAnalyzer<'db> { missing_fields: Vec, ) -> Vec<(Field, Type<'db>)> { let interner = DbInterner::new_with(db, None, None); - let field_types = db.field_types_ns(variant); + let field_types = db.field_types(variant); missing_fields .into_iter() 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 df3dc53f7c03..91fb4d0a6715 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -9393,7 +9393,7 @@ fn main(a$0: T) {} *a* ```rust - a: T + a: T ``` --- From 491554dbc1c4696ceb30629491d99a3b1b6d5c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Thu, 2 Oct 2025 15:16:53 +0200 Subject: [PATCH 045/170] add tests for unsizing coercions (both ICE) --- tests/ui/dst/dst-object-from-unsized-type.rs | 3 +++ .../next-solver/unsize-goal-mismatch-2.rs | 18 ++++++++++++++++++ .../next-solver/unsize-goal-mismatch.rs | 19 +++++++++++++++++++ .../ui/traits/next-solver/unsize-overflow.rs | 7 +++++++ 4 files changed, 47 insertions(+) create mode 100644 tests/ui/traits/next-solver/unsize-goal-mismatch-2.rs create mode 100644 tests/ui/traits/next-solver/unsize-goal-mismatch.rs create mode 100644 tests/ui/traits/next-solver/unsize-overflow.rs diff --git a/tests/ui/dst/dst-object-from-unsized-type.rs b/tests/ui/dst/dst-object-from-unsized-type.rs index 3cd5b1ed6f46..5ba6c571a39a 100644 --- a/tests/ui/dst/dst-object-from-unsized-type.rs +++ b/tests/ui/dst/dst-object-from-unsized-type.rs @@ -1,4 +1,7 @@ // Test that we cannot create objects from unsized types. +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver trait Foo { fn foo(&self) {} } impl Foo for str {} diff --git a/tests/ui/traits/next-solver/unsize-goal-mismatch-2.rs b/tests/ui/traits/next-solver/unsize-goal-mismatch-2.rs new file mode 100644 index 000000000000..c651309e01b1 --- /dev/null +++ b/tests/ui/traits/next-solver/unsize-goal-mismatch-2.rs @@ -0,0 +1,18 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +// Test from trait-system-refactor-initiative#241: +// Used to ICE in mir typeck because of ambiguity in the new solver. +// The wrong (first) trait bound was selected. +// This is fixed with new logic for unsizing coercions +// that's independent from that of the old solver, which this test verifies. + +trait Super {} +trait Trait: Super + for<'hr> Super<&'hr ()> {} + +fn foo<'a>(x: Box>) -> Box> { + x +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/unsize-goal-mismatch.rs b/tests/ui/traits/next-solver/unsize-goal-mismatch.rs new file mode 100644 index 000000000000..c9e201899724 --- /dev/null +++ b/tests/ui/traits/next-solver/unsize-goal-mismatch.rs @@ -0,0 +1,19 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +// Test from trait-system-refactor-initiative#241: +// Used to ICE in mir typeck because of ambiguity in the new solver. +// The wrong (first) trait bound was selected. +// This is fixed with new logic for unsizing coercions +// that's independent from that of the old solver, which this test verifies. + +trait Super<'a> {} +trait Trait<'a>: Super<'a> + for<'hr> Super<'hr> {} + +fn foo<'a>(x: Box>) -> Box> { + x + //~^ ERROR type annotations needed: cannot satisfy `dyn Trait<'_>: Unsize> +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/unsize-overflow.rs b/tests/ui/traits/next-solver/unsize-overflow.rs new file mode 100644 index 000000000000..036be02aaeae --- /dev/null +++ b/tests/ui/traits/next-solver/unsize-overflow.rs @@ -0,0 +1,7 @@ +//@ compile-flags: -Znext-solver +#![recursion_limit = "8"] + +fn main() { + let _: Box = Box::new(&&&&&&&1); + //~^ ERROR overflow evaluating the requirement `Box<&&&&&&&i32>: CoerceUnsized> +} From 14a2770c0f13c0adc700a108f27a75eee3292e97 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 2 Jun 2025 17:16:55 +0000 Subject: [PATCH 046/170] Rename nit --- compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs | 2 +- compiler/rustc_trait_selection/src/solve/fulfill.rs | 2 +- .../rustc_trait_selection/src/solve/fulfill/derive_errors.rs | 2 +- compiler/rustc_trait_selection/src/solve/inspect/analyse.rs | 2 +- compiler/rustc_trait_selection/src/solve/select.rs | 2 +- compiler/rustc_trait_selection/src/traits/coherence.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index 367e2b6b372a..33f15409acf7 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -6,7 +6,7 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_span::Span; use rustc_trait_selection::solve::Certainty; use rustc_trait_selection::solve::inspect::{ - InspectConfig, InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor, + InspectConfig, InspectGoal, InferCtxtProofTreeExt, ProofTreeVisitor, }; use tracing::{debug, instrument, trace}; diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index ddd79e1dc940..7b61a653ae31 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -24,7 +24,7 @@ use tracing::instrument; use self::derive_errors::*; use super::Certainty; use super::delegate::SolverDelegate; -use super::inspect::{self, ProofTreeInferCtxtExt}; +use super::inspect::{self, InferCtxtProofTreeExt}; use crate::traits::{FulfillmentError, ScrubbedTraitError}; mod derive_errors; diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index eef0ddcbf596..904a0ac45777 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -15,7 +15,7 @@ use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt as _} use tracing::{instrument, trace}; use crate::solve::delegate::SolverDelegate; -use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor}; +use crate::solve::inspect::{self, InferCtxtProofTreeExt, ProofTreeVisitor}; use crate::solve::{Certainty, deeply_normalize_for_diagnostics}; use crate::traits::{FulfillmentError, FulfillmentErrorCode, wf}; diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 488315054c6a..cdbf2b0aeb83 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -463,7 +463,7 @@ pub trait ProofTreeVisitor<'tcx> { fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> Self::Result; } -#[extension(pub trait ProofTreeInferCtxtExt<'tcx>)] +#[extension(pub trait InferCtxtProofTreeExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { fn visit_proof_tree>( &self, diff --git a/compiler/rustc_trait_selection/src/solve/select.rs b/compiler/rustc_trait_selection/src/solve/select.rs index d7e7ffd5d28c..1feaab240415 100644 --- a/compiler/rustc_trait_selection/src/solve/select.rs +++ b/compiler/rustc_trait_selection/src/solve/select.rs @@ -12,7 +12,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::Span; use thin_vec::thin_vec; -use crate::solve::inspect::{self, ProofTreeInferCtxtExt}; +use crate::solve::inspect::{self, InferCtxtProofTreeExt}; #[extension(pub trait InferCtxtSelectExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index e488fb6802f8..4c8648480bbd 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -29,7 +29,7 @@ use tracing::{debug, instrument, warn}; use super::ObligationCtxt; use crate::error_reporting::traits::suggest_new_overflow_limit; use crate::infer::InferOk; -use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor}; +use crate::solve::inspect::{InspectGoal, InferCtxtProofTreeExt, ProofTreeVisitor}; use crate::solve::{SolverDelegate, deeply_normalize_for_diagnostics, inspect}; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::select::IntercrateAmbiguityCause; From 56894773c8f9a72e40e388a25bd6c495998cdcce Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 2 Jun 2025 17:45:06 +0000 Subject: [PATCH 047/170] Rework unsizing coercions in new solver --- compiler/rustc_hir_typeck/src/coercion.rs | 298 +++++++++++------- .../src/fn_ctxt/inspect_obligations.rs | 2 +- .../src/traits/coherence.rs | 2 +- 3 files changed, 181 insertions(+), 121 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 1e5fea1db9fc..7766075bade9 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -35,13 +35,13 @@ //! // and are then unable to coerce `&7i32` to `&mut i32`. //! ``` -use std::ops::Deref; +use std::ops::{ControlFlow, Deref}; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, struct_span_code_err}; -use rustc_hir as hir; use rustc_hir::attrs::InlineAttr; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{self as hir, LangItem}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::infer::relate::RelateResult; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, InferResult, RegionVariableOrigin}; @@ -56,6 +56,8 @@ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span}; use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::solve::inspect::{self, InferCtxtProofTreeExt, ProofTreeVisitor}; +use rustc_trait_selection::solve::{Certainty, Goal, NoSolution}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ self, ImplSource, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt, @@ -639,131 +641,140 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Adjust::Pointer(PointerCoercion::Unsize), )?; - let mut selcx = traits::SelectionContext::new(self); - // Create an obligation for `Source: CoerceUnsized`. let cause = self.cause(self.cause.span, ObligationCauseCode::Coercion { source, target }); + let pred = ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]); + let obligation = Obligation::new(self.tcx, cause, self.fcx.param_env, pred); - // Use a FIFO queue for this custom fulfillment procedure. - // - // A Vec (or SmallVec) is not a natural choice for a queue. However, - // this code path is hot, and this queue usually has a max length of 1 - // and almost never more than 3. By using a SmallVec we avoid an - // allocation, at the (very small) cost of (occasionally) having to - // shift subsequent elements down when removing the front element. - let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![Obligation::new( - self.tcx, - cause, - self.fcx.param_env, - ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]) - )]; + if self.next_trait_solver() { + coercion.obligations.push(obligation); - // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid - // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where - // inference might unify those two inner type variables later. - let traits = [coerce_unsized_did, unsize_did]; - while !queue.is_empty() { - let obligation = queue.remove(0); - let trait_pred = match obligation.predicate.kind().no_bound_vars() { - Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred))) - if traits.contains(&trait_pred.def_id()) => - { - self.resolve_vars_if_possible(trait_pred) - } - // Eagerly process alias-relate obligations in new trait solver, - // since these can be emitted in the process of solving trait goals, - // but we need to constrain vars before processing goals mentioning - // them. - Some(ty::PredicateKind::AliasRelate(..)) => { - let ocx = ObligationCtxt::new(self); - ocx.register_obligation(obligation); - if !ocx.try_evaluate_obligations().is_empty() { - return Err(TypeError::Mismatch); - } - coercion.obligations.extend(ocx.into_pending_obligations()); - continue; - } - _ => { - coercion.obligations.push(obligation); - continue; - } - }; - debug!("coerce_unsized resolve step: {:?}", trait_pred); - match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) { - // Uncertain or unimplemented. - Ok(None) => { - if trait_pred.def_id() == unsize_did { - let self_ty = trait_pred.self_ty(); - let unsize_ty = trait_pred.trait_ref.args[1].expect_ty(); - debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred); - match (self_ty.kind(), unsize_ty.kind()) { - (&ty::Infer(ty::TyVar(v)), ty::Dynamic(..)) - if self.type_var_is_sized(v) => - { - debug!("coerce_unsized: have sized infer {:?}", v); - coercion.obligations.push(obligation); - // `$0: Unsize` where we know that `$0: Sized`, try going - // for unsizing. - } - _ => { - // Some other case for `$0: Unsize`. Note that we - // hit this case even if `Something` is a sized type, so just - // don't do the coercion. - debug!("coerce_unsized: ambiguous unsize"); - return Err(TypeError::Mismatch); - } - } - } else { - debug!("coerce_unsized: early return - ambiguous"); - return Err(TypeError::Mismatch); - } - } - Err(SelectionError::Unimplemented) => { - debug!("coerce_unsized: early return - can't prove obligation"); - return Err(TypeError::Mismatch); - } + if self + .infcx + .visit_proof_tree( + Goal::new(self.tcx, self.param_env, pred), + &mut CoerceVisitor { fcx: self.fcx, span: self.cause.span }, + ) + .is_break() + { + return Err(TypeError::Mismatch); + } + } else { + let mut selcx = traits::SelectionContext::new(self); + // Use a FIFO queue for this custom fulfillment procedure. + // + // A Vec (or SmallVec) is not a natural choice for a queue. However, + // this code path is hot, and this queue usually has a max length of 1 + // and almost never more than 3. By using a SmallVec we avoid an + // allocation, at the (very small) cost of (occasionally) having to + // shift subsequent elements down when removing the front element. + let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![obligation]; - Err(SelectionError::TraitDynIncompatible(_)) => { - // Dyn compatibility errors in coercion will *always* be due to the - // fact that the RHS of the coercion is a non-dyn compatible `dyn Trait` - // written in source somewhere (otherwise we will never have lowered - // the dyn trait from HIR to middle). - // - // There's no reason to emit yet another dyn compatibility error, - // especially since the span will differ slightly and thus not be - // deduplicated at all! - self.fcx.set_tainted_by_errors( - self.fcx - .dcx() - .span_delayed_bug(self.cause.span, "dyn compatibility during coercion"), - ); - } - Err(err) => { - let guar = self.err_ctxt().report_selection_error( - obligation.clone(), - &obligation, - &err, - ); - self.fcx.set_tainted_by_errors(guar); - // Treat this like an obligation and follow through - // with the unsizing - the lack of a coercion should - // be silent, as it causes a type mismatch later. - } - - Ok(Some(ImplSource::UserDefined(impl_source))) => { - queue.extend(impl_source.nested); - // Certain incoherent `CoerceUnsized` implementations may cause ICEs, - // so check the impl's validity. Taint the body so that we don't try - // to evaluate these invalid coercions in CTFE. We only need to do this - // for local impls, since upstream impls should be valid. - if impl_source.impl_def_id.is_local() - && let Err(guar) = - self.tcx.ensure_ok().coerce_unsized_info(impl_source.impl_def_id) + // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid + // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where + // inference might unify those two inner type variables later. + let traits = [coerce_unsized_did, unsize_did]; + while !queue.is_empty() { + let obligation = queue.remove(0); + let trait_pred = match obligation.predicate.kind().no_bound_vars() { + Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred))) + if traits.contains(&trait_pred.def_id()) => { - self.fcx.set_tainted_by_errors(guar); + self.resolve_vars_if_possible(trait_pred) } + // Eagerly process alias-relate obligations in new trait solver, + // since these can be emitted in the process of solving trait goals, + // but we need to constrain vars before processing goals mentioning + // them. + Some(ty::PredicateKind::AliasRelate(..)) => { + let ocx = ObligationCtxt::new(self); + ocx.register_obligation(obligation); + if !ocx.try_evaluate_obligations().is_empty() { + return Err(TypeError::Mismatch); + } + coercion.obligations.extend(ocx.into_pending_obligations()); + continue; + } + _ => { + coercion.obligations.push(obligation); + continue; + } + }; + debug!("coerce_unsized resolve step: {:?}", trait_pred); + match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) { + // Uncertain or unimplemented. + Ok(None) => { + if trait_pred.def_id() == unsize_did { + let self_ty = trait_pred.self_ty(); + let unsize_ty = trait_pred.trait_ref.args[1].expect_ty(); + debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred); + match (self_ty.kind(), unsize_ty.kind()) { + (&ty::Infer(ty::TyVar(v)), ty::Dynamic(..)) + if self.type_var_is_sized(v) => + { + debug!("coerce_unsized: have sized infer {:?}", v); + coercion.obligations.push(obligation); + // `$0: Unsize` where we know that `$0: Sized`, try going + // for unsizing. + } + _ => { + // Some other case for `$0: Unsize`. Note that we + // hit this case even if `Something` is a sized type, so just + // don't do the coercion. + debug!("coerce_unsized: ambiguous unsize"); + return Err(TypeError::Mismatch); + } + } + } else { + debug!("coerce_unsized: early return - ambiguous"); + return Err(TypeError::Mismatch); + } + } + Err(SelectionError::Unimplemented) => { + debug!("coerce_unsized: early return - can't prove obligation"); + return Err(TypeError::Mismatch); + } + + Err(SelectionError::TraitDynIncompatible(_)) => { + // Dyn compatibility errors in coercion will *always* be due to the + // fact that the RHS of the coercion is a non-dyn compatible `dyn Trait` + // writen in source somewhere (otherwise we will never have lowered + // the dyn trait from HIR to middle). + // + // There's no reason to emit yet another dyn compatibility error, + // especially since the span will differ slightly and thus not be + // deduplicated at all! + self.fcx.set_tainted_by_errors(self.fcx.dcx().span_delayed_bug( + self.cause.span, + "dyn compatibility during coercion", + )); + } + Err(err) => { + let guar = self.err_ctxt().report_selection_error( + obligation.clone(), + &obligation, + &err, + ); + self.fcx.set_tainted_by_errors(guar); + // Treat this like an obligation and follow through + // with the unsizing - the lack of a coercion should + // be silent, as it causes a type mismatch later. + } + Ok(Some(ImplSource::UserDefined(impl_source))) => { + queue.extend(impl_source.nested); + // Certain incoherent `CoerceUnsized` implementations may cause ICEs, + // so check the impl's validity. Taint the body so that we don't try + // to evaluate these invalid coercions in CTFE. We only need to do this + // for local impls, since upstream impls should be valid. + if impl_source.impl_def_id.is_local() + && let Err(guar) = + self.tcx.ensure_ok().coerce_unsized_info(impl_source.impl_def_id) + { + self.fcx.set_tainted_by_errors(guar); + } + } + Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), } - Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), } } @@ -2005,3 +2016,52 @@ impl AsCoercionSite for hir::Arm<'_> { self.body } } + +/// Recursively visit goals to decide whether an unsizing is possible. +/// `Break`s when it isn't, and an error should be raised. +/// `Continue`s when an unsizing ok based on an implementation of the `Unsize` trait / lang item. +struct CoerceVisitor<'a, 'tcx> { + fcx: &'a FnCtxt<'a, 'tcx>, + span: Span, +} + +impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> { + type Result = ControlFlow<()>; + + fn span(&self) -> Span { + self.span + } + + fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result { + let Some(pred) = goal.goal().predicate.as_trait_clause() else { + return ControlFlow::Continue(()); + }; + + if !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize) + && !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::CoerceUnsized) + { + return ControlFlow::Continue(()); + } + + match goal.result() { + Ok(Certainty::Yes) => ControlFlow::Continue(()), + Err(NoSolution) => ControlFlow::Break(()), + Ok(Certainty::Maybe { .. }) => { + // FIXME: structurally normalize? + if self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize) + && let ty::Dynamic(..) = pred.skip_binder().trait_ref.args.type_at(1).kind() + && let ty::Infer(ty::TyVar(vid)) = *pred.self_ty().skip_binder().kind() + && self.fcx.type_var_is_sized(vid) + { + ControlFlow::Continue(()) + } else if let Some(cand) = goal.unique_applicable_candidate() + && cand.shallow_certainty() == Certainty::Yes + { + cand.visit_nested_no_probe(self) + } else { + ControlFlow::Break(()) + } + } + } + } +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index 33f15409acf7..ff37cb75a5c5 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -6,7 +6,7 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_span::Span; use rustc_trait_selection::solve::Certainty; use rustc_trait_selection::solve::inspect::{ - InspectConfig, InspectGoal, InferCtxtProofTreeExt, ProofTreeVisitor, + InferCtxtProofTreeExt, InspectConfig, InspectGoal, ProofTreeVisitor, }; use tracing::{debug, instrument, trace}; diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 4c8648480bbd..1ccda94dc89b 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -29,7 +29,7 @@ use tracing::{debug, instrument, warn}; use super::ObligationCtxt; use crate::error_reporting::traits::suggest_new_overflow_limit; use crate::infer::InferOk; -use crate::solve::inspect::{InspectGoal, InferCtxtProofTreeExt, ProofTreeVisitor}; +use crate::solve::inspect::{InferCtxtProofTreeExt, InspectGoal, ProofTreeVisitor}; use crate::solve::{SolverDelegate, deeply_normalize_for_diagnostics, inspect}; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::select::IntercrateAmbiguityCause; From b5263103b83cf26c432bc50a94cb4d88ff743a94 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 20 Oct 2025 15:54:03 +0200 Subject: [PATCH 048/170] fix: Fix `signature_help` to proto conversion creating invalid utf16 offsets --- .../crates/rust-analyzer/src/lsp/to_proto.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) 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 d51ddb86d197..496d7caa1f95 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 @@ -493,8 +493,15 @@ pub(crate) fn signature_help( .parameter_ranges() .iter() .map(|it| { - let start = call_info.signature[..it.start().into()].chars().count() as u32; - let end = call_info.signature[..it.end().into()].chars().count() as u32; + let start = call_info.signature[..it.start().into()] + .chars() + .map(|c| c.len_utf16()) + .sum::() as u32; + let end = start + + call_info.signature[it.start().into()..it.end().into()] + .chars() + .map(|c| c.len_utf16()) + .sum::() as u32; [start, end] }) .map(|label_offsets| lsp_types::ParameterInformation { @@ -513,9 +520,9 @@ pub(crate) fn signature_help( label.push_str(", "); } first = false; - let start = label.chars().count() as u32; + let start = label.len() as u32; label.push_str(param); - let end = label.chars().count() as u32; + let end = label.len() as u32; params.push(lsp_types::ParameterInformation { label: lsp_types::ParameterLabel::LabelOffsets([start, end]), documentation: None, From 14c771a24c7075677ca792817a62654ff85c2e41 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 20 Oct 2025 19:00:42 +0300 Subject: [PATCH 049/170] Fix beta Clippy --- src/tools/rust-analyzer/crates/hir-ty/src/db.rs | 2 +- src/tools/rust-analyzer/xtask/src/release.rs | 2 +- 2 files changed, 2 insertions(+), 2 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 c79ff9857898..2ef796332299 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -291,7 +291,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { // cycle_initial = crate::variance::variances_of_cycle_initial, cycle_result = crate::variance::variances_of_cycle_initial, )] - fn variances_of(&self, def: GenericDefId) -> VariancesOf<'_>; + fn variances_of<'db>(&'db self, def: GenericDefId) -> VariancesOf<'db>; } #[test] diff --git a/src/tools/rust-analyzer/xtask/src/release.rs b/src/tools/rust-analyzer/xtask/src/release.rs index d06a25c8929b..13cb44ebed5a 100644 --- a/src/tools/rust-analyzer/xtask/src/release.rs +++ b/src/tools/rust-analyzer/xtask/src/release.rs @@ -43,7 +43,7 @@ impl flags::Release { .unwrap_or_default(); let tags = cmd!(sh, "git tag --list").read()?; - let prev_tag = tags.lines().filter(|line| is_release_tag(line)).next_back().unwrap(); + let prev_tag = tags.lines().rfind(|line| is_release_tag(line)).unwrap(); let contents = changelog::get_changelog(sh, changelog_n, &commit, prev_tag, &today)?; let path = changelog_dir.join(format!("{today}-changelog-{changelog_n}.adoc")); From 078fe7ca29c1ec46e847e105415d718951e38922 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 20 Oct 2025 19:02:49 +0300 Subject: [PATCH 050/170] resolve: When suppressing `out_of_scope_macro_calls` suppress `unused_imports` as well --- compiler/rustc_resolve/src/macros.rs | 10 ++++++++-- tests/ui/attributes/key-value-expansion-scope-pass.rs | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index de71568e454a..4a5894c9ffa8 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -1107,8 +1107,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // If such resolution is successful and gives the same result // (e.g. if the macro is re-imported), then silence the lint. let no_macro_rules = self.arenas.alloc_macro_rules_scope(MacroRulesScope::Empty); + let ident = path.segments[0].ident; let fallback_binding = self.reborrow().resolve_ident_in_scope_set( - path.segments[0].ident, + ident, ScopeSet::Macro(MacroKind::Bang), &ParentScope { macro_rules: no_macro_rules, ..*parent_scope }, None, @@ -1116,7 +1117,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None, None, ); - if fallback_binding.ok().and_then(|b| b.res().opt_def_id()) != Some(def_id) { + if let Ok(fallback_binding) = fallback_binding + && fallback_binding.res().opt_def_id() == Some(def_id) + { + // Silence `unused_imports` on the fallback import as well. + self.get_mut().record_use(ident, fallback_binding, Used::Other); + } else { let location = match parent_scope.module.kind { ModuleKind::Def(kind, def_id, name) => { if let Some(name) = name { diff --git a/tests/ui/attributes/key-value-expansion-scope-pass.rs b/tests/ui/attributes/key-value-expansion-scope-pass.rs index 6b1f4e5bd4bf..3a913b92c974 100644 --- a/tests/ui/attributes/key-value-expansion-scope-pass.rs +++ b/tests/ui/attributes/key-value-expansion-scope-pass.rs @@ -3,6 +3,7 @@ //@ check-pass //@ edition:2018 +#![warn(unused_imports)] #![doc = in_root!()] macro_rules! in_root { () => { "" } } From 5765a82f479bc90d5247215887dda85a9ef14820 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 20 Oct 2025 20:58:30 +0000 Subject: [PATCH 051/170] Prepare for merging from rust-lang/rust This updates the rust-version file to 4068bafedd8ba724e332a5221c06a6fa531a30d2. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 47552aee08f7..a7367c5c8826 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -4fa824bb78318a3cba8c7339d5754b4909922547 +4068bafedd8ba724e332a5221c06a6fa531a30d2 From b0545d660918bce6644cde3273cc78730d62a638 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 20 Oct 2025 23:08:28 +0200 Subject: [PATCH 052/170] how-to-build-and-run.md: remove stray instructions the previous command already builds using in-tree std --- .../rustc-dev-guide/src/building/how-to-build-and-run.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index ace834a55b71..efb21726e3c6 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -235,12 +235,6 @@ What this command does is: - Build `library` (the standard libraries) with the stage1 compiler that was just built. - Assemble a working stage1 sysroot, containing the stage1 compiler and stage1 standard libraries. -To build `rustc` with the in-tree `std`, use this command instead: - -```console -./x build library --stage 2 -``` - This final product (stage1 compiler + libs built using that compiler) is what you need to build other Rust programs (unless you use `#![no_std]` or `#![no_core]`). From 37147c4135a0a10db1e10dc4d983a8fbedb9deb9 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 21 Oct 2025 11:27:50 +0800 Subject: [PATCH 053/170] Add a FIXME for unordered fields --- .../src/handlers/convert_named_struct_to_tuple_struct.rs | 1 + 1 file changed, 1 insertion(+) 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 0847719d6922..e518c39dabc2 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 @@ -187,6 +187,7 @@ fn process_struct_name_reference( return None; } + // FIXME: Processing RecordPat and RecordExpr for unordered fields, and insert RestPat let parent = full_path.syntax().parent()?; match_ast! { match parent { From e6656c19b4dff3f9f3440461860e4e8525444b84 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 21 Oct 2025 11:16:04 +0800 Subject: [PATCH 054/170] Fix invalid RestPat for convert_tuple_struct_to_named_struct ```rust struct X$0(i8, i16, i32, i64); fn foo(X(a, .., d): X) {} ``` **Before this PR**: ```rust struct X { field1: i8, field2: i16, field3: i32, field4: i64 } fn foo(X { field1: a, field2: .., field3: d }: X) {} ``` **After this PR**: ```rust struct X { field1: i8, field2: i16, field3: i32, field4: i64 } fn foo(X { field1: a, field4: d, .. }: X) {} ``` --- .../convert_tuple_struct_to_named_struct.rs | 64 ++++++++++++++++--- 1 file changed, 56 insertions(+), 8 deletions(-) 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 3d78895477b3..61d844928a8a 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 @@ -154,14 +154,7 @@ fn edit_struct_references( ast::TupleStructPat(tuple_struct_pat) => { Some(make.record_pat_with_fields( tuple_struct_pat.path()?, - ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map( - |(pat, name)| { - ast::make::record_pat_field( - ast::make::name_ref(&name.to_string()), - pat, - ) - }, - ), None), + generate_record_pat_list(&tuple_struct_pat, names), ).syntax().clone()) }, // for tuple struct creations like Foo(42) @@ -284,6 +277,24 @@ fn generate_names(fields: impl Iterator) -> Vec ast::RecordPatFieldList { + let pure_fields = pat.fields().filter(|p| !matches!(p, ast::Pat::RestPat(_))); + let rest_len = names.len().saturating_sub(pure_fields.clone().count()); + let rest_pat = pat.fields().find_map(|p| ast::RestPat::cast(p.syntax().clone())); + let rest_idx = + pat.fields().position(|p| ast::RestPat::can_cast(p.syntax().kind())).unwrap_or(names.len()); + let before_rest = pat.fields().zip(names).take(rest_idx); + let after_rest = pure_fields.zip(names.iter().skip(rest_len)).skip(rest_idx); + + let fields = before_rest + .chain(after_rest) + .map(|(pat, name)| ast::make::record_pat_field(ast::make::name_ref(&name.text()), pat)); + ast::make::record_pat_field_list(fields, rest_pat) +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -358,6 +369,43 @@ impl A { ); } + #[test] + fn convert_struct_and_rest_pat() { + check_assist( + convert_tuple_struct_to_named_struct, + r#" +struct Inner; +struct A$0(Inner); +fn foo(A(..): A) {} +"#, + r#" +struct Inner; +struct A { field1: Inner } +fn foo(A { .. }: A) {} +"#, + ); + + check_assist( + convert_tuple_struct_to_named_struct, + r#" +struct A; +struct B; +struct C; +struct D; +struct X$0(A, B, C, D); +fn foo(X(a, .., d): X) {} +"#, + r#" +struct A; +struct B; +struct C; +struct D; +struct X { field1: A, field2: B, field3: C, field4: D } +fn foo(X { field1: a, field4: d, .. }: X) {} +"#, + ); + } + #[test] fn convert_simple_struct_cursor_on_struct_keyword() { check_assist( From 3f4da0db4a8cf850cb59b2e84277e1d12a7813b9 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 31 Aug 2025 18:35:42 -0500 Subject: [PATCH 055/170] Use inner span ctxt when lowering paren The need for this arose when encountering range expressions surrounded by parenthesis, where we want the span desugaring mark to be preserved. --- compiler/rustc_ast_lowering/src/expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 4a9b9f544b53..0e3b4c34caee 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -68,7 +68,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut ex = self.lower_expr_mut(ex); // Include parens in span, but only if it is a super-span. if e.span.contains(ex.span) { - ex.span = self.lower_span(e.span); + ex.span = self.lower_span(e.span.with_ctxt(ex.span.ctxt())); } // Merge attributes into the inner expression. if !e.attrs.is_empty() { From 3bdf45f7dbfe2b96770b3e3614c54425538cb5ec Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 10 Sep 2025 16:06:58 -0500 Subject: [PATCH 056/170] Mark range expr with desugaring --- compiler/rustc_ast_lowering/src/expr.rs | 16 ++++++++++------ compiler/rustc_hir/src/hir.rs | 6 ++++++ compiler/rustc_hir_typeck/src/expr.rs | 10 +++++++--- .../src/fn_ctxt/suggestions.rs | 10 ++++++---- compiler/rustc_lint/src/types/literal.rs | 8 ++++---- compiler/rustc_span/src/hygiene.rs | 3 +++ .../clippy_lints/src/loops/manual_memcpy.rs | 1 + .../src/loops/manual_slice_fill.rs | 1 + .../src/loops/needless_range_loop.rs | 9 +++++---- .../clippy_lints/src/manual_is_ascii_check.rs | 1 + .../src/methods/iter_next_slice.rs | 1 + .../map_with_unused_argument_over_ranges.rs | 4 ++-- .../clippy/clippy_lints/src/methods/mod.rs | 4 ++-- .../clippy/clippy_lints/src/no_effect.rs | 6 +++--- src/tools/clippy/clippy_lints/src/ranges.rs | 16 +++++++++------- .../src/unnecessary_struct_initialization.rs | 19 ++++++++++++++++--- src/tools/clippy/clippy_utils/src/higher.rs | 8 ++++++++ src/tools/clippy/clippy_utils/src/lib.rs | 2 +- src/tools/clippy/clippy_utils/src/source.rs | 12 ++++++++++-- .../tests/ui/range_plus_minus_one.stderr | 16 ++++++++-------- .../ui/reversed_empty_ranges_fixable.fixed | 4 ++-- .../ui/reversed_empty_ranges_fixable.stderr | 12 ++++++------ .../reversed_empty_ranges_loops_fixable.fixed | 2 +- ...reversed_empty_ranges_loops_fixable.stderr | 6 +++--- tests/ui/iterators/collect-into-array.rs | 1 + tests/ui/iterators/collect-into-array.stderr | 6 ++++-- tests/ui/iterators/collect-into-slice.rs | 2 ++ tests/ui/iterators/collect-into-slice.stderr | 12 ++++++++---- ...range-expr-root-of-constant-issue-40749.rs | 2 ++ 29 files changed, 133 insertions(+), 67 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 0e3b4c34caee..561887b01b13 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -62,6 +62,7 @@ impl<'hir> LoweringContext<'_, 'hir> { pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> { ensure_sufficient_stack(|| { + let mut span = self.lower_span(e.span); match &e.kind { // Parenthesis expression does not have a HirId and is handled specially. ExprKind::Paren(ex) => { @@ -287,7 +288,8 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_span(*brackets_span), ), ExprKind::Range(e1, e2, lims) => { - self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), *lims) + span = self.mark_span_with_reason(DesugaringKind::RangeExpr, span, None); + self.lower_expr_range(span, e1.as_deref(), e2.as_deref(), *lims) } ExprKind::Underscore => { let guar = self.dcx().emit_err(UnderscoreExprLhsAssign { span: e.span }); @@ -379,7 +381,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span), }; - hir::Expr { hir_id: expr_hir_id, kind, span: self.lower_span(e.span) } + hir::Expr { hir_id: expr_hir_id, kind, span } }) } @@ -1475,7 +1477,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> { let e1 = self.lower_expr_mut(e1); let e2 = self.lower_expr_mut(e2); - let fn_path = hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, self.lower_span(span)); + let fn_path = hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, span); let fn_expr = self.arena.alloc(self.expr(span, hir::ExprKind::Path(fn_path))); hir::ExprKind::Call(fn_expr, arena_vec![self; e1, e2]) } @@ -1552,14 +1554,16 @@ impl<'hir> LoweringContext<'_, 'hir> { ) })) .map(|(s, e)| { + let span = self.lower_span(e.span); + let span = self.mark_span_with_reason(DesugaringKind::RangeExpr, span, None); let expr = self.lower_expr(e); - let ident = Ident::new(s, self.lower_span(e.span)); - self.expr_field(ident, expr, e.span) + let ident = Ident::new(s, span); + self.expr_field(ident, expr, span) }), ); hir::ExprKind::Struct( - self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))), + self.arena.alloc(hir::QPath::LangItem(lang_item, span)), fields, hir::StructTailExpr::None, ) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 394747f81581..007c0e7e7e19 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2457,6 +2457,12 @@ impl Expr<'_> { } } + /// If this is a desugared range expression, + /// returns the span of the range without desugaring context. + pub fn range_span(&self) -> Option { + is_range_literal(self).then(|| self.span.parent_callsite().unwrap()) + } + /// Check if expression is an integer literal that can be used /// where `usize` is expected. pub fn is_size_lit(&self) -> bool { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 2605aa18b91e..0b1c725ba408 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2509,11 +2509,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .tcx .sess .source_map() - .span_extend_while_whitespace(range_start.span) + .span_extend_while_whitespace(range_start.expr.span) .shrink_to_hi() - .to(range_end.span); + .to(range_end.expr.span); - err.subdiagnostic(TypeMismatchFruTypo { expr_span: range_start.span, fru_span, expr }); + err.subdiagnostic(TypeMismatchFruTypo { + expr_span: range_start.expr.span, + fru_span, + expr, + }); // Suppress any range expr type mismatches self.dcx().try_steal_replace_and_emit_err( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 709a8b74d0b7..30f57975e45f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1666,7 +1666,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match expr.kind { ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => { err.span_suggestion_verbose( - start.span.shrink_to_hi().with_hi(end.span.lo()), + start.expr.span.shrink_to_hi().with_hi(end.expr.span.lo()), "remove the unnecessary `.` operator for a floating point literal", '.', Applicability::MaybeIncorrect, @@ -1674,8 +1674,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { true } ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => { + let range_span = expr.span.parent_callsite().unwrap(); err.span_suggestion_verbose( - expr.span.with_lo(start.span.hi()), + range_span.with_lo(start.expr.span.hi()), "remove the unnecessary `.` operator for a floating point literal", '.', Applicability::MaybeIncorrect, @@ -1683,8 +1684,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { true } ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => { + let range_span = expr.span.parent_callsite().unwrap(); err.span_suggestion_verbose( - expr.span.until(end.span), + range_span.until(end.expr.span), "remove the unnecessary `.` operator and add an integer part for a floating point literal", "0.", Applicability::MaybeIncorrect, @@ -2693,7 +2695,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bool, /* suggest `&` or `&mut` type annotation */ )> { let sess = self.sess(); - let sp = expr.span; + let sp = expr.range_span().unwrap_or(expr.span); let sm = sess.source_map(); // If the span is from an external macro, there's no suggestion we can make. diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index 1febbff4bbf8..d49f599407c2 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -1,4 +1,4 @@ -use hir::{ExprKind, Node, is_range_literal}; +use hir::{ExprKind, Node}; use rustc_abi::{Integer, Size}; use rustc_hir::{HirId, attrs}; use rustc_middle::ty::Ty; @@ -44,7 +44,7 @@ fn lint_overflowing_range_endpoint<'tcx>( let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { return false; }; - if !is_range_literal(struct_expr) { + let Some(range_span) = struct_expr.range_span() else { return false; }; let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { @@ -71,7 +71,7 @@ fn lint_overflowing_range_endpoint<'tcx>( return false; }; UseInclusiveRange::WithoutParen { - sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()), + sugg: range_span.shrink_to_lo().to(lit_span.shrink_to_hi()), start, literal: lit_val - 1, suffix, @@ -87,7 +87,7 @@ fn lint_overflowing_range_endpoint<'tcx>( cx.emit_span_lint( OVERFLOWING_LITERALS, - struct_expr.span, + range_span, RangeEndpointOutOfRange { ty, sub: sub_sugg }, ); diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 633cbc3a4ae2..51da538de94d 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1247,6 +1247,7 @@ pub enum DesugaringKind { /// rewriting it. source: bool, }, + RangeExpr, } impl DesugaringKind { @@ -1268,6 +1269,7 @@ impl DesugaringKind { DesugaringKind::FormatLiteral { source: false } => { "expression that expanded into a format string literal" } + DesugaringKind::RangeExpr => "range expression", } } @@ -1287,6 +1289,7 @@ impl DesugaringKind { DesugaringKind::Contract => value == "Contract", DesugaringKind::PatTyRange => value == "PatTyRange", DesugaringKind::FormatLiteral { .. } => value == "FormatLiteral", + DesugaringKind::RangeExpr => value == "RangeExpr", } } } diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs index a2da43c2ca24..7e48fe28011a 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs @@ -28,6 +28,7 @@ pub(super) fn check<'tcx>( start: Some(start), end: Some(end), limits, + span: _, }) = higher::Range::hir(arg) // the var must be a single name && let PatKind::Binding(_, canonical_id, _, _) = pat.kind diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs index 15c656cc7bc7..94cddb4a1506 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs @@ -31,6 +31,7 @@ pub(super) fn check<'tcx>( start: Some(start), end: Some(end), limits: RangeLimits::HalfOpen, + span: _, }) = higher::Range::hir(arg) && let ExprKind::Lit(Spanned { node: LitKind::Int(Pu128(0), _), diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index 11edb929d70b..7d890621d47c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -30,6 +30,7 @@ pub(super) fn check<'tcx>( start: Some(start), ref end, limits, + span, }) = higher::Range::hir(arg) // the var must be a single name && let PatKind::Binding(_, canonical_id, ident, _) = pat.kind @@ -149,7 +150,7 @@ pub(super) fn check<'tcx>( span_lint_and_then( cx, NEEDLESS_RANGE_LOOP, - arg.span, + span, format!("the loop variable `{}` is used to index `{indexed}`", ident.name), |diag| { diag.multipart_suggestion( @@ -157,7 +158,7 @@ pub(super) fn check<'tcx>( vec![ (pat.span, format!("({}, )", ident.name)), ( - arg.span, + span, format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), ), ], @@ -175,12 +176,12 @@ pub(super) fn check<'tcx>( span_lint_and_then( cx, NEEDLESS_RANGE_LOOP, - arg.span, + span, format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), |diag| { diag.multipart_suggestion( "consider using an iterator", - vec![(pat.span, "".to_string()), (arg.span, repl)], + vec![(pat.span, "".to_string()), (span, repl)], Applicability::HasPlaceholders, ); }, diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index 8c6abbeac448..456b48808090 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -110,6 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { start: Some(start), end: Some(end), limits: RangeLimits::Closed, + span: _, }) = higher::Range::hir(receiver) && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_)) { diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs index 01f35ff02d44..f37b15371c25 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs @@ -30,6 +30,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen, + span: _, }) = higher::Range::hir(index_expr) && let hir::ExprKind::Lit(start_lit) = &start_expr.kind && let ast::LitKind::Int(start_idx, _) = start_lit.node diff --git a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs index a2a522a60687..38ca15cb1334 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs @@ -63,7 +63,7 @@ pub(super) fn check( receiver: &Expr<'_>, arg: &Expr<'_>, msrv: Msrv, - method_call_span: Span, + method_name_span: Span, ) { let mut applicability = Applicability::MaybeIncorrect; if let Some(range) = higher::Range::hir(receiver) @@ -105,7 +105,7 @@ pub(super) fn check( // collate all our parts here and then remove those that are empty. let mut parts = vec![ ( - receiver.span.to(method_call_span), + ex.span.with_hi(method_name_span.hi()), format!("{exec_context}::iter::{method_to_use_name}"), ), new_span, diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index c9066be51c44..48fa5f7bdc6e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -4854,8 +4854,8 @@ impl_lint_pass!(Methods => [ /// come from expansion. pub fn method_call<'tcx>(recv: &'tcx Expr<'tcx>) -> Option<(Symbol, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind - && !args.iter().any(|e| e.span.from_expansion()) - && !receiver.span.from_expansion() + && !args.iter().any(|e| e.range_span().unwrap_or(e.span).from_expansion()) + && !receiver.range_span().unwrap_or(receiver.span).from_expansion() { Some((path.ident.name, receiver, args, path.ident.span, call_span)) } else { diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 701923cf6efc..fdb8e1b475c1 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -122,7 +122,7 @@ impl NoEffect { return true; } - if expr.span.from_expansion() { + if expr.range_span().unwrap_or(expr.span).from_expansion() { return false; } let expr = peel_blocks(expr); @@ -273,7 +273,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if let StmtKind::Semi(expr) = stmt.kind && !stmt.span.in_external_macro(cx.sess().source_map()) && let ctxt = stmt.span.ctxt() - && expr.span.ctxt() == ctxt + && expr.range_span().unwrap_or(expr.span).ctxt() == ctxt && let Some(reduced) = reduce_expression(cx, expr) && reduced.iter().all(|e| e.span.ctxt() == ctxt) { @@ -330,7 +330,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { } fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option>> { - if expr.span.from_expansion() { + if expr.range_span().unwrap_or(expr.span).from_expansion() { return None; } match expr.kind { diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index e4c91b7efd2b..ac2cc11d3020 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -501,17 +501,18 @@ fn check_range_switch<'tcx>( msg: &'static str, operator: &str, ) { - if expr.span.can_be_used_for_suggestions() - && let Some(higher::Range { + if let Some(range) = higher::Range::hir(expr) + && let higher::Range { start, end: Some(end), limits, - }) = higher::Range::hir(expr) + span, + } = range + && span.can_be_used_for_suggestions() && limits == kind && let Some(y) = predicate(cx, end) && can_switch_ranges(cx, expr, kind, cx.typeck_results().expr_ty(y)) { - let span = expr.span; span_lint_and_then(cx, lint, span, msg, |diag| { let mut app = Applicability::MachineApplicable; let start = start.map_or(String::new(), |x| { @@ -567,6 +568,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { start: Some(start), end: Some(end), limits, + span, }) = higher::Range::hir(expr) && let ty = cx.typeck_results().expr_ty(start) && let ty::Int(_) | ty::Uint(_) = ty.kind() @@ -582,7 +584,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { span_lint( cx, REVERSED_EMPTY_RANGES, - expr.span, + span, "this range is reversed and using it to index a slice will panic at run-time", ); } @@ -591,7 +593,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { span_lint_and_then( cx, REVERSED_EMPTY_RANGES, - expr.span, + span, "this range is empty so it will yield no values", |diag| { if ordering != Ordering::Equal { @@ -603,7 +605,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { }; diag.span_suggestion( - expr.span, + span, "consider using the following if you are attempting to iterate over this \ range in reverse", format!("({end_snippet}{dots}{start_snippet}).rev()"), diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs index 51596859e4c7..51397accefea 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs @@ -6,6 +6,7 @@ use clippy_utils::{get_parent_expr, is_mutable}; use rustc_hir::{Expr, ExprField, ExprKind, Path, QPath, StructTailExpr, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::{DesugaringKind, Ident}; declare_clippy_lint! { /// ### What it does @@ -52,7 +53,8 @@ impl LateLintPass<'_> for UnnecessaryStruct { return; }; - if expr.span.from_expansion() { + let expr_span = expr.range_span().unwrap_or(expr.span); + if expr_span.from_expansion() { // Prevent lint from hitting inside macro code return; } @@ -80,7 +82,7 @@ impl LateLintPass<'_> for UnnecessaryStruct { span_lint_and_sugg( cx, UNNECESSARY_STRUCT_INITIALIZATION, - expr.span, + expr_span, "unnecessary struct building", "replace with", snippet(cx, sugg, "..").into_owned(), @@ -130,7 +132,7 @@ fn same_path_in_all_fields<'tcx>( // expression type matches && ty == cx.typeck_results().expr_ty(src_expr) // field name matches - && f.ident == ident + && ident_without_range_desugaring(f.ident) == ident // assigned from a path expression && let ExprKind::Path(QPath::Resolved(None, src_path)) = src_expr.kind { @@ -197,3 +199,14 @@ fn path_matches_base(path: &Path<'_>, base: &Expr<'_>) -> bool { }; path.res == base_path.res } + +fn ident_without_range_desugaring(ident: Ident) -> Ident { + if ident.span.desugaring_kind() == Some(DesugaringKind::RangeExpr) { + Ident { + span: ident.span.parent_callsite().unwrap(), + ..ident + } + } else { + ident + } +} diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 3383e66fe368..9d895c9aa649 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -209,12 +209,14 @@ pub struct Range<'a> { pub end: Option<&'a Expr<'a>>, /// Whether the interval is open or closed. pub limits: ast::RangeLimits, + pub span: Span } impl<'a> Range<'a> { /// Higher a `hir` range to something similar to `ast::ExprKind::Range`. #[expect(clippy::similar_names)] pub fn hir(expr: &'a Expr<'_>) -> Option> { + let span = expr.range_span()?; match expr.kind { ExprKind::Call(path, [arg1, arg2]) if matches!( @@ -226,6 +228,7 @@ impl<'a> Range<'a> { start: Some(arg1), end: Some(arg2), limits: ast::RangeLimits::Closed, + span, }) }, ExprKind::Struct(path, fields, StructTailExpr::None) => match (path, fields) { @@ -233,12 +236,14 @@ impl<'a> Range<'a> { start: None, end: None, limits: ast::RangeLimits::HalfOpen, + span, }), (QPath::LangItem(hir::LangItem::RangeFrom, ..), [field]) if field.ident.name == sym::start => { Some(Range { start: Some(field.expr), end: None, limits: ast::RangeLimits::HalfOpen, + span, }) }, (QPath::LangItem(hir::LangItem::Range, ..), [field1, field2]) => { @@ -251,6 +256,7 @@ impl<'a> Range<'a> { start: Some(start), end: Some(end), limits: ast::RangeLimits::HalfOpen, + span, }) }, (QPath::LangItem(hir::LangItem::RangeToInclusive, ..), [field]) if field.ident.name == sym::end => { @@ -258,12 +264,14 @@ impl<'a> Range<'a> { start: None, end: Some(field.expr), limits: ast::RangeLimits::Closed, + span, }) }, (QPath::LangItem(hir::LangItem::RangeTo, ..), [field]) if field.ident.name == sym::end => Some(Range { start: None, end: Some(field.expr), limits: ast::RangeLimits::HalfOpen, + span, }), _ => None, }, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index c8943523e179..6ee991eae137 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1282,7 +1282,7 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { /// If the given `Expr` is not some kind of range, the function returns `false`. pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - if let Some(Range { start, end, limits }) = Range::hir(expr) { + if let Some(Range { start, end, limits, .. }) = Range::hir(expr) { let start_is_none_or_min = start.is_none_or(|start| { if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 83d7f6bc565d..954a71f6c320 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -13,8 +13,8 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::source_map::{SourceMap, original_sp}; use rustc_span::{ - BytePos, DUMMY_SP, FileNameDisplayPreference, Pos, RelativeBytePos, SourceFile, SourceFileAndLine, Span, SpanData, - SyntaxContext, hygiene, + BytePos, DesugaringKind, DUMMY_SP, FileNameDisplayPreference, Pos, RelativeBytePos, SourceFile, SourceFileAndLine, + Span, SpanData, SyntaxContext, hygiene, }; use std::borrow::Cow; use std::fmt; @@ -670,6 +670,14 @@ fn snippet_with_context_sess<'a>( default: &'a str, applicability: &mut Applicability, ) -> (Cow<'a, str>, bool) { + // If it is just range desugaring, use the desugaring span since it may include parenthesis. + if span.desugaring_kind() == Some(DesugaringKind::RangeExpr) && span.parent_callsite().unwrap().ctxt() == outer { + return ( + snippet_with_applicability_sess(sess, span, default, applicability), + false, + ) + } + let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else( || { // The span is from a macro argument, and the outer context is the macro using the argument diff --git a/src/tools/clippy/tests/ui/range_plus_minus_one.stderr b/src/tools/clippy/tests/ui/range_plus_minus_one.stderr index a419d935bd62..60abe50efa10 100644 --- a/src/tools/clippy/tests/ui/range_plus_minus_one.stderr +++ b/src/tools/clippy/tests/ui/range_plus_minus_one.stderr @@ -32,22 +32,22 @@ LL | for _ in 1..ONE + ONE {} | ^^^^^^^^^^^^ help: use: `1..=ONE` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:70:5 + --> tests/ui/range_plus_minus_one.rs:70:6 | LL | (1..10 + 1).for_each(|_| {}); - | ^^^^^^^^^^^ help: use: `(1..=10)` + | ^^^^^^^^^ help: use: `1..=10` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:75:5 + --> tests/ui/range_plus_minus_one.rs:75:6 | LL | (1..10 + 1).into_iter().for_each(|_| {}); - | ^^^^^^^^^^^ help: use: `(1..=10)` + | ^^^^^^^^^ help: use: `1..=10` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:80:17 + --> tests/ui/range_plus_minus_one.rs:80:18 | LL | let _ = (1..10 + 1).start_bound(); - | ^^^^^^^^^^^ help: use: `(1..=10)` + | ^^^^^^^^^ help: use: `1..=10` error: an inclusive range would be more readable --> tests/ui/range_plus_minus_one.rs:86:16 @@ -80,10 +80,10 @@ LL | a[0..2 + 1][0] = 1; | ^^^^^^^^ help: use: `0..=2` error: an exclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:180:5 + --> tests/ui/range_plus_minus_one.rs:180:6 | LL | (1..=n - 1).sum() - | ^^^^^^^^^^^ help: use: `(1..n)` + | ^^^^^^^^^ help: use: `1..n` | = note: `-D clippy::range-minus-one` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::range_minus_one)]` diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed index ba5059bbaa37..6c866aceed4f 100644 --- a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed @@ -6,9 +6,9 @@ const ANSWER: i32 = 42; fn main() { // These should be linted: - (21..=42).rev().for_each(|x| println!("{}", x)); + ((21..=42).rev()).for_each(|x| println!("{}", x)); //~^ reversed_empty_ranges - let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); + let _ = ((21..ANSWER).rev()).filter(|x| x % 2 == 0).take(10).collect::>(); //~^ reversed_empty_ranges for _ in (-42..=-21).rev() {} diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr index 3fadc4c169f1..f3d3ac298cec 100644 --- a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr @@ -1,27 +1,27 @@ error: this range is empty so it will yield no values - --> tests/ui/reversed_empty_ranges_fixable.rs:9:5 + --> tests/ui/reversed_empty_ranges_fixable.rs:9:6 | LL | (42..=21).for_each(|x| println!("{}", x)); - | ^^^^^^^^^ + | ^^^^^^^ | = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::reversed_empty_ranges)]` help: consider using the following if you are attempting to iterate over this range in reverse | LL - (42..=21).for_each(|x| println!("{}", x)); -LL + (21..=42).rev().for_each(|x| println!("{}", x)); +LL + ((21..=42).rev()).for_each(|x| println!("{}", x)); | error: this range is empty so it will yield no values - --> tests/ui/reversed_empty_ranges_fixable.rs:11:13 + --> tests/ui/reversed_empty_ranges_fixable.rs:11:14 | LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); - | ^^^^^^^^^^^^ + | ^^^^^^^^^^ | help: consider using the following if you are attempting to iterate over this range in reverse | LL - let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); -LL + let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); +LL + let _ = ((21..ANSWER).rev()).filter(|x| x % 2 == 0).take(10).collect::>(); | error: this range is empty so it will yield no values diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed index 55080da8a137..9fb46c9533cd 100644 --- a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed @@ -34,7 +34,7 @@ fn main() { println!("{}", i); } - for i in (0..10).rev().map(|x| x * 2) { + for i in ((0..10).rev()).map(|x| x * 2) { //~^ reversed_empty_ranges println!("{}", i); } diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr index eadd9d3675e1..775d109296a1 100644 --- a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr @@ -37,15 +37,15 @@ LL + for i in (0..MAX_LEN).rev() { | error: this range is empty so it will yield no values - --> tests/ui/reversed_empty_ranges_loops_fixable.rs:37:14 + --> tests/ui/reversed_empty_ranges_loops_fixable.rs:37:15 | LL | for i in (10..0).map(|x| x * 2) { - | ^^^^^^^ + | ^^^^^ | help: consider using the following if you are attempting to iterate over this range in reverse | LL - for i in (10..0).map(|x| x * 2) { -LL + for i in (0..10).rev().map(|x| x * 2) { +LL + for i in ((0..10).rev()).map(|x| x * 2) { | error: this range is empty so it will yield no values diff --git a/tests/ui/iterators/collect-into-array.rs b/tests/ui/iterators/collect-into-array.rs index 99d0d9bd7355..b0d6d48b6b44 100644 --- a/tests/ui/iterators/collect-into-array.rs +++ b/tests/ui/iterators/collect-into-array.rs @@ -1,6 +1,7 @@ fn main() { let whatever: [u32; 10] = (0..10).collect(); //~^ ERROR an array of type `[u32; 10]` cannot be built directly from an iterator + //~| NOTE required by a bound introduced by this call //~| NOTE try collecting into a `Vec<{integer}>`, then using `.try_into()` //~| NOTE required by a bound in `collect` } diff --git a/tests/ui/iterators/collect-into-array.stderr b/tests/ui/iterators/collect-into-array.stderr index 38e5de803ccf..6b6e8552ed8d 100644 --- a/tests/ui/iterators/collect-into-array.stderr +++ b/tests/ui/iterators/collect-into-array.stderr @@ -1,8 +1,10 @@ error[E0277]: an array of type `[u32; 10]` cannot be built directly from an iterator - --> $DIR/collect-into-array.rs:2:39 + --> $DIR/collect-into-array.rs:2:32 | LL | let whatever: [u32; 10] = (0..10).collect(); - | ^^^^^^^ try collecting into a `Vec<{integer}>`, then using `.try_into()` + | ^^^^^ ------- required by a bound introduced by this call + | | + | try collecting into a `Vec<{integer}>`, then using `.try_into()` | = help: the trait `FromIterator<{integer}>` is not implemented for `[u32; 10]` note: required by a bound in `collect` diff --git a/tests/ui/iterators/collect-into-slice.rs b/tests/ui/iterators/collect-into-slice.rs index 120e56a6549e..c90cfa4e90d3 100644 --- a/tests/ui/iterators/collect-into-slice.rs +++ b/tests/ui/iterators/collect-into-slice.rs @@ -5,6 +5,7 @@ fn process_slice(data: &[i32]) { fn main() { let some_generated_vec = (0..10).collect(); //~^ ERROR the size for values of type `[i32]` cannot be known at compilation time + //~| NOTE required by a bound introduced by this call //~| ERROR the size for values of type `[i32]` cannot be known at compilation time //~| ERROR a slice of type `[i32]` cannot be built since `[i32]` has no definite size //~| NOTE try explicitly collecting into a `Vec<{integer}>` @@ -17,6 +18,7 @@ fn main() { let some_generated_vec = (0..10).collect(); //~^ ERROR a slice of type `&[i32]` cannot be built since we need to store the elements somewhere + //~| NOTE required by a bound introduced by this call //~| NOTE try explicitly collecting into a `Vec<{integer}>` //~| NOTE required by a bound in `collect` process_slice(some_generated_vec); diff --git a/tests/ui/iterators/collect-into-slice.stderr b/tests/ui/iterators/collect-into-slice.stderr index e5729a2badc7..b5444a71cc17 100644 --- a/tests/ui/iterators/collect-into-slice.stderr +++ b/tests/ui/iterators/collect-into-slice.stderr @@ -1,8 +1,10 @@ error[E0277]: a slice of type `[i32]` cannot be built since `[i32]` has no definite size - --> $DIR/collect-into-slice.rs:6:38 + --> $DIR/collect-into-slice.rs:6:31 | LL | let some_generated_vec = (0..10).collect(); - | ^^^^^^^ try explicitly collecting into a `Vec<{integer}>` + | ^^^^^ ------- required by a bound introduced by this call + | | + | try explicitly collecting into a `Vec<{integer}>` | = help: the trait `FromIterator<{integer}>` is not implemented for `[i32]` note: required by a bound in `collect` @@ -28,10 +30,12 @@ note: required by an implicit `Sized` bound in `collect` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0277]: a slice of type `&[i32]` cannot be built since we need to store the elements somewhere - --> $DIR/collect-into-slice.rs:18:38 + --> $DIR/collect-into-slice.rs:19:31 | LL | let some_generated_vec = (0..10).collect(); - | ^^^^^^^ try explicitly collecting into a `Vec<{integer}>` + | ^^^^^ ------- required by a bound introduced by this call + | | + | try explicitly collecting into a `Vec<{integer}>` | = help: the trait `FromIterator<{integer}>` is not implemented for `&[i32]` note: required by a bound in `collect` diff --git a/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs b/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs index 7871612dc8b7..bec92448d1c8 100644 --- a/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs +++ b/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs @@ -4,4 +4,6 @@ fn main() { //~| NOTE expected type `usize` //~| NOTE found struct `RangeTo<{integer}>` //~| NOTE expected `usize`, found `RangeTo<{integer}> + //~| NOTE in this expansion of desugaring of range expression + //~| NOTE in this expansion of desugaring of range expression } From 6d61dadfab94a98827f960a7de5a96a88542dd6d Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 1 Sep 2025 12:42:10 -0500 Subject: [PATCH 057/170] Reuse expr_needs_parens --- .../src/error_reporting/traits/suggestions.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 136a61598d50..4ecc48d6cc40 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -20,7 +20,7 @@ use rustc_hir::intravisit::{Visitor, VisitorExt}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ self as hir, AmbigArg, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node, - expr_needs_parens, is_range_literal, + expr_needs_parens, }; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_middle::middle::privacy::Level; @@ -668,12 +668,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } let derefs = "*".repeat(steps); - let needs_parens = steps > 0 - && match expr.kind { - hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, - _ if is_range_literal(expr) => true, - _ => false, - }; + let needs_parens = steps > 0 && expr_needs_parens(expr); let mut suggestion = if needs_parens { vec![ ( From ba61c29ce1c45bf772e9a3bb6a366ecd14b18e58 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Wed, 27 Aug 2025 15:28:32 -0700 Subject: [PATCH 058/170] Allow env vars set in cargo.extraEnv to be resolved by the env! macro --- .../project-model/src/cargo_workspace.rs | 11 +-- .../crates/project-model/src/env.rs | 78 +++++++++++++++---- .../crates/project-model/src/workspace.rs | 41 ++++++---- 3 files changed, 91 insertions(+), 39 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index 04fb2275893c..76ba01f3a263 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -49,8 +49,9 @@ pub struct CargoWorkspace { is_virtual_workspace: bool, /// Whether this workspace represents the sysroot workspace. is_sysroot: bool, - /// Environment variables set in the `.cargo/config` file. - config_env: Env, + /// Environment variables set in the `.cargo/config` file and the extraEnv + /// configuration option. + env: Env, requires_rustc_private: bool, } @@ -325,7 +326,7 @@ impl CargoWorkspace { pub fn new( mut meta: cargo_metadata::Metadata, ws_manifest_path: ManifestPath, - cargo_config_env: Env, + cargo_env: Env, is_sysroot: bool, ) -> CargoWorkspace { let mut pkg_by_id = FxHashMap::default(); @@ -498,7 +499,7 @@ impl CargoWorkspace { is_virtual_workspace, requires_rustc_private, is_sysroot, - config_env: cargo_config_env, + env: cargo_env, } } @@ -589,7 +590,7 @@ impl CargoWorkspace { } pub fn env(&self) -> &Env { - &self.config_env + &self.env } pub fn is_sysroot(&self) -> bool { diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs index d281492fc98c..ae0458af7aa7 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/env.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs @@ -1,6 +1,7 @@ //! Cargo-like environment variables injection. use base_db::Env; use paths::Utf8Path; +use rustc_hash::FxHashMap; use toolchain::Tool; use crate::{ManifestPath, PackageData, TargetKind, cargo_config_file::CargoConfigFile}; @@ -60,8 +61,14 @@ pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: Targe env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_")); } -pub(crate) fn cargo_config_env(manifest: &ManifestPath, config: &Option) -> Env { +pub(crate) fn cargo_config_env( + manifest: &ManifestPath, + config: &Option, + extra_env: &FxHashMap>, +) -> Env { let mut env = Env::default(); + env.extend(extra_env.iter().filter_map(|(k, v)| v.as_ref().map(|v| (k.clone(), v.clone())))); + let Some(serde_json::Value::Object(env_json)) = config.as_ref().and_then(|c| c.get("env")) else { return env; @@ -72,22 +79,34 @@ pub(crate) fn cargo_config_env(manifest: &ManifestPath, config: &Option>::as_ref(manifest.parent()); for (key, entry) in env_json { - let serde_json::Value::Object(entry) = entry else { - continue; - }; - let Some(value) = entry.get("value").and_then(|v| v.as_str()) else { - continue; + let value = match entry { + serde_json::Value::String(s) => s.clone(), + serde_json::Value::Object(entry) => { + // Each entry MUST have a `value` key. + let Some(value) = entry.get("value").and_then(|v| v.as_str()) else { + continue; + }; + // If the entry already exists in the environment AND the `force` key is not set to + // true, then don't overwrite the value. + if extra_env.get(key).is_some_and(Option::is_some) + && !entry.get("force").and_then(|v| v.as_bool()).unwrap_or(false) + { + continue; + } + + if entry + .get("relative") + .and_then(|v| v.as_bool()) + .is_some_and(std::convert::identity) + { + base.join(value).to_string() + } else { + value.to_owned() + } + } + _ => continue, }; - let value = if entry - .get("relative") - .and_then(|v| v.as_bool()) - .is_some_and(std::convert::identity) - { - base.join(value).to_string() - } else { - value.to_owned() - }; env.insert(key, value); } @@ -113,7 +132,19 @@ fn parse_output_cargo_config_env_works() { }, "TEST": { "value": "test" - } + }, + "FORCED": { + "value": "test", + "force": true + }, + "UNFORCED": { + "value": "test", + "force": false + }, + "OVERWRITTEN": { + "value": "test" + }, + "NOT_AN_OBJECT": "value" } } "#; @@ -121,9 +152,22 @@ fn parse_output_cargo_config_env_works() { let cwd = paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap(); let manifest = paths::AbsPathBuf::assert(cwd.join("Cargo.toml")); let manifest = ManifestPath::try_from(manifest).unwrap(); - let env = cargo_config_env(&manifest, &Some(config)); + let extra_env = [ + ("FORCED", Some("ignored")), + ("UNFORCED", Some("newvalue")), + ("OVERWRITTEN", Some("newvalue")), + ("TEST", None), + ] + .iter() + .map(|(k, v)| (k.to_string(), v.map(ToString::to_string))) + .collect(); + let env = cargo_config_env(&manifest, &Some(config), &extra_env); assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str())); assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str())); assert_eq!(env.get("INVALID").as_deref(), Some("../relative")); assert_eq!(env.get("TEST").as_deref(), Some("test")); + assert_eq!(env.get("FORCED").as_deref(), Some("test")); + assert_eq!(env.get("UNFORCED").as_deref(), Some("newvalue")); + assert_eq!(env.get("OVERWRITTEN").as_deref(), Some("newvalue")); + assert_eq!(env.get("NOT_AN_OBJECT").as_deref(), Some("value")); } diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 22b84791aee9..b88db419574d 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -387,36 +387,36 @@ impl ProjectWorkspace { progress, ) }); - let cargo_config_extra_env = - s.spawn(move || cargo_config_env(cargo_toml, &config_file)); + let cargo_env = + s.spawn(move || cargo_config_env(cargo_toml, &config_file, &config.extra_env)); thread::Result::Ok(( rustc_cfg.join()?, target_data.join()?, rustc_dir.join()?, loaded_sysroot.join()?, cargo_metadata.join()?, - cargo_config_extra_env.join()?, + cargo_env.join()?, )) }); - let ( - rustc_cfg, - data_layout, - mut rustc, - loaded_sysroot, - cargo_metadata, - cargo_config_extra_env, - ) = match join { - Ok(it) => it, - Err(e) => std::panic::resume_unwind(e), - }; + let (rustc_cfg, data_layout, mut rustc, loaded_sysroot, cargo_metadata, mut cargo_env) = + match join { + Ok(it) => it, + Err(e) => std::panic::resume_unwind(e), + }; + + for (key, value) in config.extra_env.iter() { + if let Some(value) = value { + cargo_env.insert(key.clone(), value.clone()); + } + } let (meta, error) = cargo_metadata.with_context(|| { format!( "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}", ) })?; - let cargo = CargoWorkspace::new(meta, cargo_toml.clone(), cargo_config_extra_env, false); + let cargo = CargoWorkspace::new(meta, cargo_toml.clone(), cargo_env, false); if let Some(loaded_sysroot) = loaded_sysroot { tracing::info!(src_root = ?sysroot.rust_lib_src_root(), root = %loaded_sysroot, "Loaded sysroot"); sysroot.set_workspace(loaded_sysroot); @@ -586,7 +586,8 @@ impl ProjectWorkspace { .unwrap_or_else(|| dir.join("target").into()); let cargo_script = fetch_metadata.exec(&target_dir, false, &|_| ()).ok().map(|(ws, error)| { - let cargo_config_extra_env = cargo_config_env(detached_file, &config_file); + let cargo_config_extra_env = + cargo_config_env(detached_file, &config_file, &config.extra_env); ( CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false), WorkspaceBuildScripts::default(), @@ -1089,7 +1090,13 @@ fn project_json_to_crate_graph( }, file_id, )| { - let env = env.clone().into_iter().collect(); + let mut env = env.clone().into_iter().collect::(); + // Override existing env vars with those from `extra_env` + env.extend( + extra_env + .iter() + .filter_map(|(k, v)| v.as_ref().map(|v| (k.clone(), v.clone()))), + ); let target_cfgs = match target.as_deref() { Some(target) => cfg_cache.entry(target).or_insert_with(|| { From d76463c8573964ffcdff9f47f98bfe0f8254e62b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 21 Oct 2025 22:58:31 +0300 Subject: [PATCH 059/170] Clear next-solver cache before reporting memory usage in analysis-stats The cache shouldn't be included, as it is mostly temporary (per-revision). --- .../crates/hir-ty/src/next_solver/interner.rs | 9 +++++++++ src/tools/rust-analyzer/crates/hir/src/lib.rs | 1 + .../crates/rust-analyzer/src/cli/analysis_stats.rs | 2 ++ 3 files changed, 12 insertions(+) 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 42f1d926d7db..ce8b76837a3c 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 @@ -2,6 +2,7 @@ use std::{fmt, ops::ControlFlow}; +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; @@ -2239,4 +2240,12 @@ mod tls_cache { }) }) } + + /// Clears the thread-local trait solver cache. + /// + /// Should be called before getting memory usage estimations, as the solver cache + /// is per-revision and usually should be excluded from estimations. + pub fn clear_tls_solver_cache() { + GLOBAL_CACHE.with_borrow_mut(|handle| *handle = None); + } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 48eafb0bd4c6..941890312317 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -171,6 +171,7 @@ pub use { method_resolution::TyFingerprint, mir::{MirEvalError, MirLowerError}, next_solver::abi::Safety, + next_solver::clear_tls_solver_cache, }, intern::{Symbol, sym}, }; 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 717bd230a21e..de24bc09ff0f 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 @@ -345,6 +345,8 @@ impl flags::AnalysisStats { self.run_term_search(&workspace, db, &vfs, &file_ids, verbosity); } + hir::clear_tls_solver_cache(); + let db = host.raw_database_mut(); db.trigger_lru_eviction(); From 385bd28558f87d06a5ae46f8698786e075e4fd21 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 22 Oct 2025 05:17:27 +0300 Subject: [PATCH 060/170] Fix "cannot insert `true` or `false` to cfg" error in fixtures --- src/tools/rust-analyzer/crates/cfg/src/lib.rs | 7 +++++++ src/tools/rust-analyzer/crates/test-fixture/src/lib.rs | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs index 906106ca5db0..b1ec4c273a85 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs @@ -115,6 +115,13 @@ impl CfgOptions { pub fn shrink_to_fit(&mut self) { self.enabled.shrink_to_fit(); } + + pub fn append(&mut self, other: CfgOptions) { + // Do not call `insert_any_atom()`, as it'll check for `true` and `false`, but this is not + // needed since we already checked for that when constructing `other`. Furthermore, this + // will always err, as `other` inevitably contains `true` (just as we do). + self.enabled.extend(other.enabled); + } } impl Extend for CfgOptions { diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index aefe81f83e29..a718b96a8252 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -252,7 +252,7 @@ impl ChangeFixture { assert!(default_crate_root.is_none()); default_crate_root = Some(file_id); default_edition = meta.edition; - default_cfg.extend(meta.cfg.into_iter()); + default_cfg.append(meta.cfg); default_env.extend_from_other(&meta.env); } From 911edbfe81978d99fd16df674821e4aff9b829a0 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 15 Sep 2025 17:42:55 +0800 Subject: [PATCH 061/170] Add heuristic sensing `is_in_block` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example --- ```rust fn foo() -> [i32; 2] { l$0 [0, n] } ``` **Before this PR**: ```text loop~ line!(…)~ macro_rules! line ``` **After this PR**: ```text let~ loop~ letm~ line!(…)~ macro_rules! line ``` --- .../ide-completion/src/context/analysis.rs | 26 +++ .../ide-completion/src/tests/expression.rs | 167 ++++++++++++++++++ 2 files changed, 193 insertions(+) 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 873eceff5f5f..f0a03dedfe88 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 @@ -1890,11 +1890,37 @@ fn is_in_breakable(node: &SyntaxNode) -> Option<(BreakableKind, SyntaxNode)> { } fn is_in_block(node: &SyntaxNode) -> bool { + if has_in_newline_expr_first(node) { + return true; + }; node.parent() .map(|node| ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind())) .unwrap_or(false) } +/// Similar to `has_parens`, heuristic sensing incomplete statement before ambigiguous `Expr` +/// +/// Heuristic: +/// +/// If the `PathExpr` is left part of the `Expr` and there is a newline after the `PathExpr`, +/// it is considered that the `PathExpr` is not part of the `Expr`. +fn has_in_newline_expr_first(node: &SyntaxNode) -> bool { + if ast::PathExpr::can_cast(node.kind()) + && let Some(NodeOrToken::Token(next)) = node.next_sibling_or_token() + && next.kind() == SyntaxKind::WHITESPACE + && next.text().contains('\n') + && let Some(stmt_like) = node + .ancestors() + .take_while(|it| it.text_range().start() == node.text_range().start()) + .filter_map(Either::::cast) + .last() + { + stmt_like.syntax().parent().and_then(ast::StmtList::cast).is_some() + } else { + false + } +} + fn next_non_trivia_token(e: impl Into) -> Option { let mut token = match e.into() { SyntaxElement::Node(n) => n.last_token()?, 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 5363a68af723..f75fa7943ba6 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 @@ -2946,6 +2946,173 @@ fn let_in_let_chain() { check_edit("let", r#"fn f() { if true && $0 {} }"#, r#"fn f() { if true && let $1 = $0 {} }"#); } +#[test] +fn let_in_previous_line_of_ambiguous_expr() { + check_edit( + "let", + r#" + fn f() { + $0 + (1, 2).foo(); + }"#, + r#" + fn f() { + let $1 = $0; + (1, 2).foo(); + }"#, + ); + + check_edit( + "let", + r#" + fn f() { + $0 + (1, 2) + }"#, + r#" + fn f() { + let $1 = $0; + (1, 2) + }"#, + ); + + check_edit( + "let", + r#" + fn f() -> i32 { + $0 + -2 + }"#, + r#" + fn f() -> i32 { + let $1 = $0; + -2 + }"#, + ); + + check_edit( + "let", + r#" + fn f() -> [i32; 2] { + $0 + [1, 2] + }"#, + r#" + fn f() -> [i32; 2] { + let $1 = $0; + [1, 2] + }"#, + ); + + check_edit( + "let", + r#" + fn f() -> [u8; 2] { + $0 + *b"01" + }"#, + r#" + fn f() -> [u8; 2] { + let $1 = $0; + *b"01" + }"#, + ); + + check( + r#" + fn foo() { + $0 + *b"01" + }"#, + expect![[r#" + fn foo() fn() + 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 + "#]], + ); + + check( + r#" + fn foo() { + match $0 {} + }"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw const + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw return + kw self:: + kw true + kw unsafe + kw while + kw while let + "#]], + ); + + check( + r#" + fn foo() { + $0 *b"01" + }"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw const + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw return + kw self:: + kw true + kw unsafe + kw while + kw while let + "#]], + ); +} + #[test] fn private_inherent_and_public_trait() { check( From 6fe555360e25f6e65f979c8c8d56dde47f1c8838 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 11 Oct 2025 13:49:51 +0800 Subject: [PATCH 062/170] Add shorthand field completion for record-expr Example --- ```rust struct Foo { bar: bool, n: i32 } fn baz() { let bar = true; let foo: Foo = Fo$0; } ``` **Before this PR**: ```rust struct Foo { bar: bool, n: i32 } fn baz() { let bar = true; let foo: Foo = Foo { bar: ${1:()}, n: ${2:()} }$0; } ``` **After this PR**: ```rust struct Foo { bar: bool, n: i32 } fn baz() { let bar = true; let foo: Foo = Foo { bar$1, n: ${2:()} }$0; } ``` --- .../ide-completion/src/completions/record.rs | 27 +++++++++++++++++++ .../ide-completion/src/render/variant.rs | 21 ++++++++++----- 2 files changed, 42 insertions(+), 6 deletions(-) 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 28b324d61afa..bfa567009c01 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 @@ -178,6 +178,33 @@ fn baz() { ) } + #[test] + fn literal_struct_completion_shorthand() { + check_edit( + "FooDesc{}", + r#" +struct FooDesc { pub bar: bool, n: i32 } + +fn create_foo(foo_desc: &FooDesc) -> () { () } + +fn baz() { + let bar = true; + let foo = create_foo(&$0); +} + "#, + r#" +struct FooDesc { pub bar: bool, n: i32 } + +fn create_foo(foo_desc: &FooDesc) -> () { () } + +fn baz() { + let bar = true; + let foo = create_foo(&FooDesc { bar$1, n: ${2:()} }$0); +} + "#, + ) + } + #[test] fn enum_variant_no_snippets() { let conf = CompletionConfig { snippet_cap: SnippetCap::new(false), ..TEST_CONFIG }; 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 42324b4290a7..37d0fa18c497 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 @@ -26,14 +26,23 @@ pub(crate) fn render_record_lit( return RenderedLiteral { literal: path.to_owned(), detail: path.to_owned() }; } let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| { + let mut fmt_field = |fill, tab| { + let field_name = field.name(ctx.db); + + if let Some(local) = ctx.locals.get(&field_name) + && local + .ty(ctx.db) + .could_unify_with_deeply(ctx.db, &field.ty(ctx.db).to_type(ctx.db)) + { + f(&format_args!("{}{tab}", field_name.display(ctx.db, ctx.edition))) + } else { + f(&format_args!("{}: {fill}", field_name.display(ctx.db, ctx.edition))) + } + }; if snippet_cap.is_some() { - f(&format_args!( - "{}: ${{{}:()}}", - field.name(ctx.db).display(ctx.db, ctx.edition), - idx + 1 - )) + fmt_field(format_args!("${{{}:()}}", idx + 1), format_args!("${}", idx + 1)) } else { - f(&format_args!("{}: ()", field.name(ctx.db).display(ctx.db, ctx.edition))) + fmt_field(format_args!("()"), format_args!("")) } }); From 70e3d8ca5ca03a0031e4e2f14debd60d5bc8bab9 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 30 Aug 2025 08:47:01 +0800 Subject: [PATCH 063/170] Add type keywords completions Example --- ``` kw dyn kw fn kw for kw impl ``` --- .../crates/ide-completion/src/completions.rs | 7 + .../ide-completion/src/completions/type.rs | 1 + .../crates/ide-completion/src/tests/item.rs | 8 + .../ide-completion/src/tests/predicate.rs | 12 ++ .../ide-completion/src/tests/special.rs | 12 ++ .../ide-completion/src/tests/type_pos.rs | 175 ++++++++++++++++++ 6 files changed, 215 insertions(+) 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 ed58e862d437..abae3cb36802 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -111,6 +111,13 @@ impl Completions { } } + pub(crate) fn add_type_keywords(&mut self, ctx: &CompletionContext<'_>) { + self.add_keyword_snippet(ctx, "fn", "fn($1)"); + self.add_keyword_snippet(ctx, "dyn", "dyn $0"); + self.add_keyword_snippet(ctx, "impl", "impl $0"); + self.add_keyword_snippet(ctx, "for", "for<$1>"); + } + pub(crate) fn add_super_keyword( &mut self, ctx: &CompletionContext<'_>, 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 3112462cda4e..3465b73321e9 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 @@ -205,6 +205,7 @@ pub(crate) fn complete_type_path( }; acc.add_nameref_keywords_with_colon(ctx); + acc.add_type_keywords(ctx); ctx.process_all_names(&mut |name, def, doc_aliases| { if scope_def_applicable(def) { acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases); 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 ed87b339fedf..61a9da8c278a 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 @@ -23,6 +23,10 @@ impl Tra$0 un Union Union bt u32 u32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ) @@ -45,6 +49,10 @@ impl Trait for Str$0 un Union Union bt u32 u32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs index 65036f6a2240..682b8904e550 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs @@ -22,6 +22,10 @@ struct Foo<'lt, T, const C: usize> where $0 {} un Union Union bt u32 u32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -95,6 +99,10 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {} un Union Union bt u32 u32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -120,6 +128,10 @@ impl Record { un Union Union bt u32 u32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index c438ca788062..59a0c144c893 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -1492,6 +1492,10 @@ fn foo(_: a_$0) { } expect![[r#" bt u32 u32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -1506,6 +1510,10 @@ fn foo() { tp T bt u32 u32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -1531,6 +1539,10 @@ fn foo() {} expect![[r#" bt u32 u32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); 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 125e11e9e358..3bbba18c2b9f 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 @@ -25,6 +25,10 @@ struct Foo<'lt, T, const C: usize> { un Union Union bt u32 u32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ) @@ -50,6 +54,10 @@ struct Foo<'lt, T, const C: usize>(f$0); un Union Union bt u32 u32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw pub kw pub(crate) kw pub(super) @@ -76,6 +84,37 @@ fn x<'lt, T, const C: usize>() -> $0 un Union Union bt u32 u32 kw crate:: + kw dyn + kw fn + kw for + kw impl + kw self:: + "#]], + ); +} + +#[test] +fn fn_return_type_after_reference() { + check_with_base_items( + r#" +fn x<'lt, T, const C: usize>(_: &()) -> &$0 +"#, + expect![[r#" + en Enum Enum + ma makro!(…) macro_rules! makro + md module + st Record Record + st Tuple Tuple + st Unit Unit + tt Trait + tp T + un Union Union + bt u32 u32 + kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -106,6 +145,10 @@ fn foo() -> B$0 { bt u32 u32 it () kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ) @@ -131,6 +174,10 @@ const FOO: $0 = Foo(2); bt u32 u32 it Foo kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -157,6 +204,10 @@ fn f2() { bt u32 u32 it i32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -185,6 +236,10 @@ fn f2() { bt u32 u32 it u64 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -210,6 +265,10 @@ fn f2(x: u64) -> $0 { bt u32 u32 it u64 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -236,6 +295,10 @@ fn f2(x: $0) { bt u32 u32 it i32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -270,6 +333,10 @@ fn foo<'lt, T, const C: usize>() { bt u32 u32 it a::Foo> kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -299,6 +366,10 @@ fn foo<'lt, T, const C: usize>() { bt u32 u32 it Foo kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -325,6 +396,10 @@ fn foo<'lt, T, const C: usize>() { un Union Union bt u32 u32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -392,6 +467,10 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} un Union Union bt u32 u32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -442,6 +521,10 @@ impl Tr<$0 un Union Union bt u32 u32 kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -487,6 +570,10 @@ fn f(t: impl MyTrait() { S::; } ct CONST Unit ma makro!(…) macro_rules! makro kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -968,6 +1135,10 @@ fn foo<'a>() { S::<'static, 'static, F$0, _>; } ct CONST Unit ma makro!(…) macro_rules! makro kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); @@ -980,6 +1151,10 @@ fn foo<'a>() { S::<'static, F$0, _, _>; } lt 'a ma makro!(…) macro_rules! makro kw crate:: + kw dyn + kw fn + kw for + kw impl kw self:: "#]], ); From 922aad6b6d22ced38024fc5d2506f2b3884f643f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 22 Oct 2025 14:37:12 +0800 Subject: [PATCH 064/170] Improve parsing of missing name in MethodCallExpr Usually, this occurs when preparing to input a method name However, once an identifier is entered, it is not reasonable for the parsing result to change from `CallExpr(FieldExpr())` to `MethodCallExpr()` Example --- ```rust fn foo() { x. () } ``` **Before this PR**: ```text 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 " CALL_EXPR FIELD_EXPR PATH_EXPR PATH PATH_SEGMENT NAME_REF IDENT "x" DOT "." WHITESPACE "\n " ARG_LIST L_PAREN "(" R_PAREN ")" WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" error 17: expected field name or number ``` **After this PR**: ```text 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 " METHOD_CALL_EXPR PATH_EXPR PATH PATH_SEGMENT NAME_REF IDENT "x" DOT "." WHITESPACE "\n " ARG_LIST L_PAREN "(" R_PAREN ")" WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" error 17: expected method name, field name or number ``` --- .../crates/parser/src/grammar/expressions.rs | 25 ++++++++++++---- .../parser/test_data/generated/runner.rs | 4 +++ .../err/postfix_dot_expr_ambiguity.rast | 29 +++++++++++++++++++ .../inline/err/postfix_dot_expr_ambiguity.rs | 4 +++ 4 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.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 41fd72d8d5a2..76d26c1ecdfc 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -430,6 +430,11 @@ fn postfix_expr( // } T!['('] if allow_calls => call_expr(p, lhs), T!['['] if allow_calls => index_expr(p, lhs), + // test_err postfix_dot_expr_ambiguity + // fn foo() { + // x. + // () + // } T![.] => match postfix_dot_expr::(p, lhs) { Ok(it) => it, Err(it) => { @@ -458,6 +463,7 @@ fn postfix_dot_expr( if PATH_NAME_REF_KINDS.contains(p.nth(nth1)) && (p.nth(nth2) == T!['('] || p.nth_at(nth2, T![::])) + || p.nth(nth1) == T!['('] { return Ok(method_call_expr::(p, lhs)); } @@ -526,19 +532,26 @@ fn method_call_expr( lhs: CompletedMarker, ) -> CompletedMarker { if FLOAT_RECOVERY { - assert!(p.at_ts(PATH_NAME_REF_KINDS) && (p.nth(1) == T!['('] || p.nth_at(1, T![::]))); - } else { assert!( - p.at(T![.]) - && PATH_NAME_REF_KINDS.contains(p.nth(1)) - && (p.nth(2) == T!['('] || p.nth_at(2, T![::])) + p.at_ts(PATH_NAME_REF_KINDS) && (p.nth(1) == T!['('] || p.nth_at(1, T![::])) + || p.current() == T!['('] + ); + } else { + assert!(p.at(T![.])); + assert!( + PATH_NAME_REF_KINDS.contains(p.nth(1)) && (p.nth(2) == T!['('] || p.nth_at(2, T![::])) + || p.nth(1) == T!['('] ); } let m = lhs.precede(p); if !FLOAT_RECOVERY { p.bump(T![.]); } - name_ref_mod_path(p); + if p.at_ts(PATH_NAME_REF_KINDS) { + name_ref_mod_path(p); + } else { + p.error("expected method name, field name or number"); + } generic_args::opt_generic_arg_list_expr(p); if p.at(T!['(']) { arg_list(p); 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 cd6d433d0efa..9bdbe5633033 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 @@ -844,6 +844,10 @@ mod err { run_and_expect_errors("test_data/parser/inline/err/pointer_type_no_mutability.rs"); } #[test] + fn postfix_dot_expr_ambiguity() { + run_and_expect_errors("test_data/parser/inline/err/postfix_dot_expr_ambiguity.rs"); + } + #[test] fn precise_capturing_invalid() { run_and_expect_errors("test_data/parser/inline/err/precise_capturing_invalid.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rast new file mode 100644 index 000000000000..4ee318de2515 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rast @@ -0,0 +1,29 @@ +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 " + METHOD_CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + DOT "." + WHITESPACE "\n " + ARG_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 17: expected method name, field name or number diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rs new file mode 100644 index 000000000000..c1aed3034288 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rs @@ -0,0 +1,4 @@ +fn foo() { + x. + () +} From 7773fe0fb8808f81a6e982c587e9e16a1648cb2e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 27 Jul 2025 20:17:10 +0300 Subject: [PATCH 065/170] Rewrite attribute handling Basically, we switch to expanding cfg_attr in AST form, filter irrelevant attributes from the item tree, and move hir-def attributes (non-item-tree) to be flag-based. The main motivation is memory usage, although this also simplifies the code, and fixes some bugs around handling of `cfg_attr`s. --- src/tools/rust-analyzer/Cargo.lock | 6 +- src/tools/rust-analyzer/Cargo.toml | 5 +- .../crates/base-db/src/editioned_file_id.rs | 291 +++ .../rust-analyzer/crates/base-db/src/input.rs | 7 +- .../rust-analyzer/crates/base-db/src/lib.rs | 39 +- src/tools/rust-analyzer/crates/cfg/Cargo.toml | 1 + .../rust-analyzer/crates/cfg/src/cfg_expr.rs | 59 + .../rust-analyzer/crates/cfg/src/tests.rs | 42 +- .../rust-analyzer/crates/hir-def/Cargo.toml | 4 +- .../rust-analyzer/crates/hir-def/src/attr.rs | 901 --------- .../rust-analyzer/crates/hir-def/src/attrs.rs | 1613 +++++++++++++++++ .../rust-analyzer/crates/hir-def/src/db.rs | 71 +- .../crates/hir-def/src/expr_store/expander.rs | 14 +- .../crates/hir-def/src/expr_store/lower.rs | 22 +- .../crates/hir-def/src/expr_store/pretty.rs | 19 +- .../src/expr_store/tests/body/block.rs | 4 +- .../src/expr_store/tests/signatures.rs | 14 +- .../crates/hir-def/src/import_map.rs | 34 +- .../crates/hir-def/src/item_tree.rs | 40 +- .../crates/hir-def/src/item_tree/attrs.rs | 220 +++ .../crates/hir-def/src/item_tree/lower.rs | 35 +- .../crates/hir-def/src/item_tree/pretty.rs | 12 +- .../crates/hir-def/src/item_tree/tests.rs | 9 +- .../crates/hir-def/src/lang_item.rs | 7 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 94 +- .../hir-def/src/macro_expansion_tests/mbe.rs | 43 +- .../hir-def/src/macro_expansion_tests/mod.rs | 15 + .../src/macro_expansion_tests/proc_macros.rs | 116 +- .../crates/hir-def/src/nameres.rs | 15 +- .../crates/hir-def/src/nameres/assoc.rs | 39 +- .../hir-def/src/nameres/attr_resolution.rs | 10 +- .../crates/hir-def/src/nameres/collector.rs | 179 +- .../crates/hir-def/src/nameres/diagnostics.rs | 14 +- .../hir-def/src/nameres/mod_resolution.rs | 5 +- .../crates/hir-def/src/nameres/proc_macro.rs | 24 +- .../crates/hir-def/src/signatures.rs | 130 +- .../rust-analyzer/crates/hir-def/src/src.rs | 9 +- .../crates/hir-def/src/test_db.rs | 33 +- .../crates/hir-expand/Cargo.toml | 2 + .../crates/hir-expand/src/attrs.rs | 814 +++++---- .../crates/hir-expand/src/builtin/fn_macro.rs | 2 +- .../crates/hir-expand/src/cfg_process.rs | 682 ++++--- .../rust-analyzer/crates/hir-expand/src/db.rs | 179 +- .../crates/hir-expand/src/declarative.rs | 58 +- .../crates/hir-expand/src/files.rs | 33 +- .../crates/hir-expand/src/fixup.rs | 5 +- .../crates/hir-expand/src/lib.rs | 165 +- .../crates/hir-expand/src/mod_path.rs | 59 +- .../crates/hir-expand/src/span_map.rs | 13 +- .../crates/hir-ty/src/consteval.rs | 3 +- .../hir-ty/src/diagnostics/decl_check.rs | 6 +- .../diagnostics/match_check/pat_analysis.rs | 6 +- .../hir-ty/src/diagnostics/unsafe_check.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 14 +- .../crates/hir-ty/src/infer/coerce.rs | 14 +- .../crates/hir-ty/src/infer/expr.rs | 14 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 4 +- .../crates/hir-ty/src/layout/adt.rs | 35 +- .../crates/hir-ty/src/method_resolution.rs | 5 +- .../crates/hir-ty/src/mir/eval/shim.rs | 45 +- .../crates/hir-ty/src/next_solver/interner.rs | 59 +- .../crates/hir-ty/src/target_feature.rs | 46 +- .../crates/hir-ty/src/tests/incremental.rs | 49 +- .../rust-analyzer/crates/hir-ty/src/utils.rs | 8 +- .../rust-analyzer/crates/hir/src/attrs.rs | 256 ++- .../crates/hir/src/diagnostics.rs | 13 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 267 +-- .../rust-analyzer/crates/hir/src/semantics.rs | 71 +- .../hir/src/semantics/child_by_source.rs | 13 +- .../rust-analyzer/crates/hir/src/symbols.rs | 8 +- .../src/handlers/add_missing_match_arms.rs | 6 +- .../handlers/destructure_struct_binding.rs | 4 +- .../src/handlers/move_module_to_file.rs | 10 +- .../crates/ide-assists/src/lib.rs | 4 +- .../crates/ide-assists/src/tests.rs | 4 +- .../crates/ide-assists/src/utils.rs | 13 +- .../src/completions/attribute/lint.rs | 2 +- .../src/completions/flyimport.rs | 4 +- .../ide-completion/src/completions/postfix.rs | 2 +- .../ide-completion/src/completions/snippet.rs | 2 +- .../crates/ide-completion/src/context.rs | 22 +- .../crates/ide-completion/src/item.rs | 12 +- .../crates/ide-completion/src/render.rs | 13 +- .../ide-completion/src/render/literal.rs | 2 +- .../ide-completion/src/render/pattern.rs | 2 +- .../ide-completion/src/render/variant.rs | 6 +- .../crates/ide-completion/src/tests.rs | 4 +- .../rust-analyzer/crates/ide-db/src/defs.rs | 38 +- .../crates/ide-db/src/documentation.rs | 379 +--- .../crates/ide-db/src/ra_fixture.rs | 12 +- .../crates/ide-db/src/rust_doc.rs | 2 +- .../rust-analyzer/crates/ide-db/src/search.rs | 16 +- .../ide-db/src/test_data/test_doc_alias.txt | 30 +- .../test_symbol_index_collection.txt | 134 +- .../test_symbols_exclude_imports.txt | 2 +- .../test_data/test_symbols_with_imports.txt | 4 +- .../rust-analyzer/crates/ide-db/src/traits.rs | 6 +- .../src/handlers/inactive_code.rs | 3 +- .../src/handlers/invalid_derive_target.rs | 4 +- .../src/handlers/macro_error.rs | 22 +- .../src/handlers/malformed_derive.rs | 4 +- .../src/handlers/unresolved_macro_call.rs | 5 +- .../crates/ide-diagnostics/src/lib.rs | 43 +- .../crates/ide-ssr/src/from_comment.rs | 2 +- .../rust-analyzer/crates/ide-ssr/src/lib.rs | 6 +- .../crates/ide-ssr/src/search.rs | 8 +- .../rust-analyzer/crates/ide/src/doc_links.rs | 38 +- .../crates/ide/src/doc_links/tests.rs | 69 +- .../rust-analyzer/crates/ide/src/fixture.rs | 32 +- .../crates/ide/src/goto_implementation.rs | 2 +- .../crates/ide/src/highlight_related.rs | 2 +- .../crates/ide/src/hover/render.rs | 52 +- .../crates/ide/src/inlay_hints.rs | 4 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 13 +- .../crates/ide/src/navigation_target.rs | 38 +- .../crates/ide/src/references.rs | 5 +- .../rust-analyzer/crates/ide/src/runnables.rs | 50 +- .../crates/ide/src/signature_help.rs | 36 +- .../crates/ide/src/static_index.rs | 6 +- .../crates/ide/src/syntax_highlighting.rs | 2 +- .../ide/src/syntax_highlighting/html.rs | 2 +- .../ide/src/syntax_highlighting/inject.rs | 189 +- .../test_data/highlight_doctest.html | 72 +- .../rust-analyzer/crates/ide/src/typing.rs | 5 +- .../crates/ide/src/typing/on_enter.rs | 2 +- .../crates/ide/src/view_item_tree.rs | 2 +- .../rust-analyzer/src/cli/analysis_stats.rs | 8 +- .../crates/rust-analyzer/src/cli/scip.rs | 6 +- .../crates/rust-analyzer/src/cli/ssr.rs | 2 +- .../src/cli/unresolved_references.rs | 2 +- .../crates/rust-analyzer/src/lsp/to_proto.rs | 4 +- .../crates/syntax-bridge/src/lib.rs | 68 +- .../rust-analyzer/crates/syntax/src/ast.rs | 4 +- .../crates/syntax/src/ast/node_ext.rs | 38 +- .../crates/syntax/src/ast/token_ext.rs | 6 +- .../crates/syntax/src/ast/traits.rs | 67 +- .../crates/test-fixture/src/lib.rs | 45 +- 137 files changed, 4999 insertions(+), 3851 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-def/src/attr.rs create mode 100644 src/tools/rust-analyzer/crates/hir-def/src/attrs.rs create mode 100644 src/tools/rust-analyzer/crates/hir-def/src/item_tree/attrs.rs diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index ea8d1a781dcc..d31d233dc4b6 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -725,6 +725,7 @@ dependencies = [ name = "hir-expand" version = "0.0.0" dependencies = [ + "arrayvec", "base-db", "cfg", "cov-mark", @@ -743,6 +744,7 @@ dependencies = [ "stdx", "syntax", "syntax-bridge", + "thin-vec", "tracing", "triomphe", "tt", @@ -1991,9 +1993,9 @@ dependencies = [ [[package]] name = "rowan" -version = "0.15.15" +version = "0.15.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49" +checksum = "d4f1e4a001f863f41ea8d0e6a0c34b356d5b733db50dadab3efef640bafb779b" dependencies = [ "countme", "hashbrown 0.14.5", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 8a108974681a..767dbcae9031 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -52,7 +52,7 @@ debug = 2 # local crates macros = { path = "./crates/macros", version = "0.0.0" } base-db = { path = "./crates/base-db", version = "0.0.0" } -cfg = { path = "./crates/cfg", version = "0.0.0", features = ["tt"] } +cfg = { path = "./crates/cfg", version = "0.0.0", features = ["tt", "syntax"] } hir = { path = "./crates/hir", version = "0.0.0" } hir-def = { path = "./crates/hir-def", version = "0.0.0" } hir-expand = { path = "./crates/hir-expand", version = "0.0.0" } @@ -131,7 +131,7 @@ process-wrap = { version = "8.2.1", features = ["std"] } pulldown-cmark-to-cmark = "10.0.4" pulldown-cmark = { version = "0.9.6", default-features = false } rayon = "1.10.0" -rowan = "=0.15.15" +rowan = "=0.15.17" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it salsa = { version = "0.24.0", default-features = true, features = [ @@ -167,6 +167,7 @@ tracing-subscriber = { version = "0.3.20", default-features = false, features = triomphe = { version = "0.1.14", default-features = false, features = ["std"] } url = "2.5.4" xshell = "0.2.7" +thin-vec = "0.2.14" petgraph = { version = "0.8.2", default-features = false } # We need to freeze the version of the crate, as the raw-api feature is considered unstable diff --git a/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs b/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs new file mode 100644 index 000000000000..2f8969c0ea33 --- /dev/null +++ b/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs @@ -0,0 +1,291 @@ +//! Defines [`EditionedFileId`], an interned wrapper around [`span::EditionedFileId`] that +//! is interned (so queries can take it) and remembers its crate. + +use core::fmt; +use std::hash::{Hash, Hasher}; + +use span::Edition; +use vfs::FileId; + +use crate::{Crate, RootQueryDb}; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct EditionedFileId( + salsa::Id, + std::marker::PhantomData<&'static salsa::plumbing::interned::Value>, +); + +const _: () = { + use salsa::plumbing as zalsa_; + use zalsa_::interned as zalsa_struct_; + type Configuration_ = EditionedFileId; + + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct EditionedFileIdData { + editioned_file_id: span::EditionedFileId, + krate: Crate, + } + + /// We like to include the origin crate in an `EditionedFileId` (for use in the item tree), + /// but this poses us a problem. + /// + /// Spans contain `EditionedFileId`s, and we don't want to make them store the crate too + /// because that will increase their size, which will increase memory usage significantly. + /// Furthermore, things using spans do not generally need the crate: they are using the + /// file id for queries like `ast_id_map` or `parse`, which do not care about the crate. + /// + /// To solve this, we hash **only the `span::EditionedFileId`**, but on still compare + /// the crate in equality check. This preserves the invariant of `Hash` and `Eq` - + /// although same hashes can be used for different items, same file ids used for multiple + /// crates is a rare thing, and different items always have different hashes. Then, + /// when we only have a `span::EditionedFileId`, we use the `intern()` method to + /// reuse existing file ids, and create new one only if needed. See [`from_span_guess_origin`]. + /// + /// See this for more info: https://rust-lang.zulipchat.com/#narrow/channel/185405-t-compiler.2Frust-analyzer/topic/Letting.20EditionedFileId.20know.20its.20crate/near/530189401 + /// + /// [`from_span_guess_origin`]: EditionedFileId::from_span_guess_origin + #[derive(Hash, PartialEq, Eq)] + struct WithoutCrate { + editioned_file_id: span::EditionedFileId, + } + + impl Hash for EditionedFileIdData { + #[inline] + fn hash(&self, state: &mut H) { + let EditionedFileIdData { editioned_file_id, krate: _ } = *self; + editioned_file_id.hash(state); + } + } + + impl zalsa_struct_::HashEqLike for EditionedFileIdData { + #[inline] + fn hash(&self, state: &mut H) { + Hash::hash(self, state); + } + + #[inline] + fn eq(&self, data: &WithoutCrate) -> bool { + let EditionedFileIdData { editioned_file_id, krate: _ } = *self; + editioned_file_id == data.editioned_file_id + } + } + + impl zalsa_::HasJar for EditionedFileId { + type Jar = zalsa_struct_::JarImpl; + const KIND: zalsa_::JarKind = zalsa_::JarKind::Struct; + } + + zalsa_::register_jar! { + zalsa_::ErasedJar::erase::() + } + + impl zalsa_struct_::Configuration for EditionedFileId { + const LOCATION: salsa::plumbing::Location = + salsa::plumbing::Location { file: file!(), line: line!() }; + const DEBUG_NAME: &'static str = "EditionedFileId"; + const REVISIONS: std::num::NonZeroUsize = std::num::NonZeroUsize::MAX; + const PERSIST: bool = false; + + type Fields<'a> = EditionedFileIdData; + type Struct<'db> = EditionedFileId; + + fn serialize(_: &Self::Fields<'_>, _: S) -> Result + where + S: zalsa_::serde::Serializer, + { + unimplemented!("attempted to serialize value that set `PERSIST` to false") + } + + fn deserialize<'de, D>(_: D) -> Result, D::Error> + where + D: zalsa_::serde::Deserializer<'de>, + { + unimplemented!("attempted to deserialize value that cannot set `PERSIST` to false"); + } + } + + impl Configuration_ { + pub fn ingredient(zalsa: &zalsa_::Zalsa) -> &zalsa_struct_::IngredientImpl { + static CACHE: zalsa_::IngredientCache> = + zalsa_::IngredientCache::new(); + + // SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the only + // ingredient created by our jar is the struct ingredient. + unsafe { + CACHE.get_or_create(zalsa, || { + zalsa.lookup_jar_by_type::>() + }) + } + } + } + + impl zalsa_::AsId for EditionedFileId { + fn as_id(&self) -> salsa::Id { + self.0.as_id() + } + } + impl zalsa_::FromId for EditionedFileId { + fn from_id(id: salsa::Id) -> Self { + Self(::from_id(id), std::marker::PhantomData) + } + } + + unsafe impl Send for EditionedFileId {} + unsafe impl Sync for EditionedFileId {} + + impl std::fmt::Debug for EditionedFileId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::default_debug_fmt(*self, f) + } + } + + impl zalsa_::SalsaStructInDb for EditionedFileId { + type MemoIngredientMap = salsa::plumbing::MemoIngredientSingletonIndex; + + fn lookup_ingredient_index(aux: &zalsa_::Zalsa) -> salsa::plumbing::IngredientIndices { + aux.lookup_jar_by_type::>().into() + } + + fn entries(zalsa: &zalsa_::Zalsa) -> impl Iterator + '_ { + let _ingredient_index = + zalsa.lookup_jar_by_type::>(); + ::ingredient(zalsa).entries(zalsa).map(|entry| entry.key()) + } + + #[inline] + fn cast(id: salsa::Id, type_id: std::any::TypeId) -> Option { + if type_id == std::any::TypeId::of::() { + Some(::from_id(id)) + } else { + None + } + } + + #[inline] + unsafe fn memo_table( + zalsa: &zalsa_::Zalsa, + id: zalsa_::Id, + current_revision: zalsa_::Revision, + ) -> zalsa_::MemoTableWithTypes<'_> { + // SAFETY: Guaranteed by caller. + unsafe { + zalsa.table().memos::>(id, current_revision) + } + } + } + + unsafe impl zalsa_::Update for EditionedFileId { + unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool { + if unsafe { *old_pointer } != new_value { + unsafe { *old_pointer = new_value }; + true + } else { + false + } + } + } + + impl EditionedFileId { + pub fn from_span( + db: &(impl salsa::Database + ?Sized), + editioned_file_id: span::EditionedFileId, + krate: Crate, + ) -> Self { + let (zalsa, zalsa_local) = db.zalsas(); + Configuration_::ingredient(zalsa).intern( + zalsa, + zalsa_local, + EditionedFileIdData { editioned_file_id, krate }, + |_, data| data, + ) + } + + /// Guesses the crate for the file. + /// + /// Only use this if you cannot precisely determine the origin. This can happen in one of two cases: + /// + /// 1. The file is not in the module tree. + /// 2. You are latency sensitive and cannot afford calling the def map to precisely compute the origin + /// (e.g. on enter feature, folding, etc.). + pub fn from_span_guess_origin( + db: &dyn RootQueryDb, + editioned_file_id: span::EditionedFileId, + ) -> Self { + let (zalsa, zalsa_local) = db.zalsas(); + Configuration_::ingredient(zalsa).intern( + zalsa, + zalsa_local, + WithoutCrate { editioned_file_id }, + |_, _| { + // FileId not in the database. + let krate = db + .relevant_crates(editioned_file_id.file_id()) + .first() + .copied() + .unwrap_or_else(|| db.all_crates()[0]); + EditionedFileIdData { editioned_file_id, krate } + }, + ) + } + + pub fn editioned_file_id(self, db: &dyn salsa::Database) -> span::EditionedFileId { + let zalsa = db.zalsa(); + let fields = Configuration_::ingredient(zalsa).fields(zalsa, self); + fields.editioned_file_id + } + + pub fn krate(self, db: &dyn salsa::Database) -> Crate { + let zalsa = db.zalsa(); + let fields = Configuration_::ingredient(zalsa).fields(zalsa, self); + fields.krate + } + + /// Default debug formatting for this struct (may be useful if you define your own `Debug` impl) + pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + zalsa_::with_attached_database(|db| { + let zalsa = db.zalsa(); + let fields = Configuration_::ingredient(zalsa).fields(zalsa, this); + fmt::Debug::fmt(fields, f) + }) + .unwrap_or_else(|| { + f.debug_tuple("EditionedFileId").field(&zalsa_::AsId::as_id(&this)).finish() + }) + } + } +}; + +impl EditionedFileId { + #[inline] + pub fn new(db: &dyn salsa::Database, file_id: FileId, edition: Edition, krate: Crate) -> Self { + EditionedFileId::from_span(db, span::EditionedFileId::new(file_id, edition), krate) + } + + /// Attaches the current edition and guesses the crate for the file. + /// + /// Only use this if you cannot precisely determine the origin. This can happen in one of two cases: + /// + /// 1. The file is not in the module tree. + /// 2. You are latency sensitive and cannot afford calling the def map to precisely compute the origin + /// (e.g. on enter feature, folding, etc.). + #[inline] + pub fn current_edition_guess_origin(db: &dyn RootQueryDb, file_id: FileId) -> Self { + Self::from_span_guess_origin(db, span::EditionedFileId::current_edition(file_id)) + } + + #[inline] + pub fn file_id(self, db: &dyn salsa::Database) -> vfs::FileId { + let id = self.editioned_file_id(db); + id.file_id() + } + + #[inline] + pub fn unpack(self, db: &dyn salsa::Database) -> (vfs::FileId, span::Edition) { + let id = self.editioned_file_id(db); + (id.file_id(), id.edition()) + } + + #[inline] + pub fn edition(self, db: &dyn salsa::Database) -> Edition { + self.editioned_file_id(db).edition() + } +} diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index cac74778a26b..28539d59825f 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -829,9 +829,10 @@ pub(crate) fn transitive_rev_deps(db: &dyn RootQueryDb, of: Crate) -> FxHashSet< rev_deps } -impl BuiltCrateData { - pub fn root_file_id(&self, db: &dyn salsa::Database) -> EditionedFileId { - EditionedFileId::new(db, self.root_file_id, self.edition) +impl Crate { + pub fn root_file_id(self, db: &dyn salsa::Database) -> EditionedFileId { + let data = self.data(db); + EditionedFileId::new(db, data.root_file_id, data.edition, self) } } 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 0e411bcfae60..32909af5d78d 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -5,6 +5,7 @@ pub use salsa_macros; // FIXME: Rename this crate, base db is non descriptive mod change; +mod editioned_file_id; mod input; pub mod target; @@ -17,6 +18,7 @@ use std::{ pub use crate::{ change::FileChange, + editioned_file_id::EditionedFileId, input::{ BuiltCrateData, BuiltDependency, Crate, CrateBuilder, CrateBuilderId, CrateDataBuilder, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CratesIdMap, CratesMap, @@ -29,7 +31,6 @@ pub use query_group::{self}; use rustc_hash::{FxHashSet, FxHasher}; use salsa::{Durability, Setter}; pub use semver::{BuildMetadata, Prerelease, Version, VersionReq}; -use span::Edition; use syntax::{Parse, SyntaxError, ast}; use triomphe::Arc; pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet}; @@ -175,42 +176,6 @@ impl Files { } } -#[salsa_macros::interned(no_lifetime, debug, constructor=from_span, revisions = usize::MAX)] -#[derive(PartialOrd, Ord)] -pub struct EditionedFileId { - pub editioned_file_id: span::EditionedFileId, -} - -impl EditionedFileId { - // Salsa already uses the name `new`... - #[inline] - pub fn new(db: &dyn salsa::Database, file_id: FileId, edition: Edition) -> Self { - EditionedFileId::from_span(db, span::EditionedFileId::new(file_id, edition)) - } - - #[inline] - pub fn current_edition(db: &dyn salsa::Database, file_id: FileId) -> Self { - EditionedFileId::new(db, file_id, Edition::CURRENT) - } - - #[inline] - pub fn file_id(self, db: &dyn salsa::Database) -> vfs::FileId { - let id = self.editioned_file_id(db); - id.file_id() - } - - #[inline] - pub fn unpack(self, db: &dyn salsa::Database) -> (vfs::FileId, span::Edition) { - let id = self.editioned_file_id(db); - (id.file_id(), id.edition()) - } - - #[inline] - pub fn edition(self, db: &dyn SourceDatabase) -> Edition { - self.editioned_file_id(db).edition() - } -} - #[salsa_macros::input(debug)] pub struct FileText { #[returns(ref)] diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index e17969bd82d4..9e2a95dbf32c 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -18,6 +18,7 @@ tracing.workspace = true # locals deps tt = { workspace = true, optional = true } +syntax = { workspace = true, optional = true } intern.workspace = true [dev-dependencies] diff --git a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs index 7a21015e14be..76e0aba859e6 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs @@ -63,6 +63,8 @@ impl From for CfgExpr { } impl CfgExpr { + // FIXME: Parsing from `tt` is only used in a handful of places, reconsider + // if we should switch them to AST. #[cfg(feature = "tt")] pub fn parse(tt: &tt::TopSubtree) -> CfgExpr { next_cfg_expr(&mut tt.iter()).unwrap_or(CfgExpr::Invalid) @@ -73,6 +75,13 @@ impl CfgExpr { next_cfg_expr(tt).unwrap_or(CfgExpr::Invalid) } + #[cfg(feature = "syntax")] + pub fn parse_from_ast( + ast: &mut std::iter::Peekable, + ) -> CfgExpr { + next_cfg_expr_from_ast(ast).unwrap_or(CfgExpr::Invalid) + } + /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option { match self { @@ -89,6 +98,56 @@ impl CfgExpr { } } +#[cfg(feature = "syntax")] +fn next_cfg_expr_from_ast( + it: &mut std::iter::Peekable, +) -> Option { + use intern::sym; + use syntax::{NodeOrToken, SyntaxKind, T, ast}; + + let name = match it.next() { + None => return None, + Some(NodeOrToken::Token(ident)) if ident.kind().is_any_identifier() => { + Symbol::intern(ident.text()) + } + Some(_) => return Some(CfgExpr::Invalid), + }; + + let ret = match it.peek() { + Some(NodeOrToken::Token(eq)) if eq.kind() == T![=] => { + it.next(); + if let Some(NodeOrToken::Token(literal)) = it.peek() + && matches!(literal.kind(), SyntaxKind::STRING) + { + let literal = tt::token_to_literal(literal.text(), ()).symbol; + it.next(); + CfgAtom::KeyValue { key: name, value: literal.clone() }.into() + } else { + return Some(CfgExpr::Invalid); + } + } + Some(NodeOrToken::Node(subtree)) => { + let mut subtree_iter = ast::TokenTreeChildren::new(subtree).peekable(); + it.next(); + let mut subs = std::iter::from_fn(|| next_cfg_expr_from_ast(&mut subtree_iter)); + match name { + s if s == sym::all => CfgExpr::All(subs.collect()), + s if s == sym::any => CfgExpr::Any(subs.collect()), + s if s == sym::not => { + CfgExpr::Not(Box::new(subs.next().unwrap_or(CfgExpr::Invalid))) + } + _ => CfgExpr::Invalid, + } + } + _ => CfgAtom::Flag(name).into(), + }; + + // Eat comma separator + while it.next().is_some_and(|it| it.as_token().is_none_or(|it| it.kind() != T![,])) {} + + Some(ret) +} + #[cfg(feature = "tt")] fn next_cfg_expr(it: &mut tt::iter::TtIter<'_, S>) -> Option { use intern::sym; diff --git a/src/tools/rust-analyzer/crates/cfg/src/tests.rs b/src/tools/rust-analyzer/crates/cfg/src/tests.rs index 6766748097f0..52c581dbbd3a 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/tests.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/tests.rs @@ -1,7 +1,10 @@ use arbitrary::{Arbitrary, Unstructured}; use expect_test::{Expect, expect}; use intern::Symbol; -use syntax::{AstNode, Edition, ast}; +use syntax::{ + AstNode, Edition, + ast::{self, TokenTreeChildren}, +}; use syntax_bridge::{ DocCommentDesugarMode, dummy_test_span_utils::{DUMMY, DummyTestSpanMap}, @@ -10,24 +13,33 @@ use syntax_bridge::{ use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr}; +#[track_caller] +fn parse_ast_cfg(tt: &ast::TokenTree) -> CfgExpr { + CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(tt).peekable()) +} + +#[track_caller] fn assert_parse_result(input: &str, expected: CfgExpr) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); - let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); + let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = syntax_node_to_token_tree( - tt.syntax(), + tt_ast.syntax(), DummyTestSpanMap, DUMMY, DocCommentDesugarMode::ProcMacro, ); let cfg = CfgExpr::parse(&tt); assert_eq!(cfg, expected); + let cfg = parse_ast_cfg(&tt_ast); + assert_eq!(cfg, expected); } +#[track_caller] fn check_dnf(input: &str, expect: Expect) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); - let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); + let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = syntax_node_to_token_tree( - tt.syntax(), + tt_ast.syntax(), DummyTestSpanMap, DUMMY, DocCommentDesugarMode::ProcMacro, @@ -35,13 +47,17 @@ fn check_dnf(input: &str, expect: Expect) { let cfg = CfgExpr::parse(&tt); let actual = format!("#![cfg({})]", DnfExpr::new(&cfg)); expect.assert_eq(&actual); + let cfg = parse_ast_cfg(&tt_ast); + let actual = format!("#![cfg({})]", DnfExpr::new(&cfg)); + expect.assert_eq(&actual); } +#[track_caller] fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); - let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); + let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = syntax_node_to_token_tree( - tt.syntax(), + tt_ast.syntax(), DummyTestSpanMap, DUMMY, DocCommentDesugarMode::ProcMacro, @@ -50,14 +66,18 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { let dnf = DnfExpr::new(&cfg); let why_inactive = dnf.why_inactive(opts).unwrap().to_string(); expect.assert_eq(&why_inactive); + let cfg = parse_ast_cfg(&tt_ast); + let dnf = DnfExpr::new(&cfg); + let why_inactive = dnf.why_inactive(opts).unwrap().to_string(); + expect.assert_eq(&why_inactive); } #[track_caller] fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); - let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); + let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = syntax_node_to_token_tree( - tt.syntax(), + tt_ast.syntax(), DummyTestSpanMap, DUMMY, DocCommentDesugarMode::ProcMacro, @@ -66,6 +86,10 @@ fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) { let dnf = DnfExpr::new(&cfg); let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::>(); assert_eq!(hints, expected_hints); + let cfg = parse_ast_cfg(&tt_ast); + let dnf = DnfExpr::new(&cfg); + let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::>(); + assert_eq!(hints, expected_hints); } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index abb4819a7672..e1f60742d324 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -45,7 +45,8 @@ mbe.workspace = true cfg.workspace = true tt.workspace = true span.workspace = true -thin-vec = "0.2.14" +thin-vec.workspace = true +syntax-bridge.workspace = true [dev-dependencies] expect-test.workspace = true @@ -53,7 +54,6 @@ expect-test.workspace = true # local deps test-utils.workspace = true test-fixture.workspace = true -syntax-bridge.workspace = true [features] in-rust-tree = ["hir-expand/in-rust-tree"] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs deleted file mode 100644 index b4fcfa11aea7..000000000000 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ /dev/null @@ -1,901 +0,0 @@ -//! A higher level attributes based on TokenTree, with also some shortcuts. - -use std::{borrow::Cow, convert::identity, hash::Hash, ops}; - -use base_db::Crate; -use cfg::{CfgExpr, CfgOptions}; -use either::Either; -use hir_expand::{ - HirFileId, InFile, - attrs::{Attr, AttrId, RawAttrs, collect_attrs}, - span_map::SpanMapRef, -}; -use intern::{Symbol, sym}; -use la_arena::{ArenaMap, Idx, RawIdx}; -use mbe::DelimiterKind; -use rustc_abi::ReprOptions; -use span::AstIdNode; -use syntax::{ - AstPtr, - ast::{self, HasAttrs}, -}; -use triomphe::Arc; -use tt::iter::{TtElement, TtIter}; - -use crate::{ - AdtId, AstIdLoc, AttrDefId, GenericParamId, HasModule, LocalFieldId, Lookup, MacroId, - VariantId, - db::DefDatabase, - item_tree::block_item_tree_query, - lang_item::LangItem, - nameres::{ModuleOrigin, ModuleSource}, - src::{HasChildSource, HasSource}, -}; - -/// Desugared attributes of an item post `cfg_attr` expansion. -#[derive(Default, Debug, Clone, PartialEq, Eq)] -pub struct Attrs(RawAttrs); - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct AttrsWithOwner { - attrs: Attrs, - owner: AttrDefId, -} - -impl Attrs { - pub fn new( - db: &dyn DefDatabase, - owner: &dyn ast::HasAttrs, - span_map: SpanMapRef<'_>, - cfg_options: &CfgOptions, - ) -> Self { - Attrs(RawAttrs::new_expanded(db, owner, span_map, cfg_options)) - } - - pub fn get(&self, id: AttrId) -> Option<&Attr> { - (**self).iter().find(|attr| attr.id == id) - } - - pub(crate) fn expand_cfg_attr( - db: &dyn DefDatabase, - krate: Crate, - raw_attrs: RawAttrs, - ) -> Attrs { - Attrs(raw_attrs.expand_cfg_attr(db, krate)) - } - - pub(crate) fn is_cfg_enabled_for( - db: &dyn DefDatabase, - owner: &dyn ast::HasAttrs, - span_map: SpanMapRef<'_>, - cfg_options: &CfgOptions, - ) -> Result<(), CfgExpr> { - RawAttrs::attrs_iter_expanded::(db, owner, span_map, cfg_options) - .filter_map(|attr| attr.cfg()) - .find_map(|cfg| match cfg_options.check(&cfg).is_none_or(identity) { - true => None, - false => Some(cfg), - }) - .map_or(Ok(()), Err) - } -} - -impl ops::Deref for Attrs { - type Target = [Attr]; - - fn deref(&self) -> &[Attr] { - &self.0 - } -} - -impl ops::Deref for AttrsWithOwner { - type Target = Attrs; - - fn deref(&self) -> &Attrs { - &self.attrs - } -} - -impl Attrs { - pub const EMPTY: Self = Self(RawAttrs::EMPTY); - - pub(crate) fn fields_attrs_query( - db: &dyn DefDatabase, - v: VariantId, - ) -> Arc> { - let _p = tracing::info_span!("fields_attrs_query").entered(); - let mut res = ArenaMap::default(); - let (fields, file_id, krate) = match v { - VariantId::EnumVariantId(it) => { - let loc = it.lookup(db); - let krate = loc.parent.lookup(db).container.krate; - let source = loc.source(db); - (source.value.field_list(), source.file_id, krate) - } - VariantId::StructId(it) => { - let loc = it.lookup(db); - let krate = loc.container.krate; - let source = loc.source(db); - (source.value.field_list(), source.file_id, krate) - } - VariantId::UnionId(it) => { - let loc = it.lookup(db); - let krate = loc.container.krate; - let source = loc.source(db); - ( - source.value.record_field_list().map(ast::FieldList::RecordFieldList), - source.file_id, - krate, - ) - } - }; - let Some(fields) = fields else { - return Arc::new(res); - }; - - let cfg_options = krate.cfg_options(db); - let span_map = db.span_map(file_id); - - match fields { - ast::FieldList::RecordFieldList(fields) => { - let mut idx = 0; - for field in fields.fields() { - let attrs = - Attrs(RawAttrs::new_expanded(db, &field, span_map.as_ref(), cfg_options)); - if attrs.is_cfg_enabled(cfg_options).is_ok() { - res.insert(Idx::from_raw(RawIdx::from(idx)), attrs); - idx += 1; - } - } - } - ast::FieldList::TupleFieldList(fields) => { - let mut idx = 0; - for field in fields.fields() { - let attrs = - Attrs(RawAttrs::new_expanded(db, &field, span_map.as_ref(), cfg_options)); - if attrs.is_cfg_enabled(cfg_options).is_ok() { - res.insert(Idx::from_raw(RawIdx::from(idx)), attrs); - idx += 1; - } - } - } - } - - res.shrink_to_fit(); - Arc::new(res) - } -} - -impl Attrs { - #[inline] - pub fn by_key(&self, key: Symbol) -> AttrQuery<'_> { - AttrQuery { attrs: self, key } - } - - #[inline] - pub fn rust_analyzer_tool(&self) -> impl Iterator { - self.iter() - .filter(|&attr| attr.path.segments().first().is_some_and(|s| *s == sym::rust_analyzer)) - } - - #[inline] - pub fn cfg(&self) -> Option { - let mut cfgs = self.by_key(sym::cfg).tt_values().map(CfgExpr::parse); - let first = cfgs.next()?; - match cfgs.next() { - Some(second) => { - let cfgs = [first, second].into_iter().chain(cfgs); - Some(CfgExpr::All(cfgs.collect())) - } - None => Some(first), - } - } - - #[inline] - pub fn cfgs(&self) -> impl Iterator + '_ { - self.by_key(sym::cfg).tt_values().map(CfgExpr::parse) - } - - #[inline] - pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Result<(), CfgExpr> { - self.cfgs().try_for_each(|cfg| { - if cfg_options.check(&cfg) != Some(false) { Ok(()) } else { Err(cfg) } - }) - } - - #[inline] - pub fn lang(&self) -> Option<&Symbol> { - self.by_key(sym::lang).string_value() - } - - #[inline] - pub fn lang_item(&self) -> Option { - self.by_key(sym::lang).string_value().and_then(LangItem::from_symbol) - } - - #[inline] - pub fn has_doc_hidden(&self) -> bool { - self.by_key(sym::doc).tt_values().any(|tt| { - tt.top_subtree().delimiter.kind == DelimiterKind::Parenthesis && - matches!(tt.token_trees().flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::hidden) - }) - } - - #[inline] - pub fn has_doc_notable_trait(&self) -> bool { - self.by_key(sym::doc).tt_values().any(|tt| { - tt.top_subtree().delimiter.kind == DelimiterKind::Parenthesis && - matches!(tt.token_trees().flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::notable_trait) - }) - } - - #[inline] - pub fn doc_exprs(&self) -> impl Iterator + '_ { - self.by_key(sym::doc).tt_values().map(DocExpr::parse) - } - - #[inline] - pub fn doc_aliases(&self) -> impl Iterator + '_ { - self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec()) - } - - #[inline] - pub fn export_name(&self) -> Option<&Symbol> { - self.by_key(sym::export_name).string_value() - } - - #[inline] - pub fn is_proc_macro(&self) -> bool { - self.by_key(sym::proc_macro).exists() - } - - #[inline] - pub fn is_proc_macro_attribute(&self) -> bool { - self.by_key(sym::proc_macro_attribute).exists() - } - - #[inline] - pub fn is_proc_macro_derive(&self) -> bool { - self.by_key(sym::proc_macro_derive).exists() - } - - #[inline] - pub fn is_test(&self) -> bool { - self.iter().any(|it| { - it.path() - .segments() - .iter() - .rev() - .zip([sym::core, sym::prelude, sym::v1, sym::test].iter().rev()) - .all(|it| it.0 == it.1) - }) - } - - #[inline] - pub fn is_ignore(&self) -> bool { - self.by_key(sym::ignore).exists() - } - - #[inline] - pub fn is_bench(&self) -> bool { - self.by_key(sym::bench).exists() - } - - #[inline] - pub fn is_unstable(&self) -> bool { - self.by_key(sym::unstable).exists() - } - - #[inline] - pub fn rustc_legacy_const_generics(&self) -> Option>> { - self.by_key(sym::rustc_legacy_const_generics) - .tt_values() - .next() - .map(parse_rustc_legacy_const_generics) - .filter(|it| !it.is_empty()) - .map(Box::new) - } - - #[inline] - pub fn repr(&self) -> Option { - self.by_key(sym::repr).tt_values().filter_map(parse_repr_tt).fold(None, |acc, repr| { - acc.map_or(Some(repr), |mut acc| { - merge_repr(&mut acc, repr); - Some(acc) - }) - }) - } -} - -fn parse_rustc_legacy_const_generics(tt: &crate::tt::TopSubtree) -> Box<[u32]> { - let mut indices = Vec::new(); - let mut iter = tt.iter(); - while let (Some(first), second) = (iter.next(), iter.next()) { - match first { - TtElement::Leaf(tt::Leaf::Literal(lit)) => match lit.symbol.as_str().parse() { - Ok(index) => indices.push(index), - Err(_) => break, - }, - _ => break, - } - - if let Some(comma) = second { - match comma { - TtElement::Leaf(tt::Leaf::Punct(punct)) if punct.char == ',' => {} - _ => break, - } - } - } - - indices.into_boxed_slice() -} - -fn merge_repr(this: &mut ReprOptions, other: ReprOptions) { - let ReprOptions { int, align, pack, flags, field_shuffle_seed: _ } = this; - flags.insert(other.flags); - *align = (*align).max(other.align); - *pack = match (*pack, other.pack) { - (Some(pack), None) | (None, Some(pack)) => Some(pack), - _ => (*pack).min(other.pack), - }; - if other.int.is_some() { - *int = other.int; - } -} - -fn parse_repr_tt(tt: &crate::tt::TopSubtree) -> Option { - use crate::builtin_type::{BuiltinInt, BuiltinUint}; - use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; - - match tt.top_subtree().delimiter { - tt::Delimiter { kind: DelimiterKind::Parenthesis, .. } => {} - _ => return None, - } - - let mut acc = ReprOptions::default(); - let mut tts = tt.iter(); - while let Some(tt) = tts.next() { - let TtElement::Leaf(tt::Leaf::Ident(ident)) = tt else { - continue; - }; - let repr = match &ident.sym { - s if *s == sym::packed => { - let pack = if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() { - tts.next(); - if let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = tt_iter.next() { - lit.symbol.as_str().parse().unwrap_or_default() - } else { - 0 - } - } else { - 0 - }; - let pack = Some(Align::from_bytes(pack).unwrap_or(Align::ONE)); - ReprOptions { pack, ..Default::default() } - } - s if *s == sym::align => { - let mut align = None; - if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() { - tts.next(); - if let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = tt_iter.next() - && let Ok(a) = lit.symbol.as_str().parse() - { - align = Align::from_bytes(a).ok(); - } - } - ReprOptions { align, ..Default::default() } - } - s if *s == sym::C => ReprOptions { flags: ReprFlags::IS_C, ..Default::default() }, - s if *s == sym::transparent => { - ReprOptions { flags: ReprFlags::IS_TRANSPARENT, ..Default::default() } - } - s if *s == sym::simd => ReprOptions { flags: ReprFlags::IS_SIMD, ..Default::default() }, - repr => { - let mut int = None; - if let Some(builtin) = BuiltinInt::from_suffix_sym(repr) - .map(Either::Left) - .or_else(|| BuiltinUint::from_suffix_sym(repr).map(Either::Right)) - { - int = Some(match builtin { - Either::Left(bi) => match bi { - BuiltinInt::Isize => IntegerType::Pointer(true), - BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true), - BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true), - BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true), - BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true), - BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true), - }, - Either::Right(bu) => match bu { - BuiltinUint::Usize => IntegerType::Pointer(false), - BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false), - BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false), - BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false), - BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false), - BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false), - }, - }); - } - ReprOptions { int, ..Default::default() } - } - }; - merge_repr(&mut acc, repr); - } - - Some(acc) -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum DocAtom { - /// eg. `#[doc(hidden)]` - Flag(Symbol), - /// eg. `#[doc(alias = "it")]` - /// - /// Note that a key can have multiple values that are all considered "active" at the same time. - /// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`. - KeyValue { key: Symbol, value: Symbol }, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum DocExpr { - Invalid, - /// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]` - Atom(DocAtom), - /// eg. `#[doc(alias("x", "y"))]` - Alias(Vec), -} - -impl From for DocExpr { - fn from(atom: DocAtom) -> Self { - DocExpr::Atom(atom) - } -} - -impl DocExpr { - fn parse(tt: &tt::TopSubtree) -> DocExpr { - next_doc_expr(tt.iter()).unwrap_or(DocExpr::Invalid) - } - - pub fn aliases(&self) -> &[Symbol] { - match self { - DocExpr::Atom(DocAtom::KeyValue { key, value }) if *key == sym::alias => { - std::slice::from_ref(value) - } - DocExpr::Alias(aliases) => aliases, - _ => &[], - } - } -} - -fn next_doc_expr(mut it: TtIter<'_, S>) -> Option { - let name = match it.next() { - None => return None, - Some(TtElement::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), - Some(_) => return Some(DocExpr::Invalid), - }; - - // Peek - let ret = match it.peek() { - Some(TtElement::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => { - it.next(); - match it.next() { - Some(TtElement::Leaf(tt::Leaf::Literal(tt::Literal { - symbol: text, - kind: tt::LitKind::Str, - .. - }))) => DocAtom::KeyValue { key: name, value: text.clone() }.into(), - _ => return Some(DocExpr::Invalid), - } - } - Some(TtElement::Subtree(_, subtree_iter)) => { - it.next(); - let subs = parse_comma_sep(subtree_iter); - match &name { - s if *s == sym::alias => DocExpr::Alias(subs), - _ => DocExpr::Invalid, - } - } - _ => DocAtom::Flag(name).into(), - }; - Some(ret) -} - -fn parse_comma_sep(iter: TtIter<'_, S>) -> Vec { - iter.filter_map(|tt| match tt { - TtElement::Leaf(tt::Leaf::Literal(tt::Literal { - kind: tt::LitKind::Str, symbol, .. - })) => Some(symbol.clone()), - _ => None, - }) - .collect() -} - -impl AttrsWithOwner { - pub fn new(db: &dyn DefDatabase, owner: AttrDefId) -> Self { - Self { attrs: db.attrs(owner), owner } - } - - pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { - let _p = tracing::info_span!("attrs_query").entered(); - // FIXME: this should use `Trace` to avoid duplication in `source_map` below - match def { - AttrDefId::ModuleId(module) => { - let def_map = module.def_map(db); - let mod_data = &def_map[module.local_id]; - - let raw_attrs = match mod_data.origin { - ModuleOrigin::File { definition, declaration_tree_id, declaration, .. } => { - let decl_attrs = declaration_tree_id - .item_tree(db) - .raw_attrs(declaration.upcast()) - .clone(); - let tree = db.file_item_tree(definition.into()); - let def_attrs = tree.top_level_raw_attrs().clone(); - decl_attrs.merge(def_attrs) - } - ModuleOrigin::CrateRoot { definition } => { - let tree = db.file_item_tree(definition.into()); - tree.top_level_raw_attrs().clone() - } - ModuleOrigin::Inline { definition_tree_id, definition } => { - definition_tree_id.item_tree(db).raw_attrs(definition.upcast()).clone() - } - ModuleOrigin::BlockExpr { id, .. } => { - let tree = block_item_tree_query(db, id); - tree.top_level_raw_attrs().clone() - } - }; - Attrs::expand_cfg_attr(db, module.krate, raw_attrs) - } - AttrDefId::FieldId(it) => db.fields_attrs(it.parent)[it.local_id].clone(), - AttrDefId::EnumVariantId(it) => attrs_from_ast_id_loc(db, it), - AttrDefId::AdtId(it) => match it { - AdtId::StructId(it) => attrs_from_ast_id_loc(db, it), - AdtId::EnumId(it) => attrs_from_ast_id_loc(db, it), - AdtId::UnionId(it) => attrs_from_ast_id_loc(db, it), - }, - AttrDefId::TraitId(it) => attrs_from_ast_id_loc(db, it), - AttrDefId::MacroId(it) => match it { - MacroId::Macro2Id(it) => attrs_from_ast_id_loc(db, it), - MacroId::MacroRulesId(it) => attrs_from_ast_id_loc(db, it), - MacroId::ProcMacroId(it) => attrs_from_ast_id_loc(db, it), - }, - AttrDefId::ImplId(it) => attrs_from_ast_id_loc(db, it), - AttrDefId::ConstId(it) => attrs_from_ast_id_loc(db, it), - AttrDefId::StaticId(it) => attrs_from_ast_id_loc(db, it), - AttrDefId::FunctionId(it) => attrs_from_ast_id_loc(db, it), - AttrDefId::TypeAliasId(it) => attrs_from_ast_id_loc(db, it), - AttrDefId::GenericParamId(it) => match it { - GenericParamId::ConstParamId(it) => { - let src = it.parent().child_source(db); - // FIXME: We should be never getting `None` here. - Attrs(match src.value.get(it.local_id()) { - Some(val) => RawAttrs::new_expanded( - db, - val, - db.span_map(src.file_id).as_ref(), - def.krate(db).cfg_options(db), - ), - None => RawAttrs::EMPTY, - }) - } - GenericParamId::TypeParamId(it) => { - let src = it.parent().child_source(db); - // FIXME: We should be never getting `None` here. - Attrs(match src.value.get(it.local_id()) { - Some(val) => RawAttrs::new_expanded( - db, - val, - db.span_map(src.file_id).as_ref(), - def.krate(db).cfg_options(db), - ), - None => RawAttrs::EMPTY, - }) - } - GenericParamId::LifetimeParamId(it) => { - let src = it.parent.child_source(db); - // FIXME: We should be never getting `None` here. - Attrs(match src.value.get(it.local_id) { - Some(val) => RawAttrs::new_expanded( - db, - val, - db.span_map(src.file_id).as_ref(), - def.krate(db).cfg_options(db), - ), - None => RawAttrs::EMPTY, - }) - } - }, - AttrDefId::ExternBlockId(it) => attrs_from_ast_id_loc(db, it), - AttrDefId::ExternCrateId(it) => attrs_from_ast_id_loc(db, it), - AttrDefId::UseId(it) => attrs_from_ast_id_loc(db, it), - } - } - - pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap { - let owner = match self.owner { - AttrDefId::ModuleId(module) => { - // Modules can have 2 attribute owners (the `mod x;` item, and the module file itself). - - let def_map = module.def_map(db); - let mod_data = &def_map[module.local_id]; - match mod_data.declaration_source(db) { - Some(it) => { - let mut map = AttrSourceMap::new(InFile::new(it.file_id, &it.value)); - if let InFile { file_id, value: ModuleSource::SourceFile(file) } = - mod_data.definition_source(db) - { - map.append_module_inline_attrs(AttrSourceMap::new(InFile::new( - file_id, &file, - ))); - } - return map; - } - None => { - let InFile { file_id, value } = mod_data.definition_source(db); - let attrs_owner = match &value { - ModuleSource::SourceFile(file) => file as &dyn ast::HasAttrs, - ModuleSource::Module(module) => module as &dyn ast::HasAttrs, - ModuleSource::BlockExpr(block) => block as &dyn ast::HasAttrs, - }; - return AttrSourceMap::new(InFile::new(file_id, attrs_owner)); - } - } - } - AttrDefId::FieldId(id) => { - let map = db.fields_attrs_source_map(id.parent); - let file_id = id.parent.file_id(db); - let root = db.parse_or_expand(file_id); - let owner = ast::AnyHasAttrs::new(map[id.local_id].to_node(&root)); - InFile::new(file_id, owner) - } - AttrDefId::AdtId(adt) => match adt { - AdtId::StructId(id) => any_has_attrs(db, id), - AdtId::UnionId(id) => any_has_attrs(db, id), - AdtId::EnumId(id) => any_has_attrs(db, id), - }, - AttrDefId::FunctionId(id) => any_has_attrs(db, id), - AttrDefId::EnumVariantId(id) => any_has_attrs(db, id), - AttrDefId::StaticId(id) => any_has_attrs(db, id), - AttrDefId::ConstId(id) => any_has_attrs(db, id), - AttrDefId::TraitId(id) => any_has_attrs(db, id), - AttrDefId::TypeAliasId(id) => any_has_attrs(db, id), - AttrDefId::MacroId(id) => match id { - MacroId::Macro2Id(id) => any_has_attrs(db, id), - MacroId::MacroRulesId(id) => any_has_attrs(db, id), - MacroId::ProcMacroId(id) => any_has_attrs(db, id), - }, - AttrDefId::ImplId(id) => any_has_attrs(db, id), - AttrDefId::GenericParamId(id) => match id { - GenericParamId::ConstParamId(id) => id - .parent() - .child_source(db) - .map(|source| ast::AnyHasAttrs::new(source[id.local_id()].clone())), - GenericParamId::TypeParamId(id) => id - .parent() - .child_source(db) - .map(|source| ast::AnyHasAttrs::new(source[id.local_id()].clone())), - GenericParamId::LifetimeParamId(id) => id - .parent - .child_source(db) - .map(|source| ast::AnyHasAttrs::new(source[id.local_id].clone())), - }, - AttrDefId::ExternBlockId(id) => any_has_attrs(db, id), - AttrDefId::ExternCrateId(id) => any_has_attrs(db, id), - AttrDefId::UseId(id) => any_has_attrs(db, id), - }; - - AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs)) - } -} - -#[derive(Debug)] -pub struct AttrSourceMap { - source: Vec>, - file_id: HirFileId, - /// If this map is for a module, this will be the [`HirFileId`] of the module's definition site, - /// while `file_id` will be the one of the module declaration site. - /// The usize is the index into `source` from which point on the entries reside in the def site - /// file. - mod_def_site_file_id: Option<(HirFileId, usize)>, -} - -impl AttrSourceMap { - fn new(owner: InFile<&dyn ast::HasAttrs>) -> Self { - Self { - source: collect_attrs(owner.value).map(|(_, it)| it).collect(), - file_id: owner.file_id, - mod_def_site_file_id: None, - } - } - - /// Append a second source map to this one, this is required for modules, whose outline and inline - /// attributes can reside in different files - fn append_module_inline_attrs(&mut self, other: Self) { - assert!(self.mod_def_site_file_id.is_none() && other.mod_def_site_file_id.is_none()); - let len = self.source.len(); - self.source.extend(other.source); - if other.file_id != self.file_id { - self.mod_def_site_file_id = Some((other.file_id, len)); - } - } - - /// Maps the lowered `Attr` back to its original syntax node. - /// - /// `attr` must come from the `owner` used for AttrSourceMap - /// - /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of - /// the attribute represented by `Attr`. - pub fn source_of(&self, attr: &Attr) -> InFile<&Either> { - self.source_of_id(attr.id) - } - - pub fn source_of_id(&self, id: AttrId) -> InFile<&Either> { - let ast_idx = id.ast_index(); - let file_id = match self.mod_def_site_file_id { - Some((file_id, def_site_cut)) if def_site_cut <= ast_idx => file_id, - _ => self.file_id, - }; - - self.source - .get(ast_idx) - .map(|it| InFile::new(file_id, it)) - .unwrap_or_else(|| panic!("cannot find attr at index {id:?}")) - } -} - -#[derive(Debug, Clone)] -pub struct AttrQuery<'attr> { - attrs: &'attr Attrs, - key: Symbol, -} - -impl<'attr> AttrQuery<'attr> { - #[inline] - pub fn tt_values(self) -> impl Iterator { - self.attrs().filter_map(|attr| attr.token_tree_value()) - } - - #[inline] - pub fn string_value(self) -> Option<&'attr Symbol> { - self.attrs().find_map(|attr| attr.string_value()) - } - - #[inline] - pub fn string_value_with_span(self) -> Option<(&'attr Symbol, span::Span)> { - self.attrs().find_map(|attr| attr.string_value_with_span()) - } - - #[inline] - pub fn string_value_unescape(self) -> Option> { - self.attrs().find_map(|attr| attr.string_value_unescape()) - } - - #[inline] - pub fn exists(self) -> bool { - self.attrs().next().is_some() - } - - #[inline] - pub fn attrs(self) -> impl Iterator + Clone { - let key = self.key; - self.attrs.iter().filter(move |attr| attr.path.as_ident().is_some_and(|s| *s == key)) - } - - /// Find string value for a specific key inside token tree - /// - /// ```ignore - /// #[doc(html_root_url = "url")] - /// ^^^^^^^^^^^^^ key - /// ``` - #[inline] - pub fn find_string_value_in_tt(self, key: Symbol) -> Option<&'attr str> { - self.tt_values().find_map(|tt| { - let name = tt.iter() - .skip_while(|tt| !matches!(tt, TtElement::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == key)) - .nth(2); - - match name { - Some(TtElement::Leaf(tt::Leaf::Literal(tt::Literal{ symbol: text, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_) , ..}))) => Some(text.as_str()), - _ => None - } - }) - } -} - -fn any_has_attrs<'db>( - db: &(dyn DefDatabase + 'db), - id: impl Lookup>, -) -> InFile { - id.lookup(db).source(db).map(ast::AnyHasAttrs::new) -} - -fn attrs_from_ast_id_loc<'db, N: AstIdNode + HasAttrs>( - db: &(dyn DefDatabase + 'db), - lookup: impl Lookup + HasModule>, -) -> Attrs { - let loc = lookup.lookup(db); - let source = loc.source(db); - let span_map = db.span_map(source.file_id); - let cfg_options = loc.krate(db).cfg_options(db); - Attrs(RawAttrs::new_expanded(db, &source.value, span_map.as_ref(), cfg_options)) -} - -pub(crate) fn fields_attrs_source_map( - db: &dyn DefDatabase, - def: VariantId, -) -> Arc>>> { - let mut res = ArenaMap::default(); - let child_source = def.child_source(db); - - for (idx, variant) in child_source.value.iter() { - res.insert( - idx, - variant - .as_ref() - .either(|l| AstPtr::new(l).wrap_left(), |r| AstPtr::new(r).wrap_right()), - ); - } - - Arc::new(res) -} - -#[cfg(test)] -mod tests { - //! This module contains tests for doc-expression parsing. - //! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`. - - use intern::Symbol; - use span::EditionedFileId; - use triomphe::Arc; - - use hir_expand::span_map::{RealSpanMap, SpanMap}; - use span::FileId; - use syntax::{AstNode, TextRange, ast}; - use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree}; - - use crate::attr::{DocAtom, DocExpr}; - - fn assert_parse_result(input: &str, expected: DocExpr) { - let source_file = ast::SourceFile::parse(input, span::Edition::CURRENT).ok().unwrap(); - let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute( - EditionedFileId::current_edition(FileId::from_raw(0)), - ))); - let tt = syntax_node_to_token_tree( - tt.syntax(), - map.as_ref(), - map.span_for_range(TextRange::empty(0.into())), - DocCommentDesugarMode::ProcMacro, - ); - let cfg = DocExpr::parse(&tt); - assert_eq!(cfg, expected); - } - - #[test] - fn test_doc_expr_parser() { - assert_parse_result("#![doc(hidden)]", DocAtom::Flag(Symbol::intern("hidden")).into()); - - assert_parse_result( - r#"#![doc(alias = "foo")]"#, - DocAtom::KeyValue { key: Symbol::intern("alias"), value: Symbol::intern("foo") }.into(), - ); - - assert_parse_result( - r#"#![doc(alias("foo"))]"#, - DocExpr::Alias([Symbol::intern("foo")].into()), - ); - assert_parse_result( - r#"#![doc(alias("foo", "bar", "baz"))]"#, - DocExpr::Alias( - [Symbol::intern("foo"), Symbol::intern("bar"), Symbol::intern("baz")].into(), - ), - ); - - assert_parse_result( - r#" - #[doc(alias("Bar", "Qux"))] - struct Foo;"#, - DocExpr::Alias([Symbol::intern("Bar"), Symbol::intern("Qux")].into()), - ); - } -} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs new file mode 100644 index 000000000000..1897cb5205aa --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -0,0 +1,1613 @@ +//! Attributes for anything that is not name resolution. +//! +//! The fundamental idea of this module stems from the observation that most "interesting" +//! attributes have a more memory-compact form than storing their full syntax, and +//! that most of the attributes are flags, and those that are not are rare. Therefore, +//! this module defines [`AttrFlags`], which is a bitflag enum that contains only a yes/no +//! answer to whether an attribute is present on an item. For most attributes, that's all +//! that is interesting us; for the rest of them, we define another query that extracts +//! their data. A key part is that every one of those queries will have a wrapper method +//! that queries (or is given) the `AttrFlags` and checks for the presence of the attribute; +//! if it is not present, we do not call the query, to prevent Salsa from needing to record +//! its value. This way, queries are only called on items that have the attribute, which is +//! usually only a few. +//! +//! An exception to this model that is also defined in this module is documentation (doc +//! comments and `#[doc = "..."]` attributes). But it also has a more compact form than +//! the attribute: a concatenated string of the full docs as well as a source map +//! to map it back to AST (which is needed for things like resolving links in doc comments +//! and highlight injection). The lowering and upmapping of doc comments is a bit complicated, +//! but it is encapsulated in the [`Docs`] struct. + +use std::{ + convert::Infallible, + iter::Peekable, + ops::{ControlFlow, Range}, +}; + +use base_db::Crate; +use cfg::{CfgExpr, CfgOptions}; +use either::Either; +use hir_expand::{ + HirFileId, InFile, Lookup, + attrs::{Meta, expand_cfg_attr, expand_cfg_attr_with_doc_comments}, +}; +use intern::Symbol; +use itertools::Itertools; +use la_arena::ArenaMap; +use rustc_abi::ReprOptions; +use rustc_hash::FxHashSet; +use smallvec::SmallVec; +use syntax::{ + AstNode, AstToken, NodeOrToken, SmolStr, SyntaxNode, SyntaxToken, T, + ast::{self, AttrDocCommentIter, HasAttrs, IsString, TokenTreeChildren}, +}; +use tt::{TextRange, TextSize}; + +use crate::{ + AdtId, AstIdLoc, AttrDefId, FieldId, FunctionId, GenericDefId, HasModule, InternedModuleId, + LifetimeParamId, LocalFieldId, MacroId, TypeOrConstParamId, VariantId, + db::DefDatabase, + hir::generics::{GenericParams, LocalLifetimeParamId, LocalTypeOrConstParamId}, + lang_item::LangItem, + nameres::ModuleOrigin, + src::{HasChildSource, HasSource}, +}; + +#[inline] +fn attrs_from_ast_id_loc>( + db: &dyn DefDatabase, + lookup: impl Lookup + HasModule>, +) -> (InFile, Crate) { + let loc = lookup.lookup(db); + let source = loc.source(db); + let krate = loc.krate(db); + (source.map(|it| it.into()), krate) +} + +#[inline] +fn extract_doc_tt_attr(attr_flags: &mut AttrFlags, tt: ast::TokenTree) { + for atom in DocAtom::parse(tt) { + match atom { + DocAtom::Flag(flag) => match &*flag { + "notable_trait" => attr_flags.insert(AttrFlags::IS_DOC_NOTABLE_TRAIT), + "hidden" => attr_flags.insert(AttrFlags::IS_DOC_HIDDEN), + _ => {} + }, + DocAtom::KeyValue { key, value: _ } => match &*key { + "alias" => attr_flags.insert(AttrFlags::HAS_DOC_ALIASES), + "keyword" => attr_flags.insert(AttrFlags::HAS_DOC_KEYWORD), + _ => {} + }, + DocAtom::Alias(_) => attr_flags.insert(AttrFlags::HAS_DOC_ALIASES), + } + } +} + +fn extract_ra_completions(attr_flags: &mut AttrFlags, tt: ast::TokenTree) { + let tt = TokenTreeChildren::new(&tt); + if let Ok(NodeOrToken::Token(option)) = tt.exactly_one() + && option.kind().is_any_identifier() + { + match option.text() { + "ignore_flyimport" => attr_flags.insert(AttrFlags::COMPLETE_IGNORE_FLYIMPORT), + "ignore_methods" => attr_flags.insert(AttrFlags::COMPLETE_IGNORE_METHODS), + "ignore_flyimport_methods" => { + attr_flags.insert(AttrFlags::COMPLETE_IGNORE_FLYIMPORT_METHODS) + } + _ => {} + } + } +} + +fn extract_rustc_skip_during_method_dispatch(attr_flags: &mut AttrFlags, tt: ast::TokenTree) { + let iter = TokenTreeChildren::new(&tt); + for kind in iter { + if let NodeOrToken::Token(kind) = kind + && kind.kind().is_any_identifier() + { + match kind.text() { + "array" => attr_flags.insert(AttrFlags::RUSTC_SKIP_ARRAY_DURING_METHOD_DISPATCH), + "boxed_slice" => { + attr_flags.insert(AttrFlags::RUSTC_SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH) + } + _ => {} + } + } + } +} + +#[inline] +fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow { + match attr { + Meta::NamedKeyValue { name: Some(name), value, .. } => match name.text() { + "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED), + "lang" => attr_flags.insert(AttrFlags::LANG_ITEM), + "path" => attr_flags.insert(AttrFlags::HAS_PATH), + "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE), + "export_name" => { + if let Some(value) = value + && let Some(value) = ast::String::cast(value) + && let Ok(value) = value.value() + && *value == *"main" + { + attr_flags.insert(AttrFlags::IS_EXPORT_NAME_MAIN); + } + } + _ => {} + }, + Meta::TokenTree { path, tt } => match path.segments.len() { + 1 => match path.segments[0].text() { + "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED), + "cfg" => attr_flags.insert(AttrFlags::HAS_CFG), + "doc" => extract_doc_tt_attr(attr_flags, tt), + "repr" => attr_flags.insert(AttrFlags::HAS_REPR), + "target_feature" => attr_flags.insert(AttrFlags::HAS_TARGET_FEATURE), + "proc_macro_derive" | "rustc_builtin_macro" => { + attr_flags.insert(AttrFlags::IS_DERIVE_OR_BUILTIN_MACRO) + } + "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE), + "rustc_layout_scalar_valid_range_start" | "rustc_layout_scalar_valid_range_end" => { + attr_flags.insert(AttrFlags::RUSTC_LAYOUT_SCALAR_VALID_RANGE) + } + "rustc_legacy_const_generics" => { + attr_flags.insert(AttrFlags::HAS_LEGACY_CONST_GENERICS) + } + "rustc_skip_during_method_dispatch" => { + extract_rustc_skip_during_method_dispatch(attr_flags, tt) + } + _ => {} + }, + 2 => match path.segments[0].text() { + "rust_analyzer" => match path.segments[1].text() { + "completions" => extract_ra_completions(attr_flags, tt), + _ => {} + }, + _ => {} + }, + _ => {} + }, + Meta::Path { path } => { + match path.segments.len() { + 1 => match path.segments[0].text() { + "rustc_has_incoherent_inherent_impls" => { + attr_flags.insert(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) + } + "rustc_allow_incoherent_impl" => { + attr_flags.insert(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL) + } + "fundamental" => attr_flags.insert(AttrFlags::FUNDAMENTAL), + "no_std" => attr_flags.insert(AttrFlags::IS_NO_STD), + "may_dangle" => attr_flags.insert(AttrFlags::MAY_DANGLE), + "rustc_paren_sugar" => attr_flags.insert(AttrFlags::RUSTC_PAREN_SUGAR), + "rustc_coinductive" => attr_flags.insert(AttrFlags::RUSTC_COINDUCTIVE), + "rustc_force_inline" => attr_flags.insert(AttrFlags::RUSTC_FORCE_INLINE), + "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE), + "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED), + "macro_export" => attr_flags.insert(AttrFlags::IS_MACRO_EXPORT), + "no_mangle" => attr_flags.insert(AttrFlags::NO_MANGLE), + "non_exhaustive" => attr_flags.insert(AttrFlags::NON_EXHAUSTIVE), + "ignore" => attr_flags.insert(AttrFlags::IS_IGNORE), + "bench" => attr_flags.insert(AttrFlags::IS_BENCH), + "rustc_const_panic_str" => attr_flags.insert(AttrFlags::RUSTC_CONST_PANIC_STR), + "rustc_intrinsic" => attr_flags.insert(AttrFlags::RUSTC_INTRINSIC), + "rustc_safe_intrinsic" => attr_flags.insert(AttrFlags::RUSTC_SAFE_INTRINSIC), + "rustc_intrinsic_must_be_overridden" => { + attr_flags.insert(AttrFlags::RUSTC_INTRINSIC_MUST_BE_OVERRIDDEN) + } + "rustc_allocator" => attr_flags.insert(AttrFlags::RUSTC_ALLOCATOR), + "rustc_deallocator" => attr_flags.insert(AttrFlags::RUSTC_DEALLOCATOR), + "rustc_reallocator" => attr_flags.insert(AttrFlags::RUSTC_REALLOCATOR), + "rustc_allocator_zeroed" => { + attr_flags.insert(AttrFlags::RUSTC_ALLOCATOR_ZEROED) + } + "rustc_reservation_impl" => { + attr_flags.insert(AttrFlags::RUSTC_RESERVATION_IMPL) + } + "rustc_deprecated_safe_2024" => { + attr_flags.insert(AttrFlags::RUSTC_DEPRECATED_SAFE_2024) + } + "rustc_skip_array_during_method_dispatch" => { + attr_flags.insert(AttrFlags::RUSTC_SKIP_ARRAY_DURING_METHOD_DISPATCH) + } + _ => {} + }, + 2 => match path.segments[0].text() { + "rust_analyzer" => match path.segments[1].text() { + "skip" => attr_flags.insert(AttrFlags::RUST_ANALYZER_SKIP), + _ => {} + }, + _ => {} + }, + _ => {} + } + + if path.is_test { + attr_flags.insert(AttrFlags::IS_TEST); + } + } + _ => {} + }; + ControlFlow::Continue(()) +} + +bitflags::bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct AttrFlags: u64 { + const RUST_ANALYZER_SKIP = 1 << 0; + + const LANG_ITEM = 1 << 1; + + const HAS_DOC_ALIASES = 1 << 2; + const HAS_DOC_KEYWORD = 1 << 3; + const IS_DOC_NOTABLE_TRAIT = 1 << 4; + const IS_DOC_HIDDEN = 1 << 5; + + const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 6; + const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7; + const FUNDAMENTAL = 1 << 8; + const RUSTC_SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 9; + const RUSTC_SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 10; + const HAS_REPR = 1 << 11; + const HAS_TARGET_FEATURE = 1 << 12; + const RUSTC_DEPRECATED_SAFE_2024 = 1 << 13; + const HAS_LEGACY_CONST_GENERICS = 1 << 14; + const NO_MANGLE = 1 << 15; + const NON_EXHAUSTIVE = 1 << 16; + const RUSTC_RESERVATION_IMPL = 1 << 17; + const RUSTC_CONST_PANIC_STR = 1 << 18; + const MAY_DANGLE = 1 << 19; + + const RUSTC_INTRINSIC = 1 << 20; + const RUSTC_SAFE_INTRINSIC = 1 << 21; + const RUSTC_INTRINSIC_MUST_BE_OVERRIDDEN = 1 << 22; + const RUSTC_ALLOCATOR = 1 << 23; + const RUSTC_DEALLOCATOR = 1 << 24; + const RUSTC_REALLOCATOR = 1 << 25; + const RUSTC_ALLOCATOR_ZEROED = 1 << 26; + + const IS_UNSTABLE = 1 << 27; + const IS_IGNORE = 1 << 28; + // FIXME: `IS_TEST` and `IS_BENCH` should be based on semantic information, not textual match. + const IS_BENCH = 1 << 29; + const IS_TEST = 1 << 30; + const IS_EXPORT_NAME_MAIN = 1 << 31; + const IS_MACRO_EXPORT = 1 << 32; + const IS_NO_STD = 1 << 33; + const IS_DERIVE_OR_BUILTIN_MACRO = 1 << 34; + const IS_DEPRECATED = 1 << 35; + const HAS_PATH = 1 << 36; + const HAS_CFG = 1 << 37; + + const COMPLETE_IGNORE_FLYIMPORT = 1 << 38; + const COMPLETE_IGNORE_FLYIMPORT_METHODS = 1 << 39; + const COMPLETE_IGNORE_METHODS = 1 << 40; + + const RUSTC_LAYOUT_SCALAR_VALID_RANGE = 1 << 41; + const RUSTC_PAREN_SUGAR = 1 << 42; + const RUSTC_COINDUCTIVE = 1 << 43; + const RUSTC_FORCE_INLINE = 1 << 44; + } +} + +fn attrs_source( + db: &dyn DefDatabase, + owner: AttrDefId, +) -> (InFile, Option>, Crate) { + let (owner, krate) = match owner { + AttrDefId::ModuleId(id) => { + let id = id.loc(db); + let def_map = id.def_map(db); + let (definition, declaration) = match def_map[id.local_id].origin { + ModuleOrigin::CrateRoot { definition } => { + let file = db.parse(definition).tree(); + (InFile::new(definition.into(), ast::AnyHasAttrs::from(file)), None) + } + ModuleOrigin::File { declaration, declaration_tree_id, definition, .. } => { + let declaration = InFile::new(declaration_tree_id.file_id(), declaration); + let declaration = declaration.with_value(declaration.to_node(db)); + let definition_source = db.parse(definition).tree(); + (InFile::new(definition.into(), definition_source.into()), Some(declaration)) + } + ModuleOrigin::Inline { definition_tree_id, definition } => { + let definition = InFile::new(definition_tree_id.file_id(), definition); + let definition = definition.with_value(definition.to_node(db).into()); + (definition, None) + } + ModuleOrigin::BlockExpr { block, .. } => { + let definition = block.to_node(db); + (block.with_value(definition.into()), None) + } + }; + return (definition, declaration, id.krate); + } + AttrDefId::AdtId(AdtId::StructId(it)) => attrs_from_ast_id_loc(db, it), + AttrDefId::AdtId(AdtId::UnionId(it)) => attrs_from_ast_id_loc(db, it), + AttrDefId::AdtId(AdtId::EnumId(it)) => attrs_from_ast_id_loc(db, it), + AttrDefId::FunctionId(it) => attrs_from_ast_id_loc(db, it), + AttrDefId::EnumVariantId(it) => attrs_from_ast_id_loc(db, it), + AttrDefId::StaticId(it) => attrs_from_ast_id_loc(db, it), + AttrDefId::ConstId(it) => attrs_from_ast_id_loc(db, it), + AttrDefId::TraitId(it) => attrs_from_ast_id_loc(db, it), + AttrDefId::TypeAliasId(it) => attrs_from_ast_id_loc(db, it), + AttrDefId::MacroId(MacroId::MacroRulesId(it)) => attrs_from_ast_id_loc(db, it), + AttrDefId::MacroId(MacroId::Macro2Id(it)) => attrs_from_ast_id_loc(db, it), + AttrDefId::MacroId(MacroId::ProcMacroId(it)) => attrs_from_ast_id_loc(db, it), + AttrDefId::ImplId(it) => attrs_from_ast_id_loc(db, it), + AttrDefId::ExternBlockId(it) => attrs_from_ast_id_loc(db, it), + AttrDefId::ExternCrateId(it) => attrs_from_ast_id_loc(db, it), + AttrDefId::UseId(it) => attrs_from_ast_id_loc(db, it), + }; + (owner, None, krate) +} + +fn collect_attrs( + db: &dyn DefDatabase, + owner: AttrDefId, + mut callback: impl FnMut(Meta) -> ControlFlow, +) -> Option { + let (source, outer_mod_decl, krate) = attrs_source(db, owner); + + let mut cfg_options = None; + expand_cfg_attr( + outer_mod_decl + .into_iter() + .flat_map(|it| it.value.attrs()) + .chain(ast::attrs_including_inner(&source.value)), + || cfg_options.get_or_insert_with(|| krate.cfg_options(db)), + move |meta, _, _, _| callback(meta), + ) +} + +fn collect_field_attrs( + db: &dyn DefDatabase, + variant: VariantId, + mut field_attrs: impl FnMut(&CfgOptions, InFile) -> T, +) -> ArenaMap { + let (variant_syntax, krate) = match variant { + VariantId::EnumVariantId(it) => attrs_from_ast_id_loc(db, it), + VariantId::StructId(it) => attrs_from_ast_id_loc(db, it), + VariantId::UnionId(it) => attrs_from_ast_id_loc(db, it), + }; + let cfg_options = krate.cfg_options(db); + let variant_syntax = variant_syntax + .with_value(ast::VariantDef::cast(variant_syntax.value.syntax().clone()).unwrap()); + let fields = match &variant_syntax.value { + ast::VariantDef::Struct(it) => it.field_list(), + ast::VariantDef::Union(it) => it.record_field_list().map(ast::FieldList::RecordFieldList), + ast::VariantDef::Variant(it) => it.field_list(), + }; + let Some(fields) = fields else { + return ArenaMap::new(); + }; + + let mut result = ArenaMap::new(); + let mut idx = 0; + match fields { + ast::FieldList::RecordFieldList(fields) => { + for field in fields.fields() { + if AttrFlags::is_cfg_enabled_for(&field, cfg_options).is_ok() { + result.insert( + la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(idx)), + field_attrs(cfg_options, variant_syntax.with_value(field.into())), + ); + idx += 1; + } + } + } + ast::FieldList::TupleFieldList(fields) => { + for field in fields.fields() { + if AttrFlags::is_cfg_enabled_for(&field, cfg_options).is_ok() { + result.insert( + la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(idx)), + field_attrs(cfg_options, variant_syntax.with_value(field.into())), + ); + idx += 1; + } + } + } + } + result.shrink_to_fit(); + result +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +pub struct RustcLayoutScalarValidRange { + pub start: Option, + pub end: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct DocsSourceMapLine { + /// The offset in [`Docs::docs`]. + string_offset: TextSize, + /// The offset in the AST of the text. + ast_offset: TextSize, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Docs { + /// The concatenated string of all `#[doc = "..."]` attributes and documentation comments. + docs: String, + /// A sorted map from an offset in `docs` to an offset in the source code. + docs_source_map: Vec, + /// If the item is an outlined module (`mod foo;`), `docs_source_map` store the concatenated + /// list of the outline and inline docs (outline first). Then, this field contains the [`HirFileId`] + /// of the outline declaration, and the index in `docs` from which the inline docs + /// begin. + outline_mod: Option<(HirFileId, usize)>, + inline_file: HirFileId, + /// The size the prepended prefix, which does not map to real doc comments. + prefix_len: TextSize, + /// The offset in `docs` from which the docs are inner attributes/comments. + inline_inner_docs_start: Option, + /// Like `inline_inner_docs_start`, but for `outline_mod`. This can happen only when merging `Docs` + /// (as outline modules don't have inner attributes). + outline_inner_docs_start: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum IsInnerDoc { + No, + Yes, +} + +impl IsInnerDoc { + #[inline] + pub fn yes(self) -> bool { + self == IsInnerDoc::Yes + } +} + +impl Docs { + #[inline] + pub fn docs(&self) -> &str { + &self.docs + } + + #[inline] + pub fn into_docs(self) -> String { + self.docs + } + + pub fn find_ast_range( + &self, + mut string_range: TextRange, + ) -> Option<(InFile, IsInnerDoc)> { + if string_range.start() < self.prefix_len { + return None; + } + string_range -= self.prefix_len; + + let mut file = self.inline_file; + let mut inner_docs_start = self.inline_inner_docs_start; + // Check whether the range is from the outline, the inline, or both. + let source_map = if let Some((outline_mod_file, outline_mod_end)) = self.outline_mod { + if let Some(first_inline) = self.docs_source_map.get(outline_mod_end) { + if string_range.end() <= first_inline.string_offset { + // The range is completely in the outline. + file = outline_mod_file; + inner_docs_start = self.outline_inner_docs_start; + &self.docs_source_map[..outline_mod_end] + } else if string_range.start() >= first_inline.string_offset { + // The range is completely in the inline. + &self.docs_source_map[outline_mod_end..] + } else { + // The range is combined from the outline and the inline - cannot map it back. + return None; + } + } else { + // There is no inline. + file = outline_mod_file; + inner_docs_start = self.outline_inner_docs_start; + &self.docs_source_map + } + } else { + // There is no outline. + &self.docs_source_map + }; + + let after_range = + source_map.partition_point(|line| line.string_offset <= string_range.start()) - 1; + let after_range = &source_map[after_range..]; + let line = after_range.first()?; + if after_range.get(1).is_some_and(|next_line| next_line.string_offset < string_range.end()) + { + // The range is combined from two lines - cannot map it back. + return None; + } + let ast_range = string_range - line.string_offset + line.ast_offset; + let is_inner = if inner_docs_start + .is_some_and(|inner_docs_start| string_range.start() >= inner_docs_start) + { + IsInnerDoc::Yes + } else { + IsInnerDoc::No + }; + Some((InFile::new(file, ast_range), is_inner)) + } + + #[inline] + pub fn shift_by(&mut self, offset: TextSize) { + self.prefix_len += offset; + } + + pub fn prepend_str(&mut self, s: &str) { + self.prefix_len += TextSize::of(s); + self.docs.insert_str(0, s); + } + + pub fn append_str(&mut self, s: &str) { + self.docs.push_str(s); + } + + pub fn append(&mut self, other: &Docs) { + let other_offset = TextSize::of(&self.docs); + + assert!( + self.outline_mod.is_none() && other.outline_mod.is_none(), + "cannot merge `Docs` that have `outline_mod` set" + ); + self.outline_mod = Some((self.inline_file, self.docs_source_map.len())); + self.inline_file = other.inline_file; + self.outline_inner_docs_start = self.inline_inner_docs_start; + self.inline_inner_docs_start = other.inline_inner_docs_start.map(|it| it + other_offset); + + self.docs.push_str(&other.docs); + self.docs_source_map.extend(other.docs_source_map.iter().map( + |&DocsSourceMapLine { string_offset, ast_offset }| DocsSourceMapLine { + ast_offset, + string_offset: string_offset + other_offset, + }, + )); + } + + fn extend_with_doc_comment(&mut self, comment: ast::Comment, indent: &mut usize) { + let Some((doc, offset)) = comment.doc_comment() else { return }; + self.extend_with_doc_str(doc, comment.syntax().text_range().start() + offset, indent); + } + + fn extend_with_doc_attr(&mut self, value: SyntaxToken, indent: &mut usize) { + let Some(value) = ast::String::cast(value) else { return }; + let Some(value_offset) = value.text_range_between_quotes() else { return }; + let value_offset = value_offset.start(); + let Ok(value) = value.value() else { return }; + // FIXME: Handle source maps for escaped text. + self.extend_with_doc_str(&value, value_offset, indent); + } + + fn extend_with_doc_str(&mut self, doc: &str, mut offset_in_ast: TextSize, indent: &mut usize) { + for line in doc.split('\n') { + self.docs_source_map.push(DocsSourceMapLine { + string_offset: TextSize::of(&self.docs), + ast_offset: offset_in_ast, + }); + offset_in_ast += TextSize::of(line) + TextSize::of("\n"); + + let line = line.trim_end(); + if let Some(line_indent) = line.chars().position(|ch| !ch.is_whitespace()) { + // Empty lines are handled because `position()` returns `None` for them. + *indent = std::cmp::min(*indent, line_indent); + } + self.docs.push_str(line); + self.docs.push('\n'); + } + } + + fn remove_indent(&mut self, indent: usize, start_source_map_index: usize) { + /// In case of panics, we want to avoid corrupted UTF-8 in `self.docs`, so we clear it. + struct Guard<'a>(&'a mut Docs); + impl Drop for Guard<'_> { + fn drop(&mut self) { + let Docs { + docs, + docs_source_map, + outline_mod, + inline_file: _, + prefix_len: _, + inline_inner_docs_start: _, + outline_inner_docs_start: _, + } = self.0; + // Don't use `String::clear()` here because it's not guaranteed to not do UTF-8-dependent things, + // and we may have temporarily broken the string's encoding. + unsafe { docs.as_mut_vec() }.clear(); + // This is just to avoid panics down the road. + docs_source_map.clear(); + *outline_mod = None; + } + } + + if self.docs.is_empty() { + return; + } + + let guard = Guard(self); + let source_map = &mut guard.0.docs_source_map[start_source_map_index..]; + let Some(&DocsSourceMapLine { string_offset: mut copy_into, .. }) = source_map.first() + else { + return; + }; + // We basically want to remove multiple ranges from a string. Doing this efficiently (without O(N^2) + // or allocations) requires unsafe. Basically, for each line, we copy the line minus the indent into + // consecutive to the previous line (which may have moved). Then at the end we truncate. + let mut accumulated_offset = TextSize::new(0); + for idx in 0..source_map.len() { + let string_end_offset = source_map + .get(idx + 1) + .map_or_else(|| TextSize::of(&guard.0.docs), |next_attr| next_attr.string_offset); + let line_source = &mut source_map[idx]; + let line_docs = + &guard.0.docs[TextRange::new(line_source.string_offset, string_end_offset)]; + let line_docs_len = TextSize::of(line_docs); + let indent_size = line_docs.char_indices().nth(indent).map_or_else( + || TextSize::of(line_docs) - TextSize::of("\n"), + |(offset, _)| TextSize::new(offset as u32), + ); + unsafe { guard.0.docs.as_bytes_mut() }.copy_within( + Range::::from(TextRange::new( + line_source.string_offset + indent_size, + string_end_offset, + )), + copy_into.into(), + ); + copy_into += line_docs_len - indent_size; + + if let Some(inner_attrs_start) = &mut guard.0.inline_inner_docs_start + && *inner_attrs_start == line_source.string_offset + { + *inner_attrs_start -= accumulated_offset; + } + // The removals in the string accumulate, but in the AST not, because it already points + // to the beginning of each attribute. + // Also, we need to shift the AST offset of every line, but the string offset of the first + // line should not get shifted (in general, the shift for the string offset is by the + // number of lines until the current one, excluding the current one). + line_source.string_offset -= accumulated_offset; + line_source.ast_offset += indent_size; + + accumulated_offset += indent_size; + } + // Don't use `String::truncate()` here because it's not guaranteed to not do UTF-8-dependent things, + // and we may have temporarily broken the string's encoding. + unsafe { guard.0.docs.as_mut_vec() }.truncate(copy_into.into()); + + std::mem::forget(guard); + } + + fn remove_last_newline(&mut self) { + self.docs.truncate(self.docs.len().saturating_sub(1)); + } + + fn shrink_to_fit(&mut self) { + let Docs { + docs, + docs_source_map, + outline_mod: _, + inline_file: _, + prefix_len: _, + inline_inner_docs_start: _, + outline_inner_docs_start: _, + } = self; + docs.shrink_to_fit(); + docs_source_map.shrink_to_fit(); + } +} + +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct DeriveInfo { + pub trait_name: Symbol, + pub helpers: Box<[Symbol]>, +} + +fn extract_doc_aliases(result: &mut Vec, attr: Meta) -> ControlFlow { + if let Meta::TokenTree { path, tt } = attr + && path.is1("doc") + { + for atom in DocAtom::parse(tt) { + match atom { + DocAtom::Alias(aliases) => { + result.extend(aliases.into_iter().map(|alias| Symbol::intern(&alias))) + } + DocAtom::KeyValue { key, value } if key == "alias" => { + result.push(Symbol::intern(&value)) + } + _ => {} + } + } + } + ControlFlow::Continue(()) +} + +fn extract_cfgs(result: &mut Vec, attr: Meta) -> ControlFlow { + if let Meta::TokenTree { path, tt } = attr + && path.is1("cfg") + { + result.push(CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(&tt).peekable())); + } + ControlFlow::Continue(()) +} + +fn extract_docs<'a>( + get_cfg_options: &dyn Fn() -> &'a CfgOptions, + source: InFile, + outer_mod_decl: Option>, + inner_attrs_node: Option, +) -> Option> { + let mut result = Docs { + docs: String::new(), + docs_source_map: Vec::new(), + outline_mod: None, + inline_file: source.file_id, + prefix_len: TextSize::new(0), + inline_inner_docs_start: None, + outline_inner_docs_start: None, + }; + + let mut cfg_options = None; + let mut extend_with_attrs = + |result: &mut Docs, node: &SyntaxNode, expect_inner_attrs, indent: &mut usize| { + expand_cfg_attr_with_doc_comments::<_, Infallible>( + AttrDocCommentIter::from_syntax_node(node).filter(|attr| match attr { + Either::Left(attr) => attr.kind().is_inner() == expect_inner_attrs, + Either::Right(comment) => comment.kind().doc.is_some_and(|kind| { + (kind == ast::CommentPlacement::Inner) == expect_inner_attrs + }), + }), + || cfg_options.get_or_insert_with(get_cfg_options), + |attr| { + match attr { + Either::Right(doc_comment) => { + result.extend_with_doc_comment(doc_comment, indent) + } + Either::Left((attr, _, _, _)) => match attr { + // FIXME: Handle macros: `#[doc = concat!("foo", "bar")]`. + Meta::NamedKeyValue { + name: Some(name), value: Some(value), .. + } if name.text() == "doc" => { + result.extend_with_doc_attr(value, indent); + } + _ => {} + }, + } + ControlFlow::Continue(()) + }, + ); + }; + + if let Some(outer_mod_decl) = outer_mod_decl { + let mut indent = usize::MAX; + extend_with_attrs(&mut result, outer_mod_decl.value.syntax(), false, &mut indent); + result.remove_indent(indent, 0); + result.outline_mod = Some((outer_mod_decl.file_id, result.docs_source_map.len())); + } + + let inline_source_map_start = result.docs_source_map.len(); + let mut indent = usize::MAX; + extend_with_attrs(&mut result, source.value.syntax(), false, &mut indent); + if let Some(inner_attrs_node) = &inner_attrs_node { + result.inline_inner_docs_start = Some(TextSize::of(&result.docs)); + extend_with_attrs(&mut result, inner_attrs_node, true, &mut indent); + } + result.remove_indent(indent, inline_source_map_start); + + result.remove_last_newline(); + + result.shrink_to_fit(); + + if result.docs.is_empty() { None } else { Some(Box::new(result)) } +} + +#[salsa::tracked] +impl AttrFlags { + #[salsa::tracked] + pub fn query(db: &dyn DefDatabase, owner: AttrDefId) -> AttrFlags { + let mut attr_flags = AttrFlags::empty(); + collect_attrs(db, owner, |attr| match_attr_flags(&mut attr_flags, attr)); + attr_flags + } + + #[inline] + pub fn query_field(db: &dyn DefDatabase, field: FieldId) -> AttrFlags { + return field_attr_flags(db, field.parent) + .get(field.local_id) + .copied() + .unwrap_or_else(AttrFlags::empty); + + #[salsa::tracked(returns(ref))] + fn field_attr_flags( + db: &dyn DefDatabase, + variant: VariantId, + ) -> ArenaMap { + collect_field_attrs(db, variant, |cfg_options, field| { + let mut attr_flags = AttrFlags::empty(); + expand_cfg_attr( + field.value.attrs(), + || cfg_options, + |attr, _, _, _| match_attr_flags(&mut attr_flags, attr), + ); + attr_flags + }) + } + } + + #[inline] + pub fn query_generic_params( + db: &dyn DefDatabase, + def: GenericDefId, + ) -> &(ArenaMap, ArenaMap) + { + let generic_params = GenericParams::new(db, def); + let params_count_excluding_self = + generic_params.len() - usize::from(generic_params.trait_self_param().is_some()); + if params_count_excluding_self == 0 { + return const { &(ArenaMap::new(), ArenaMap::new()) }; + } + return generic_params_attr_flags(db, def); + + #[salsa::tracked(returns(ref))] + fn generic_params_attr_flags( + db: &dyn DefDatabase, + def: GenericDefId, + ) -> (ArenaMap, ArenaMap) + { + let mut lifetimes = ArenaMap::new(); + let mut type_and_consts = ArenaMap::new(); + + let mut cfg_options = None; + let mut cfg_options = + || *cfg_options.get_or_insert_with(|| def.krate(db).cfg_options(db)); + + let lifetimes_source = HasChildSource::::child_source(&def, db); + for (lifetime_id, lifetime) in lifetimes_source.value.iter() { + let mut attr_flags = AttrFlags::empty(); + expand_cfg_attr(lifetime.attrs(), &mut cfg_options, |attr, _, _, _| { + match_attr_flags(&mut attr_flags, attr) + }); + if !attr_flags.is_empty() { + lifetimes.insert(lifetime_id, attr_flags); + } + } + + let type_and_consts_source = + HasChildSource::::child_source(&def, db); + for (type_or_const_id, type_or_const) in type_and_consts_source.value.iter() { + let mut attr_flags = AttrFlags::empty(); + expand_cfg_attr(type_or_const.attrs(), &mut cfg_options, |attr, _, _, _| { + match_attr_flags(&mut attr_flags, attr) + }); + if !attr_flags.is_empty() { + type_and_consts.insert(type_or_const_id, attr_flags); + } + } + + lifetimes.shrink_to_fit(); + type_and_consts.shrink_to_fit(); + (lifetimes, type_and_consts) + } + } + + #[inline] + pub fn query_lifetime_param(db: &dyn DefDatabase, owner: LifetimeParamId) -> AttrFlags { + AttrFlags::query_generic_params(db, owner.parent) + .0 + .get(owner.local_id) + .copied() + .unwrap_or_else(AttrFlags::empty) + } + #[inline] + pub fn query_type_or_const_param(db: &dyn DefDatabase, owner: TypeOrConstParamId) -> AttrFlags { + AttrFlags::query_generic_params(db, owner.parent) + .1 + .get(owner.local_id) + .copied() + .unwrap_or_else(AttrFlags::empty) + } + + pub(crate) fn is_cfg_enabled_for( + owner: &dyn HasAttrs, + cfg_options: &CfgOptions, + ) -> Result<(), CfgExpr> { + let attrs = ast::attrs_including_inner(owner); + let result = expand_cfg_attr( + attrs, + || cfg_options, + |attr, _, _, _| { + if let Meta::TokenTree { path, tt } = attr + && path.is1("cfg") + && let cfg = + CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(&tt).peekable()) + && cfg_options.check(&cfg) == Some(false) + { + ControlFlow::Break(cfg) + } else { + ControlFlow::Continue(()) + } + }, + ); + match result { + Some(cfg) => Err(cfg), + None => Ok(()), + } + } + + #[inline] + pub fn lang_item(db: &dyn DefDatabase, owner: AttrDefId) -> Option { + AttrFlags::query(db, owner).lang_item_with_attrs(db, owner) + } + + #[inline] + pub fn lang_item_with_attrs(self, db: &dyn DefDatabase, owner: AttrDefId) -> Option { + if !self.contains(AttrFlags::LANG_ITEM) { + // Don't create the query in case this is not a lang item, this wastes memory. + return None; + } + + return lang_item(db, owner); + + #[salsa::tracked] + fn lang_item(db: &dyn DefDatabase, owner: AttrDefId) -> Option { + collect_attrs(db, owner, |attr| { + if let Meta::NamedKeyValue { name: Some(name), value: Some(value), .. } = attr + && name.text() == "lang" + && let Some(value) = ast::String::cast(value) + && let Ok(value) = value.value() + && let symbol = Symbol::intern(&value) + && let Some(lang_item) = LangItem::from_symbol(&symbol) + { + ControlFlow::Break(lang_item) + } else { + ControlFlow::Continue(()) + } + }) + } + } + + #[inline] + pub fn repr(db: &dyn DefDatabase, owner: AdtId) -> Option { + if !AttrFlags::query(db, owner.into()).contains(AttrFlags::HAS_REPR) { + // Don't create the query in case this has no repr, this wastes memory. + return None; + } + + return repr(db, owner); + + #[salsa::tracked] + fn repr(db: &dyn DefDatabase, owner: AdtId) -> Option { + let mut result = None; + collect_attrs::(db, owner.into(), |attr| { + if let Meta::TokenTree { path, tt } = attr + && path.is1("repr") + && let Some(repr) = parse_repr_tt(&tt) + { + match &mut result { + Some(existing) => merge_repr(existing, repr), + None => result = Some(repr), + } + } + ControlFlow::Continue(()) + }); + result + } + } + + /// Call this only if there are legacy const generics, to save memory. + #[salsa::tracked(returns(ref))] + pub(crate) fn legacy_const_generic_indices( + db: &dyn DefDatabase, + owner: FunctionId, + ) -> Option> { + let result = collect_attrs(db, owner.into(), |attr| { + if let Meta::TokenTree { path, tt } = attr + && path.is1("rustc_legacy_const_generics") + { + let result = parse_rustc_legacy_const_generics(tt); + ControlFlow::Break(result) + } else { + ControlFlow::Continue(()) + } + }); + result.filter(|it| !it.is_empty()) + } + + // There aren't typically many crates, so it's okay to always make this a query without a flag. + #[salsa::tracked(returns(ref))] + pub fn doc_html_root_url(db: &dyn DefDatabase, krate: Crate) -> Option { + let root_file_id = krate.root_file_id(db); + let syntax = db.parse(root_file_id).tree(); + + let mut cfg_options = None; + expand_cfg_attr( + syntax.attrs(), + || cfg_options.get_or_insert(krate.cfg_options(db)), + |attr, _, _, _| { + if let Meta::TokenTree { path, tt } = attr + && path.is1("doc") + && let Some(result) = DocAtom::parse(tt).into_iter().find_map(|atom| { + if let DocAtom::KeyValue { key, value } = atom + && key == "html_root_url" + { + Some(value) + } else { + None + } + }) + { + ControlFlow::Break(result) + } else { + ControlFlow::Continue(()) + } + }, + ) + } + + #[inline] + pub fn target_features(db: &dyn DefDatabase, owner: FunctionId) -> &FxHashSet { + if !AttrFlags::query(db, owner.into()).contains(AttrFlags::HAS_TARGET_FEATURE) { + return const { &FxHashSet::with_hasher(rustc_hash::FxBuildHasher) }; + } + + return target_features(db, owner); + + #[salsa::tracked(returns(ref))] + fn target_features(db: &dyn DefDatabase, owner: FunctionId) -> FxHashSet { + let mut result = FxHashSet::default(); + collect_attrs::(db, owner.into(), |attr| { + if let Meta::TokenTree { path, tt } = attr + && path.is1("target_feature") + && let mut tt = TokenTreeChildren::new(&tt) + && let Some(NodeOrToken::Token(enable_ident)) = tt.next() + && enable_ident.text() == "enable" + && let Some(NodeOrToken::Token(eq_token)) = tt.next() + && eq_token.kind() == T![=] + && let Some(NodeOrToken::Token(features)) = tt.next() + && let Some(features) = ast::String::cast(features) + && let Ok(features) = features.value() + && tt.next().is_none() + { + result.extend(features.split(',').map(Symbol::intern)); + } + ControlFlow::Continue(()) + }); + result.shrink_to_fit(); + result + } + } + + #[inline] + pub fn rustc_layout_scalar_valid_range( + db: &dyn DefDatabase, + owner: AdtId, + ) -> RustcLayoutScalarValidRange { + if !AttrFlags::query(db, owner.into()).contains(AttrFlags::RUSTC_LAYOUT_SCALAR_VALID_RANGE) + { + return RustcLayoutScalarValidRange::default(); + } + + return rustc_layout_scalar_valid_range(db, owner); + + #[salsa::tracked] + fn rustc_layout_scalar_valid_range( + db: &dyn DefDatabase, + owner: AdtId, + ) -> RustcLayoutScalarValidRange { + let mut result = RustcLayoutScalarValidRange::default(); + collect_attrs::(db, owner.into(), |attr| { + if let Meta::TokenTree { path, tt } = attr + && (path.is1("rustc_layout_scalar_valid_range_start") + || path.is1("rustc_layout_scalar_valid_range_end")) + && let tt = TokenTreeChildren::new(&tt) + && let Ok(NodeOrToken::Token(value)) = tt.exactly_one() + && let Some(value) = ast::IntNumber::cast(value) + && let Ok(value) = value.value() + { + if path.is1("rustc_layout_scalar_valid_range_start") { + result.start = Some(value) + } else { + result.end = Some(value); + } + } + ControlFlow::Continue(()) + }); + result + } + } + + #[inline] + pub fn doc_aliases(self, db: &dyn DefDatabase, owner: Either) -> &[Symbol] { + if !self.contains(AttrFlags::HAS_DOC_ALIASES) { + return &[]; + } + return match owner { + Either::Left(it) => doc_aliases(db, it), + Either::Right(field) => fields_doc_aliases(db, field.parent) + .get(field.local_id) + .map(|it| &**it) + .unwrap_or_default(), + }; + + #[salsa::tracked(returns(ref))] + fn doc_aliases(db: &dyn DefDatabase, owner: AttrDefId) -> Box<[Symbol]> { + let mut result = Vec::new(); + collect_attrs::(db, owner, |attr| extract_doc_aliases(&mut result, attr)); + result.into_boxed_slice() + } + + #[salsa::tracked(returns(ref))] + fn fields_doc_aliases( + db: &dyn DefDatabase, + variant: VariantId, + ) -> ArenaMap> { + collect_field_attrs(db, variant, |cfg_options, field| { + let mut result = Vec::new(); + expand_cfg_attr( + field.value.attrs(), + || cfg_options, + |attr, _, _, _| extract_doc_aliases(&mut result, attr), + ); + result.into_boxed_slice() + }) + } + } + + #[inline] + pub fn cfgs(self, db: &dyn DefDatabase, owner: Either) -> Option<&CfgExpr> { + if !self.contains(AttrFlags::HAS_CFG) { + return None; + } + return match owner { + Either::Left(it) => cfgs(db, it).as_ref(), + Either::Right(field) => { + fields_cfgs(db, field.parent).get(field.local_id).and_then(|it| it.as_ref()) + } + }; + + // We LRU this query because it is only used by IDE. + #[salsa::tracked(returns(ref), lru = 250)] + fn cfgs(db: &dyn DefDatabase, owner: AttrDefId) -> Option { + let mut result = Vec::new(); + collect_attrs::(db, owner, |attr| extract_cfgs(&mut result, attr)); + match result.len() { + 0 => None, + 1 => result.into_iter().next(), + _ => Some(CfgExpr::All(result.into_boxed_slice())), + } + } + + // We LRU this query because it is only used by IDE. + #[salsa::tracked(returns(ref), lru = 50)] + fn fields_cfgs( + db: &dyn DefDatabase, + variant: VariantId, + ) -> ArenaMap> { + collect_field_attrs(db, variant, |cfg_options, field| { + let mut result = Vec::new(); + expand_cfg_attr( + field.value.attrs(), + || cfg_options, + |attr, _, _, _| extract_cfgs(&mut result, attr), + ); + match result.len() { + 0 => None, + 1 => result.into_iter().next(), + _ => Some(CfgExpr::All(result.into_boxed_slice())), + } + }) + } + } + + #[inline] + pub fn doc_keyword(db: &dyn DefDatabase, owner: InternedModuleId) -> Option { + if !AttrFlags::query(db, AttrDefId::ModuleId(owner)).contains(AttrFlags::HAS_DOC_KEYWORD) { + return None; + } + return doc_keyword(db, owner); + + #[salsa::tracked] + fn doc_keyword(db: &dyn DefDatabase, owner: InternedModuleId) -> Option { + collect_attrs(db, AttrDefId::ModuleId(owner), |attr| { + if let Meta::TokenTree { path, tt } = attr + && path.is1("doc") + { + for atom in DocAtom::parse(tt) { + if let DocAtom::KeyValue { key, value } = atom + && key == "keyword" + { + return ControlFlow::Break(Symbol::intern(&value)); + } + } + } + ControlFlow::Continue(()) + }) + } + } + + // We LRU this query because it is only used by IDE. + #[salsa::tracked(returns(ref), lru = 250)] + pub fn docs(db: &dyn DefDatabase, owner: AttrDefId) -> Option> { + let (source, outer_mod_decl, krate) = attrs_source(db, owner); + let inner_attrs_node = source.value.inner_attributes_node(); + extract_docs(&|| krate.cfg_options(db), source, outer_mod_decl, inner_attrs_node) + } + + #[inline] + pub fn field_docs(db: &dyn DefDatabase, field: FieldId) -> Option<&Docs> { + return fields_docs(db, field.parent).get(field.local_id).and_then(|it| it.as_deref()); + + // We LRU this query because it is only used by IDE. + #[salsa::tracked(returns(ref), lru = 50)] + pub fn fields_docs( + db: &dyn DefDatabase, + variant: VariantId, + ) -> ArenaMap>> { + collect_field_attrs(db, variant, |cfg_options, field| { + extract_docs(&|| cfg_options, field, None, None) + }) + } + } + + #[inline] + pub fn derive_info(db: &dyn DefDatabase, owner: MacroId) -> Option<&DeriveInfo> { + if !AttrFlags::query(db, owner.into()).contains(AttrFlags::IS_DERIVE_OR_BUILTIN_MACRO) { + return None; + } + + return derive_info(db, owner).as_ref(); + + #[salsa::tracked(returns(ref))] + fn derive_info(db: &dyn DefDatabase, owner: MacroId) -> Option { + collect_attrs(db, owner.into(), |attr| { + if let Meta::TokenTree { path, tt } = attr + && path.segments.len() == 1 + && matches!( + path.segments[0].text(), + "proc_macro_derive" | "rustc_builtin_macro" + ) + && let mut tt = TokenTreeChildren::new(&tt) + && let Some(NodeOrToken::Token(trait_name)) = tt.next() + && trait_name.kind().is_any_identifier() + { + let trait_name = Symbol::intern(trait_name.text()); + + let helpers = if let Some(NodeOrToken::Token(comma)) = tt.next() + && comma.kind() == T![,] + && let Some(NodeOrToken::Token(attributes)) = tt.next() + && attributes.text() == "attributes" + && let Some(NodeOrToken::Node(attributes)) = tt.next() + { + attributes + .syntax() + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .filter(|it| it.kind().is_any_identifier()) + .map(|it| Symbol::intern(it.text())) + .collect::>() + } else { + Box::new([]) + }; + + ControlFlow::Break(DeriveInfo { trait_name, helpers }) + } else { + ControlFlow::Continue(()) + } + }) + } + } +} + +fn merge_repr(this: &mut ReprOptions, other: ReprOptions) { + let ReprOptions { int, align, pack, flags, field_shuffle_seed: _ } = this; + flags.insert(other.flags); + *align = (*align).max(other.align); + *pack = match (*pack, other.pack) { + (Some(pack), None) | (None, Some(pack)) => Some(pack), + _ => (*pack).min(other.pack), + }; + if other.int.is_some() { + *int = other.int; + } +} + +fn parse_repr_tt(tt: &ast::TokenTree) -> Option { + use crate::builtin_type::{BuiltinInt, BuiltinUint}; + use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; + + let mut tts = TokenTreeChildren::new(tt).peekable(); + + let mut acc = ReprOptions::default(); + while let Some(tt) = tts.next() { + let NodeOrToken::Token(ident) = tt else { + continue; + }; + if !ident.kind().is_any_identifier() { + continue; + } + let repr = match ident.text() { + "packed" => { + let pack = if let Some(NodeOrToken::Node(tt)) = tts.peek() { + let tt = tt.clone(); + tts.next(); + let mut tt_iter = TokenTreeChildren::new(&tt); + if let Some(NodeOrToken::Token(lit)) = tt_iter.next() + && let Some(lit) = ast::IntNumber::cast(lit) + && let Ok(lit) = lit.value() + && let Ok(lit) = lit.try_into() + { + lit + } else { + 0 + } + } else { + 0 + }; + let pack = Some(Align::from_bytes(pack).unwrap_or(Align::ONE)); + ReprOptions { pack, ..Default::default() } + } + "align" => { + let mut align = None; + if let Some(NodeOrToken::Node(tt)) = tts.peek() { + let tt = tt.clone(); + tts.next(); + let mut tt_iter = TokenTreeChildren::new(&tt); + if let Some(NodeOrToken::Token(lit)) = tt_iter.next() + && let Some(lit) = ast::IntNumber::cast(lit) + && let Ok(lit) = lit.value() + && let Ok(lit) = lit.try_into() + { + align = Align::from_bytes(lit).ok(); + } + } + ReprOptions { align, ..Default::default() } + } + "C" => ReprOptions { flags: ReprFlags::IS_C, ..Default::default() }, + "transparent" => ReprOptions { flags: ReprFlags::IS_TRANSPARENT, ..Default::default() }, + "simd" => ReprOptions { flags: ReprFlags::IS_SIMD, ..Default::default() }, + repr => { + let mut int = None; + if let Some(builtin) = BuiltinInt::from_suffix(repr) + .map(Either::Left) + .or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right)) + { + int = Some(match builtin { + Either::Left(bi) => match bi { + BuiltinInt::Isize => IntegerType::Pointer(true), + BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true), + BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true), + BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true), + BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true), + BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true), + }, + Either::Right(bu) => match bu { + BuiltinUint::Usize => IntegerType::Pointer(false), + BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false), + BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false), + BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false), + BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false), + BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false), + }, + }); + } + ReprOptions { int, ..Default::default() } + } + }; + merge_repr(&mut acc, repr); + } + + Some(acc) +} + +fn parse_rustc_legacy_const_generics(tt: ast::TokenTree) -> Box<[u32]> { + TokenTreeChildren::new(&tt) + .filter_map(|param| { + ast::IntNumber::cast(param.into_token()?)?.value().ok()?.try_into().ok() + }) + .collect() +} + +#[derive(Debug)] +enum DocAtom { + /// eg. `#[doc(hidden)]` + Flag(SmolStr), + /// eg. `#[doc(alias = "it")]` + /// + /// Note that a key can have multiple values that are all considered "active" at the same time. + /// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`. + KeyValue { key: SmolStr, value: SmolStr }, + /// eg. `#[doc(alias("x", "y"))]` + Alias(Vec), +} + +impl DocAtom { + fn parse(tt: ast::TokenTree) -> SmallVec<[DocAtom; 1]> { + let mut iter = TokenTreeChildren::new(&tt).peekable(); + let mut result = SmallVec::new(); + while iter.peek().is_some() { + if let Some(expr) = next_doc_expr(&mut iter) { + result.push(expr); + } + } + result + } +} + +fn next_doc_expr(it: &mut Peekable) -> Option { + let name = match it.next() { + Some(NodeOrToken::Token(token)) if token.kind().is_any_identifier() => { + SmolStr::new(token.text()) + } + _ => return None, + }; + + let ret = match it.peek() { + Some(NodeOrToken::Token(eq)) if eq.kind() == T![=] => { + it.next(); + if let Some(NodeOrToken::Token(value)) = it.next() + && let Some(value) = ast::String::cast(value) + && let Ok(value) = value.value() + { + DocAtom::KeyValue { key: name, value: SmolStr::new(&*value) } + } else { + return None; + } + } + Some(NodeOrToken::Node(subtree)) => { + if name != "alias" { + return None; + } + let aliases = TokenTreeChildren::new(subtree) + .filter_map(|alias| { + Some(SmolStr::new(&*ast::String::cast(alias.into_token()?)?.value().ok()?)) + }) + .collect(); + it.next(); + DocAtom::Alias(aliases) + } + _ => DocAtom::Flag(name), + }; + Some(ret) +} + +#[cfg(test)] +mod tests { + use expect_test::expect; + use hir_expand::InFile; + use test_fixture::WithFixture; + use tt::{TextRange, TextSize}; + + use crate::attrs::IsInnerDoc; + use crate::{attrs::Docs, test_db::TestDB}; + + #[test] + fn docs() { + let (_db, file_id) = TestDB::with_single_file(""); + let mut docs = Docs { + docs: String::new(), + docs_source_map: Vec::new(), + outline_mod: None, + inline_file: file_id.into(), + prefix_len: TextSize::new(0), + inline_inner_docs_start: None, + outline_inner_docs_start: None, + }; + let mut indent = usize::MAX; + + let outer = " foo\n\tbar baz"; + let mut ast_offset = TextSize::new(123); + for line in outer.split('\n') { + docs.extend_with_doc_str(line, ast_offset, &mut indent); + ast_offset += TextSize::of(line) + TextSize::of("\n"); + } + + docs.inline_inner_docs_start = Some(TextSize::of(&docs.docs)); + ast_offset += TextSize::new(123); + let inner = " bar \n baz"; + for line in inner.split('\n') { + docs.extend_with_doc_str(line, ast_offset, &mut indent); + ast_offset += TextSize::of(line) + TextSize::of("\n"); + } + + assert_eq!(indent, 1); + expect![[r#" + [ + DocsSourceMapLine { + string_offset: 0, + ast_offset: 123, + }, + DocsSourceMapLine { + string_offset: 5, + ast_offset: 128, + }, + DocsSourceMapLine { + string_offset: 15, + ast_offset: 261, + }, + DocsSourceMapLine { + string_offset: 20, + ast_offset: 267, + }, + ] + "#]] + .assert_debug_eq(&docs.docs_source_map); + + docs.remove_indent(indent, 0); + + assert_eq!(docs.inline_inner_docs_start, Some(TextSize::new(13))); + + assert_eq!(docs.docs, "foo\nbar baz\nbar\nbaz\n"); + expect![[r#" + [ + DocsSourceMapLine { + string_offset: 0, + ast_offset: 124, + }, + DocsSourceMapLine { + string_offset: 4, + ast_offset: 129, + }, + DocsSourceMapLine { + string_offset: 13, + ast_offset: 262, + }, + DocsSourceMapLine { + string_offset: 17, + ast_offset: 268, + }, + ] + "#]] + .assert_debug_eq(&docs.docs_source_map); + + docs.append(&docs.clone()); + docs.prepend_str("prefix---"); + assert_eq!(docs.docs, "prefix---foo\nbar baz\nbar\nbaz\nfoo\nbar baz\nbar\nbaz\n"); + expect![[r#" + [ + DocsSourceMapLine { + string_offset: 0, + ast_offset: 124, + }, + DocsSourceMapLine { + string_offset: 4, + ast_offset: 129, + }, + DocsSourceMapLine { + string_offset: 13, + ast_offset: 262, + }, + DocsSourceMapLine { + string_offset: 17, + ast_offset: 268, + }, + DocsSourceMapLine { + string_offset: 21, + ast_offset: 124, + }, + DocsSourceMapLine { + string_offset: 25, + ast_offset: 129, + }, + DocsSourceMapLine { + string_offset: 34, + ast_offset: 262, + }, + DocsSourceMapLine { + string_offset: 38, + ast_offset: 268, + }, + ] + "#]] + .assert_debug_eq(&docs.docs_source_map); + + let range = |start, end| TextRange::new(TextSize::new(start), TextSize::new(end)); + let in_file = |range| InFile::new(file_id.into(), range); + assert_eq!(docs.find_ast_range(range(0, 2)), None); + assert_eq!(docs.find_ast_range(range(8, 10)), None); + assert_eq!( + docs.find_ast_range(range(9, 10)), + Some((in_file(range(124, 125)), IsInnerDoc::No)) + ); + assert_eq!(docs.find_ast_range(range(20, 23)), None); + assert_eq!( + docs.find_ast_range(range(23, 25)), + Some((in_file(range(263, 265)), IsInnerDoc::Yes)) + ); + } +} 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 4e1d598623ab..ad2990087672 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -1,23 +1,21 @@ //! Defines database & queries for name resolution. use base_db::{Crate, RootQueryDb, SourceDatabase}; -use either::Either; use hir_expand::{ EditionedFileId, HirFileId, InFile, Lookup, MacroCallId, MacroDefId, MacroDefKind, db::ExpandDatabase, }; -use intern::sym; use la_arena::ArenaMap; -use syntax::{AstPtr, ast}; use triomphe::Arc; use crate::{ - AssocItemId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, - EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, - FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalFieldId, Macro2Id, Macro2Loc, - MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, - ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, - TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId, - attr::{Attrs, AttrsWithOwner}, + AssocItemId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, CrateRootModuleId, DefWithBodyId, + EnumId, EnumLoc, EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, + ExternCrateLoc, FunctionId, FunctionLoc, GenericDefId, HasModule, ImplId, ImplLoc, + InternedModuleId, LocalFieldId, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, + MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, + StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, + VariantId, + attrs::AttrFlags, expr_store::{ Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, scope::ExprScopes, }, @@ -30,7 +28,6 @@ use crate::{ ConstSignature, EnumSignature, FunctionSignature, ImplSignature, StaticSignature, StructSignature, TraitSignature, TypeAliasSignature, UnionSignature, }, - tt, visibility::{self, Visibility}, }; @@ -238,28 +235,11 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase { def: GenericDefId, ) -> (Arc, Arc, Arc); - // region:attrs - - #[salsa::invoke(Attrs::fields_attrs_query)] - fn fields_attrs(&self, def: VariantId) -> Arc>; - - // should this really be a query? - #[salsa::invoke(crate::attr::fields_attrs_source_map)] - fn fields_attrs_source_map( - &self, - def: VariantId, - ) -> Arc>>>; - - // FIXME: Make this a non-interned query. - #[salsa::invoke_interned(AttrsWithOwner::attrs_query)] - fn attrs(&self, def: AttrDefId) -> Attrs; - + // FIXME: Get rid of this, call `AttrFlags::lang_item()` directly. #[salsa::transparent] #[salsa::invoke(lang_item::lang_attr)] fn lang_attr(&self, def: AttrDefId) -> Option; - // endregion:attrs - #[salsa::invoke(ImportMap::import_map_query)] fn import_map(&self, krate: Crate) -> Arc; @@ -303,36 +283,9 @@ fn include_macro_invoc( } fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: Crate) -> bool { - let file = crate_id.data(db).root_file_id(db); - let item_tree = db.file_item_tree(file.into()); - let attrs = item_tree.top_level_raw_attrs(); - for attr in &**attrs { - match attr.path().as_ident() { - Some(ident) if *ident == sym::no_std => return true, - Some(ident) if *ident == sym::cfg_attr => {} - _ => continue, - } - - // This is a `cfg_attr`; check if it could possibly expand to `no_std`. - // Syntax is: `#[cfg_attr(condition(cfg, style), attr0, attr1, <...>)]` - let tt = match attr.token_tree_value() { - Some(tt) => tt.token_trees(), - None => continue, - }; - - let segments = - tt.split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(p)) if p.char == ',')); - for output in segments.skip(1) { - match output.flat_tokens() { - [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::no_std => { - return true; - } - _ => {} - } - } - } - - false + let root_module = CrateRootModuleId::from(crate_id).module(db); + let attrs = AttrFlags::query(db, AttrDefId::ModuleId(InternedModuleId::new(db, root_module))); + attrs.contains(AttrFlags::IS_NO_STD) } fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { 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 23b9712d1e6c..6a2f06b0a6f6 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 @@ -17,11 +17,10 @@ use syntax::{AstNode, Parse, ast}; use triomphe::Arc; use tt::TextRange; -use crate::attr::Attrs; -use crate::expr_store::HygieneId; -use crate::macro_call_as_call_id; -use crate::nameres::DefMap; -use crate::{MacroId, UnresolvedMacro, db::DefDatabase}; +use crate::{ + MacroId, UnresolvedMacro, attrs::AttrFlags, db::DefDatabase, expr_store::HygieneId, + macro_call_as_call_id, nameres::DefMap, +}; #[derive(Debug)] pub(super) struct Expander { @@ -70,11 +69,10 @@ impl Expander { pub(super) fn is_cfg_enabled( &self, - db: &dyn DefDatabase, - has_attrs: &dyn HasAttrs, + owner: &dyn HasAttrs, cfg_options: &CfgOptions, ) -> Result<(), cfg::CfgExpr> { - Attrs::is_cfg_enabled_for(db, has_attrs, self.span_map.as_ref(), cfg_options) + AttrFlags::is_cfg_enabled_for(owner, cfg_options) } pub(super) fn call_syntax_ctx(&self) -> SyntaxContext { 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 3794cb18e936..fbe0b1ab9596 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 @@ -12,7 +12,6 @@ use cfg::CfgOptions; use either::Either; use hir_expand::{ HirFileId, InFile, MacroDefId, - mod_path::tool_path, name::{AsName, Name}, span_map::SpanMapRef, }; @@ -34,6 +33,7 @@ use tt::TextRange; use crate::{ AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, + attrs::AttrFlags, builtin_type::BuiltinUint, db::DefDatabase, expr_store::{ @@ -87,14 +87,16 @@ pub(super) fn lower_body( let mut params = vec![]; let mut collector = ExprCollector::new(db, module, current_file_id); - let skip_body = match owner { - DefWithBodyId::FunctionId(it) => db.attrs(it.into()), - DefWithBodyId::StaticId(it) => db.attrs(it.into()), - DefWithBodyId::ConstId(it) => db.attrs(it.into()), - DefWithBodyId::VariantId(it) => db.attrs(it.into()), - } - .rust_analyzer_tool() - .any(|attr| *attr.path() == tool_path![skip]); + let skip_body = AttrFlags::query( + db, + match owner { + DefWithBodyId::FunctionId(it) => it.into(), + DefWithBodyId::StaticId(it) => it.into(), + DefWithBodyId::ConstId(it) => it.into(), + DefWithBodyId::VariantId(it) => it.into(), + }, + ) + .contains(AttrFlags::RUST_ANALYZER_SKIP); // If #[rust_analyzer::skip] annotated, only construct enough information for the signature // and skip the body. if skip_body { @@ -2485,7 +2487,7 @@ impl ExprCollector<'_> { /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when /// not. fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> bool { - let enabled = self.expander.is_cfg_enabled(self.db, owner, self.cfg_options); + let enabled = self.expander.is_cfg_enabled(owner, self.cfg_options); match enabled { Ok(()) => true, Err(cfg) => { 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 5b9da3c5e668..e386e8d0c596 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 @@ -12,7 +12,8 @@ use span::Edition; use syntax::ast::HasName; use crate::{ - AdtId, DefWithBodyId, GenericDefId, TypeParamId, VariantId, + AdtId, DefWithBodyId, FunctionId, GenericDefId, StructId, TypeParamId, VariantId, + attrs::AttrFlags, expr_store::path::{GenericArg, GenericArgs}, hir::{ Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, Statement, @@ -167,7 +168,7 @@ pub fn print_signature(db: &dyn DefDatabase, owner: GenericDefId, edition: Editi GenericDefId::AdtId(id) => match id { AdtId::StructId(id) => { let signature = db.struct_signature(id); - print_struct(db, &signature, edition) + print_struct(db, id, &signature, edition) } AdtId::UnionId(id) => { format!("unimplemented {id:?}") @@ -179,7 +180,7 @@ pub fn print_signature(db: &dyn DefDatabase, owner: GenericDefId, edition: Editi GenericDefId::ConstId(id) => format!("unimplemented {id:?}"), GenericDefId::FunctionId(id) => { let signature = db.function_signature(id); - print_function(db, &signature, edition) + print_function(db, id, &signature, edition) } GenericDefId::ImplId(id) => format!("unimplemented {id:?}"), GenericDefId::StaticId(id) => format!("unimplemented {id:?}"), @@ -208,7 +209,8 @@ pub fn print_path( pub fn print_struct( db: &dyn DefDatabase, - StructSignature { name, generic_params, store, flags, shape, repr }: &StructSignature, + id: StructId, + StructSignature { name, generic_params, store, flags, shape }: &StructSignature, edition: Edition, ) -> String { let mut p = Printer { @@ -219,7 +221,7 @@ pub fn print_struct( line_format: LineFormat::Newline, edition, }; - if let Some(repr) = repr { + if let Some(repr) = AttrFlags::repr(db, id.into()) { if repr.c() { wln!(p, "#[repr(C)]"); } @@ -255,7 +257,8 @@ pub fn print_struct( pub fn print_function( db: &dyn DefDatabase, - FunctionSignature { + id: FunctionId, + signature @ FunctionSignature { name, generic_params, store, @@ -263,10 +266,10 @@ pub fn print_function( ret_type, abi, flags, - legacy_const_generics_indices, }: &FunctionSignature, edition: Edition, ) -> String { + let legacy_const_generics_indices = signature.legacy_const_generics_indices(db, id); let mut p = Printer { db, store, @@ -298,7 +301,7 @@ pub fn print_function( if i != 0 { w!(p, ", "); } - if legacy_const_generics_indices.as_ref().is_some_and(|idx| idx.contains(&(i as u32))) { + if legacy_const_generics_indices.is_some_and(|idx| idx.contains(&(i as u32))) { w!(p, "const: "); } p.print_type_ref(*param); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs index c7707378a5b3..0cb9325b502e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs @@ -189,8 +189,8 @@ fn f() { } "#, expect![[r#" - BlockId(3c01) in BlockRelativeModuleId { block: Some(BlockId(3c00)), local_id: Idx::(1) } - BlockId(3c00) in BlockRelativeModuleId { block: None, local_id: Idx::(0) } + BlockId(3801) in BlockRelativeModuleId { block: Some(BlockId(3800)), local_id: Idx::(1) } + BlockId(3800) in BlockRelativeModuleId { block: None, local_id: Idx::(0) } crate scope "#]], ); 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 b68674c7a74f..2dac4e7fc84b 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 @@ -38,14 +38,24 @@ fn lower_and_print(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expe match def { GenericDefId::AdtId(adt_id) => match adt_id { crate::AdtId::StructId(struct_id) => { - out += &print_struct(&db, &db.struct_signature(struct_id), Edition::CURRENT); + out += &print_struct( + &db, + struct_id, + &db.struct_signature(struct_id), + Edition::CURRENT, + ); } crate::AdtId::UnionId(_id) => (), crate::AdtId::EnumId(_id) => (), }, GenericDefId::ConstId(_id) => (), GenericDefId::FunctionId(function_id) => { - out += &print_function(&db, &db.function_signature(function_id), Edition::CURRENT) + out += &print_function( + &db, + function_id, + &db.function_signature(function_id), + Edition::CURRENT, + ) } GenericDefId::ImplId(_id) => (), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index f31f355cfa5d..67cf466276c5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -13,7 +13,8 @@ use stdx::format_to; use triomphe::Arc; use crate::{ - AssocItemId, AttrDefId, Complete, FxIndexMap, ModuleDefId, ModuleId, TraitId, + AssocItemId, AttrDefId, Complete, FxIndexMap, InternedModuleId, ModuleDefId, ModuleId, TraitId, + attrs::AttrFlags, db::DefDatabase, item_scope::{ImportOrExternCrate, ItemInNs}, nameres::{DefMap, assoc::TraitItems, crate_def_map}, @@ -165,17 +166,34 @@ impl ImportMap { } } else { match item { - ItemInNs::Types(id) | ItemInNs::Values(id) => id.try_into().ok(), + ItemInNs::Types(id) | ItemInNs::Values(id) => match id { + ModuleDefId::ModuleId(it) => { + Some(AttrDefId::ModuleId(InternedModuleId::new(db, it))) + } + ModuleDefId::FunctionId(it) => Some(it.into()), + ModuleDefId::AdtId(it) => Some(it.into()), + ModuleDefId::EnumVariantId(it) => Some(it.into()), + ModuleDefId::ConstId(it) => Some(it.into()), + ModuleDefId::StaticId(it) => Some(it.into()), + ModuleDefId::TraitId(it) => Some(it.into()), + ModuleDefId::TypeAliasId(it) => Some(it.into()), + ModuleDefId::MacroId(it) => Some(it.into()), + ModuleDefId::BuiltinType(_) => None, + }, ItemInNs::Macros(id) => Some(id.into()), } }; let (is_doc_hidden, is_unstable, do_not_complete) = match attr_id { None => (false, false, Complete::Yes), Some(attr_id) => { - let attrs = db.attrs(attr_id); + let attrs = AttrFlags::query(db, attr_id); let do_not_complete = - Complete::extract(matches!(attr_id, AttrDefId::TraitId(_)), &attrs); - (attrs.has_doc_hidden(), attrs.is_unstable(), do_not_complete) + Complete::extract(matches!(attr_id, AttrDefId::TraitId(_)), attrs); + ( + attrs.contains(AttrFlags::IS_DOC_HIDDEN), + attrs.contains(AttrFlags::IS_UNSTABLE), + do_not_complete, + ) } }; @@ -239,15 +257,15 @@ impl ImportMap { }; let attr_id = item.into(); - let attrs = &db.attrs(attr_id); + let attrs = AttrFlags::query(db, attr_id); let item_do_not_complete = Complete::extract(false, attrs); let do_not_complete = Complete::for_trait_item(trait_import_info.complete, item_do_not_complete); let assoc_item_info = ImportInfo { container: trait_import_info.container, name: assoc_item_name.clone(), - is_doc_hidden: attrs.has_doc_hidden(), - is_unstable: attrs.is_unstable(), + is_doc_hidden: attrs.contains(AttrFlags::IS_DOC_HIDDEN), + is_unstable: attrs.contains(AttrFlags::IS_UNSTABLE), complete: do_not_complete, }; 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 f35df8d3a7e1..2a104fff2b92 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 @@ -30,6 +30,7 @@ //! In general, any item in the `ItemTree` stores its `AstId`, which allows mapping it back to its //! surface syntax. +mod attrs; mod lower; mod pretty; #[cfg(test)] @@ -43,10 +44,8 @@ use std::{ }; use ast::{AstNode, StructKind}; -use base_db::Crate; use hir_expand::{ ExpandTo, HirFileId, - attrs::RawAttrs, mod_path::{ModPath, PathKind}, name::Name, }; @@ -59,9 +58,12 @@ use syntax::{SyntaxKind, ast, match_ast}; use thin_vec::ThinVec; use triomphe::Arc; -use crate::{BlockId, Lookup, attr::Attrs, db::DefDatabase}; +use crate::{BlockId, Lookup, db::DefDatabase}; -pub(crate) use crate::item_tree::lower::{lower_use_tree, visibility_from_ast}; +pub(crate) use crate::item_tree::{ + attrs::*, + lower::{lower_use_tree, visibility_from_ast}, +}; #[derive(Copy, Clone, Eq, PartialEq)] pub(crate) struct RawVisibilityId(u32); @@ -96,7 +98,7 @@ pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> let mut item_tree = match_ast! { match syntax { ast::SourceFile(file) => { - let top_attrs = RawAttrs::new(db, &file, ctx.span_map()); + let top_attrs = ctx.lower_attrs(&file); let mut item_tree = ctx.lower_module_items(&file); item_tree.top_attrs = top_attrs; item_tree @@ -132,7 +134,7 @@ pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> attrs: FxHashMap::default(), small_data: FxHashMap::default(), big_data: FxHashMap::default(), - top_attrs: RawAttrs::EMPTY, + top_attrs: AttrsOrCfg::empty(), vis: ItemVisibilities { arena: ThinVec::new() }, }) }) @@ -168,7 +170,7 @@ pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc attrs: FxHashMap::default(), small_data: FxHashMap::default(), big_data: FxHashMap::default(), - top_attrs: RawAttrs::EMPTY, + top_attrs: AttrsOrCfg::empty(), vis: ItemVisibilities { arena: ThinVec::new() }, }) }) @@ -182,8 +184,8 @@ pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc #[derive(Debug, Default, Eq, PartialEq)] pub struct ItemTree { top_level: Box<[ModItemId]>, - top_attrs: RawAttrs, - attrs: FxHashMap, RawAttrs>, + top_attrs: AttrsOrCfg, + attrs: FxHashMap, AttrsOrCfg>, vis: ItemVisibilities, big_data: FxHashMap, BigModItem>, small_data: FxHashMap, SmallModItem>, @@ -197,26 +199,12 @@ impl ItemTree { } /// Returns the inner attributes of the source file. - pub(crate) fn top_level_raw_attrs(&self) -> &RawAttrs { + pub(crate) fn top_level_attrs(&self) -> &AttrsOrCfg { &self.top_attrs } - /// Returns the inner attributes of the source file. - pub(crate) fn top_level_attrs(&self, db: &dyn DefDatabase, krate: Crate) -> Attrs { - Attrs::expand_cfg_attr(db, krate, self.top_attrs.clone()) - } - - pub(crate) fn raw_attrs(&self, of: FileAstId) -> &RawAttrs { - self.attrs.get(&of).unwrap_or(&RawAttrs::EMPTY) - } - - pub(crate) fn attrs( - &self, - db: &dyn DefDatabase, - krate: Crate, - of: FileAstId, - ) -> Attrs { - Attrs::expand_cfg_attr(db, krate, self.raw_attrs(of).clone()) + pub(crate) fn attrs(&self, of: FileAstId) -> Option<&AttrsOrCfg> { + self.attrs.get(&of) } /// Returns a count of a few, expensive items. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/attrs.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/attrs.rs new file mode 100644 index 000000000000..5c635a4b3831 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/attrs.rs @@ -0,0 +1,220 @@ +//! Defines attribute helpers for name resolution. +//! +//! Notice we don't preserve all attributes for name resolution, to save space: +//! for example, we skip doc comments (desugared to `#[doc = "..."]` attributes) +//! and `#[inline]`. The filtered attributes are listed in [`hir_expand::attrs`]. + +use std::{ + borrow::Cow, + convert::Infallible, + ops::{self, ControlFlow}, +}; + +use cfg::{CfgExpr, CfgOptions}; +use either::Either; +use hir_expand::{ + attrs::{Attr, AttrId, AttrInput, Meta, collect_item_tree_attrs}, + mod_path::ModPath, + name::Name, + span_map::SpanMapRef, +}; +use intern::{Interned, Symbol, sym}; +use syntax::{AstNode, T, ast}; +use syntax_bridge::DocCommentDesugarMode; +use tt::token_to_literal; + +use crate::{db::DefDatabase, item_tree::lower::Ctx}; + +#[derive(Debug, PartialEq, Eq)] +pub(crate) enum AttrsOrCfg { + Enabled { + attrs: AttrsOwned, + }, + /// This only collects the attributes up to the disabled `cfg` (this is what needed for crate-level attributes.) + CfgDisabled(Box<(CfgExpr, AttrsOwned)>), +} + +impl Default for AttrsOrCfg { + #[inline] + fn default() -> Self { + AttrsOrCfg::Enabled { attrs: AttrsOwned(Box::new([])) } + } +} + +impl AttrsOrCfg { + pub(crate) fn lower<'a>( + db: &dyn DefDatabase, + owner: &dyn ast::HasAttrs, + cfg_options: &dyn Fn() -> &'a CfgOptions, + span_map: SpanMapRef<'_>, + ) -> AttrsOrCfg { + let mut attrs = Vec::new(); + let result = + collect_item_tree_attrs::(owner, cfg_options, |meta, container, _, _| { + // NOTE: We cannot early return from this function, *every* attribute must be pushed, otherwise we'll mess the `AttrId` + // tracking. + let (span, path_range, input) = match meta { + Meta::NamedKeyValue { path_range, name: _, value } => { + let span = span_map.span_for_range(path_range); + let input = value.map(|value| { + Box::new(AttrInput::Literal(token_to_literal( + value.text(), + span_map.span_for_range(value.text_range()), + ))) + }); + (span, path_range, input) + } + Meta::TokenTree { path, tt } => { + let span = span_map.span_for_range(path.range); + let tt = syntax_bridge::syntax_node_to_token_tree( + tt.syntax(), + span_map, + span, + DocCommentDesugarMode::ProcMacro, + ); + let input = Some(Box::new(AttrInput::TokenTree(tt))); + (span, path.range, input) + } + Meta::Path { path } => { + let span = span_map.span_for_range(path.range); + (span, path.range, None) + } + }; + + let path = container.token_at_offset(path_range.start()).right_biased().and_then( + |first_path_token| { + let is_abs = matches!(first_path_token.kind(), T![:] | T![::]); + let segments = + std::iter::successors(Some(first_path_token), |it| it.next_token()) + .take_while(|it| it.text_range().end() <= path_range.end()) + .filter(|it| it.kind().is_any_identifier()); + ModPath::from_tokens( + db, + &mut |range| span_map.span_for_range(range).ctx, + is_abs, + segments, + ) + }, + ); + let path = path.unwrap_or_else(|| Name::missing().into()); + + attrs.push(Attr { path: Interned::new(path), input, ctxt: span.ctx }); + ControlFlow::Continue(()) + }); + let attrs = AttrsOwned(attrs.into_boxed_slice()); + match result { + Some(Either::Right(cfg)) => AttrsOrCfg::CfgDisabled(Box::new((cfg, attrs))), + None => AttrsOrCfg::Enabled { attrs }, + } + } +} + +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct AttrsOwned(Box<[Attr]>); + +#[derive(Debug, Clone, Copy)] +pub(crate) struct Attrs<'a>(&'a [Attr]); + +impl ops::Deref for Attrs<'_> { + type Target = [Attr]; + + #[inline] + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl Ctx<'_> { + #[inline] + pub(super) fn lower_attrs(&self, owner: &dyn ast::HasAttrs) -> AttrsOrCfg { + AttrsOrCfg::lower(self.db, owner, &|| self.cfg_options(), self.span_map()) + } +} + +impl AttrsOwned { + #[inline] + pub(crate) fn as_ref(&self) -> Attrs<'_> { + Attrs(&self.0) + } +} + +impl<'a> Attrs<'a> { + pub(crate) const EMPTY: Self = Attrs(&[]); + + #[inline] + pub(crate) fn by_key(self, key: Symbol) -> AttrQuery<'a> { + AttrQuery { attrs: self, key } + } + + #[inline] + pub(crate) fn iter(self) -> impl Iterator { + self.0.iter().enumerate().map(|(id, attr)| (AttrId::from_item_tree_index(id as u32), attr)) + } + + #[inline] + pub(crate) fn iter_after( + self, + after: Option, + ) -> impl Iterator { + let skip = after.map_or(0, |after| after.item_tree_index() + 1); + self.0[skip as usize..] + .iter() + .enumerate() + .map(move |(id, attr)| (AttrId::from_item_tree_index(id as u32 + skip), attr)) + } + + #[inline] + pub(crate) fn is_proc_macro(&self) -> bool { + self.by_key(sym::proc_macro).exists() + } + + #[inline] + pub(crate) fn is_proc_macro_attribute(&self) -> bool { + self.by_key(sym::proc_macro_attribute).exists() + } +} +#[derive(Debug, Clone)] +pub(crate) struct AttrQuery<'attr> { + attrs: Attrs<'attr>, + key: Symbol, +} + +impl<'attr> AttrQuery<'attr> { + #[inline] + pub(crate) fn tt_values(self) -> impl Iterator { + self.attrs().filter_map(|attr| attr.token_tree_value()) + } + + #[inline] + pub(crate) fn string_value_with_span(self) -> Option<(&'attr Symbol, span::Span)> { + self.attrs().find_map(|attr| attr.string_value_with_span()) + } + + #[inline] + pub(crate) fn string_value_unescape(self) -> Option> { + self.attrs().find_map(|attr| attr.string_value_unescape()) + } + + #[inline] + pub(crate) fn exists(self) -> bool { + self.attrs().next().is_some() + } + + #[inline] + pub(crate) fn attrs(self) -> impl Iterator + Clone { + let key = self.key; + self.attrs.0.iter().filter(move |attr| attr.path.as_ident().is_some_and(|s| *s == key)) + } +} + +impl AttrsOrCfg { + #[inline] + pub(super) fn empty() -> Self { + AttrsOrCfg::Enabled { attrs: AttrsOwned(Box::new([])) } + } + + #[inline] + pub(super) fn is_empty(&self) -> bool { + matches!(self, AttrsOrCfg::Enabled { attrs } if attrs.as_ref().is_empty()) + } +} 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 454e06399583..b50a75169158 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 @@ -1,8 +1,9 @@ //! AST -> `ItemTree` lowering code. -use std::{cell::OnceCell, collections::hash_map::Entry}; +use std::cell::OnceCell; use base_db::FxIndexSet; +use cfg::CfgOptions; use hir_expand::{ HirFileId, mod_path::PathKind, @@ -22,18 +23,19 @@ use crate::{ item_tree::{ BigModItem, Const, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl, ImportAlias, Interned, ItemTree, ItemTreeAstId, Macro2, MacroCall, MacroRules, Mod, - ModItemId, ModKind, ModPath, RawAttrs, RawVisibility, RawVisibilityId, SmallModItem, - Static, Struct, StructKind, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, - VisibilityExplicitness, + ModItemId, ModKind, ModPath, RawVisibility, RawVisibilityId, SmallModItem, Static, Struct, + StructKind, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, VisibilityExplicitness, + attrs::AttrsOrCfg, }, }; pub(super) struct Ctx<'a> { - db: &'a dyn DefDatabase, + pub(super) db: &'a dyn DefDatabase, tree: ItemTree, source_ast_id_map: Arc, span_map: OnceCell, file: HirFileId, + cfg_options: OnceCell<&'a CfgOptions>, top_level: Vec, visibilities: FxIndexSet, } @@ -45,12 +47,18 @@ impl<'a> Ctx<'a> { tree: ItemTree::default(), source_ast_id_map: db.ast_id_map(file), file, + cfg_options: OnceCell::new(), span_map: OnceCell::new(), visibilities: FxIndexSet::default(), top_level: Vec::new(), } } + #[inline] + pub(super) fn cfg_options(&self) -> &'a CfgOptions { + self.cfg_options.get_or_init(|| self.file.krate(self.db).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() } @@ -98,7 +106,7 @@ impl<'a> Ctx<'a> { } pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree { - self.tree.top_attrs = RawAttrs::new(self.db, block, self.span_map()); + self.tree.top_attrs = self.lower_attrs(block); self.top_level = block .statements() .filter_map(|stmt| match stmt { @@ -144,22 +152,15 @@ impl<'a> Ctx<'a> { // FIXME: Handle `global_asm!()`. ast::Item::AsmExpr(_) => return None, }; - let attrs = RawAttrs::new(self.db, item, self.span_map()); + let attrs = self.lower_attrs(item); self.add_attrs(mod_item.ast_id(), attrs); Some(mod_item) } - fn add_attrs(&mut self, item: FileAstId, attrs: RawAttrs) { + fn add_attrs(&mut self, item: FileAstId, attrs: AttrsOrCfg) { if !attrs.is_empty() { - match self.tree.attrs.entry(item) { - Entry::Occupied(mut entry) => { - *entry.get_mut() = entry.get().merge(attrs); - } - Entry::Vacant(entry) => { - entry.insert(attrs); - } - } + self.tree.attrs.insert(item, attrs); } } @@ -352,7 +353,7 @@ impl<'a> Ctx<'a> { ast::ExternItem::TypeAlias(ty) => self.lower_type_alias(ty)?.into(), ast::ExternItem::MacroCall(call) => self.lower_macro_call(call)?.into(), }; - let attrs = RawAttrs::new(self.db, &item, self.span_map()); + let attrs = self.lower_attrs(&item); self.add_attrs(mod_item.ast_id(), attrs); Some(mod_item) }) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index 94a6cce3ce33..66a2d14a734f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -7,8 +7,8 @@ use span::{Edition, ErasedFileAstId}; use crate::{ item_tree::{ Const, DefDatabase, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl, ItemTree, - Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, RawAttrs, RawVisibilityId, Static, - Struct, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, + Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, RawVisibilityId, Static, Struct, + Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, attrs::AttrsOrCfg, }, visibility::RawVisibility, }; @@ -85,9 +85,13 @@ impl Printer<'_> { } } - fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool, separated_by: &str) { + fn print_attrs(&mut self, attrs: &AttrsOrCfg, inner: bool, separated_by: &str) { + let AttrsOrCfg::Enabled { attrs } = attrs else { + w!(self, "#[cfg(false)]{separated_by}"); + return; + }; let inner = if inner { "!" } else { "" }; - for attr in &**attrs { + for attr in &*attrs.as_ref() { w!( self, "#{}[{}{}]{}", diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs index 91b42bef8f79..a57432f33c3d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs @@ -30,10 +30,8 @@ use crate::{A, B}; use a::{c, d::{e}}; "#, - expect![[r##" - #![doc = " file comment"] + expect![[r#" #![no_std] - #![doc = " another file comment"] // AstId: ExternCrate[070B, 0] pub(self) extern crate self as renamed; @@ -47,13 +45,12 @@ use a::{c, d::{e}}; // AstId: Use[0000, 1] pub(self) use globs::*; - #[doc = " docs on import"] // AstId: Use[0000, 2] pub(self) use crate::{A, B}; // AstId: Use[0000, 3] pub(self) use a::{c, d::{e}}; - "##]], + "#]], ); } @@ -195,8 +192,6 @@ mod inline { mod outline; "#, expect![[r##" - #[doc = " outer"] - #[doc = " inner"] // AstId: Module[03AE, 0] pub(self) mod inline { // AstId: Use[0000, 0] 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 df0705bf90cb..4f97baadd183 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 @@ -10,6 +10,7 @@ use triomphe::Arc; use crate::{ AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, + attrs::AttrFlags, db::DefDatabase, expr_store::path::Path, nameres::{assoc::TraitItems, crate_def_map, crate_local_def_map}, @@ -213,14 +214,14 @@ impl LangItems { T: Into + Copy, { let _p = tracing::info_span!("collect_lang_item").entered(); - if let Some(lang_item) = lang_attr(db, item.into()) { + if let Some(lang_item) = AttrFlags::lang_item(db, item.into()) { self.items.entry(lang_item).or_insert_with(|| constructor(item)); } } } pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option { - db.attrs(item).lang_item() + AttrFlags::lang_item(db, item) } pub(crate) fn notable_traits_in_deps(db: &dyn DefDatabase, krate: Crate) -> Arc<[Arc<[TraitId]>]> { @@ -240,7 +241,7 @@ pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option for (_, module_data) in crate_def_map.modules() { for def in module_data.scope.declarations() { if let ModuleDefId::TraitId(trait_) = def - && db.attrs(trait_.into()).has_doc_notable_trait() + && AttrFlags::query(db, trait_.into()).contains(AttrFlags::IS_DOC_NOTABLE_TRAIT) { traits.push(trait_); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index e5c213ca937c..c3c9fc75252d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -19,7 +19,7 @@ extern crate ra_ap_rustc_abi as rustc_abi; pub mod db; -pub mod attr; +pub mod attrs; pub mod builtin_type; pub mod item_scope; pub mod per_ns; @@ -45,7 +45,7 @@ pub mod find_path; pub mod import_map; pub mod visibility; -use intern::{Interned, Symbol, sym}; +use intern::{Interned, Symbol}; pub use rustc_abi as layout; use thin_vec::ThinVec; use triomphe::Arc; @@ -80,7 +80,7 @@ use syntax::{AstNode, ast}; pub use hir_expand::{Intern, Lookup, tt}; use crate::{ - attr::Attrs, + attrs::AttrFlags, builtin_type::BuiltinType, db::DefDatabase, expr_store::ExpressionStoreSourceMap, @@ -956,10 +956,16 @@ impl CallableDefId { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +// FIXME: We probably should use this in more places. +/// This is used to avoid interning the whole `AttrDefId`, so we intern just modules and not everything. +#[salsa_macros::interned(debug, no_lifetime)] +pub struct InternedModuleId { + pub loc: ModuleId, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, salsa_macros::Supertype)] pub enum AttrDefId { - ModuleId(ModuleId), - FieldId(FieldId), + ModuleId(InternedModuleId), AdtId(AdtId), FunctionId(FunctionId), EnumVariantId(EnumVariantId), @@ -969,15 +975,12 @@ pub enum AttrDefId { TypeAliasId(TypeAliasId), MacroId(MacroId), ImplId(ImplId), - GenericParamId(GenericParamId), ExternBlockId(ExternBlockId), ExternCrateId(ExternCrateId), UseId(UseId), } impl_from!( - ModuleId, - FieldId, AdtId(StructId, EnumId, UnionId), EnumVariantId, StaticId, @@ -987,41 +990,11 @@ impl_from!( TypeAliasId, MacroId(Macro2Id, MacroRulesId, ProcMacroId), ImplId, - GenericParamId, ExternCrateId, UseId for AttrDefId ); -impl TryFrom for AttrDefId { - type Error = (); - - fn try_from(value: ModuleDefId) -> Result { - match value { - ModuleDefId::ModuleId(it) => Ok(it.into()), - ModuleDefId::FunctionId(it) => Ok(it.into()), - ModuleDefId::AdtId(it) => Ok(it.into()), - ModuleDefId::EnumVariantId(it) => Ok(it.into()), - ModuleDefId::ConstId(it) => Ok(it.into()), - ModuleDefId::StaticId(it) => Ok(it.into()), - ModuleDefId::TraitId(it) => Ok(it.into()), - ModuleDefId::TypeAliasId(it) => Ok(it.into()), - ModuleDefId::MacroId(id) => Ok(id.into()), - ModuleDefId::BuiltinType(_) => Err(()), - } - } -} - -impl From for AttrDefId { - fn from(acid: ItemContainerId) -> Self { - match acid { - ItemContainerId::ModuleId(mid) => AttrDefId::ModuleId(mid), - ItemContainerId::ImplId(iid) => AttrDefId::ImplId(iid), - ItemContainerId::TraitId(tid) => AttrDefId::TraitId(tid), - ItemContainerId::ExternBlockId(id) => AttrDefId::ExternBlockId(id), - } - } -} impl From for AttrDefId { fn from(assoc: AssocItemId) -> Self { match assoc { @@ -1262,8 +1235,7 @@ impl HasModule for GenericDefId { impl HasModule for AttrDefId { fn module(&self, db: &dyn DefDatabase) -> ModuleId { match self { - AttrDefId::ModuleId(it) => *it, - AttrDefId::FieldId(it) => it.parent.module(db), + AttrDefId::ModuleId(it) => it.loc(db), AttrDefId::AdtId(it) => it.module(db), AttrDefId::FunctionId(it) => it.module(db), AttrDefId::EnumVariantId(it) => it.module(db), @@ -1273,12 +1245,6 @@ impl HasModule for AttrDefId { AttrDefId::TypeAliasId(it) => it.module(db), AttrDefId::ImplId(it) => it.module(db), AttrDefId::ExternBlockId(it) => it.module(db), - AttrDefId::GenericParamId(it) => match it { - GenericParamId::TypeParamId(it) => it.parent(), - GenericParamId::ConstParamId(it) => it.parent(), - GenericParamId::LifetimeParamId(it) => it.parent, - } - .module(db), AttrDefId::MacroId(it) => it.module(db), AttrDefId::ExternCrateId(it) => it.module(db), AttrDefId::UseId(it) => it.module(db), @@ -1402,32 +1368,18 @@ pub enum Complete { } impl Complete { - pub fn extract(is_trait: bool, attrs: &Attrs) -> Complete { - let mut do_not_complete = Complete::Yes; - for ra_attr in attrs.rust_analyzer_tool() { - let segments = ra_attr.path.segments(); - if segments.len() != 2 { - continue; - } - let action = segments[1].symbol(); - if *action == sym::completions { - match ra_attr.token_tree_value().map(|tt| tt.token_trees().flat_tokens()) { - Some([tt::TokenTree::Leaf(tt::Leaf::Ident(ident))]) => { - if ident.sym == sym::ignore_flyimport { - do_not_complete = Complete::IgnoreFlyimport; - } else if is_trait { - if ident.sym == sym::ignore_methods { - do_not_complete = Complete::IgnoreMethods; - } else if ident.sym == sym::ignore_flyimport_methods { - do_not_complete = Complete::IgnoreFlyimportMethods; - } - } - } - _ => {} - } + #[inline] + pub fn extract(is_trait: bool, attrs: AttrFlags) -> Complete { + if attrs.contains(AttrFlags::COMPLETE_IGNORE_FLYIMPORT) { + return Complete::IgnoreFlyimport; + } else if is_trait { + if attrs.contains(AttrFlags::COMPLETE_IGNORE_METHODS) { + return Complete::IgnoreMethods; + } else if attrs.contains(AttrFlags::COMPLETE_IGNORE_FLYIMPORT_METHODS) { + return Complete::IgnoreFlyimportMethods; } } - do_not_complete + Complete::Yes } #[inline] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs index c489c1f7c1da..115b487b7ac8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -300,21 +300,21 @@ fn match_by_first_token_literally() { check( r#" macro_rules! m { - ($i:ident) => ( mod $i {} ); + ($i:ident) => ( enum $i {} ); (= $i:ident) => ( fn $i() {} ); (+ $i:ident) => ( struct $i; ) } -m! { foo } +m! { Foo } m! { = bar } m! { + Baz } "#, expect![[r#" macro_rules! m { - ($i:ident) => ( mod $i {} ); + ($i:ident) => ( enum $i {} ); (= $i:ident) => ( fn $i() {} ); (+ $i:ident) => ( struct $i; ) } -mod foo {} +enum Foo {} fn bar() {} struct Baz; "#]], @@ -326,21 +326,21 @@ fn match_by_last_token_literally() { check( r#" macro_rules! m { - ($i:ident) => ( mod $i {} ); + ($i:ident) => ( enum $i {} ); ($i:ident =) => ( fn $i() {} ); ($i:ident +) => ( struct $i; ) } -m! { foo } +m! { Foo } m! { bar = } m! { Baz + } "#, expect![[r#" macro_rules! m { - ($i:ident) => ( mod $i {} ); + ($i:ident) => ( enum $i {} ); ($i:ident =) => ( fn $i() {} ); ($i:ident +) => ( struct $i; ) } -mod foo {} +enum Foo {} fn bar() {} struct Baz; "#]], @@ -352,21 +352,21 @@ fn match_by_ident() { check( r#" macro_rules! m { - ($i:ident) => ( mod $i {} ); + ($i:ident) => ( enum $i {} ); (spam $i:ident) => ( fn $i() {} ); (eggs $i:ident) => ( struct $i; ) } -m! { foo } +m! { Foo } m! { spam bar } m! { eggs Baz } "#, expect![[r#" macro_rules! m { - ($i:ident) => ( mod $i {} ); + ($i:ident) => ( enum $i {} ); (spam $i:ident) => ( fn $i() {} ); (eggs $i:ident) => ( struct $i; ) } -mod foo {} +enum Foo {} fn bar() {} struct Baz; "#]], @@ -378,12 +378,12 @@ fn match_by_separator_token() { check( r#" macro_rules! m { - ($($i:ident),*) => ($(mod $i {} )*); + ($($i:ident),*) => ($(enum $i {} )*); ($($i:ident)#*) => ($(fn $i() {} )*); ($i:ident ,# $ j:ident) => ( struct $i; struct $ j; ) } -m! { foo, bar } +m! { Baz, Qux } m! { foo# bar } @@ -391,13 +391,13 @@ m! { Foo,# Bar } "#, expect![[r#" macro_rules! m { - ($($i:ident),*) => ($(mod $i {} )*); + ($($i:ident),*) => ($(enum $i {} )*); ($($i:ident)#*) => ($(fn $i() {} )*); ($i:ident ,# $ j:ident) => ( struct $i; struct $ j; ) } -mod foo {} -mod bar {} +enum Baz {} +enum Qux {} fn foo() {} fn bar() {} @@ -1114,11 +1114,11 @@ fn test_single_item() { check( r#" macro_rules! m { ($i:item) => ( $i ) } -m! { mod c {} } +m! { struct C {} } "#, expect![[r#" macro_rules! m { ($i:item) => ( $i ) } -mod c {} +struct C {} "#]], ) } @@ -1144,6 +1144,7 @@ m! { type T = u8; } "#, + // The modules are counted twice, once because of the module and once because of the macro call. expect![[r#" macro_rules! m { ($($i:item)*) => ($($i )*) } extern crate a; @@ -1161,7 +1162,9 @@ trait J {} fn h() {} extern {} type T = u8; -"#]], + +mod b; +mod c {}"#]], ); } 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 e8ae499d27b2..74393411054e 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 @@ -245,6 +245,21 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream } } + for (_, module) in def_map.modules() { + let Some(src) = module.declaration_source(&db) else { + continue; + }; + 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(), + false, + false, + ); + format_to!(expanded_text, "\n{}", pp) + } + } + for impl_id in def_map[local_id].scope.impls() { let src = impl_id.lookup(&db).source(&db); if let Some(macro_file) = src.file_id.macro_file() diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs index 6952a9da1013..3f0afe61e0b8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs @@ -9,37 +9,65 @@ use crate::macro_expansion_tests::{check, check_errors}; #[test] fn attribute_macro_attr_censoring() { - cov_mark::check!(attribute_macro_attr_censoring); check( r#" //- proc_macros: identity -#[attr1] #[proc_macros::identity] #[attr2] -struct S; -"#, - expect![[r#" -#[attr1] #[proc_macros::identity] #[attr2] +//- minicore: derive +#[attr1] #[derive()] #[proc_macros::identity] #[attr2] struct S; +/// Foo +#[cfg_attr(false, doc = "abc...", attr1)] +mod foo { + #![cfg_attr(true, cfg_attr(true, foo, cfg_attr(false, bar), proc_macros::identity))] + #![cfg_attr(true, doc = "123...", attr2)] + #![attr3] + + #[cfg_attr(true, cfg(false))] + fn foo() {} + + #[cfg(true)] + fn bar() {} +} +"#, + expect![[r##" +#[attr1] #[derive()] #[proc_macros::identity] #[attr2] +struct S; + +/// Foo +#[cfg_attr(false, doc = "abc...", attr1)] +mod foo { + #![cfg_attr(true, cfg_attr(true, foo, cfg_attr(false, bar), proc_macros::identity))] + #![cfg_attr(true, doc = "123...", attr2)] + #![attr3] + + #[cfg_attr(true, cfg(false))] + fn foo() {} + + #[cfg(true)] + fn bar() {} +} + #[attr1] -#[attr2] struct S;"#]], +#[attr2] struct S; +#[doc = " Foo"] mod foo { + # ![foo] + # ![doc = "123..."] + # ![attr2] + # ![attr3] + #[cfg_attr(true , cfg(false ))] fn foo() {} + #[cfg(true )] fn bar() {} +}"##]], ); } #[test] fn derive_censoring() { - cov_mark::check!(derive_censoring); check( r#" //- proc_macros: derive_identity //- minicore:derive -#[attr1] -#[derive(Foo)] -#[derive(proc_macros::DeriveIdentity)] -#[derive(Bar)] -#[attr2] -struct S; -"#, - expect![[r#" +use derive as my_cool_derive; #[attr1] #[derive(Foo)] #[derive(proc_macros::DeriveIdentity)] @@ -47,6 +75,60 @@ struct S; #[attr2] struct S; +#[my_cool_derive()] +#[cfg_attr(true, derive(), attr1, derive(proc_macros::DeriveIdentity))] +#[my_cool_derive()] +struct Foo { + #[cfg_attr(false, cfg(false), attr2)] + v1: i32, + #[cfg_attr(true, cfg(false), attr2)] + v1: i32, + #[cfg_attr(true, attr3)] + v2: fn(#[cfg(false)] param: i32, #[cfg_attr(true, attr4)] param2: u32), + v3: Foo<{ + #[cfg(false)] + let foo = 123; + 456 + }>, + #[cfg(false)] + v4: bool // No comma here +} +"#, + expect![[r#" +use derive as my_cool_derive; +#[attr1] +#[derive(Foo)] +#[derive(proc_macros::DeriveIdentity)] +#[derive(Bar)] +#[attr2] +struct S; + +#[my_cool_derive()] +#[cfg_attr(true, derive(), attr1, derive(proc_macros::DeriveIdentity))] +#[my_cool_derive()] +struct Foo { + #[cfg_attr(false, cfg(false), attr2)] + v1: i32, + #[cfg_attr(true, cfg(false), attr2)] + v1: i32, + #[cfg_attr(true, attr3)] + v2: fn(#[cfg(false)] param: i32, #[cfg_attr(true, attr4)] param2: u32), + v3: Foo<{ + #[cfg(false)] + let foo = 123; + 456 + }>, + #[cfg(false)] + v4: bool // No comma here +} + +#[attr1] +#[my_cool_derive()] struct Foo { + v1: i32, #[attr3]v2: fn(#[attr4]param2: u32), v3: Foo< { + 456 + } + >, +} #[attr1] #[derive(Bar)] #[attr2] struct S;"#]], @@ -87,7 +169,7 @@ fn foo() { bar.; blub } fn foo() { bar.; blub } fn foo() { - bar. ; + bar.; blub }"#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 7d5e627964eb..e4b95a5a77a5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -391,19 +391,14 @@ pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefM ) .entered(); - let module_data = ModuleData::new( - ModuleOrigin::CrateRoot { definition: krate.root_file_id(db) }, - Visibility::Public, - ); + let root_file_id = crate_id.root_file_id(db); + let module_data = + ModuleData::new(ModuleOrigin::CrateRoot { definition: root_file_id }, Visibility::Public); let def_map = DefMap::empty(crate_id, Arc::new(DefMapCrateData::new(krate.edition)), module_data, None); - let (def_map, local_def_map) = collector::collect_defs( - db, - def_map, - TreeId::new(krate.root_file_id(db).into(), None), - None, - ); + let (def_map, local_def_map) = + collector::collect_defs(db, def_map, TreeId::new(root_file_id.into(), None), None); DefMapPair::new(db, def_map, local_def_map) } 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 8d2a386de8ec..b67853347bde 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 @@ -4,7 +4,8 @@ use std::mem; use cfg::CfgOptions; use hir_expand::{ - AstId, ExpandTo, HirFileId, InFile, Intern, Lookup, MacroCallKind, MacroDefKind, + AstId, AttrMacroAttrIds, ExpandTo, HirFileId, InFile, Intern, Lookup, MacroCallKind, + MacroDefKind, mod_path::ModPath, name::{AsName, Name}, span_map::SpanMap, @@ -21,8 +22,8 @@ use triomphe::Arc; use crate::{ AssocItemId, AstIdWithPath, ConstLoc, FunctionId, FunctionLoc, ImplId, ItemContainerId, ItemLoc, MacroCallId, ModuleId, TraitId, TypeAliasId, TypeAliasLoc, - attr::Attrs, db::DefDatabase, + item_tree::AttrsOrCfg, macro_call_as_call_id, nameres::{ DefMap, LocalDefMap, MacroSubNs, @@ -191,19 +192,22 @@ impl<'a> AssocItemCollector<'a> { fn collect_item(&mut self, item: ast::AssocItem) { let ast_id = self.ast_id_map.ast_id(&item); - let attrs = Attrs::new(self.db, &item, self.span_map.as_ref(), self.cfg_options); - if let Err(cfg) = attrs.is_cfg_enabled(self.cfg_options) { - self.diagnostics.push(DefDiagnostic::unconfigured_code( - self.module_id.local_id, - InFile::new(self.file_id, ast_id.erase()), - cfg, - self.cfg_options.clone(), - )); - return; - } + 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.local_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 in &*attrs { + 'attrs: for (attr_id, attr) in attrs.as_ref().iter() { let ast_id_with_path = AstIdWithPath { path: attr.path.clone(), ast_id }; match self.def_map.resolve_attr_macro( @@ -212,6 +216,7 @@ impl<'a> AssocItemCollector<'a> { self.module_id.local_id, ast_id_with_path, attr, + attr_id, ) { Ok(ResolvedAttr::Macro(call_id)) => { let loc = self.db.lookup_intern_macro_call(call_id); @@ -240,8 +245,12 @@ impl<'a> AssocItemCollector<'a> { Err(_) => { self.diagnostics.push(DefDiagnostic::unresolved_macro_call( self.module_id.local_id, - MacroCallKind::Attr { ast_id, attr_args: None, invoc_attr_index: attr.id }, - attr.path().clone(), + MacroCallKind::Attr { + ast_id, + attr_args: None, + censored_attr_ids: AttrMacroAttrIds::from_one(attr_id), + }, + (*attr.path).clone(), )); } } 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 2f56d608fcbf..fb755026c3e0 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 @@ -2,7 +2,7 @@ use base_db::Crate; use hir_expand::{ - MacroCallId, MacroCallKind, MacroDefId, + AttrMacroAttrIds, MacroCallId, MacroCallKind, MacroDefId, attrs::{Attr, AttrId, AttrInput}, inert_attr_macro::find_builtin_attr_idx, mod_path::{ModPath, PathKind}, @@ -28,6 +28,7 @@ pub enum ResolvedAttr { } impl DefMap { + /// This cannot be used to resolve items that allow derives. pub(crate) fn resolve_attr_macro( &self, local_def_map: &LocalDefMap, @@ -35,6 +36,7 @@ impl DefMap { original_module: LocalModuleId, ast_id: AstIdWithPath, attr: &Attr, + attr_id: AttrId, ) -> Result { // NB: does not currently work for derive helpers as they aren't recorded in the `DefMap` @@ -68,6 +70,9 @@ impl DefMap { db, &ast_id, attr, + // There aren't any active attributes before this one, because attribute macros + // replace their input, and derive macros are not allowed in this function. + AttrMacroAttrIds::from_one(attr_id), self.krate, db.macro_def(def), ))) @@ -102,6 +107,7 @@ pub(super) fn attr_macro_as_call_id( db: &dyn DefDatabase, item_attr: &AstIdWithPath, macro_attr: &Attr, + censored_attr_ids: AttrMacroAttrIds, krate: Crate, def: MacroDefId, ) -> MacroCallId { @@ -121,7 +127,7 @@ pub(super) fn attr_macro_as_call_id( MacroCallKind::Attr { ast_id: item_attr.ast_id, attr_args: arg.map(Arc::new), - invoc_attr_index: macro_attr.id, + censored_attr_ids, }, macro_attr.ctxt, ) 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 a2ce53835651..c3b272b403bb 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 @@ -3,14 +3,14 @@ //! `DefCollector::collect` contains the fixed-point iteration loop which //! resolves imports and expands macros. -use std::{cmp::Ordering, iter, mem, ops::Not}; +use std::{cmp::Ordering, iter, mem}; use base_db::{BuiltDependency, Crate, CrateOrigin, LangCrateOrigin}; use cfg::{CfgAtom, CfgExpr, CfgOptions}; use either::Either; use hir_expand::{ - EditionedFileId, ErasedAstId, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, - MacroDefId, MacroDefKind, + AttrMacroAttrIds, EditionedFileId, ErasedAstId, ExpandTo, HirFileId, InFile, MacroCallId, + MacroCallKind, MacroDefId, MacroDefKind, attrs::{Attr, AttrId}, builtin::{find_builtin_attr, find_builtin_derive, find_builtin_macro}, mod_path::{ModPath, PathKind}, @@ -18,9 +18,10 @@ use hir_expand::{ proc_macro::CustomProcMacroExpander, }; use intern::{Interned, sym}; -use itertools::{Itertools, izip}; +use itertools::izip; use la_arena::Idx; use rustc_hash::{FxHashMap, FxHashSet}; +use smallvec::SmallVec; use span::{Edition, FileAstId, SyntaxContext}; use syntax::ast; use triomphe::Arc; @@ -32,12 +33,11 @@ use crate::{ MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc, - attr::Attrs, db::DefDatabase, item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports}, item_tree::{ - self, FieldsShape, ImportAlias, ImportKind, ItemTree, ItemTreeAstId, Macro2, MacroCall, - MacroRules, Mod, ModItemId, ModKind, TreeId, + self, Attrs, AttrsOrCfg, FieldsShape, ImportAlias, ImportKind, ItemTree, ItemTreeAstId, + Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, TreeId, }, macro_call_as_call_id, nameres::{ @@ -102,6 +102,7 @@ pub(super) fn collect_defs( proc_macros, from_glob_import: Default::default(), skip_attrs: Default::default(), + prev_active_attrs: Default::default(), unresolved_extern_crates: Default::default(), is_proc_macro: krate.is_proc_macro, }; @@ -206,6 +207,7 @@ enum MacroDirectiveKind<'db> { }, Attr { ast_id: AstIdWithPath, + attr_id: AttrId, attr: Attr, mod_item: ModItemId, /* is this needed? */ tree: TreeId, @@ -246,28 +248,27 @@ struct DefCollector<'db> { /// This also stores the attributes to skip when we resolve derive helpers and non-macro /// non-builtin attributes in general. // FIXME: There has to be a better way to do this - skip_attrs: FxHashMap>, AttrId>, + skip_attrs: FxHashMap, AttrId>, + /// When we expand attributes, we need to censor all previous active attributes + /// on the same item. Therefore, this holds all active attributes that we already + /// expanded. + prev_active_attrs: FxHashMap, SmallVec<[AttrId; 1]>>, } impl<'db> DefCollector<'db> { fn seed_with_top_level(&mut self) { let _p = tracing::info_span!("seed_with_top_level").entered(); - let file_id = self.def_map.krate.data(self.db).root_file_id(self.db); + let file_id = self.def_map.krate.root_file_id(self.db); let item_tree = self.db.file_item_tree(file_id.into()); - let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate); + let attrs = match item_tree.top_level_attrs() { + AttrsOrCfg::Enabled { attrs } => attrs.as_ref(), + AttrsOrCfg::CfgDisabled(it) => it.1.as_ref(), + }; let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap(); - let mut process = true; - // Process other crate-level attributes. for attr in &*attrs { - if let Some(cfg) = attr.cfg() - && self.cfg_options.check(&cfg) == Some(false) - { - process = false; - break; - } let Some(attr_name) = attr.path.as_ident() else { continue }; match () { @@ -291,7 +292,7 @@ impl<'db> DefCollector<'db> { () if *attr_name == sym::feature => { let features = attr.parse_path_comma_token_tree(self.db).into_iter().flatten().filter_map( - |(feat, _)| match feat.segments() { + |(feat, _, _)| match feat.segments() { [name] => Some(name.symbol().clone()), _ => None, }, @@ -344,7 +345,7 @@ impl<'db> DefCollector<'db> { self.inject_prelude(); - if !process { + if matches!(item_tree.top_level_attrs(), AttrsOrCfg::CfgDisabled(_)) { return; } @@ -362,10 +363,7 @@ impl<'db> DefCollector<'db> { fn seed_with_inner(&mut self, tree_id: TreeId) { let item_tree = tree_id.item_tree(self.db); - let is_cfg_enabled = item_tree - .top_level_attrs(self.db, self.def_map.krate) - .cfg() - .is_none_or(|cfg| self.cfg_options.check(&cfg) != Some(false)); + let is_cfg_enabled = matches!(item_tree.top_level_attrs(), AttrsOrCfg::Enabled { .. }); if is_cfg_enabled { self.inject_prelude(); @@ -456,18 +454,18 @@ impl<'db> DefCollector<'db> { self.unresolved_macros.iter().enumerate().find_map(|(idx, directive)| match &directive .kind { - MacroDirectiveKind::Attr { ast_id, mod_item, attr, tree, item_tree } => { + MacroDirectiveKind::Attr { ast_id, mod_item, attr_id, attr, tree, item_tree } => { self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( directive.module_id, MacroCallKind::Attr { ast_id: ast_id.ast_id, attr_args: None, - invoc_attr_index: attr.id, + censored_attr_ids: AttrMacroAttrIds::from_one(*attr_id), }, - attr.path().clone(), + (*attr.path).clone(), )); - self.skip_attrs.insert(ast_id.ast_id.with_value(mod_item.ast_id()), attr.id); + self.skip_attrs.insert(ast_id.ast_id.with_value(mod_item.ast_id()), *attr_id); Some((idx, directive, *mod_item, *tree, *item_tree)) } @@ -1350,6 +1348,7 @@ impl<'db> DefCollector<'db> { MacroDirectiveKind::Attr { ast_id: file_ast_id, mod_item, + attr_id, attr, tree, item_tree, @@ -1362,7 +1361,7 @@ impl<'db> DefCollector<'db> { let mod_dir = collector.mod_dirs[&directive.module_id].clone(); collector .skip_attrs - .insert(InFile::new(file_id, mod_item.ast_id()), attr.id); + .insert(InFile::new(file_id, mod_item.ast_id()), *attr_id); ModCollector { def_collector: collector, @@ -1398,7 +1397,6 @@ impl<'db> DefCollector<'db> { // being cfg'ed out). // Ideally we will just expand them to nothing here. But we are only collecting macro calls, // not expanding them, so we have no way to do that. - // If you add an ignored attribute here, also add it to `Semantics::might_be_inside_macro_call()`. if matches!( def.kind, MacroDefKind::BuiltInAttr(_, expander) @@ -1410,8 +1408,18 @@ impl<'db> DefCollector<'db> { } } - let call_id = || { - attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def) + let mut call_id = || { + let active_attrs = self.prev_active_attrs.entry(ast_id).or_default(); + active_attrs.push(*attr_id); + + attr_macro_as_call_id( + self.db, + file_ast_id, + attr, + AttrMacroAttrIds::from_many(active_attrs), + self.def_map.krate, + def, + ) }; if matches!(def, MacroDefId { kind: MacroDefKind::BuiltInAttr(_, exp), .. } @@ -1429,7 +1437,7 @@ impl<'db> DefCollector<'db> { let diag = DefDiagnostic::invalid_derive_target( directive.module_id, ast_id, - attr.id, + *attr_id, ); self.def_map.diagnostics.push(diag); return recollect_without(self); @@ -1442,7 +1450,7 @@ impl<'db> DefCollector<'db> { Some(derive_macros) => { let call_id = call_id(); let mut len = 0; - for (idx, (path, call_site)) in derive_macros.enumerate() { + for (idx, (path, call_site, _)) in derive_macros.enumerate() { let ast_id = AstIdWithPath::new( file_id, ast_id.value, @@ -1453,7 +1461,7 @@ impl<'db> DefCollector<'db> { depth: directive.depth + 1, kind: MacroDirectiveKind::Derive { ast_id, - derive_attr: attr.id, + derive_attr: *attr_id, derive_pos: idx, ctxt: call_site.ctx, derive_macro_id: call_id, @@ -1469,13 +1477,13 @@ impl<'db> DefCollector<'db> { // Check the comment in [`builtin_attr_macro`]. self.def_map.modules[directive.module_id] .scope - .init_derive_attribute(ast_id, attr.id, call_id, len + 1); + .init_derive_attribute(ast_id, *attr_id, call_id, len + 1); } None => { let diag = DefDiagnostic::malformed_derive( directive.module_id, ast_id, - attr.id, + *attr_id, ); self.def_map.diagnostics.push(diag); } @@ -1712,16 +1720,17 @@ impl ModCollector<'_, '_> { }; let mut process_mod_item = |item: ModItemId| { - let attrs = self.item_tree.attrs(db, krate, item.ast_id()); - if let Some(cfg) = attrs.cfg() - && !self.is_cfg_enabled(&cfg) - { - let ast_id = item.ast_id().erase(); - self.emit_unconfigured_diagnostic(InFile::new(self.file_id(), ast_id), &cfg); - return; - } + let attrs = match self.item_tree.attrs(item.ast_id()) { + Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(), + None => Attrs::EMPTY, + Some(AttrsOrCfg::CfgDisabled(cfg)) => { + let ast_id = item.ast_id().erase(); + self.emit_unconfigured_diagnostic(InFile::new(self.file_id(), ast_id), &cfg.0); + return; + } + }; - if let Err(()) = self.resolve_attributes(&attrs, item, container) { + if let Err(()) = self.resolve_attributes(attrs, item, container) { // Do not process the item. It has at least one non-builtin attribute, so the // fixed-point algorithm is required to resolve the rest of them. return; @@ -1733,7 +1742,7 @@ impl ModCollector<'_, '_> { self.def_collector.crate_local_def_map.unwrap_or(&self.def_collector.local_def_map); match item { - ModItemId::Mod(m) => self.collect_module(m, &attrs), + ModItemId::Mod(m) => self.collect_module(m, attrs), ModItemId::Use(item_tree_id) => { let id = UseLoc { container: module, id: InFile::new(self.file_id(), item_tree_id) } @@ -2006,7 +2015,7 @@ impl ModCollector<'_, '_> { ); return; }; - for (path, _) in paths { + for (path, _, _) in paths { if let Some(name) = path.as_ident() { single_imports.push(name.clone()); } @@ -2020,7 +2029,7 @@ impl ModCollector<'_, '_> { ); } - fn collect_module(&mut self, module_ast_id: ItemTreeAstId, attrs: &Attrs) { + fn collect_module(&mut self, module_ast_id: ItemTreeAstId, attrs: Attrs<'_>) { let path_attr = attrs.by_key(sym::path).string_value_unescape(); let is_macro_use = attrs.by_key(sym::macro_use).exists(); let module = &self.item_tree[module_ast_id]; @@ -2061,23 +2070,18 @@ impl ModCollector<'_, '_> { self.file_id(), &module.name, path_attr.as_deref(), + self.def_collector.def_map.krate, ) { Ok((file_id, is_mod_rs, mod_dir)) => { let item_tree = db.file_item_tree(file_id.into()); - let krate = self.def_collector.def_map.krate; - let is_enabled = item_tree - .top_level_attrs(db, krate) - .cfg() - .and_then(|cfg| self.is_cfg_enabled(&cfg).not().then_some(cfg)) - .map_or(Ok(()), Err); - match is_enabled { - Err(cfg) => { + match item_tree.top_level_attrs() { + AttrsOrCfg::CfgDisabled(cfg) => { self.emit_unconfigured_diagnostic( InFile::new(self.file_id(), module_ast_id.erase()), - &cfg, + &cfg.0, ); } - Ok(()) => { + AttrsOrCfg::Enabled { attrs } => { let module_id = self.push_child_module( module.name.clone(), ast_id.value, @@ -2093,11 +2097,8 @@ impl ModCollector<'_, '_> { mod_dir, } .collect_in_top_module(item_tree.top_level_items()); - let is_macro_use = is_macro_use - || item_tree - .top_level_attrs(db, krate) - .by_key(sym::macro_use) - .exists(); + let is_macro_use = + is_macro_use || attrs.as_ref().by_key(sym::macro_use).exists(); if is_macro_use { self.import_all_legacy_macros(module_id); } @@ -2185,36 +2186,16 @@ impl ModCollector<'_, '_> { /// assumed to be resolved already. fn resolve_attributes( &mut self, - attrs: &Attrs, + attrs: Attrs<'_>, mod_item: ModItemId, container: ItemContainerId, ) -> Result<(), ()> { - let mut ignore_up_to = self + let ignore_up_to = self .def_collector .skip_attrs .get(&InFile::new(self.file_id(), mod_item.ast_id())) .copied(); - let iter = attrs - .iter() - .dedup_by(|a, b| { - // FIXME: this should not be required, all attributes on an item should have a - // unique ID! - // Still, this occurs because `#[cfg_attr]` can "expand" to multiple attributes: - // #[cfg_attr(not(off), unresolved, unresolved)] - // struct S; - // We should come up with a different way to ID attributes. - a.id == b.id - }) - .skip_while(|attr| match ignore_up_to { - Some(id) if attr.id == id => { - ignore_up_to = None; - true - } - Some(_) => true, - None => false, - }); - - for attr in iter { + for (attr_id, attr) in attrs.iter_after(ignore_up_to) { if self.def_collector.def_map.is_builtin_or_registered_attr(&attr.path) { continue; } @@ -2229,6 +2210,7 @@ impl ModCollector<'_, '_> { depth: self.macro_depth + 1, kind: MacroDirectiveKind::Attr { ast_id, + attr_id, attr: attr.clone(), mod_item, tree: self.tree_id, @@ -2244,9 +2226,14 @@ impl ModCollector<'_, '_> { } fn collect_macro_rules(&mut self, ast_id: ItemTreeAstId, module: ModuleId) { - let krate = self.def_collector.def_map.krate; let mac = &self.item_tree[ast_id]; - let attrs = self.item_tree.attrs(self.def_collector.db, krate, ast_id.upcast()); + let attrs = match self.item_tree.attrs(ast_id.upcast()) { + Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(), + None => Attrs::EMPTY, + Some(AttrsOrCfg::CfgDisabled(_)) => { + unreachable!("we only get here if the macro is not cfg'ed out") + } + }; let f_ast_id = InFile::new(self.file_id(), ast_id.upcast()); let export_attr = || attrs.by_key(sym::macro_export); @@ -2326,9 +2313,14 @@ impl ModCollector<'_, '_> { } fn collect_macro_def(&mut self, ast_id: ItemTreeAstId, module: ModuleId) { - let krate = self.def_collector.def_map.krate; let mac = &self.item_tree[ast_id]; - let attrs = self.item_tree.attrs(self.def_collector.db, krate, ast_id.upcast()); + let attrs = match self.item_tree.attrs(ast_id.upcast()) { + Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(), + None => Attrs::EMPTY, + Some(AttrsOrCfg::CfgDisabled(_)) => { + unreachable!("we only get here if the macro is not cfg'ed out") + } + }; let f_ast_id = InFile::new(self.file_id(), ast_id.upcast()); // Case 1: builtin macros @@ -2514,10 +2506,6 @@ impl ModCollector<'_, '_> { Some((a, b)) } - fn is_cfg_enabled(&self, cfg: &CfgExpr) -> bool { - self.def_collector.cfg_options.check(cfg) != Some(false) - } - fn emit_unconfigured_diagnostic(&mut self, ast_id: ErasedAstId, cfg: &CfgExpr) { self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code( self.module_id, @@ -2557,6 +2545,7 @@ mod tests { proc_macros: Default::default(), from_glob_import: Default::default(), skip_attrs: Default::default(), + prev_active_attrs: Default::default(), is_proc_macro: false, unresolved_extern_crates: Default::default(), }; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs index c495a0744919..6a07c56aeebe 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs @@ -17,8 +17,8 @@ pub enum DefDiagnosticKind { UnconfiguredCode { ast_id: ErasedAstId, cfg: CfgExpr, opts: CfgOptions }, UnresolvedMacroCall { ast: MacroCallKind, path: ModPath }, UnimplementedBuiltinMacro { ast: AstId }, - InvalidDeriveTarget { ast: AstId, id: usize }, - MalformedDerive { ast: AstId, id: usize }, + InvalidDeriveTarget { ast: AstId, id: AttrId }, + MalformedDerive { ast: AstId, id: AttrId }, MacroDefError { ast: AstId, message: String }, MacroError { ast: AstId, path: ModPath, err: ExpandErrorKind }, } @@ -119,10 +119,7 @@ impl DefDiagnostic { ast: AstId, id: AttrId, ) -> Self { - Self { - in_module: container, - kind: DefDiagnosticKind::InvalidDeriveTarget { ast, id: id.ast_index() }, - } + Self { in_module: container, kind: DefDiagnosticKind::InvalidDeriveTarget { ast, id } } } pub(super) fn malformed_derive( @@ -130,9 +127,6 @@ impl DefDiagnostic { ast: AstId, id: AttrId, ) -> Self { - Self { - in_module: container, - kind: DefDiagnosticKind::MalformedDerive { ast, id: id.ast_index() }, - } + Self { in_module: container, kind: DefDiagnosticKind::MalformedDerive { ast, id } } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs index 0c50f13edfb6..140b77ac002f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs @@ -1,6 +1,6 @@ //! This module resolves `mod foo;` declaration to file. use arrayvec::ArrayVec; -use base_db::AnchoredPath; +use base_db::{AnchoredPath, Crate}; use hir_expand::{EditionedFileId, name::Name}; use crate::{HirFileId, db::DefDatabase}; @@ -62,6 +62,7 @@ impl ModDir { file_id: HirFileId, name: &Name, attr_path: Option<&str>, + krate: Crate, ) -> Result<(EditionedFileId, bool, ModDir), Box<[String]>> { let name = name.as_str(); @@ -91,7 +92,7 @@ impl ModDir { if let Some(mod_dir) = self.child(dir_path, !root_dir_owner) { return Ok(( // FIXME: Edition, is this rightr? - EditionedFileId::new(db, file_id, orig_file_id.edition(db)), + EditionedFileId::new(db, file_id, orig_file_id.edition(db), krate), is_mod_rs, mod_dir, )); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs index cd8882183bb4..cd45afe57d7c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs @@ -3,8 +3,10 @@ use hir_expand::name::{AsName, Name}; use intern::sym; -use crate::attr::Attrs; -use crate::tt::{Leaf, TokenTree, TopSubtree, TtElement}; +use crate::{ + item_tree::Attrs, + tt::{Leaf, TokenTree, TopSubtree, TtElement}, +}; #[derive(Debug, PartialEq, Eq)] pub struct ProcMacroDef { @@ -29,8 +31,8 @@ impl ProcMacroKind { } } -impl Attrs { - pub fn parse_proc_macro_decl(&self, func_name: &Name) -> Option { +impl Attrs<'_> { + pub(crate) fn parse_proc_macro_decl(&self, func_name: &Name) -> Option { if self.is_proc_macro() { Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Bang }) } else if self.is_proc_macro_attribute() { @@ -51,15 +53,10 @@ impl Attrs { } } - pub fn parse_proc_macro_derive(&self) -> Option<(Name, Box<[Name]>)> { + pub(crate) fn parse_proc_macro_derive(&self) -> Option<(Name, Box<[Name]>)> { let derive = self.by_key(sym::proc_macro_derive).tt_values().next()?; parse_macro_name_and_helper_attrs(derive) } - - pub fn parse_rustc_builtin_macro(&self) -> Option<(Name, Box<[Name]>)> { - let derive = self.by_key(sym::rustc_builtin_macro).tt_values().next()?; - parse_macro_name_and_helper_attrs(derive) - } } // This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have @@ -84,14 +81,11 @@ pub(crate) fn parse_macro_name_and_helper_attrs(tt: &TopSubtree) -> Option<(Name let helpers = tt::TokenTreesView::new(&tt.token_trees().flat_tokens()[3..]).try_into_subtree()?; let helpers = helpers .iter() - .filter( - |tt| !matches!(tt, TtElement::Leaf(Leaf::Punct(comma)) if comma.char == ','), - ) - .map(|tt| match tt { + .filter_map(|tt| match tt { TtElement::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()), _ => None, }) - .collect::>>()?; + .collect::>(); Some((trait_name.as_name(), helpers)) } 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 ebbf87cad668..c9e8955ad68c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -21,7 +21,7 @@ use triomphe::Arc; use crate::{ ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, HasModule, ImplId, ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId, - attr::Attrs, + attrs::AttrFlags, db::DefDatabase, expr_store::{ ExpressionStore, ExpressionStoreSourceMap, @@ -48,12 +48,13 @@ pub struct StructSignature { pub store: Arc, pub flags: StructFlags, pub shape: FieldsShape, - pub repr: Option, } bitflags! { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct StructFlags: u8 { + /// Indicates whether this struct has `#[repr]`. + const HAS_REPR = 1 << 0; /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute. const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1; /// Indicates whether the struct has a `#[fundamental]` attribute. @@ -75,16 +76,19 @@ impl StructSignature { pub fn query(db: &dyn DefDatabase, id: StructId) -> (Arc, Arc) { let loc = id.lookup(db); let InFile { file_id, value: source } = loc.source(db); - let attrs = db.attrs(id.into()); + let attrs = AttrFlags::query(db, id.into()); let mut flags = StructFlags::empty(); - if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() { + if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) { flags |= StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS; } - if attrs.by_key(sym::fundamental).exists() { + if attrs.contains(AttrFlags::FUNDAMENTAL) { flags |= StructFlags::FUNDAMENTAL; } - if let Some(lang) = attrs.lang_item() { + if attrs.contains(AttrFlags::HAS_REPR) { + flags |= StructFlags::HAS_REPR; + } + if let Some(lang) = attrs.lang_item_with_attrs(db, id.into()) { match lang { LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA, LangItem::OwnedBox => flags |= StructFlags::IS_BOX, @@ -94,7 +98,6 @@ impl StructSignature { _ => (), } } - let repr = attrs.repr(); let shape = adt_shape(source.kind()); let (store, generic_params, source_map) = lower_generic_params( @@ -112,11 +115,19 @@ impl StructSignature { flags, shape, name: as_name_opt(source.name()), - repr, }), Arc::new(source_map), ) } + + #[inline] + pub fn repr(&self, db: &dyn DefDatabase, id: StructId) -> Option { + if self.flags.contains(StructFlags::HAS_REPR) { + AttrFlags::repr(db, id.into()) + } else { + None + } + } } #[inline] @@ -134,22 +145,22 @@ pub struct UnionSignature { pub generic_params: Arc, pub store: Arc, pub flags: StructFlags, - pub repr: Option, } impl UnionSignature { pub fn query(db: &dyn DefDatabase, id: UnionId) -> (Arc, Arc) { let loc = id.lookup(db); - let attrs = db.attrs(id.into()); + let attrs = AttrFlags::query(db, id.into()); let mut flags = StructFlags::empty(); - if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() { + if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) { flags |= StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS; } - if attrs.by_key(sym::fundamental).exists() { + if attrs.contains(AttrFlags::FUNDAMENTAL) { flags |= StructFlags::FUNDAMENTAL; } - - let repr = attrs.repr(); + if attrs.contains(AttrFlags::HAS_REPR) { + flags |= StructFlags::HAS_REPR; + } let InFile { file_id, value: source } = loc.source(db); let (store, generic_params, source_map) = lower_generic_params( @@ -165,7 +176,6 @@ impl UnionSignature { generic_params, store, flags, - repr, name: as_name_opt(source.name()), }), Arc::new(source_map), @@ -186,20 +196,17 @@ pub struct EnumSignature { pub generic_params: Arc, pub store: Arc, pub flags: EnumFlags, - pub repr: Option, } impl EnumSignature { pub fn query(db: &dyn DefDatabase, id: EnumId) -> (Arc, Arc) { let loc = id.lookup(db); - let attrs = db.attrs(id.into()); + let attrs = AttrFlags::query(db, id.into()); let mut flags = EnumFlags::empty(); - if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() { + if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) { flags |= EnumFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS; } - let repr = attrs.repr(); - let InFile { file_id, value: source } = loc.source(db); let (store, generic_params, source_map) = lower_generic_params( db, @@ -215,15 +222,14 @@ impl EnumSignature { generic_params, store, flags, - repr, name: as_name_opt(source.name()), }), Arc::new(source_map), ) } - pub fn variant_body_type(&self) -> IntegerType { - match self.repr { + pub fn variant_body_type(db: &dyn DefDatabase, id: EnumId) -> IntegerType { + match AttrFlags::repr(db, id.into()) { Some(ReprOptions { int: Some(builtin), .. }) => builtin, _ => IntegerType::Pointer(true), } @@ -251,9 +257,9 @@ impl ConstSignature { let loc = id.lookup(db); let module = loc.container.module(db); - let attrs = db.attrs(id.into()); + let attrs = AttrFlags::query(db, id.into()); let mut flags = ConstFlags::empty(); - if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() { + if attrs.contains(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL) { flags |= ConstFlags::RUSTC_ALLOW_INCOHERENT_IMPL; } let source = loc.source(db); @@ -306,9 +312,9 @@ impl StaticSignature { let loc = id.lookup(db); let module = loc.container.module(db); - let attrs = db.attrs(id.into()); + let attrs = AttrFlags::query(db, id.into()); let mut flags = StaticFlags::empty(); - if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() { + if attrs.contains(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL) { flags |= StaticFlags::RUSTC_ALLOW_INCOHERENT_IMPL; } @@ -433,7 +439,7 @@ impl TraitSignature { let loc = id.lookup(db); let mut flags = TraitFlags::empty(); - let attrs = db.attrs(id.into()); + let attrs = AttrFlags::query(db, id.into()); let source = loc.source(db); if source.value.auto_token().is_some() { flags.insert(TraitFlags::AUTO); @@ -444,34 +450,23 @@ impl TraitSignature { if source.value.eq_token().is_some() { flags.insert(TraitFlags::ALIAS); } - if attrs.by_key(sym::fundamental).exists() { + if attrs.contains(AttrFlags::FUNDAMENTAL) { flags |= TraitFlags::FUNDAMENTAL; } - if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() { + if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) { flags |= TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS; } - if attrs.by_key(sym::rustc_paren_sugar).exists() { + if attrs.contains(AttrFlags::RUSTC_PAREN_SUGAR) { flags |= TraitFlags::RUSTC_PAREN_SUGAR; } - if attrs.by_key(sym::rustc_coinductive).exists() { + if attrs.contains(AttrFlags::RUSTC_COINDUCTIVE) { flags |= TraitFlags::COINDUCTIVE; } - let mut skip_array_during_method_dispatch = - attrs.by_key(sym::rustc_skip_array_during_method_dispatch).exists(); - let mut skip_boxed_slice_during_method_dispatch = false; - for tt in attrs.by_key(sym::rustc_skip_during_method_dispatch).tt_values() { - for tt in tt.iter() { - if let tt::iter::TtElement::Leaf(tt::Leaf::Ident(ident)) = tt { - skip_array_during_method_dispatch |= ident.sym == sym::array; - skip_boxed_slice_during_method_dispatch |= ident.sym == sym::boxed_slice; - } - } - } - if skip_array_during_method_dispatch { + if attrs.contains(AttrFlags::RUSTC_SKIP_ARRAY_DURING_METHOD_DISPATCH) { flags |= TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH; } - if skip_boxed_slice_during_method_dispatch { + if attrs.contains(AttrFlags::RUSTC_SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH) { flags |= TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH; } @@ -503,7 +498,8 @@ bitflags! { const HAS_TARGET_FEATURE = 1 << 9; const DEPRECATED_SAFE_2024 = 1 << 10; const EXPLICIT_SAFE = 1 << 11; - const RUSTC_INTRINSIC = 1 << 12; + const HAS_LEGACY_CONST_GENERICS = 1 << 12; + const RUSTC_INTRINSIC = 1 << 13; } } @@ -516,8 +512,6 @@ pub struct FunctionSignature { pub ret_type: Option, pub abi: Option, pub flags: FnFlags, - // FIXME: we should put this behind a fn flags + query to avoid bloating the struct - pub legacy_const_generics_indices: Option>>, } impl FunctionSignature { @@ -529,23 +523,26 @@ impl FunctionSignature { let module = loc.container.module(db); let mut flags = FnFlags::empty(); - let attrs = db.attrs(id.into()); - if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() { + let attrs = AttrFlags::query(db, id.into()); + if attrs.contains(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL) { flags.insert(FnFlags::RUSTC_ALLOW_INCOHERENT_IMPL); } - if attrs.by_key(sym::target_feature).exists() { + if attrs.contains(AttrFlags::HAS_TARGET_FEATURE) { flags.insert(FnFlags::HAS_TARGET_FEATURE); } - if attrs.by_key(sym::rustc_intrinsic).exists() { + + if attrs.contains(AttrFlags::RUSTC_INTRINSIC) { flags.insert(FnFlags::RUSTC_INTRINSIC); } - let legacy_const_generics_indices = attrs.rustc_legacy_const_generics(); + if attrs.contains(AttrFlags::HAS_LEGACY_CONST_GENERICS) { + flags.insert(FnFlags::HAS_LEGACY_CONST_GENERICS); + } let source = loc.source(db); if source.value.unsafe_token().is_some() { - if attrs.by_key(sym::rustc_deprecated_safe_2024).exists() { + if attrs.contains(AttrFlags::RUSTC_DEPRECATED_SAFE_2024) { flags.insert(FnFlags::DEPRECATED_SAFE_2024); } else { flags.insert(FnFlags::UNSAFE); @@ -587,7 +584,6 @@ impl FunctionSignature { ret_type, abi, flags, - legacy_const_generics_indices, name, }), Arc::new(source_map), @@ -636,6 +632,19 @@ impl FunctionSignature { self.flags.contains(FnFlags::HAS_TARGET_FEATURE) } + #[inline] + pub fn legacy_const_generics_indices<'db>( + &self, + db: &'db dyn DefDatabase, + id: FunctionId, + ) -> Option<&'db [u32]> { + if !self.flags.contains(FnFlags::HAS_LEGACY_CONST_GENERICS) { + return None; + } + + AttrFlags::legacy_const_generic_indices(db, id).as_deref() + } + pub fn is_intrinsic(db: &dyn DefDatabase, id: FunctionId) -> bool { let data = db.function_signature(id); data.flags.contains(FnFlags::RUSTC_INTRINSIC) @@ -679,11 +688,11 @@ impl TypeAliasSignature { let loc = id.lookup(db); let mut flags = TypeAliasFlags::empty(); - let attrs = db.attrs(id.into()); - if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() { + let attrs = AttrFlags::query(db, id.into()); + if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) { flags.insert(TypeAliasFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPL); } - if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() { + if attrs.contains(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL) { flags.insert(TypeAliasFlags::RUSTC_ALLOW_INCOHERENT_IMPL); } if matches!(loc.container, ItemContainerId::ExternBlockId(_)) { @@ -866,7 +875,7 @@ fn lower_fields( let mut has_fields = false; for (ty, field) in fields.value { has_fields = true; - match Attrs::is_cfg_enabled_for(db, &field, col.span_map(), cfg_options) { + match AttrFlags::is_cfg_enabled_for(&field, cfg_options) { Ok(()) => { let type_ref = col.lower_type_ref_opt(ty, &mut ExprCollector::impl_trait_error_allocator); @@ -928,7 +937,6 @@ impl EnumVariants { let loc = e.lookup(db); let source = loc.source(db); let ast_id_map = db.ast_id_map(source.file_id); - let span_map = db.span_map(source.file_id); let mut diagnostics = ThinVec::new(); let cfg_options = loc.container.krate.cfg_options(db); @@ -940,7 +948,7 @@ impl EnumVariants { .variants() .filter_map(|variant| { let ast_id = ast_id_map.ast_id(&variant); - match Attrs::is_cfg_enabled_for(db, &variant, span_map.as_ref(), cfg_options) { + match AttrFlags::is_cfg_enabled_for(&variant, cfg_options) { Ok(()) => { let enum_variant = EnumVariantLoc { id: source.with_value(ast_id), parent: e, index } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs index 367b543cf908..153fd195f0ad 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs @@ -7,7 +7,7 @@ use syntax::{AstNode, AstPtr, ast}; use crate::{ AstIdLoc, GenericDefId, LocalFieldId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, - UseId, VariantId, attr::Attrs, db::DefDatabase, + UseId, VariantId, attrs::AttrFlags, db::DefDatabase, }; pub trait HasSource { @@ -145,15 +145,13 @@ impl HasChildSource for VariantId { (lookup.source(db).map(|it| it.kind()), lookup.container) } }; - let span_map = db.span_map(src.file_id); let mut map = ArenaMap::new(); match &src.value { ast::StructKind::Tuple(fl) => { let cfg_options = container.krate.cfg_options(db); let mut idx = 0; for fd in fl.fields() { - let enabled = - Attrs::is_cfg_enabled_for(db, &fd, span_map.as_ref(), cfg_options).is_ok(); + let enabled = AttrFlags::is_cfg_enabled_for(&fd, cfg_options).is_ok(); if !enabled { continue; } @@ -168,8 +166,7 @@ impl HasChildSource for VariantId { let cfg_options = container.krate.cfg_options(db); let mut idx = 0; for fd in fl.fields() { - let enabled = - Attrs::is_cfg_enabled_for(db, &fd, span_map.as_ref(), cfg_options).is_ok(); + let enabled = AttrFlags::is_cfg_enabled_for(&fd, cfg_options).is_ok(); if !enabled { continue; } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs index 12a1c1554cc1..3bb9c361b3c8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs @@ -190,7 +190,15 @@ impl TestDB { let mut res = DefMap::ROOT; for (module, data) in def_map.modules() { let src = data.definition_source(self); - if src.file_id != position.file_id { + // We're not comparing the `base_db::EditionedFileId`, but rather the VFS `FileId`, because + // `position.file_id` is created before the def map, causing it to have to wrong crate + // attached often, which means it won't compare equal. This should not be a problem in real + // r-a session, only in tests, because in real r-a we only guess the crate on syntactic-only + // (e.g. on-enter) handlers. The rest pick the `EditionedFileId` from the def map. + let Some(file_id) = src.file_id.file_id() else { + continue; + }; + if file_id.file_id(self) != position.file_id.file_id(self) { continue; } @@ -230,7 +238,15 @@ impl TestDB { let mut fn_def = None; for (_, module) in def_map.modules() { let file_id = module.definition_source(self).file_id; - if file_id != position.file_id { + // We're not comparing the `base_db::EditionedFileId`, but rather the VFS `FileId`, because + // `position.file_id` is created before the def map, causing it to have to wrong crate + // attached often, which means it won't compare equal. This should not be a problem in real + // r-a session, only in tests, because in real r-a we only guess the crate on syntactic-only + // (e.g. on-enter) handlers. The rest pick the `EditionedFileId` from the def map. + let Some(file_id) = file_id.file_id() else { + continue; + }; + if file_id.file_id(self) != position.file_id.file_id(self) { continue; } for decl in module.scope.declarations() { @@ -253,26 +269,25 @@ impl TestDB { }; if size != Some(new_size) { size = Some(new_size); - fn_def = Some(it); + fn_def = Some((it, file_id)); } } } } // Find the innermost block expression that has a `DefMap`. - let def_with_body = fn_def?.into(); + let (def_with_body, file_id) = fn_def?; + let def_with_body = def_with_body.into(); let source_map = self.body_with_source_map(def_with_body).1; let scopes = self.expr_scopes(def_with_body); - let root_syntax_node = self.parse(position.file_id).syntax_node(); + let root_syntax_node = self.parse(file_id).syntax_node(); let scope_iter = algo::ancestors_at_offset(&root_syntax_node, position.offset).filter_map(|node| { let block = ast::BlockExpr::cast(node)?; let expr = ast::Expr::from(block); - let expr_id = source_map - .node_expr(InFile::new(position.file_id.into(), &expr))? - .as_expr() - .unwrap(); + let expr_id = + source_map.node_expr(InFile::new(file_id.into(), &expr))?.as_expr().unwrap(); let scope = scopes.scope_for(expr_id).unwrap(); Some(scope) }); diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml index 80a3c0848653..4fa476afb64a 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml @@ -23,6 +23,8 @@ triomphe.workspace = true query-group.workspace = true salsa.workspace = true salsa-macros.workspace = true +arrayvec.workspace = true +thin-vec.workspace = true # local deps stdx.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 986f8764f5c9..e1807cd2e1e9 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -1,200 +1,397 @@ -//! A higher level attributes based on TokenTree, with also some shortcuts. -use std::iter; -use std::{borrow::Cow, fmt, ops}; +//! Defines the basics of attributes lowering. +//! +//! The heart and soul of this module is [`expand_cfg_attr()`], alongside its sibling +//! [`expand_cfg_attr_with_doc_comments()`]. It is used to implement all attribute lowering +//! in r-a. Its basic job is to list attributes; however, attributes do not necessarily map +//! into [`ast::Attr`], because `cfg_attr` can map to zero, one, or more attributes +//! (`#[cfg_attr(predicate, attr1, attr2, ...)]`). To bridge this gap, this module defines +//! [`Meta`], which represents a desugared attribute. Various bits of r-a need different +//! things from [`Meta`], therefore it contains many parts. The basic idea is: +//! +//! - There are three kinds of attributes, `path = value`, `path`, and `path(token_tree)`. +//! - Most bits of rust-analyzer only need to deal with some paths. Therefore, we keep +//! the path only if it has up to 2 segments, or one segment for `path = value`. +//! We also only keep the value in `path = value` if it is a literal. However, we always +//! save the all relevant ranges of attributes (the path range, and the full attribute range) +//! for parts of r-a (e.g. name resolution) that need a faithful representation of the +//! attribute. +//! +//! [`expand_cfg_attr()`] expands `cfg_attr`s as it goes (as its name implies), to list +//! all attributes. +//! +//! Another thing to note is that we need to be able to map an attribute back to a range +//! (for diagnostic purposes etc.). This is only ever needed for attributes that participate +//! in name resolution. An attribute is mapped back by its [`AttrId`], which is just an +//! index into the item tree attributes list. To minimize the risk of bugs, we have one +//! place (here) and one function ([`is_item_tree_filtered_attr()`]) that decides whether +//! an attribute participate in name resolution. +use std::{ + borrow::Cow, cell::OnceCell, convert::Infallible, fmt, iter::Peekable, ops::ControlFlow, +}; + +use ::tt::{TextRange, TextSize}; +use arrayvec::ArrayVec; use base_db::Crate; use cfg::{CfgExpr, CfgOptions}; use either::Either; -use intern::{Interned, Symbol, sym}; - +use intern::{Interned, Symbol}; use mbe::{DelimiterKind, Punct}; -use smallvec::{SmallVec, smallvec}; -use span::{Span, SyntaxContext}; -use syntax::unescape; -use syntax::{AstNode, AstToken, SyntaxNode, ast, match_ast}; -use syntax_bridge::{DocCommentDesugarMode, desugar_doc_comment_text, syntax_node_to_token_tree}; -use triomphe::ThinArc; +use parser::T; +use smallvec::SmallVec; +use span::{RealSpanMap, Span, SyntaxContext}; +use syntax::{ + AstNode, NodeOrToken, SyntaxNode, SyntaxToken, + ast::{self, TokenTreeChildren}, + unescape, +}; +use syntax_bridge::DocCommentDesugarMode; use crate::{ + AstId, db::ExpandDatabase, mod_path::ModPath, - name::Name, span_map::SpanMapRef, - tt::{self, TopSubtree, token_to_literal}, + tt::{self, TopSubtree}, }; -/// Syntactical attributes, without filtering of `cfg_attr`s. -#[derive(Default, Debug, Clone, PartialEq, Eq)] -pub struct RawAttrs { - // FIXME: This can become `Box<[Attr]>` if https://internals.rust-lang.org/t/layout-of-dst-box/21728?u=chrefr is accepted. - entries: Option>, +#[derive(Debug)] +pub struct AttrPath { + /// This can be empty if the path is not of 1 or 2 segments exactly. + pub segments: ArrayVec, + pub range: TextRange, + // FIXME: This shouldn't be textual, `#[test]` needs name resolution. + // And if textual, it shouldn't be here, it should be in hir-def/src/attrs.rs. But some macros + // fully qualify `test` as `core::prelude::vX::test`, and this is more than 2 segments, so hir-def + // attrs can't find it. But this will mean we have to push every up-to-4-segments path, which + // may impact perf. So it was easier to just hack it here. + pub is_test: bool, } -impl ops::Deref for RawAttrs { - type Target = [Attr]; - - fn deref(&self) -> &[Attr] { - match &self.entries { - Some(it) => &it.slice, - None => &[], - } - } -} - -impl RawAttrs { - pub const EMPTY: Self = Self { entries: None }; - - pub fn new( - db: &dyn ExpandDatabase, - owner: &dyn ast::HasAttrs, - span_map: SpanMapRef<'_>, - ) -> Self { - let entries: Vec<_> = Self::attrs_iter::(db, owner, span_map).collect(); - - let entries = if entries.is_empty() { - None - } else { - Some(ThinArc::from_header_and_iter((), entries.into_iter())) - }; - - RawAttrs { entries } - } - - /// A [`RawAttrs`] that has its `#[cfg_attr(...)]` attributes expanded. - pub fn new_expanded( - db: &dyn ExpandDatabase, - owner: &dyn ast::HasAttrs, - span_map: SpanMapRef<'_>, - cfg_options: &CfgOptions, - ) -> Self { - let entries: Vec<_> = - Self::attrs_iter_expanded::(db, owner, span_map, cfg_options).collect(); - - let entries = if entries.is_empty() { - None - } else { - Some(ThinArc::from_header_and_iter((), entries.into_iter())) - }; - - RawAttrs { entries } - } - - pub fn attrs_iter( - db: &dyn ExpandDatabase, - owner: &dyn ast::HasAttrs, - span_map: SpanMapRef<'_>, - ) -> impl Iterator { - collect_attrs(owner).filter_map(move |(id, attr)| match attr { - Either::Left(attr) => { - attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id)) +impl AttrPath { + #[inline] + fn extract(path: &ast::Path) -> Self { + let mut is_test = false; + let segments = (|| { + let mut segments = ArrayVec::new(); + let segment2 = path.segment()?.name_ref()?.syntax().first_token()?; + if segment2.text() == "test" { + // `#[test]` or `#[core::prelude::vX::test]`. + is_test = true; } - Either::Right(comment) if DESUGAR_COMMENTS => comment.doc_comment().map(|doc| { - let span = span_map.span_for_range(comment.syntax().text_range()); - let (text, kind) = desugar_doc_comment_text(doc, DocCommentDesugarMode::ProcMacro); - Attr { - id, - input: Some(Box::new(AttrInput::Literal(tt::Literal { - symbol: text, - span, - kind, - suffix: None, - }))), - path: Interned::new(ModPath::from(Name::new_symbol(sym::doc, span.ctx))), - ctxt: span.ctx, + let segment1 = path.qualifier(); + if let Some(segment1) = segment1 { + if segment1.qualifier().is_some() { + None + } else { + let segment1 = segment1.segment()?.name_ref()?.syntax().first_token()?; + segments.push(segment1); + segments.push(segment2); + Some(segments) } - }), - Either::Right(_) => None, - }) - } - - pub fn attrs_iter_expanded( - db: &dyn ExpandDatabase, - owner: &dyn ast::HasAttrs, - span_map: SpanMapRef<'_>, - cfg_options: &CfgOptions, - ) -> impl Iterator { - Self::attrs_iter::(db, owner, span_map) - .flat_map(|attr| attr.expand_cfg_attr(db, cfg_options)) - } - - pub fn merge(&self, other: Self) -> Self { - match (&self.entries, other.entries) { - (None, None) => Self::EMPTY, - (None, entries @ Some(_)) => Self { entries }, - (Some(entries), None) => Self { entries: Some(entries.clone()) }, - (Some(a), Some(b)) => { - let last_ast_index = a.slice.last().map_or(0, |it| it.id.ast_index() + 1); - let items = a - .slice - .iter() - .cloned() - .chain(b.slice.iter().map(|it| { - let mut it = it.clone(); - let id = it.id.ast_index() + last_ast_index; - it.id = AttrId::new(id, it.id.is_inner_attr()); - it - })) - .collect::>(); - Self { entries: Some(ThinArc::from_header_and_iter((), items.into_iter())) } + } else { + segments.push(segment2); + Some(segments) } + })(); + AttrPath { + segments: segments.unwrap_or(ArrayVec::new()), + range: path.syntax().text_range(), + is_test, } } - /// Processes `cfg_attr`s - pub fn expand_cfg_attr(self, db: &dyn ExpandDatabase, krate: Crate) -> RawAttrs { - let has_cfg_attrs = - self.iter().any(|attr| attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr)); - if !has_cfg_attrs { - return self; + #[inline] + pub fn is1(&self, segment: &str) -> bool { + self.segments.len() == 1 && self.segments[0].text() == segment + } +} + +#[derive(Debug)] +pub enum Meta { + /// `name` is `None` if not a single token. `value` is a literal or `None`. + NamedKeyValue { + path_range: TextRange, + name: Option, + value: Option, + }, + TokenTree { + path: AttrPath, + tt: ast::TokenTree, + }, + Path { + path: AttrPath, + }, +} + +impl Meta { + #[inline] + pub fn path_range(&self) -> TextRange { + match self { + Meta::NamedKeyValue { path_range, .. } => *path_range, + Meta::TokenTree { path, .. } | Meta::Path { path } => path.range, + } + } + + fn extract(iter: &mut Peekable) -> Option<(Self, TextSize)> { + let mut start_offset = None; + if let Some(NodeOrToken::Token(colon1)) = iter.peek() + && colon1.kind() == T![:] + { + start_offset = Some(colon1.text_range().start()); + iter.next(); + iter.next_if(|it| it.as_token().is_some_and(|it| it.kind() == T![:])); + } + let first_segment = iter + .next_if(|it| it.as_token().is_some_and(|it| it.kind().is_any_identifier()))? + .into_token()?; + let mut is_test = first_segment.text() == "test"; + let start_offset = start_offset.unwrap_or_else(|| first_segment.text_range().start()); + + let mut segments_len = 1; + let mut second_segment = None; + let mut path_range = first_segment.text_range(); + while iter.peek().and_then(NodeOrToken::as_token).is_some_and(|it| it.kind() == T![:]) + && let _ = iter.next() + && iter.peek().and_then(NodeOrToken::as_token).is_some_and(|it| it.kind() == T![:]) + && let _ = iter.next() + && let Some(NodeOrToken::Token(segment)) = iter.peek() + && segment.kind().is_any_identifier() + { + segments_len += 1; + is_test = segment.text() == "test"; + second_segment = Some(segment.clone()); + path_range = TextRange::new(path_range.start(), segment.text_range().end()); + iter.next(); } - let cfg_options = krate.cfg_options(db); - let new_attrs = self - .iter() - .cloned() - .flat_map(|attr| attr.expand_cfg_attr(db, cfg_options)) - .collect::>(); - let entries = if new_attrs.is_empty() { - None - } else { - Some(ThinArc::from_header_and_iter((), new_attrs.into_iter())) + let segments = |first, second| { + let mut segments = ArrayVec::new(); + if segments_len <= 2 { + segments.push(first); + if let Some(second) = second { + segments.push(second); + } + } + segments }; - RawAttrs { entries } + let meta = match iter.peek() { + Some(NodeOrToken::Token(eq)) if eq.kind() == T![=] => { + iter.next(); + let value = match iter.peek() { + Some(NodeOrToken::Token(token)) if token.kind().is_literal() => { + // No need to consume it, it will be consumed by `extract_and_eat_comma()`. + Some(token.clone()) + } + _ => None, + }; + let name = if second_segment.is_none() { Some(first_segment) } else { None }; + Meta::NamedKeyValue { path_range, name, value } + } + Some(NodeOrToken::Node(tt)) => Meta::TokenTree { + path: AttrPath { + segments: segments(first_segment, second_segment), + range: path_range, + is_test, + }, + tt: tt.clone(), + }, + _ => Meta::Path { + path: AttrPath { + segments: segments(first_segment, second_segment), + range: path_range, + is_test, + }, + }, + }; + Some((meta, start_offset)) } - pub fn is_empty(&self) -> bool { - self.entries.is_none() + fn extract_possibly_unsafe( + iter: &mut Peekable, + container: &ast::TokenTree, + ) -> Option<(Self, TextRange)> { + if iter.peek().is_some_and(|it| it.as_token().is_some_and(|it| it.kind() == T![unsafe])) { + iter.next(); + let tt = iter.next()?.into_node()?; + let result = Self::extract(&mut TokenTreeChildren::new(&tt).peekable()).map( + |(meta, start_offset)| (meta, TextRange::new(start_offset, tt_end_offset(&tt))), + ); + while iter.next().is_some_and(|it| it.as_token().is_none_or(|it| it.kind() != T![,])) {} + result + } else { + Self::extract(iter).map(|(meta, start_offset)| { + let end_offset = 'find_end_offset: { + for it in iter { + if let NodeOrToken::Token(it) = it + && it.kind() == T![,] + { + break 'find_end_offset it.text_range().start(); + } + } + tt_end_offset(container) + }; + (meta, TextRange::new(start_offset, end_offset)) + }) + } } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct AttrId { - id: u32, +fn tt_end_offset(tt: &ast::TokenTree) -> TextSize { + tt.syntax().last_token().unwrap().text_range().start() } -// FIXME: This only handles a single level of cfg_attr nesting -// that is `#[cfg_attr(all(), cfg_attr(all(), cfg(any())))]` breaks again -impl AttrId { - const INNER_ATTR_SET_BIT: u32 = 1 << 31; +/// The callback is passed a desugared form of the attribute ([`Meta`]), a [`SyntaxNode`] fully containing it +/// (note: it may not be the direct parent), the range within the [`SyntaxNode`] bounding the attribute, +/// and the outermost `ast::Attr`. Note that one node may map to multiple [`Meta`]s due to `cfg_attr`. +#[inline] +pub fn expand_cfg_attr<'a, BreakValue>( + attrs: impl Iterator, + cfg_options: impl FnMut() -> &'a CfgOptions, + mut callback: impl FnMut(Meta, &SyntaxNode, TextRange, &ast::Attr) -> ControlFlow, +) -> Option { + expand_cfg_attr_with_doc_comments::( + attrs.map(Either::Left), + cfg_options, + move |Either::Left((meta, container, range, top_attr))| { + callback(meta, container, range, top_attr) + }, + ) +} - pub fn new(id: usize, is_inner: bool) -> Self { - assert!(id <= !Self::INNER_ATTR_SET_BIT as usize); - let id = id as u32; - Self { id: if is_inner { id | Self::INNER_ATTR_SET_BIT } else { id } } - } +#[inline] +pub fn expand_cfg_attr_with_doc_comments<'a, DocComment, BreakValue>( + mut attrs: impl Iterator>, + mut cfg_options: impl FnMut() -> &'a CfgOptions, + mut callback: impl FnMut( + Either<(Meta, &SyntaxNode, TextRange, &ast::Attr), DocComment>, + ) -> ControlFlow, +) -> Option { + let mut stack = SmallVec::<[_; 1]>::new(); + let result = attrs.try_for_each(|top_attr| { + let top_attr = match top_attr { + Either::Left(it) => it, + Either::Right(comment) => return callback(Either::Right(comment)), + }; + if let Some((attr_name, tt)) = top_attr.as_simple_call() + && attr_name == "cfg_attr" + { + let mut tt_iter = TokenTreeChildren::new(&tt).peekable(); + let cfg = cfg::CfgExpr::parse_from_ast(&mut tt_iter); + if cfg_options().check(&cfg) != Some(false) { + stack.push((tt_iter, tt)); + while let Some((tt_iter, tt)) = stack.last_mut() { + let Some((attr, range)) = Meta::extract_possibly_unsafe(tt_iter, tt) else { + stack.pop(); + continue; + }; + if let Meta::TokenTree { path, tt: nested_tt } = &attr + && path.is1("cfg_attr") + { + let mut nested_tt_iter = TokenTreeChildren::new(nested_tt).peekable(); + let cfg = cfg::CfgExpr::parse_from_ast(&mut nested_tt_iter); + if cfg_options().check(&cfg) != Some(false) { + stack.push((nested_tt_iter, nested_tt.clone())); + } + } else { + callback(Either::Left((attr, tt.syntax(), range, &top_attr)))?; + } + } + } + } else if let Some(ast_meta) = top_attr.meta() + && let Some(path) = ast_meta.path() + { + let path = AttrPath::extract(&path); + let meta = if let Some(tt) = ast_meta.token_tree() { + Meta::TokenTree { path, tt } + } else if let Some(value) = ast_meta.expr() { + let value = + if let ast::Expr::Literal(value) = value { Some(value.token()) } else { None }; + let name = + if path.segments.len() == 1 { Some(path.segments[0].clone()) } else { None }; + Meta::NamedKeyValue { name, value, path_range: path.range } + } else { + Meta::Path { path } + }; + callback(Either::Left(( + meta, + ast_meta.syntax(), + ast_meta.syntax().text_range(), + &top_attr, + )))?; + } + ControlFlow::Continue(()) + }); + result.break_value() +} - pub fn ast_index(&self) -> usize { - (self.id & !Self::INNER_ATTR_SET_BIT) as usize - } +#[inline] +pub(crate) fn is_item_tree_filtered_attr(name: &str) -> bool { + matches!( + name, + "doc" + | "stable" + | "unstable" + | "target_feature" + | "allow" + | "expect" + | "warn" + | "deny" + | "forbid" + | "repr" + | "inline" + | "track_caller" + | "must_use" + ) +} - pub fn is_inner_attr(&self) -> bool { - self.id & Self::INNER_ATTR_SET_BIT != 0 - } +/// This collects attributes exactly as the item tree needs them. This is used for the item tree, +/// as well as for resolving [`AttrId`]s. +pub fn collect_item_tree_attrs<'a, BreakValue>( + owner: &dyn ast::HasAttrs, + cfg_options: impl Fn() -> &'a CfgOptions, + mut on_attr: impl FnMut(Meta, &SyntaxNode, &ast::Attr, TextRange) -> ControlFlow, +) -> Option> { + let attrs = ast::attrs_including_inner(owner); + expand_cfg_attr( + attrs, + || cfg_options(), + |attr, container, range, top_attr| { + // We filter builtin attributes that we don't need for nameres, because this saves memory. + // I only put the most common attributes, but if some attribute becomes common feel free to add it. + // Notice, however: for an attribute to be filtered out, it *must* not be shadowable with a macro! + let filter = match &attr { + Meta::NamedKeyValue { name: Some(name), .. } => { + is_item_tree_filtered_attr(name.text()) + } + Meta::TokenTree { path, tt } if path.segments.len() == 1 => { + let name = path.segments[0].text(); + if name == "cfg" { + let cfg = + CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(tt).peekable()); + if cfg_options().check(&cfg) == Some(false) { + return ControlFlow::Break(Either::Right(cfg)); + } + true + } else { + is_item_tree_filtered_attr(name) + } + } + Meta::Path { path } => { + path.segments.len() == 1 && is_item_tree_filtered_attr(path.segments[0].text()) + } + _ => false, + }; + if !filter && let ControlFlow::Break(v) = on_attr(attr, container, top_attr, range) { + return ControlFlow::Break(Either::Left(v)); + } + ControlFlow::Continue(()) + }, + ) } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Attr { - pub id: AttrId, pub path: Interned, pub input: Option>, pub ctxt: SyntaxContext, @@ -217,131 +414,6 @@ impl fmt::Display for AttrInput { } } -impl Attr { - fn from_src( - db: &dyn ExpandDatabase, - ast: ast::Meta, - span_map: SpanMapRef<'_>, - id: AttrId, - ) -> Option { - let path = ast.path()?; - let range = path.syntax().text_range(); - let path = Interned::new(ModPath::from_src(db, path, &mut |range| { - span_map.span_for_range(range).ctx - })?); - let span = span_map.span_for_range(range); - let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { - let token = lit.token(); - Some(Box::new(AttrInput::Literal(token_to_literal(token.text(), span)))) - } else if let Some(tt) = ast.token_tree() { - let tree = syntax_node_to_token_tree( - tt.syntax(), - span_map, - span, - DocCommentDesugarMode::ProcMacro, - ); - Some(Box::new(AttrInput::TokenTree(tree))) - } else { - None - }; - Some(Attr { id, path, input, ctxt: span.ctx }) - } - - fn from_tt( - db: &dyn ExpandDatabase, - mut tt: tt::TokenTreesView<'_>, - id: AttrId, - ) -> Option { - if matches!(tt.flat_tokens(), - [tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, .. })), ..] - if *sym == sym::unsafe_ - ) { - match tt.iter().nth(1) { - Some(tt::TtElement::Subtree(_, iter)) => tt = iter.remaining(), - _ => return None, - } - } - let first = tt.flat_tokens().first()?; - let ctxt = first.first_span().ctx; - let (path, input) = { - let mut iter = tt.iter(); - let start = iter.savepoint(); - let mut input = tt::TokenTreesView::new(&[]); - let mut path = iter.from_savepoint(start); - let mut path_split_savepoint = iter.savepoint(); - while let Some(tt) = iter.next() { - path = iter.from_savepoint(start); - if !matches!( - tt, - tt::TtElement::Leaf( - tt::Leaf::Punct(tt::Punct { char: ':' | '$', .. }) | tt::Leaf::Ident(_), - ) - ) { - input = path_split_savepoint.remaining(); - break; - } - path_split_savepoint = iter.savepoint(); - } - (path, input) - }; - - let path = Interned::new(ModPath::from_tt(db, path)?); - - let input = match (input.flat_tokens().first(), input.try_into_subtree()) { - (_, Some(tree)) => { - Some(Box::new(AttrInput::TokenTree(tt::TopSubtree::from_subtree(tree)))) - } - (Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))), _) => { - match input.flat_tokens().get(1) { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => { - Some(Box::new(AttrInput::Literal(lit.clone()))) - } - _ => None, - } - } - _ => None, - }; - Some(Attr { id, path, input, ctxt }) - } - - pub fn path(&self) -> &ModPath { - &self.path - } - - pub fn expand_cfg_attr( - self, - db: &dyn ExpandDatabase, - cfg_options: &CfgOptions, - ) -> impl IntoIterator { - let is_cfg_attr = self.path.as_ident().is_some_and(|name| *name == sym::cfg_attr); - if !is_cfg_attr { - return smallvec![self]; - } - - let subtree = match self.token_tree_value() { - Some(it) => it, - _ => return smallvec![self.clone()], - }; - - let (cfg, parts) = match parse_cfg_attr_input(subtree) { - Some(it) => it, - None => return smallvec![self.clone()], - }; - let index = self.id; - let attrs = parts.filter_map(|attr| Attr::from_tt(db, attr, index)); - - let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg); - let cfg = CfgExpr::parse(&cfg); - if cfg_options.check(&cfg) == Some(false) { - smallvec![] - } else { - cov_mark::hit!(cfg_attr_active); - - attrs.collect::>() - } - } -} - impl Attr { /// #[path = "string"] pub fn string_value(&self) -> Option<&Symbol> { @@ -403,30 +475,26 @@ impl Attr { pub fn parse_path_comma_token_tree<'a>( &'a self, db: &'a dyn ExpandDatabase, - ) -> Option + 'a> { + ) -> Option)> + 'a> { let args = self.token_tree_value()?; if args.top_subtree().delimiter.kind != DelimiterKind::Parenthesis { return None; } - let paths = args - .token_trees() - .split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))) - .filter_map(move |tts| { - let span = tts.flat_tokens().first()?.first_span(); - Some((ModPath::from_tt(db, tts)?, span)) - }); - - Some(paths) + Some(parse_path_comma_token_tree(db, args)) } +} - pub fn cfg(&self) -> Option { - if *self.path.as_ident()? == sym::cfg { - self.token_tree_value().map(CfgExpr::parse) - } else { - None - } - } +fn parse_path_comma_token_tree<'a>( + db: &'a dyn ExpandDatabase, + args: &'a tt::TopSubtree, +) -> impl Iterator)> { + args.token_trees() + .split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))) + .filter_map(move |tts| { + let span = tts.flat_tokens().first()?.first_span(); + Some((ModPath::from_tt(db, tts)?, span, tts)) + }) } fn unescape(s: &str) -> Option> { @@ -455,58 +523,104 @@ fn unescape(s: &str) -> Option> { } } -pub fn collect_attrs( - owner: &dyn ast::HasAttrs, -) -> impl Iterator)> { - let inner_attrs = - inner_attributes(owner.syntax()).into_iter().flatten().zip(iter::repeat(true)); - let outer_attrs = ast::AttrDocCommentIter::from_syntax_node(owner.syntax()) - .filter(|el| match el { - Either::Left(attr) => attr.kind().is_outer(), - Either::Right(comment) => comment.is_outer(), - }) - .zip(iter::repeat(false)); - outer_attrs - .chain(inner_attrs) - .enumerate() - .map(|(id, (attr, is_inner))| (AttrId::new(id, is_inner), attr)) +/// This is an index of an attribute *that always points to the item tree attributes*. +/// +/// Outer attributes are counted first, then inner attributes. This does not support +/// out-of-line modules, which may have attributes spread across 2 files! +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct AttrId { + id: u32, } -fn inner_attributes( - syntax: &SyntaxNode, -) -> Option>> { - let node = match_ast! { - match syntax { - ast::SourceFile(_) => syntax.clone(), - ast::ExternBlock(it) => it.extern_item_list()?.syntax().clone(), - ast::Fn(it) => it.body()?.stmt_list()?.syntax().clone(), - ast::Impl(it) => it.assoc_item_list()?.syntax().clone(), - ast::Module(it) => it.item_list()?.syntax().clone(), - ast::BlockExpr(it) => { - if !it.may_carry_attributes() { - return None +impl AttrId { + #[inline] + pub fn from_item_tree_index(id: u32) -> Self { + Self { id } + } + + #[inline] + pub fn item_tree_index(self) -> u32 { + self.id + } + + /// Returns the containing `ast::Attr` (note that it may contain other attributes as well due + /// to `cfg_attr`), a `SyntaxNode` guaranteed to contain the attribute, the full range of the + /// attribute, and its desugared [`Meta`]. + pub fn find_attr_range( + self, + db: &dyn ExpandDatabase, + krate: Crate, + owner: AstId, + ) -> (ast::Attr, SyntaxNode, TextRange, Meta) { + self.find_attr_range_with_source(db, krate, &owner.to_node(db)) + } + + /// Returns the containing `ast::Attr` (note that it may contain other attributes as well due + /// to `cfg_attr`), a `SyntaxNode` guaranteed to contain the attribute, the full range of the + /// attribute, and its desugared [`Meta`]. + pub fn find_attr_range_with_source( + self, + db: &dyn ExpandDatabase, + krate: Crate, + owner: &dyn ast::HasAttrs, + ) -> (ast::Attr, SyntaxNode, TextRange, Meta) { + let cfg_options = OnceCell::new(); + let mut index = 0; + let result = collect_item_tree_attrs( + owner, + || cfg_options.get_or_init(|| krate.cfg_options(db)), + |meta, container, top_attr, range| { + if index == self.id { + return ControlFlow::Break((top_attr.clone(), container.clone(), range, meta)); } - syntax.clone() + index += 1; + ControlFlow::Continue(()) }, - _ => return None, + ); + match result { + Some(Either::Left(it)) => it, + _ => { + panic!("used an incorrect `AttrId`; crate={krate:?}, attr_id={self:?}"); + } } - }; + } - let attrs = ast::AttrDocCommentIter::from_syntax_node(&node).filter(|el| match el { - Either::Left(attr) => attr.kind().is_inner(), - Either::Right(comment) => comment.is_inner(), - }); - Some(attrs) -} - -// Input subtree is: `(cfg, $(attr),+)` -// Split it up into a `cfg` subtree and the `attr` subtrees. -fn parse_cfg_attr_input( - subtree: &TopSubtree, -) -> Option<(tt::TokenTreesView<'_>, impl Iterator>)> { - let mut parts = subtree - .token_trees() - .split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))); - let cfg = parts.next()?; - Some((cfg, parts.filter(|it| !it.is_empty()))) + pub fn find_derive_range( + self, + db: &dyn ExpandDatabase, + krate: Crate, + owner: AstId, + derive_index: u32, + ) -> TextRange { + let (_, _, derive_attr_range, derive_attr) = self.find_attr_range(db, krate, owner); + let Meta::TokenTree { tt, .. } = derive_attr else { + return derive_attr_range; + }; + // Fake the span map, as we don't really need spans here, just the offsets of the node in the file. + let span_map = RealSpanMap::absolute(span::EditionedFileId::current_edition( + span::FileId::from_raw(0), + )); + let tt = syntax_bridge::syntax_node_to_token_tree( + tt.syntax(), + SpanMapRef::RealSpanMap(&span_map), + span_map.span_for_range(tt.syntax().text_range()), + DocCommentDesugarMode::ProcMacro, + ); + let Some((_, _, derive_tts)) = + parse_path_comma_token_tree(db, &tt).nth(derive_index as usize) + else { + return derive_attr_range; + }; + let (Some(first_tt), Some(last_tt)) = + (derive_tts.flat_tokens().first(), derive_tts.flat_tokens().last()) + else { + return derive_attr_range; + }; + let start = first_tt.first_span().range.start(); + let end = match last_tt { + tt::TokenTree::Leaf(it) => it.span().range.end(), + tt::TokenTree::Subtree(it) => it.delimiter.close.range.end(), + }; + TextRange::new(start, end) + } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index 6fe63f249cd4..92bcd378149e 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -772,7 +772,7 @@ fn relative_file( if res == call_site && !allow_recursion { Err(ExpandError::other(err_span, format!("recursive inclusion of `{path_str}`"))) } else { - Ok(EditionedFileId::new(db, res, lookup.krate.data(db).edition)) + Ok(EditionedFileId::new(db, res, lookup.krate.data(db).edition, lookup.krate)) } } 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 d5ebd6ee19f5..8b82671ed4a0 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 @@ -1,373 +1,343 @@ //! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro -use std::iter::Peekable; +use std::{cell::OnceCell, ops::ControlFlow}; +use ::tt::TextRange; use base_db::Crate; -use cfg::{CfgAtom, CfgExpr}; -use intern::{Symbol, sym}; -use rustc_hash::FxHashSet; +use cfg::CfgExpr; +use parser::T; +use smallvec::SmallVec; use syntax::{ - AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, T, - ast::{self, Attr, HasAttrs, Meta, TokenTree, VariantList}, + AstNode, PreorderWithTokens, SyntaxElement, SyntaxNode, SyntaxToken, WalkEvent, + ast::{self, HasAttrs, TokenTreeChildren}, }; -use tracing::{debug, warn}; +use syntax_bridge::DocCommentDesugarMode; -use crate::{MacroCallLoc, MacroDefKind, db::ExpandDatabase, proc_macro::ProcMacroKind}; +use crate::{ + attrs::{AttrId, Meta, expand_cfg_attr, is_item_tree_filtered_attr}, + db::ExpandDatabase, + fixup::{self, SyntaxFixupUndoInfo}, + span_map::SpanMapRef, + tt::{self, DelimSpan, Span}, +}; -fn check_cfg(db: &dyn ExpandDatabase, attr: &Attr, krate: Crate) -> Option { - if !attr.simple_name().as_deref().map(|v| v == "cfg")? { - return None; - } - let cfg = parse_from_attr_token_tree(&attr.meta()?.token_tree()?)?; - let enabled = krate.cfg_options(db).check(&cfg) != Some(false); - Some(enabled) +struct ItemIsCfgedOut; + +#[derive(Debug)] +struct ExpandedAttrToProcess { + range: TextRange, } -fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: Crate) -> Option { - if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? { - return None; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum NextExpandedAttrState { + NotStarted, + InTheMiddle, +} + +#[derive(Debug)] +struct AstAttrToProcess { + range: TextRange, + expanded_attrs: SmallVec<[ExpandedAttrToProcess; 1]>, + expanded_attrs_idx: usize, + next_expanded_attr: NextExpandedAttrState, + pound_span: Span, + brackets_span: DelimSpan, + /// If `Some`, this is an inner attribute. + excl_span: Option, +} + +fn macro_input_callback( + db: &dyn ExpandDatabase, + is_derive: bool, + censor_item_tree_attr_ids: &[AttrId], + krate: Crate, + default_span: Span, + span_map: SpanMapRef<'_>, +) -> 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)); + + let mut should_strip_attr = { + let mut item_tree_attr_id = 0; + let mut censor_item_tree_attr_ids_index = 0; + move || { + let mut result = false; + if let Some(&next_censor_attr_id) = + censor_item_tree_attr_ids.get(censor_item_tree_attr_ids_index) + && next_censor_attr_id.item_tree_index() == item_tree_attr_id + { + censor_item_tree_attr_ids_index += 1; + result = true; + } + item_tree_attr_id += 1; + result + } + }; + + let mut attrs = Vec::new(); + let mut attrs_idx = 0; + let mut has_inner_attrs_owner = false; + let mut in_attr = false; + let mut done_with_attrs = false; + let mut did_top_attrs = false; + move |preorder, event| { + match event { + WalkEvent::Enter(SyntaxElement::Node(node)) => { + if done_with_attrs { + return (true, Vec::new()); + } + + if ast::Attr::can_cast(node.kind()) { + in_attr = true; + let node_range = node.text_range(); + while attrs + .get(attrs_idx) + .is_some_and(|it: &AstAttrToProcess| it.range != node_range) + { + attrs_idx += 1; + } + } else if let Some(has_attrs) = ast::AnyHasAttrs::cast(node.clone()) { + if has_inner_attrs_owner { + has_inner_attrs_owner = false; + return (true, Vec::new()); + } + + if did_top_attrs && !is_derive { + // Derives need all attributes handled, but attribute macros need only the top attributes handled. + done_with_attrs = true; + return (true, Vec::new()); + } + did_top_attrs = true; + + if let Some(inner_attrs_node) = has_attrs.inner_attributes_node() + && inner_attrs_node != *node + { + has_inner_attrs_owner = true; + } + + let node_attrs = ast::attrs_including_inner(&has_attrs); + + attrs.clear(); + node_attrs.clone().for_each(|attr| { + let span_for = |token: Option| { + token + .map(|token| span_map.span_for_range(token.text_range())) + .unwrap_or(default_span) + }; + attrs.push(AstAttrToProcess { + range: attr.syntax().text_range(), + pound_span: span_for(attr.pound_token()), + brackets_span: DelimSpan { + open: span_for(attr.l_brack_token()), + close: span_for(attr.r_brack_token()), + }, + excl_span: attr + .excl_token() + .map(|token| span_map.span_for_range(token.text_range())), + expanded_attrs: SmallVec::new(), + expanded_attrs_idx: 0, + next_expanded_attr: NextExpandedAttrState::NotStarted, + }); + }); + + attrs_idx = 0; + let strip_current_item = expand_cfg_attr( + node_attrs, + &cfg_options, + |attr, _container, range, top_attr| { + // Find the attr. + while attrs[attrs_idx].range != top_attr.syntax().text_range() { + attrs_idx += 1; + } + + let mut strip_current_attr = false; + match attr { + Meta::NamedKeyValue { name, .. } => { + if name + .is_none_or(|name| !is_item_tree_filtered_attr(name.text())) + { + strip_current_attr = should_strip_attr(); + } + } + Meta::TokenTree { path, tt } => { + if path.segments.len() != 1 + || !is_item_tree_filtered_attr(path.segments[0].text()) + { + strip_current_attr = should_strip_attr(); + } + + if path.segments.len() == 1 { + let name = path.segments[0].text(); + + if name == "cfg" { + let cfg_expr = CfgExpr::parse_from_ast( + &mut TokenTreeChildren::new(&tt).peekable(), + ); + if cfg_options().check(&cfg_expr) == Some(false) { + return ControlFlow::Break(ItemIsCfgedOut); + } + strip_current_attr = true; + } + } + } + Meta::Path { path } => { + if path.segments.len() != 1 + || !is_item_tree_filtered_attr(path.segments[0].text()) + { + strip_current_attr = should_strip_attr(); + } + } + } + + if !strip_current_attr { + attrs[attrs_idx] + .expanded_attrs + .push(ExpandedAttrToProcess { range }); + } + + ControlFlow::Continue(()) + }, + ); + attrs_idx = 0; + + if strip_current_item.is_some() { + preorder.skip_subtree(); + attrs.clear(); + + 'eat_comma: { + // If there is a comma after this node, eat it too. + let mut events_until_comma = 0; + for event in preorder.clone() { + match event { + WalkEvent::Enter(SyntaxElement::Node(_)) + | WalkEvent::Leave(_) => {} + WalkEvent::Enter(SyntaxElement::Token(token)) => { + let kind = token.kind(); + if kind == T![,] { + break; + } else if !kind.is_trivia() { + break 'eat_comma; + } + } + } + events_until_comma += 1; + } + preorder.nth(events_until_comma); + } + + return (false, Vec::new()); + } + } + } + WalkEvent::Leave(SyntaxElement::Node(node)) => { + if ast::Attr::can_cast(node.kind()) { + in_attr = false; + attrs_idx += 1; + } + } + WalkEvent::Enter(SyntaxElement::Token(token)) => { + if !in_attr { + return (true, Vec::new()); + } + + let Some(ast_attr) = attrs.get_mut(attrs_idx) else { + return (true, Vec::new()); + }; + let token_range = token.text_range(); + let Some(expanded_attr) = ast_attr.expanded_attrs.get(ast_attr.expanded_attrs_idx) + else { + // No expanded attributes in this `ast::Attr`, or we finished them all already, either way + // the remaining tokens should be discarded. + return (false, Vec::new()); + }; + match ast_attr.next_expanded_attr { + NextExpandedAttrState::NotStarted => { + if token_range.start() >= expanded_attr.range.start() { + // We started the next attribute. + let mut insert_tokens = Vec::with_capacity(3); + insert_tokens.push(tt::Leaf::Punct(tt::Punct { + char: '#', + spacing: tt::Spacing::Alone, + span: ast_attr.pound_span, + })); + if let Some(span) = ast_attr.excl_span { + insert_tokens.push(tt::Leaf::Punct(tt::Punct { + char: '!', + spacing: tt::Spacing::Alone, + span, + })); + } + insert_tokens.push(tt::Leaf::Punct(tt::Punct { + char: '[', + spacing: tt::Spacing::Alone, + span: ast_attr.brackets_span.open, + })); + + ast_attr.next_expanded_attr = NextExpandedAttrState::InTheMiddle; + + return (true, insert_tokens); + } else { + // Before any attribute or between the attributes. + return (false, Vec::new()); + } + } + NextExpandedAttrState::InTheMiddle => { + if token_range.start() >= expanded_attr.range.end() { + // Finished the current attribute. + let insert_tokens = vec![tt::Leaf::Punct(tt::Punct { + char: ']', + spacing: tt::Spacing::Alone, + span: ast_attr.brackets_span.close, + })]; + + ast_attr.next_expanded_attr = NextExpandedAttrState::NotStarted; + ast_attr.expanded_attrs_idx += 1; + + // It's safe to ignore the current token because between attributes + // there is always at least one token we skip - either the closing bracket + // in `#[]` or the comma in case of multiple attrs in `cfg_attr` expansion. + return (false, insert_tokens); + } else { + // Still in the middle. + return (true, Vec::new()); + } + } + } + } + WalkEvent::Leave(SyntaxElement::Token(_)) => {} + } + (true, Vec::new()) } - check_cfg_attr_value(db, &attr.token_tree()?, krate) +} + +pub(crate) fn attr_macro_input_to_token_tree( + db: &dyn ExpandDatabase, + node: &SyntaxNode, + span_map: SpanMapRef<'_>, + span: Span, + is_derive: bool, + censor_item_tree_attr_ids: &[AttrId], + krate: Crate, +) -> (tt::TopSubtree, SyntaxFixupUndoInfo) { + let fixups = fixup::fixup_syntax(span_map, node, span, DocCommentDesugarMode::ProcMacro); + ( + syntax_bridge::syntax_node_to_token_tree_modified( + node, + span_map, + fixups.append, + fixups.remove, + span, + DocCommentDesugarMode::ProcMacro, + macro_input_callback(db, is_derive, censor_item_tree_attr_ids, krate, span, span_map), + ), + fixups.undo_info, + ) } pub fn check_cfg_attr_value( db: &dyn ExpandDatabase, - attr: &TokenTree, + attr: &ast::TokenTree, krate: Crate, ) -> Option { - let cfg_expr = parse_from_attr_token_tree(attr)?; - let enabled = krate.cfg_options(db).check(&cfg_expr) != Some(false); - Some(enabled) -} - -fn process_has_attrs_with_possible_comma( - db: &dyn ExpandDatabase, - items: impl Iterator, - krate: Crate, - remove: &mut FxHashSet, -) -> Option<()> { - for item in items { - let field_attrs = item.attrs(); - 'attrs: for attr in field_attrs { - if let Some(enabled) = check_cfg(db, &attr, krate) { - if enabled { - debug!("censoring {:?}", attr.syntax()); - remove.insert(attr.syntax().clone().into()); - } else { - debug!("censoring {:?}", item.syntax()); - remove.insert(item.syntax().clone().into()); - // We need to remove the , as well - remove_possible_comma(&item, remove); - break 'attrs; - } - } - - if let Some(enabled) = check_cfg_attr(db, &attr, krate) { - if enabled { - debug!("Removing cfg_attr tokens {:?}", attr); - let meta = attr.meta()?; - let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?; - remove.extend(removes_from_cfg_attr); - } else { - debug!("censoring type cfg_attr {:?}", item.syntax()); - remove.insert(attr.syntax().clone().into()); - } - } - } - } - Some(()) -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -enum CfgExprStage { - /// Stripping the CFGExpr part of the attribute - StrippigCfgExpr, - /// Found the comma after the CFGExpr. Will keep all tokens until the next comma or the end of the attribute - FoundComma, - /// Everything following the attribute. This could be another attribute or the end of the attribute. - // FIXME: cfg_attr with multiple attributes will not be handled correctly. We will only keep the first attribute - // Related Issue: https://github.com/rust-lang/rust-analyzer/issues/10110 - EverythingElse, -} - -/// This function creates its own set of tokens to remove. To help prevent malformed syntax as input. -fn remove_tokens_within_cfg_attr(meta: Meta) -> Option> { - let mut remove: FxHashSet = FxHashSet::default(); - debug!("Enabling attribute {}", meta); - let meta_path = meta.path()?; - debug!("Removing {:?}", meta_path.syntax()); - remove.insert(meta_path.syntax().clone().into()); - - let meta_tt = meta.token_tree()?; - debug!("meta_tt {}", meta_tt); - let mut stage = CfgExprStage::StrippigCfgExpr; - for tt in meta_tt.token_trees_and_tokens() { - debug!("Checking {:?}. Stage: {:?}", tt, stage); - match (stage, tt) { - (CfgExprStage::StrippigCfgExpr, syntax::NodeOrToken::Node(node)) => { - remove.insert(node.syntax().clone().into()); - } - (CfgExprStage::StrippigCfgExpr, syntax::NodeOrToken::Token(token)) => { - if token.kind() == T![,] { - stage = CfgExprStage::FoundComma; - } - remove.insert(token.into()); - } - (CfgExprStage::FoundComma, syntax::NodeOrToken::Token(token)) - if (token.kind() == T![,] || token.kind() == T![')']) => - { - // The end of the attribute or separator for the next attribute - stage = CfgExprStage::EverythingElse; - remove.insert(token.into()); - } - (CfgExprStage::EverythingElse, syntax::NodeOrToken::Node(node)) => { - remove.insert(node.syntax().clone().into()); - } - (CfgExprStage::EverythingElse, syntax::NodeOrToken::Token(token)) => { - remove.insert(token.into()); - } - // This is an actual attribute - _ => {} - } - } - if stage != CfgExprStage::EverythingElse { - warn!("Invalid cfg_attr attribute. {:?}", meta_tt); - return None; - } - Some(remove) -} -/// Removes a possible comma after the [AstNode] -fn remove_possible_comma(item: &impl AstNode, res: &mut FxHashSet) { - if let Some(comma) = item.syntax().next_sibling_or_token().filter(|it| it.kind() == T![,]) { - res.insert(comma); - } -} -fn process_enum( - db: &dyn ExpandDatabase, - variants: VariantList, - krate: Crate, - remove: &mut FxHashSet, -) -> Option<()> { - 'variant: for variant in variants.variants() { - for attr in variant.attrs() { - if let Some(enabled) = check_cfg(db, &attr, krate) { - if enabled { - debug!("censoring {:?}", attr.syntax()); - remove.insert(attr.syntax().clone().into()); - } else { - // Rustc does not strip the attribute if it is enabled. So we will leave it - debug!("censoring type {:?}", variant.syntax()); - remove.insert(variant.syntax().clone().into()); - // We need to remove the , as well - remove_possible_comma(&variant, remove); - continue 'variant; - } - } - - if let Some(enabled) = check_cfg_attr(db, &attr, krate) { - if enabled { - debug!("Removing cfg_attr tokens {:?}", attr); - let meta = attr.meta()?; - let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?; - remove.extend(removes_from_cfg_attr); - } else { - debug!("censoring type cfg_attr {:?}", variant.syntax()); - remove.insert(attr.syntax().clone().into()); - } - } - } - if let Some(fields) = variant.field_list() { - match fields { - ast::FieldList::RecordFieldList(fields) => { - process_has_attrs_with_possible_comma(db, fields.fields(), krate, remove)?; - } - ast::FieldList::TupleFieldList(fields) => { - process_has_attrs_with_possible_comma(db, fields.fields(), krate, remove)?; - } - } - } - } - Some(()) -} - -pub(crate) fn process_cfg_attrs( - db: &dyn ExpandDatabase, - node: &SyntaxNode, - loc: &MacroCallLoc, -) -> Option> { - // FIXME: #[cfg_eval] is not implemented. But it is not stable yet - let is_derive = match loc.def.kind { - MacroDefKind::BuiltInDerive(..) - | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) => true, - MacroDefKind::BuiltInAttr(_, expander) => expander.is_derive(), - _ => false, - }; - let mut remove = FxHashSet::default(); - - let item = ast::Item::cast(node.clone())?; - for attr in item.attrs() { - if let Some(enabled) = check_cfg_attr(db, &attr, loc.krate) { - if enabled { - debug!("Removing cfg_attr tokens {:?}", attr); - let meta = attr.meta()?; - let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?; - remove.extend(removes_from_cfg_attr); - } else { - debug!("Removing type cfg_attr {:?}", item.syntax()); - remove.insert(attr.syntax().clone().into()); - } - } - } - - if is_derive { - // Only derives get their code cfg-clean, normal attribute macros process only the cfg at their level - // (cfg_attr is handled above, cfg is handled in the def map). - match item { - ast::Item::Struct(it) => match it.field_list()? { - ast::FieldList::RecordFieldList(fields) => { - process_has_attrs_with_possible_comma( - db, - fields.fields(), - loc.krate, - &mut remove, - )?; - } - ast::FieldList::TupleFieldList(fields) => { - process_has_attrs_with_possible_comma( - db, - fields.fields(), - loc.krate, - &mut remove, - )?; - } - }, - ast::Item::Enum(it) => { - process_enum(db, it.variant_list()?, loc.krate, &mut remove)?; - } - ast::Item::Union(it) => { - process_has_attrs_with_possible_comma( - db, - it.record_field_list()?.fields(), - loc.krate, - &mut remove, - )?; - } - // FIXME: Implement for other items if necessary. As we do not support #[cfg_eval] yet, we do not need to implement it for now - _ => {} - } - } - Some(remove) -} -/// Parses a `cfg` attribute from the meta -fn parse_from_attr_token_tree(tt: &TokenTree) -> Option { - let mut iter = tt - .token_trees_and_tokens() - .filter(is_not_whitespace) - .skip(1) - .take_while(is_not_closing_paren) - .peekable(); - next_cfg_expr_from_syntax(&mut iter) -} - -fn is_not_closing_paren(element: &NodeOrToken) -> bool { - !matches!(element, NodeOrToken::Token(token) if (token.kind() == syntax::T![')'])) -} -fn is_not_whitespace(element: &NodeOrToken) -> bool { - !matches!(element, NodeOrToken::Token(token) if (token.kind() == SyntaxKind::WHITESPACE)) -} - -fn next_cfg_expr_from_syntax(iter: &mut Peekable) -> Option -where - I: Iterator>, -{ - let name = match iter.next() { - None => return None, - Some(NodeOrToken::Token(element)) => match element.kind() { - syntax::T![ident] => Symbol::intern(element.text()), - _ => return Some(CfgExpr::Invalid), - }, - Some(_) => return Some(CfgExpr::Invalid), - }; - let result = match &name { - s if [&sym::all, &sym::any, &sym::not].contains(&s) => { - let mut preds = Vec::new(); - let Some(NodeOrToken::Node(tree)) = iter.next() else { - return Some(CfgExpr::Invalid); - }; - let mut tree_iter = tree - .token_trees_and_tokens() - .filter(is_not_whitespace) - .skip(1) - .take_while(is_not_closing_paren) - .peekable(); - while tree_iter.peek().is_some() { - let pred = next_cfg_expr_from_syntax(&mut tree_iter); - if let Some(pred) = pred { - preds.push(pred); - } - } - let group = match &name { - s if *s == sym::all => CfgExpr::All(preds.into_boxed_slice()), - s if *s == sym::any => CfgExpr::Any(preds.into_boxed_slice()), - s if *s == sym::not => { - CfgExpr::Not(Box::new(preds.pop().unwrap_or(CfgExpr::Invalid))) - } - _ => unreachable!(), - }; - Some(group) - } - _ => match iter.peek() { - Some(NodeOrToken::Token(element)) if (element.kind() == syntax::T![=]) => { - iter.next(); - match iter.next() { - Some(NodeOrToken::Token(value_token)) - if (value_token.kind() == syntax::SyntaxKind::STRING) => - { - let value = value_token.text(); - Some(CfgExpr::Atom(CfgAtom::KeyValue { - key: name, - value: Symbol::intern(value.trim_matches('"')), - })) - } - _ => None, - } - } - _ => Some(CfgExpr::Atom(CfgAtom::Flag(name))), - }, - }; - if let Some(NodeOrToken::Token(element)) = iter.peek() - && element.kind() == syntax::T![,] - { - iter.next(); - } - result -} -#[cfg(test)] -mod tests { - use cfg::DnfExpr; - use expect_test::{Expect, expect}; - use syntax::{AstNode, SourceFile, ast::Attr}; - - use crate::cfg_process::parse_from_attr_token_tree; - - fn check_dnf_from_syntax(input: &str, expect: Expect) { - let parse = SourceFile::parse(input, span::Edition::CURRENT); - let node = match parse.tree().syntax().descendants().find_map(Attr::cast) { - Some(it) => it, - None => { - let node = std::any::type_name::(); - panic!("Failed to make ast node `{node}` from text {input}") - } - }; - let node = node.clone_subtree(); - assert_eq!(node.syntax().text_range().start(), 0.into()); - - let cfg = parse_from_attr_token_tree(&node.meta().unwrap().token_tree().unwrap()).unwrap(); - let actual = format!("#![cfg({})]", DnfExpr::new(&cfg)); - expect.assert_eq(&actual); - } - #[test] - fn cfg_from_attr() { - check_dnf_from_syntax(r#"#[cfg(test)]"#, expect![[r#"#![cfg(test)]"#]]); - check_dnf_from_syntax(r#"#[cfg(not(never))]"#, expect![[r#"#![cfg(not(never))]"#]]); - } + let cfg_expr = CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(attr).peekable()); + krate.cfg_options(db).check(&cfg_expr) } 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 888c1405a6bb..6b5aa39fa6bf 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -1,11 +1,9 @@ //! Defines database & queries for macro expansion. use base_db::{Crate, RootQueryDb}; -use either::Either; use mbe::MatchedArmIndex; -use rustc_hash::FxHashSet; use span::{AstIdMap, Edition, Span, SyntaxContext}; -use syntax::{AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T, ast}; +use syntax::{AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, ast}; use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree}; use triomphe::Arc; @@ -13,9 +11,9 @@ use crate::{ AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, EagerExpander, EditionedFileId, ExpandError, ExpandResult, ExpandTo, HirFileId, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, - attrs::{AttrId, AttrInput, RawAttrs, collect_attrs}, + attrs::Meta, builtin::pseudo_derive_attr_expansion, - cfg_process, + cfg_process::attr_macro_input_to_token_tree, declarative::DeclarativeMacroExpander, fixup::{self, SyntaxFixupUndoInfo}, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt}, @@ -177,7 +175,7 @@ pub fn expand_speculative( let span_map = SpanMapRef::RealSpanMap(&span_map); // Build the subtree and token mapping for the speculative args - let (mut tt, undo_info) = match loc.kind { + let (mut tt, undo_info) = match &loc.kind { MacroCallKind::FnLike { .. } => ( syntax_bridge::syntax_node_to_token_tree( speculative_args, @@ -200,48 +198,35 @@ pub fn expand_speculative( ), SyntaxFixupUndoInfo::NONE, ), - MacroCallKind::Derive { derive_attr_index: index, .. } - | MacroCallKind::Attr { invoc_attr_index: index, .. } => { - let censor = if let MacroCallKind::Derive { .. } = loc.kind { - censor_derive_input(index, &ast::Adt::cast(speculative_args.clone())?) - } else { - attr_source(index, &ast::Item::cast(speculative_args.clone())?) - .into_iter() - .map(|it| it.syntax().clone().into()) - .collect() + MacroCallKind::Derive { derive_macro_id, .. } => { + let MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } = + &derive_macro_id.loc(db).kind + else { + unreachable!("`derive_macro_id` should be `MacroCallKind::Attr`"); }; - - let censor_cfg = - cfg_process::process_cfg_attrs(db, speculative_args, &loc).unwrap_or_default(); - let mut fixups = fixup::fixup_syntax( - span_map, + attr_macro_input_to_token_tree( + db, speculative_args, + span_map, span, - DocCommentDesugarMode::ProcMacro, - ); - fixups.append.retain(|it, _| match it { - syntax::NodeOrToken::Token(_) => true, - it => !censor.contains(it) && !censor_cfg.contains(it), - }); - fixups.remove.extend(censor); - fixups.remove.extend(censor_cfg); - - ( - syntax_bridge::syntax_node_to_token_tree_modified( - speculative_args, - span_map, - fixups.append, - fixups.remove, - span, - DocCommentDesugarMode::ProcMacro, - ), - fixups.undo_info, + true, + attr_ids, + loc.krate, ) } + MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => attr_macro_input_to_token_tree( + db, + speculative_args, + span_map, + span, + false, + attr_ids, + loc.krate, + ), }; - let attr_arg = match loc.kind { - MacroCallKind::Attr { invoc_attr_index, .. } => { + let attr_arg = match &loc.kind { + MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => { if loc.def.is_attribute_derive() { // for pseudo-derive expansion we actually pass the attribute itself only ast::Attr::cast(speculative_args.clone()).and_then(|attr| attr.token_tree()).map( @@ -260,18 +245,21 @@ pub fn expand_speculative( // Attributes may have an input token tree, build the subtree and map for this as well // then try finding a token id for our token if it is inside this input subtree. let item = ast::Item::cast(speculative_args.clone())?; - let attrs = RawAttrs::new_expanded(db, &item, span_map, loc.krate.cfg_options(db)); - attrs.iter().find(|attr| attr.id == invoc_attr_index).and_then(|attr| { - match attr.input.as_deref()? { - AttrInput::TokenTree(tt) => { - let mut attr_arg = tt.clone(); - attr_arg.top_subtree_delimiter_mut().kind = - tt::DelimiterKind::Invisible; - Some(attr_arg) - } - AttrInput::Literal(_) => None, + let (_, _, _, meta) = + attr_ids.invoc_attr().find_attr_range_with_source(db, loc.krate, &item); + match meta { + Meta::TokenTree { tt, .. } => { + let mut attr_arg = syntax_bridge::syntax_node_to_token_tree( + tt.syntax(), + span_map, + span, + DocCommentDesugarMode::ProcMacro, + ); + attr_arg.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible; + Some(attr_arg) } - }) + _ => None, + } } } _ => None, @@ -433,7 +421,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let (parse, map) = parse_with_map(db, loc.kind.file_id()); let root = parse.syntax_node(); - let (censor, item_node, span) = match loc.kind { + let (is_derive, censor_item_tree_attr_ids, item_node, span) = match &loc.kind { MacroCallKind::FnLike { ast_id, .. } => { let node = &ast_id.to_ptr(db).to_node(&root); let path_range = node @@ -501,53 +489,29 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { MacroCallKind::Derive { .. } => { unreachable!("`ExpandDatabase::macro_arg` called with `MacroCallKind::Derive`") } - MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { + MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { let node = ast_id.to_ptr(db).to_node(&root); - let attr_source = attr_source(invoc_attr_index, &node); + let range = attr_ids + .invoc_attr() + .find_attr_range_with_source(db, loc.krate, &node) + .3 + .path_range(); + let span = map.span_for_range(range); - let span = map.span_for_range( - attr_source - .as_ref() - .and_then(|it| it.path()) - .map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()), - ); - // If derive attribute we need to censor the derive input - if matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive()) - && ast::Adt::can_cast(node.syntax().kind()) - { - let adt = ast::Adt::cast(node.syntax().clone()).unwrap(); - let censor_derive_input = censor_derive_input(invoc_attr_index, &adt); - (censor_derive_input, node, span) - } else { - (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span) - } + let is_derive = matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive()); + (is_derive, &**attr_ids, node, span) } }; - let (mut tt, undo_info) = { - let syntax = item_node.syntax(); - let censor_cfg = cfg_process::process_cfg_attrs(db, syntax, &loc).unwrap_or_default(); - let mut fixups = - fixup::fixup_syntax(map.as_ref(), syntax, span, DocCommentDesugarMode::ProcMacro); - fixups.append.retain(|it, _| match it { - syntax::NodeOrToken::Token(_) => true, - it => !censor.contains(it) && !censor_cfg.contains(it), - }); - fixups.remove.extend(censor); - fixups.remove.extend(censor_cfg); - - ( - syntax_bridge::syntax_node_to_token_tree_modified( - syntax, - map, - fixups.append, - fixups.remove, - span, - DocCommentDesugarMode::ProcMacro, - ), - fixups.undo_info, - ) - }; + let (mut tt, undo_info) = attr_macro_input_to_token_tree( + db, + item_node.syntax(), + map.as_ref(), + span, + is_derive, + censor_item_tree_attr_ids, + loc.krate, + ); if loc.def.is_proc_macro() { // proc macros expect their inputs without parentheses, MBEs expect it with them included @@ -557,31 +521,6 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { (Arc::new(tt), undo_info, span) } -// FIXME: Censoring info should be calculated by the caller! Namely by name resolution -/// Derives expect all `#[derive(..)]` invocations up to (and including) the currently invoked one to be stripped -fn censor_derive_input(derive_attr_index: AttrId, node: &ast::Adt) -> FxHashSet { - // FIXME: handle `cfg_attr` - cov_mark::hit!(derive_censoring); - collect_attrs(node) - .take(derive_attr_index.ast_index() + 1) - .filter_map(|(_, attr)| Either::left(attr)) - // FIXME, this resolution should not be done syntactically - // derive is a proper macro now, no longer builtin - // But we do not have resolution at this stage, this means - // we need to know about all macro calls for the given ast item here - // so we require some kind of mapping... - .filter(|attr| attr.simple_name().as_deref() == Some("derive")) - .map(|it| it.syntax().clone().into()) - .collect() -} - -/// Attributes expect the invoking attribute to be stripped -fn attr_source(invoc_attr_index: AttrId, node: &ast::Item) -> Option { - // FIXME: handle `cfg_attr` - cov_mark::hit!(attribute_macro_attr_censoring); - collect_attrs(node).nth(invoc_attr_index.ast_index()).and_then(|(_, attr)| Either::left(attr)) -} - impl TokenExpander { fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander { match id.kind { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index 0d100c1364ab..3fb9aca9649e 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -1,16 +1,20 @@ //! Compiled declarative macro expanders (`macro_rules!` and `macro`) +use std::{cell::OnceCell, ops::ControlFlow}; + use base_db::Crate; -use intern::sym; use span::{Edition, Span, SyntaxContext}; use stdx::TupleExt; -use syntax::{AstNode, ast}; +use syntax::{ + AstNode, AstToken, + ast::{self, HasAttrs}, +}; use syntax_bridge::DocCommentDesugarMode; use triomphe::Arc; use crate::{ AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, Lookup, MacroCallId, - attrs::RawAttrs, + attrs::{Meta, expand_cfg_attr}, db::ExpandDatabase, hygiene::{Transparency, apply_mark}, tt, @@ -80,29 +84,28 @@ impl DeclarativeMacroExpander { let (root, map) = crate::db::parse_with_map(db, id.file_id); let root = root.syntax_node(); - let transparency = |node| { - // ... would be nice to have the item tree here - let attrs = RawAttrs::new_expanded(db, node, map.as_ref(), def_crate.cfg_options(db)); - match attrs - .iter() - .find(|it| { - it.path - .as_ident() - .map(|it| *it == sym::rustc_macro_transparency) - .unwrap_or(false) - })? - .token_tree_value()? - .token_trees() - .flat_tokens() - { - [tt::TokenTree::Leaf(tt::Leaf::Ident(i)), ..] => match &i.sym { - s if *s == sym::transparent => Some(Transparency::Transparent), - s if *s == sym::semitransparent => Some(Transparency::SemiTransparent), - s if *s == sym::opaque => Some(Transparency::Opaque), - _ => None, + let transparency = |node: ast::AnyHasAttrs| { + let cfg_options = OnceCell::new(); + expand_cfg_attr( + node.attrs(), + || cfg_options.get_or_init(|| def_crate.cfg_options(db)), + |attr, _, _, _| { + if let Meta::NamedKeyValue { name: Some(name), value, .. } = attr + && name.text() == "rustc_macro_transparency" + && let Some(value) = value.and_then(ast::String::cast) + && let Ok(value) = value.value() + { + match &*value { + "transparent" => ControlFlow::Break(Transparency::Transparent), + "semitransparent" => ControlFlow::Break(Transparency::SemiTransparent), + "opaque" => ControlFlow::Break(Transparency::Opaque), + _ => ControlFlow::Continue(()), + } + } else { + ControlFlow::Continue(()) + } }, - _ => None, - } + ) }; let ctx_edition = |ctx: SyntaxContext| { if ctx.is_root() { @@ -133,7 +136,8 @@ impl DeclarativeMacroExpander { "expected a token tree".into(), )), }, - transparency(¯o_rules).unwrap_or(Transparency::SemiTransparent), + transparency(ast::AnyHasAttrs::from(macro_rules)) + .unwrap_or(Transparency::SemiTransparent), ), ast::Macro::MacroDef(macro_def) => ( match macro_def.body() { @@ -161,7 +165,7 @@ impl DeclarativeMacroExpander { "expected a token tree".into(), )), }, - transparency(¯o_def).unwrap_or(Transparency::Opaque), + transparency(macro_def.into()).unwrap_or(Transparency::Opaque), ), }; let edition = ctx_edition(match id.file_id { 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 a7f3e27a4553..fe557d68023d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs @@ -55,30 +55,6 @@ impl From for HirFilePosition { } } -impl FilePositionWrapper { - pub fn with_edition(self, db: &dyn ExpandDatabase, edition: span::Edition) -> FilePosition { - FilePositionWrapper { - file_id: EditionedFileId::new(db, self.file_id, edition), - offset: self.offset, - } - } -} - -impl FileRangeWrapper { - pub fn with_edition(self, db: &dyn ExpandDatabase, edition: span::Edition) -> FileRange { - FileRangeWrapper { - file_id: EditionedFileId::new(db, self.file_id, edition), - range: self.range, - } - } -} - -impl InFileWrapper { - pub fn with_edition(self, db: &dyn ExpandDatabase, edition: span::Edition) -> InRealFile { - InRealFile { file_id: EditionedFileId::new(db, self.file_id, edition), value: self.value } - } -} - impl HirFileRange { pub fn file_range(self) -> Option { Some(FileRange { file_id: self.file_id.file_id()?, range: self.range }) @@ -407,7 +383,7 @@ impl InFile { // Fall back to whole macro call. let loc = db.lookup_intern_macro_call(mac_file); - loc.kind.original_call_range(db) + loc.kind.original_call_range(db, loc.krate) } } } @@ -453,7 +429,10 @@ impl InFile { Some(it) => it, None => { let loc = db.lookup_intern_macro_call(mac_file); - (loc.kind.original_call_range(db), SyntaxContext::root(loc.def.edition)) + ( + loc.kind.original_call_range(db, loc.krate), + SyntaxContext::root(loc.def.edition), + ) } } } @@ -468,7 +447,7 @@ impl InFile { Some(it) => it, _ => { let loc = db.lookup_intern_macro_call(mac_file); - loc.kind.original_call_range(db) + loc.kind.original_call_range(db, loc.krate) } } } 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 fe77e1565987..cba1c7c1d4b0 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -523,6 +523,7 @@ mod tests { fixups.remove, span_map.span_for_range(TextRange::empty(0.into())), DocCommentDesugarMode::Mbe, + |_, _| (true, Vec::new()), ); let actual = format!("{tt}\n"); @@ -698,7 +699,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {a . __ra_fixup ;} +fn foo () {a .__ra_fixup ;} "#]], ) } @@ -713,7 +714,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {a . __ra_fixup ; bar () ;} +fn foo () {a .__ra_fixup ; bar () ;} "#]], ) } 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 472ec83ffef5..e1103ef43e0f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -25,18 +25,17 @@ mod cfg_process; mod fixup; mod prettify_macro_expansion_; -use attrs::collect_attrs; -use rustc_hash::FxHashMap; use salsa::plumbing::{AsId, FromId}; use stdx::TupleExt; +use thin_vec::ThinVec; use triomphe::Arc; use core::fmt; -use std::hash::Hash; +use std::{hash::Hash, ops}; use base_db::Crate; use either::Either; -use span::{Edition, ErasedFileAstId, FileAstId, Span, SpanAnchor, SyntaxContext}; +use span::{Edition, ErasedFileAstId, FileAstId, Span, SyntaxContext}; use syntax::{ SyntaxNode, SyntaxToken, TextRange, TextSize, ast::{self, AstNode}, @@ -317,9 +316,6 @@ pub enum MacroCallKind { Derive { ast_id: AstId, /// Syntactical index of the invoking `#[derive]` attribute. - /// - /// Outer attributes are counted first, then inner attributes. This does not support - /// out-of-line modules, which may have attributes spread across 2 files! derive_attr_index: AttrId, /// Index of the derive macro in the derive attribute derive_index: u32, @@ -329,17 +325,68 @@ pub enum MacroCallKind { }, Attr { ast_id: AstId, - // FIXME: This shouldn't be here, we can derive this from `invoc_attr_index` - // but we need to fix the `cfg_attr` handling first. + // FIXME: This shouldn't be here, we can derive this from `invoc_attr_index`. attr_args: Option>, - /// Syntactical index of the invoking `#[attribute]`. + /// 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. /// - /// Outer attributes are counted first, then inner attributes. This does not support - /// out-of-line modules, which may have attributes spread across 2 files! - invoc_attr_index: AttrId, + /// The macro should not see the attributes here. + /// + /// [`invoc_attr()`]: AttrMacroAttrIds::invoc_attr + censored_attr_ids: AttrMacroAttrIds, }, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AttrMacroAttrIds(AttrMacroAttrIdsRepr); + +impl AttrMacroAttrIds { + #[inline] + pub fn from_one(id: AttrId) -> Self { + Self(AttrMacroAttrIdsRepr::One(id)) + } + + #[inline] + pub fn from_many(ids: &[AttrId]) -> Self { + if let &[id] = ids { + Self(AttrMacroAttrIdsRepr::One(id)) + } else { + Self(AttrMacroAttrIdsRepr::ManyDerives(ids.iter().copied().collect())) + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum AttrMacroAttrIdsRepr { + One(AttrId), + ManyDerives(ThinVec), +} + +impl ops::Deref for AttrMacroAttrIds { + type Target = [AttrId]; + + #[inline] + fn deref(&self) -> &Self::Target { + match &self.0 { + AttrMacroAttrIdsRepr::One(one) => std::slice::from_ref(one), + AttrMacroAttrIdsRepr::ManyDerives(many) => many, + } + } +} + +impl AttrMacroAttrIds { + #[inline] + pub fn invoc_attr(&self) -> AttrId { + match &self.0 { + AttrMacroAttrIdsRepr::One(it) => *it, + AttrMacroAttrIdsRepr::ManyDerives(it) => { + *it.last().expect("should always have at least one `AttrId`") + } + } + } +} + impl HirFileId { pub fn edition(self, db: &dyn ExpandDatabase) -> Edition { match self { @@ -583,34 +630,20 @@ impl MacroDefId { impl MacroCallLoc { pub fn to_node(&self, db: &dyn ExpandDatabase) -> InFile { - match self.kind { + match &self.kind { MacroCallKind::FnLike { ast_id, .. } => { ast_id.with_value(ast_id.to_node(db).syntax().clone()) } MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { // FIXME: handle `cfg_attr` - ast_id.with_value(ast_id.to_node(db)).map(|it| { - collect_attrs(&it) - .nth(derive_attr_index.ast_index()) - .and_then(|it| match it.1 { - Either::Left(attr) => Some(attr.syntax().clone()), - Either::Right(_) => None, - }) - .unwrap_or_else(|| it.syntax().clone()) - }) + let (attr, _, _, _) = derive_attr_index.find_attr_range(db, self.krate, *ast_id); + ast_id.with_value(attr.syntax().clone()) } - MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { + MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { if self.def.is_attribute_derive() { - // FIXME: handle `cfg_attr` - ast_id.with_value(ast_id.to_node(db)).map(|it| { - collect_attrs(&it) - .nth(invoc_attr_index.ast_index()) - .and_then(|it| match it.1 { - Either::Left(attr) => Some(attr.syntax().clone()), - Either::Right(_) => None, - }) - .unwrap_or_else(|| it.syntax().clone()) - }) + let (attr, _, _, _) = + attr_ids.invoc_attr().find_attr_range(db, self.krate, *ast_id); + ast_id.with_value(attr.syntax().clone()) } else { ast_id.with_value(ast_id.to_node(db).syntax().clone()) } @@ -715,7 +748,7 @@ impl MacroCallKind { /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros /// get the macro path (rustc shows the whole `ast::MacroCall`), attribute macros get the /// attribute's range, and derives get only the specific derive that is being referred to. - pub fn original_call_range(self, db: &dyn ExpandDatabase) -> FileRange { + pub fn original_call_range(self, db: &dyn ExpandDatabase, krate: Crate) -> FileRange { let mut kind = self; let file_id = loop { match kind.file_id() { @@ -737,24 +770,11 @@ impl MacroCallKind { } MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { // FIXME: should be the range of the macro name, not the whole derive - // FIXME: handle `cfg_attr` - collect_attrs(&ast_id.to_node(db)) - .nth(derive_attr_index.ast_index()) - .expect("missing derive") - .1 - .expect_left("derive is a doc comment?") - .syntax() - .text_range() + derive_attr_index.find_attr_range(db, krate, ast_id).2 } // FIXME: handle `cfg_attr` - MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { - collect_attrs(&ast_id.to_node(db)) - .nth(invoc_attr_index.ast_index()) - .expect("missing attribute") - .1 - .expect_left("attribute macro is a doc comment?") - .syntax() - .text_range() + MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { + attr_ids.invoc_attr().find_attr_range(db, krate, ast_id).2 } }; @@ -873,7 +893,8 @@ impl ExpansionInfo { let span = self.exp_map.span_at(token.start()); match &self.arg_map { SpanMap::RealSpanMap(_) => { - let file_id = EditionedFileId::from_span(db, span.anchor.file_id).into(); + let file_id = + EditionedFileId::from_span_guess_origin(db, span.anchor.file_id).into(); let anchor_offset = db.ast_id_map(file_id).get_erased(span.anchor.ast_id).text_range().start(); InFile { file_id, value: smallvec::smallvec![span.range + anchor_offset] } @@ -929,7 +950,7 @@ pub fn map_node_range_up_rooted( start = start.min(span.range.start()); end = end.max(span.range.end()); } - let file_id = EditionedFileId::from_span(db, anchor.file_id); + let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id); let anchor_offset = db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start(); Some(FileRange { file_id, range: TextRange::new(start, end) + anchor_offset }) @@ -955,36 +976,12 @@ pub fn map_node_range_up( start = start.min(span.range.start()); end = end.max(span.range.end()); } - let file_id = EditionedFileId::from_span(db, anchor.file_id); + let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id); let anchor_offset = db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start(); Some((FileRange { file_id, range: TextRange::new(start, end) + anchor_offset }, ctx)) } -/// Maps up the text range out of the expansion hierarchy back into the original file its from. -/// This version will aggregate the ranges of all spans with the same anchor and syntax context. -pub fn map_node_range_up_aggregated( - db: &dyn ExpandDatabase, - exp_map: &ExpansionSpanMap, - range: TextRange, -) -> FxHashMap<(SpanAnchor, SyntaxContext), TextRange> { - let mut map = FxHashMap::default(); - for span in exp_map.spans_for_range(range) { - let range = map.entry((span.anchor, span.ctx)).or_insert_with(|| span.range); - *range = TextRange::new( - range.start().min(span.range.start()), - range.end().max(span.range.end()), - ); - } - for ((anchor, _), range) in &mut map { - let file_id = EditionedFileId::from_span(db, anchor.file_id); - let anchor_offset = - db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start(); - *range += anchor_offset; - } - map -} - /// Looks up the span at the given offset. pub fn span_for_offset( db: &dyn ExpandDatabase, @@ -992,7 +989,7 @@ pub fn span_for_offset( offset: TextSize, ) -> (FileRange, SyntaxContext) { let span = exp_map.span_at(offset); - let file_id = EditionedFileId::from_span(db, span.anchor.file_id); + let file_id = EditionedFileId::from_span_guess_origin(db, span.anchor.file_id); let anchor_offset = db.ast_id_map(file_id.into()).get_erased(span.anchor.ast_id).text_range().start(); (FileRange { file_id, range: span.range + anchor_offset }, span.ctx) @@ -1062,7 +1059,7 @@ impl ExpandTo { } } -intern::impl_internable!(ModPath, attrs::AttrInput); +intern::impl_internable!(ModPath); #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[doc(alias = "MacroFileId")] @@ -1125,6 +1122,14 @@ impl HirFileId { HirFileId::MacroFile(_) => None, } } + + #[inline] + pub fn krate(self, db: &dyn ExpandDatabase) -> Crate { + match self { + HirFileId::FileId(it) => it.krate(db), + HirFileId::MacroFile(it) => it.loc(db).krate, + } + } } impl PartialEq for HirFileId { 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 d84d978cdb7e..e9805e3f86b8 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 @@ -2,7 +2,7 @@ use std::{ fmt::{self, Display as _}, - iter, + iter::{self, Peekable}, }; use crate::{ @@ -12,10 +12,11 @@ use crate::{ tt, }; use base_db::Crate; -use intern::sym; +use intern::{Symbol, sym}; +use parser::T; use smallvec::SmallVec; use span::{Edition, SyntaxContext}; -use syntax::{AstNode, ast}; +use syntax::{AstNode, SyntaxToken, ast}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ModPath { @@ -64,6 +65,58 @@ impl ModPath { ModPath { kind, segments: SmallVec::new_const() } } + pub fn from_tokens( + db: &dyn ExpandDatabase, + span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext, + is_abs: bool, + segments: impl Iterator, + ) -> Option { + let mut segments = segments.peekable(); + let mut result = SmallVec::new_const(); + let path_kind = if is_abs { + PathKind::Abs + } else { + let first = segments.next()?; + match first.kind() { + T![crate] => PathKind::Crate, + T![self] => PathKind::Super(handle_super(&mut segments)), + T![super] => PathKind::Super(1 + handle_super(&mut segments)), + T![ident] => { + let first_text = first.text(); + if first_text == "$crate" { + let ctxt = span_for_range(first.text_range()); + resolve_crate_root(db, ctxt) + .map(PathKind::DollarCrate) + .unwrap_or(PathKind::Crate) + } else { + result.push(Name::new_symbol_root(Symbol::intern(first_text))); + PathKind::Plain + } + } + _ => return None, + } + }; + for segment in segments { + if segment.kind() != T![ident] { + return None; + } + result.push(Name::new_symbol_root(Symbol::intern(segment.text()))); + } + if result.is_empty() { + return None; + } + result.shrink_to_fit(); + return Some(ModPath { kind: path_kind, segments: result }); + + fn handle_super(segments: &mut Peekable>) -> u8 { + let mut result = 0; + while segments.next_if(|it| it.kind() == T![super]).is_some() { + result += 1; + } + result + } + } + pub fn segments(&self) -> &[Name] { &self.segments } 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 e5a778a95c7c..8b0c0d72cd49 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs @@ -1,13 +1,12 @@ //! Span maps for real files and macro expansions. use span::{Span, SyntaxContext}; -use stdx::TupleExt; use syntax::{AstNode, TextRange, ast}; use triomphe::Arc; pub use span::RealSpanMap; -use crate::{HirFileId, MacroCallId, attrs::collect_attrs, db::ExpandDatabase}; +use crate::{HirFileId, MacroCallId, db::ExpandDatabase}; pub type ExpansionSpanMap = span::SpanMap; @@ -110,26 +109,24 @@ pub(crate) fn real_span_map( // them anchors too, but only if they have no attributes attached, as those might be proc-macros // and using different anchors inside of them will prevent spans from being joinable. tree.items().for_each(|item| match &item { - ast::Item::ExternBlock(it) - if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => - { + ast::Item::ExternBlock(it) if ast::attrs_including_inner(it).next().is_none() => { if let Some(extern_item_list) = it.extern_item_list() { pairs.extend( extern_item_list.extern_items().map(ast::Item::from).map(item_to_entry), ); } } - ast::Item::Impl(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => { + ast::Item::Impl(it) if ast::attrs_including_inner(it).next().is_none() => { if let Some(assoc_item_list) = it.assoc_item_list() { pairs.extend(assoc_item_list.assoc_items().map(ast::Item::from).map(item_to_entry)); } } - ast::Item::Module(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => { + ast::Item::Module(it) if ast::attrs_including_inner(it).next().is_none() => { if let Some(item_list) = it.item_list() { pairs.extend(item_list.items().map(item_to_entry)); } } - ast::Item::Trait(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => { + ast::Item::Trait(it) if ast::attrs_including_inner(it).next().is_none() => { if let Some(assoc_item_list) = it.assoc_item_list() { pairs.extend(assoc_item_list.assoc_items().map(ast::Item::from).map(item_to_entry)); } 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 18ebe7d7a539..0a6458562e15 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -6,6 +6,7 @@ mod tests; use base_db::Crate; use hir_def::{ EnumVariantId, GeneralConstId, HasModule, StaticId, + attrs::AttrFlags, expr_store::Body, hir::{Expr, ExprId}, type_ref::LiteralConstRef, @@ -198,7 +199,7 @@ pub(crate) fn const_eval_discriminant_variant<'db>( return Ok(value); } - let repr = db.enum_signature(loc.parent).repr; + let repr = AttrFlags::repr(db, loc.parent.into()); let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed()); let mir_body = db.monomorphized_mir_body( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 0815e62f87ee..c0e223380bca 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -17,8 +17,8 @@ use std::fmt; use hir_def::{ AdtId, ConstId, EnumId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, - ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, db::DefDatabase, hir::Pat, - item_tree::FieldsShape, signatures::StaticFlags, src::HasSource, + ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, attrs::AttrFlags, + db::DefDatabase, hir::Pat, item_tree::FieldsShape, signatures::StaticFlags, src::HasSource, }; use hir_expand::{ HirFileId, @@ -201,7 +201,7 @@ impl<'a> DeclValidator<'a> { // Don't run the lint on extern "[not Rust]" fn items with the // #[no_mangle] attribute. - let no_mangle = self.db.attrs(func.into()).by_key(sym::no_mangle).exists(); + let no_mangle = AttrFlags::query(self.db, func.into()).contains(AttrFlags::NO_MANGLE); if no_mangle && data.abi.as_ref().is_some_and(|abi| *abi != sym::Rust) { cov_mark::hit!(extern_func_no_mangle_ignored); } else { 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 fb942e336e65..c70c6b611944 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 @@ -2,7 +2,9 @@ use std::{cell::LazyCell, fmt}; -use hir_def::{EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId}; +use hir_def::{ + EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId, attrs::AttrFlags, +}; use intern::sym; use rustc_pattern_analysis::{ IndexVec, PatCx, PrivateUninhabitedField, @@ -118,7 +120,7 @@ impl<'a, 'db> MatchCheckCtx<'a, 'db> { /// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`. fn is_foreign_non_exhaustive(&self, adt: hir_def::AdtId) -> bool { let is_local = adt.krate(self.db) == self.module.krate(); - !is_local && self.db.attrs(adt.into()).by_key(sym::non_exhaustive).exists() + !is_local && AttrFlags::query(self.db, adt.into()).contains(AttrFlags::NON_EXHAUSTIVE) } fn variant_id_for_adt( 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 53524d66a33c..8ac7ab19cd3b 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 @@ -144,7 +144,7 @@ struct UnsafeVisitor<'db> { inside_assignment: bool, inside_union_destructure: bool, callback: &'db mut dyn FnMut(UnsafeDiagnostic), - def_target_features: TargetFeatures, + def_target_features: TargetFeatures<'db>, // FIXME: This needs to be the edition of the span of each call. edition: Edition, /// On some targets (WASM), calling safe functions with `#[target_feature]` is always safe, even when @@ -162,7 +162,7 @@ impl<'db> UnsafeVisitor<'db> { ) -> Self { let resolver = def.resolver(db); let def_target_features = match def { - DefWithBodyId::FunctionId(func) => TargetFeatures::from_attrs(&db.attrs(func.into())), + DefWithBodyId::FunctionId(func) => TargetFeatures::from_fn(db, func), _ => TargetFeatures::default(), }; let krate = resolver.module().krate(); 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 9891f3f248bd..03ae970acaa7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -38,7 +38,7 @@ use hir_def::{ lang_item::{LangItem, LangItemTarget, lang_item}, layout::Integer, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, - signatures::{ConstSignature, StaticSignature}, + signatures::{ConstSignature, EnumSignature, StaticSignature}, type_ref::{ConstRef, LifetimeRefId, TypeRefId}, }; use hir_expand::{mod_path::ModPath, name::Name}; @@ -104,7 +104,7 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc ctx.collect_const(c, &db.const_signature(c)), DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)), DefWithBodyId::VariantId(v) => { - ctx.return_ty = match db.enum_signature(v.lookup(db).parent).variant_body_type() { + ctx.return_ty = match EnumSignature::variant_body_type(db, v.lookup(db).parent) { hir_def::layout::IntegerType::Pointer(signed) => match signed { true => ctx.types.isize, false => ctx.types.usize, @@ -759,7 +759,7 @@ pub(crate) struct InferenceContext<'body, 'db> { /// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext /// and resolve the path via its methods. This will ensure proper error reporting. pub(crate) resolver: Resolver<'db>, - target_features: OnceCell<(TargetFeatures, TargetFeatureIsSafeInTarget)>, + target_features: OnceCell<(TargetFeatures<'db>, TargetFeatureIsSafeInTarget)>, pub(crate) generic_def: GenericDefId, table: unify::InferenceTable<'db>, /// The traits in scope, disregarding block modules. This is used for caching purposes. @@ -903,14 +903,14 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } fn target_features<'a>( - db: &dyn HirDatabase, - target_features: &'a OnceCell<(TargetFeatures, TargetFeatureIsSafeInTarget)>, + db: &'db dyn HirDatabase, + target_features: &'a OnceCell<(TargetFeatures<'db>, TargetFeatureIsSafeInTarget)>, owner: DefWithBodyId, krate: Crate, - ) -> (&'a TargetFeatures, TargetFeatureIsSafeInTarget) { + ) -> (&'a TargetFeatures<'db>, TargetFeatureIsSafeInTarget) { let (target_features, target_feature_is_safe) = target_features.get_or_init(|| { let target_features = match owner { - DefWithBodyId::FunctionId(id) => TargetFeatures::from_attrs(&db.attrs(id.into())), + DefWithBodyId::FunctionId(id) => TargetFeatures::from_fn(db, id), _ => TargetFeatures::default(), }; let target_feature_is_safe = match &krate.workspace_data(db).target { 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 78889ccb89a2..9b95eef0e0d6 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 @@ -37,11 +37,11 @@ use hir_def::{ CallableDefId, + attrs::AttrFlags, hir::{ExprId, ExprOrPatId}, lang_item::LangItem, signatures::FunctionSignature, }; -use intern::sym; use rustc_ast_ir::Mutability; use rustc_type_ir::{ BoundVar, TypeAndMut, @@ -76,7 +76,7 @@ use crate::{ struct Coerce<'a, 'b, 'db> { table: &'a mut InferenceTable<'db>, has_errors: &'a mut bool, - target_features: &'a mut dyn FnMut() -> (&'b TargetFeatures, TargetFeatureIsSafeInTarget), + target_features: &'a mut dyn FnMut() -> (&'b TargetFeatures<'db>, TargetFeatureIsSafeInTarget), use_lub: bool, /// Determines whether or not allow_two_phase_borrow is set on any /// autoref adjustments we create while coercing. We don't want to @@ -864,14 +864,14 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { return Err(TypeError::IntrinsicCast); } - let attrs = self.table.db.attrs(def_id.into()); - if attrs.by_key(sym::rustc_force_inline).exists() { + let attrs = AttrFlags::query(self.table.db, def_id.into()); + if attrs.contains(AttrFlags::RUSTC_FORCE_INLINE) { return Err(TypeError::ForceInlineCast); } - if b_hdr.safety.is_safe() && attrs.by_key(sym::target_feature).exists() { + if b_hdr.safety.is_safe() && attrs.contains(AttrFlags::HAS_TARGET_FEATURE) { let fn_target_features = - TargetFeatures::from_attrs_no_implications(&attrs); + TargetFeatures::from_fn_no_implications(self.table.db, def_id); // Allow the coercion if the current function has all the features that would be // needed to call the coercee safely. let (target_features, target_feature_is_safe) = @@ -1056,7 +1056,7 @@ impl<'db> InferenceContext<'_, 'db> { let is_force_inline = |ty: Ty<'db>| { if let TyKind::FnDef(CallableIdWrapper(CallableDefId::FunctionId(did)), _) = ty.kind() { - self.db.attrs(did.into()).by_key(sym::rustc_force_inline).exists() + AttrFlags::query(self.db, did.into()).contains(AttrFlags::RUSTC_FORCE_INLINE) } else { false } 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 efb7244ff637..a1d99a45287d 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 @@ -2365,9 +2365,11 @@ impl<'db> InferenceContext<'_, 'db> { }; let data = self.db.function_signature(func); - let Some(legacy_const_generics_indices) = &data.legacy_const_generics_indices else { + let Some(legacy_const_generics_indices) = data.legacy_const_generics_indices(self.db, func) + else { return Default::default(); }; + let mut legacy_const_generics_indices = Box::<[u32]>::from(legacy_const_generics_indices); // only use legacy const generics if the param count matches with them if data.params.len() + legacy_const_generics_indices.len() != args.len() { @@ -2376,9 +2378,8 @@ impl<'db> InferenceContext<'_, 'db> { } else { // there are more parameters than there should be without legacy // const params; use them - let mut indices = legacy_const_generics_indices.as_ref().clone(); - indices.sort(); - return indices; + legacy_const_generics_indices.sort_unstable(); + return legacy_const_generics_indices; } } @@ -2391,9 +2392,8 @@ impl<'db> InferenceContext<'_, 'db> { self.infer_expr(args[arg_idx as usize], &expected, ExprIsRead::Yes); // FIXME: evaluate and unify with the const } - let mut indices = legacy_const_generics_indices.as_ref().clone(); - indices.sort(); - indices + legacy_const_generics_indices.sort_unstable(); + legacy_const_generics_indices } /// Dereferences a single level of immutable referencing. 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 fc0b9d30b333..b650f5c1a16a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -4,6 +4,7 @@ use std::fmt; use hir_def::{ AdtId, LocalFieldId, StructId, + attrs::AttrFlags, layout::{LayoutCalculatorError, LayoutData}, }; use la_arena::{Idx, RawIdx}; @@ -174,8 +175,7 @@ pub fn layout_of_ty_query<'db>( TyKind::Adt(def, args) => { match def.inner().id { hir_def::AdtId::StructId(s) => { - let data = db.struct_signature(s); - let repr = data.repr.unwrap_or_default(); + let repr = AttrFlags::repr(db, s.into()).unwrap_or_default(); if repr.simd() { return layout_of_simd_ty(db, s, repr.packed(), &args, trait_env, &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 a8f04bf8c132..ecebf7935d06 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 @@ -4,9 +4,9 @@ use std::{cmp, ops::Bound}; use hir_def::{ AdtId, VariantId, + attrs::AttrFlags, signatures::{StructFlags, VariantFields}, }; -use intern::sym; use rustc_abi::{Integer, ReprOptions, TargetDataLayout}; use rustc_index::IndexVec; use smallvec::SmallVec; @@ -44,15 +44,15 @@ pub fn layout_of_adt_query<'db>( r.push(handle_variant(s.into(), s.fields(db))?); ( r, - sig.repr.unwrap_or_default(), + AttrFlags::repr(db, s.into()).unwrap_or_default(), sig.flags.intersects(StructFlags::IS_UNSAFE_CELL | StructFlags::IS_UNSAFE_PINNED), ) } AdtId::UnionId(id) => { - let data = db.union_signature(id); + let repr = AttrFlags::repr(db, id.into()); let mut r = SmallVec::new(); r.push(handle_variant(id.into(), id.fields(db))?); - (r, data.repr.unwrap_or_default(), false) + (r, repr.unwrap_or_default(), false) } AdtId::EnumId(e) => { let variants = e.enum_variants(db); @@ -61,7 +61,7 @@ pub fn layout_of_adt_query<'db>( .iter() .map(|&(v, _, _)| handle_variant(v.into(), v.fields(db))) .collect::, _>>()?; - (r, db.enum_signature(e).repr.unwrap_or_default(), false) + (r, AttrFlags::repr(db, e.into()).unwrap_or_default(), false) } }; let variants = variants @@ -105,27 +105,12 @@ pub(crate) fn layout_of_adt_cycle_result<'db>( } fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound, Bound) { - let attrs = db.attrs(def.into()); - let get = |name| { - let attr = attrs.by_key(name).tt_values(); - for tree in attr { - if let Some(it) = tree.iter().next_as_view() { - let text = it.to_string().replace('_', ""); - let (text, base) = match text.as_bytes() { - [b'0', b'x', ..] => (&text[2..], 16), - [b'0', b'o', ..] => (&text[2..], 8), - [b'0', b'b', ..] => (&text[2..], 2), - _ => (&*text, 10), - }; - - if let Ok(it) = u128::from_str_radix(text, base) { - return Bound::Included(it); - } - } - } - Bound::Unbounded + let range = AttrFlags::rustc_layout_scalar_valid_range(db, def); + let get = |value| match value { + Some(it) => Bound::Included(it), + None => Bound::Unbounded, }; - (get(sym::rustc_layout_scalar_valid_range_start), get(sym::rustc_layout_scalar_valid_range_end)) + (get(range.start), get(range.end)) } /// Finds the appropriate Integer type and signedness for the given 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 cec63566338f..1b5f4595ca3c 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 @@ -8,11 +8,11 @@ use base_db::Crate; use hir_def::{ AdtId, AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleId, TraitId, TypeAliasId, + attrs::AttrFlags, nameres::{DefMap, block_def_map, crate_def_map}, signatures::{ConstFlags, EnumFlags, FnFlags, StructFlags, TraitFlags, TypeAliasFlags}, }; use hir_expand::name::Name; -use intern::sym; use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ @@ -230,7 +230,8 @@ impl TraitImpls { // FIXME: Reservation impls should be considered during coherence checks. If we are // (ever) to implement coherence checks, this filtering should be done by the trait // solver. - if db.attrs(impl_id.into()).by_key(sym::rustc_reservation_impl).exists() { + if AttrFlags::query(db, impl_id.into()).contains(AttrFlags::RUSTC_RESERVATION_IMPL) + { continue; } let target_trait = match db.impl_trait(impl_id) { 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 4b1adecf8c87..d4aab2d09496 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 @@ -3,9 +3,11 @@ //! use std::cmp::{self, Ordering}; -use hir_def::{CrateRootModuleId, resolver::HasResolver, signatures::FunctionSignature}; +use hir_def::{ + CrateRootModuleId, attrs::AttrFlags, resolver::HasResolver, signatures::FunctionSignature, +}; use hir_expand::name::Name; -use intern::{Symbol, sym}; +use intern::sym; use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike, Ty as _}; use stdx::never; @@ -53,7 +55,7 @@ impl<'db> Evaluator<'db> { } let function_data = self.db.function_signature(def); - let attrs = self.db.attrs(def.into()); + let attrs = AttrFlags::query(self.db, def.into()); let is_intrinsic = FunctionSignature::is_intrinsic(self.db, def); if is_intrinsic { @@ -65,7 +67,7 @@ impl<'db> Evaluator<'db> { locals, span, !function_data.has_body() - || attrs.by_key(sym::rustc_intrinsic_must_be_overridden).exists(), + || attrs.contains(AttrFlags::RUSTC_INTRINSIC_MUST_BE_OVERRIDDEN), ); } let is_extern_c = match def.lookup(self.db).container { @@ -85,18 +87,13 @@ impl<'db> Evaluator<'db> { .map(|()| true); } - let alloc_fn = - attrs.iter().filter_map(|it| it.path().as_ident()).map(|it| it.symbol()).find(|it| { - [ - &sym::rustc_allocator, - &sym::rustc_deallocator, - &sym::rustc_reallocator, - &sym::rustc_allocator_zeroed, - ] - .contains(it) - }); - if let Some(alloc_fn) = alloc_fn { - self.exec_alloc_fn(alloc_fn, args, destination)?; + if attrs.intersects( + AttrFlags::RUSTC_ALLOCATOR + | AttrFlags::RUSTC_DEALLOCATOR + | AttrFlags::RUSTC_REALLOCATOR + | AttrFlags::RUSTC_ALLOCATOR_ZEROED, + ) { + self.exec_alloc_fn(attrs, args, destination)?; return Ok(true); } if let Some(it) = self.detect_lang_function(def) { @@ -245,12 +242,14 @@ impl<'db> Evaluator<'db> { fn exec_alloc_fn( &mut self, - alloc_fn: &Symbol, + alloc_fn: AttrFlags, args: &[IntervalAndTy<'db>], destination: Interval, ) -> Result<'db, ()> { match alloc_fn { - _ if *alloc_fn == sym::rustc_allocator_zeroed || *alloc_fn == sym::rustc_allocator => { + _ if alloc_fn + .intersects(AttrFlags::RUSTC_ALLOCATOR_ZEROED | AttrFlags::RUSTC_ALLOCATOR) => + { let [size, align] = args else { return Err(MirEvalError::InternalError( "rustc_allocator args are not provided".into(), @@ -261,8 +260,8 @@ impl<'db> Evaluator<'db> { let result = self.heap_allocate(size, align)?; destination.write_from_bytes(self, &result.to_bytes())?; } - _ if *alloc_fn == sym::rustc_deallocator => { /* no-op for now */ } - _ if *alloc_fn == sym::rustc_reallocator => { + _ if alloc_fn.contains(AttrFlags::RUSTC_DEALLOCATOR) => { /* no-op for now */ } + _ if alloc_fn.contains(AttrFlags::RUSTC_REALLOCATOR) => { let [ptr, old_size, align, new_size] = args else { return Err(MirEvalError::InternalError( "rustc_allocator args are not provided".into(), @@ -288,14 +287,14 @@ impl<'db> Evaluator<'db> { fn detect_lang_function(&self, def: FunctionId) -> Option { use LangItem::*; - let attrs = self.db.attrs(def.into()); + let attrs = AttrFlags::query(self.db, def.into()); - if attrs.by_key(sym::rustc_const_panic_str).exists() { + if attrs.contains(AttrFlags::RUSTC_CONST_PANIC_STR) { // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE. return Some(LangItem::BeginPanic); } - let candidate = attrs.lang_item()?; + let candidate = attrs.lang_item_with_attrs(self.db, def.into())?; // We want to execute these functions with special logic // `PanicFmt` is not detected here as it's redirected later. if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) { 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 42f1d926d7db..f372411830c1 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 @@ -8,6 +8,7 @@ use base_db::Crate; use hir_def::{ AdtId, AttrDefId, BlockId, CallableDefId, EnumVariantId, ItemContainerId, StructId, UnionId, VariantId, + attrs::AttrFlags, lang_item::LangItem, signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags}, }; @@ -466,28 +467,28 @@ impl AdtDef { let variants = vec![(VariantIdx(0), VariantDef::Struct(struct_id))]; - let mut repr = ReprOptions::default(); - repr.align = data.repr.and_then(|r| r.align); - repr.pack = data.repr.and_then(|r| r.pack); - repr.int = data.repr.and_then(|r| r.int); - + let data_repr = data.repr(db, struct_id); let mut repr_flags = ReprFlags::empty(); if flags.is_box { repr_flags.insert(ReprFlags::IS_LINEAR); } - if data.repr.is_some_and(|r| r.c()) { + if data_repr.is_some_and(|r| r.c()) { repr_flags.insert(ReprFlags::IS_C); } - if data.repr.is_some_and(|r| r.simd()) { + if data_repr.is_some_and(|r| r.simd()) { repr_flags.insert(ReprFlags::IS_SIMD); } - repr.flags = repr_flags; + 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 data = db.union_signature(union_id); - let flags = AdtFlags { is_enum: false, is_union: true, @@ -500,22 +501,24 @@ impl AdtDef { let variants = vec![(VariantIdx(0), VariantDef::Union(union_id))]; - let mut repr = ReprOptions::default(); - repr.align = data.repr.and_then(|r| r.align); - repr.pack = data.repr.and_then(|r| r.pack); - repr.int = data.repr.and_then(|r| r.int); - + 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.repr.is_some_and(|r| r.c()) { + if data_repr.is_some_and(|r| r.c()) { repr_flags.insert(ReprFlags::IS_C); } - if data.repr.is_some_and(|r| r.simd()) { + if data_repr.is_some_and(|r| r.simd()) { repr_flags.insert(ReprFlags::IS_SIMD); } - repr.flags = repr_flags; + 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) } @@ -539,24 +542,26 @@ impl AdtDef { .map(|(idx, v)| (idx, VariantDef::Enum(v.0))) .collect(); - let data = db.enum_signature(enum_id); - - let mut repr = ReprOptions::default(); - repr.align = data.repr.and_then(|r| r.align); - repr.pack = data.repr.and_then(|r| r.pack); - repr.int = data.repr.and_then(|r| r.int); + 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.repr.is_some_and(|r| r.c()) { + if data_repr.is_some_and(|r| r.c()) { repr_flags.insert(ReprFlags::IS_C); } - if data.repr.is_some_and(|r| r.simd()) { + if data_repr.is_some_and(|r| r.simd()) { repr_flags.insert(ReprFlags::IS_SIMD); } - repr.flags = repr_flags; + + 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) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs index 0a8ed2cf0cab..2bd675ba124e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs @@ -1,31 +1,35 @@ //! Stuff for handling `#[target_feature]` (needed for unsafe check). +use std::borrow::Cow; use std::sync::LazyLock; -use hir_def::attr::Attrs; -use hir_def::tt; -use intern::{Symbol, sym}; +use hir_def::FunctionId; +use hir_def::attrs::AttrFlags; +use intern::Symbol; use rustc_hash::{FxHashMap, FxHashSet}; +use crate::db::HirDatabase; + #[derive(Debug, Default, Clone)] -pub struct TargetFeatures { - pub(crate) enabled: FxHashSet, +pub struct TargetFeatures<'db> { + pub(crate) enabled: Cow<'db, FxHashSet>, } -impl TargetFeatures { - pub fn from_attrs(attrs: &Attrs) -> Self { - let mut result = TargetFeatures::from_attrs_no_implications(attrs); +impl<'db> TargetFeatures<'db> { + pub fn from_fn(db: &'db dyn HirDatabase, owner: FunctionId) -> Self { + let mut result = TargetFeatures::from_fn_no_implications(db, owner); result.expand_implications(); result } fn expand_implications(&mut self) { let all_implications = LazyLock::force(&TARGET_FEATURE_IMPLICATIONS); - let mut queue = self.enabled.iter().cloned().collect::>(); + let enabled = self.enabled.to_mut(); + let mut queue = enabled.iter().cloned().collect::>(); while let Some(feature) = queue.pop() { if let Some(implications) = all_implications.get(&feature) { for implication in implications { - if self.enabled.insert(implication.clone()) { + if enabled.insert(implication.clone()) { queue.push(implication.clone()); } } @@ -34,25 +38,9 @@ impl TargetFeatures { } /// Retrieves the target features from the attributes, and does not expand the target features implied by them. - pub(crate) fn from_attrs_no_implications(attrs: &Attrs) -> Self { - let enabled = attrs - .by_key(sym::target_feature) - .tt_values() - .filter_map(|tt| match tt.token_trees().flat_tokens() { - [ - tt::TokenTree::Leaf(tt::Leaf::Ident(enable_ident)), - tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. })), - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - kind: tt::LitKind::Str, - symbol: features, - .. - })), - ] if enable_ident.sym == sym::enable => Some(features), - _ => None, - }) - .flat_map(|features| features.as_str().split(',').map(Symbol::intern)) - .collect(); - Self { enabled } + pub(crate) fn from_fn_no_implications(db: &'db dyn HirDatabase, owner: FunctionId) -> Self { + let enabled = AttrFlags::target_features(db, owner); + Self { enabled: Cow::Borrowed(enabled) } } } 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 bc4701970c76..50625c1c26d5 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 @@ -31,7 +31,6 @@ fn foo() -> i32 { &[("infer_shim", 1)], expect_test::expect![[r#" [ - "source_root_crates_shim", "crate_local_def_map", "file_item_tree_query", "ast_id_map_shim", @@ -40,7 +39,7 @@ fn foo() -> i32 { "infer_shim", "function_signature_shim", "function_signature_with_source_map_shim", - "attrs_shim", + "AttrFlags::query_", "body_shim", "body_with_source_map_shim", "trait_environment_shim", @@ -79,7 +78,7 @@ fn foo() -> i32 { "ast_id_map_shim", "file_item_tree_query", "real_span_map_shim", - "attrs_shim", + "AttrFlags::query_", "function_signature_with_source_map_shim", "function_signature_shim", "body_with_source_map_shim", @@ -118,7 +117,6 @@ fn baz() -> i32 { &[("infer_shim", 3)], expect_test::expect![[r#" [ - "source_root_crates_shim", "crate_local_def_map", "file_item_tree_query", "ast_id_map_shim", @@ -127,7 +125,7 @@ fn baz() -> i32 { "infer_shim", "function_signature_shim", "function_signature_with_source_map_shim", - "attrs_shim", + "AttrFlags::query_", "body_shim", "body_with_source_map_shim", "trait_environment_shim", @@ -135,8 +133,8 @@ fn baz() -> i32 { "expr_scopes_shim", "lang_item", "crate_lang_items", - "attrs_shim", - "attrs_shim", + "AttrFlags::query_", + "AttrFlags::query_", "infer_shim", "function_signature_shim", "function_signature_with_source_map_shim", @@ -189,13 +187,13 @@ fn baz() -> i32 { "ast_id_map_shim", "file_item_tree_query", "real_span_map_shim", - "attrs_shim", + "AttrFlags::query_", "function_signature_with_source_map_shim", "function_signature_shim", "body_with_source_map_shim", "body_shim", - "attrs_shim", - "attrs_shim", + "AttrFlags::query_", + "AttrFlags::query_", "function_signature_with_source_map_shim", "function_signature_shim", "body_with_source_map_shim", @@ -235,7 +233,6 @@ $0", &[("trait_impls_in_crate_shim", 1)], expect_test::expect![[r#" [ - "source_root_crates_shim", "crate_local_def_map", "file_item_tree_query", "ast_id_map_shim", @@ -307,7 +304,6 @@ $0", &[("trait_impls_in_crate_shim", 1)], expect_test::expect![[r#" [ - "source_root_crates_shim", "crate_local_def_map", "file_item_tree_query", "ast_id_map_shim", @@ -380,7 +376,6 @@ $0", &[("trait_impls_in_crate_shim", 1)], expect_test::expect![[r#" [ - "source_root_crates_shim", "crate_local_def_map", "file_item_tree_query", "ast_id_map_shim", @@ -454,7 +449,6 @@ $0", &[("trait_impls_in_crate_shim", 1)], expect_test::expect![[r#" [ - "source_root_crates_shim", "crate_local_def_map", "file_item_tree_query", "ast_id_map_shim", @@ -503,14 +497,14 @@ impl SomeStruct { "real_span_map_shim", "crate_local_def_map", "trait_impls_in_crate_shim", - "attrs_shim", + "AttrFlags::query_", "impl_trait_with_diagnostics_shim", "impl_signature_shim", "impl_signature_with_source_map_shim", "impl_self_ty_with_diagnostics_shim", "struct_signature_shim", "struct_signature_with_source_map_shim", - "attrs_shim", + "AttrFlags::query_", ] "#]], ); @@ -560,7 +554,6 @@ fn main() { &[("trait_solve_shim", 0)], expect_test::expect![[r#" [ - "source_root_crates_shim", "crate_local_def_map", "file_item_tree_query", "ast_id_map_shim", @@ -569,22 +562,22 @@ fn main() { "TraitItems::query_with_diagnostics_", "body_shim", "body_with_source_map_shim", - "attrs_shim", + "AttrFlags::query_", "ImplItems::of_", "infer_shim", "trait_signature_shim", "trait_signature_with_source_map_shim", - "attrs_shim", + "AttrFlags::query_", "function_signature_shim", "function_signature_with_source_map_shim", - "attrs_shim", + "AttrFlags::query_", "body_shim", "body_with_source_map_shim", "trait_environment_shim", "lang_item", "crate_lang_items", - "attrs_shim", - "attrs_shim", + "AttrFlags::query_", + "AttrFlags::query_", "generic_predicates_shim", "return_type_impl_traits_shim", "infer_shim", @@ -666,22 +659,22 @@ fn main() { "crate_local_def_map", "TraitItems::query_with_diagnostics_", "body_with_source_map_shim", - "attrs_shim", + "AttrFlags::query_", "body_shim", "ImplItems::of_", "infer_shim", - "attrs_shim", + "AttrFlags::query_", "trait_signature_with_source_map_shim", - "attrs_shim", + "AttrFlags::query_", "function_signature_with_source_map_shim", "function_signature_shim", "body_with_source_map_shim", "body_shim", "trait_environment_shim", "crate_lang_items", - "attrs_shim", - "attrs_shim", - "attrs_shim", + "AttrFlags::query_", + "AttrFlags::query_", + "AttrFlags::query_", "generic_predicates_shim", "return_type_impl_traits_shim", "infer_shim", diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index ca5e33fe6ad0..41dc4dc53375 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -9,6 +9,7 @@ use base_db::{ }; use hir_def::{ EnumId, EnumVariantId, FunctionId, Lookup, TraitId, + attrs::AttrFlags, db::DefDatabase, hir::generics::WherePredicate, lang_item::LangItem, @@ -119,7 +120,7 @@ pub fn target_feature_is_safe_in_target(target: &TargetData) -> TargetFeatureIsS pub fn is_fn_unsafe_to_call( db: &dyn HirDatabase, func: FunctionId, - caller_target_features: &TargetFeatures, + caller_target_features: &TargetFeatures<'_>, call_edition: Edition, target_feature_is_safe: TargetFeatureIsSafeInTarget, ) -> Unsafety { @@ -130,8 +131,7 @@ pub fn is_fn_unsafe_to_call( if data.has_target_feature() && target_feature_is_safe == TargetFeatureIsSafeInTarget::No { // RFC 2396 . - let callee_target_features = - TargetFeatures::from_attrs_no_implications(&db.attrs(func.into())); + let callee_target_features = TargetFeatures::from_fn_no_implications(db, func); if !caller_target_features.enabled.is_superset(&callee_target_features.enabled) { return Unsafety::Unsafe; } @@ -152,7 +152,7 @@ pub fn is_fn_unsafe_to_call( if is_intrinsic_block { // legacy intrinsics // extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute - if db.attrs(func.into()).by_key(sym::rustc_safe_intrinsic).exists() { + if AttrFlags::query(db, func.into()).contains(AttrFlags::RUSTC_SAFE_INTRINSIC) { Unsafety::Safe } else { Unsafety::Unsafe diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 147f1b8653be..3376c51fe5c9 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -2,9 +2,12 @@ use std::ops::ControlFlow; +use cfg::CfgExpr; +use either::Either; use hir_def::{ - AssocItemId, AttrDefId, ModuleDefId, - attr::AttrsWithOwner, + AssocItemId, AttrDefId, FieldId, InternedModuleId, LifetimeParamId, ModuleDefId, + TypeOrConstParamId, + attrs::{AttrFlags, Docs, IsInnerDoc}, expr_store::path::Path, item_scope::ItemInNs, per_ns::Namespace, @@ -15,6 +18,7 @@ use hir_expand::{ name::Name, }; use hir_ty::{db::HirDatabase, method_resolution}; +use intern::Symbol; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, @@ -22,28 +26,161 @@ use crate::{ Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, }; -pub trait HasAttrs { - fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner; +#[derive(Debug, Clone, Copy)] +pub enum AttrsOwner { + AttrDef(AttrDefId), + Field(FieldId), + LifetimeParam(LifetimeParamId), + TypeOrConstParam(TypeOrConstParamId), +} + +impl AttrsOwner { + #[inline] + fn attr_def(&self) -> Option { + match self { + AttrsOwner::AttrDef(it) => Some(*it), + _ => None, + } + } +} + +#[derive(Debug, Clone)] +pub struct AttrsWithOwner { + pub(crate) attrs: AttrFlags, + owner: AttrsOwner, +} + +impl AttrsWithOwner { + fn new(db: &dyn HirDatabase, owner: AttrDefId) -> Self { + Self { attrs: AttrFlags::query(db, owner), owner: AttrsOwner::AttrDef(owner) } + } + + fn new_field(db: &dyn HirDatabase, owner: FieldId) -> Self { + Self { attrs: AttrFlags::query_field(db, owner), owner: AttrsOwner::Field(owner) } + } + + fn new_lifetime_param(db: &dyn HirDatabase, owner: LifetimeParamId) -> Self { + Self { + attrs: AttrFlags::query_lifetime_param(db, owner), + owner: AttrsOwner::LifetimeParam(owner), + } + } + fn new_type_or_const_param(db: &dyn HirDatabase, owner: TypeOrConstParamId) -> Self { + Self { + attrs: AttrFlags::query_type_or_const_param(db, owner), + owner: AttrsOwner::TypeOrConstParam(owner), + } + } + + #[inline] + pub fn is_unstable(&self) -> bool { + self.attrs.contains(AttrFlags::IS_UNSTABLE) + } + + #[inline] + pub fn is_macro_export(&self) -> bool { + self.attrs.contains(AttrFlags::IS_MACRO_EXPORT) + } + + #[inline] + pub fn is_doc_notable_trait(&self) -> bool { + self.attrs.contains(AttrFlags::IS_DOC_NOTABLE_TRAIT) + } + + #[inline] + pub fn is_doc_hidden(&self) -> bool { + self.attrs.contains(AttrFlags::IS_DOC_HIDDEN) + } + + #[inline] + pub fn is_deprecated(&self) -> bool { + self.attrs.contains(AttrFlags::IS_DEPRECATED) + } + + #[inline] + pub fn is_non_exhaustive(&self) -> bool { + self.attrs.contains(AttrFlags::NON_EXHAUSTIVE) + } + + #[inline] + pub fn is_test(&self) -> bool { + self.attrs.contains(AttrFlags::IS_TEST) + } + + #[inline] + pub fn lang(&self, db: &dyn HirDatabase) -> Option<&'static str> { + self.owner + .attr_def() + .and_then(|owner| self.attrs.lang_item_with_attrs(db, owner)) + .map(|lang| lang.name()) + } + + #[inline] + pub fn doc_aliases<'db>(&self, db: &'db dyn HirDatabase) -> &'db [Symbol] { + let owner = match self.owner { + AttrsOwner::AttrDef(it) => Either::Left(it), + AttrsOwner::Field(it) => Either::Right(it), + AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return &[], + }; + self.attrs.doc_aliases(db, owner) + } + + #[inline] + pub fn cfgs<'db>(&self, db: &'db dyn HirDatabase) -> Option<&'db CfgExpr> { + let owner = match self.owner { + AttrsOwner::AttrDef(it) => Either::Left(it), + AttrsOwner::Field(it) => Either::Right(it), + AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return None, + }; + self.attrs.cfgs(db, owner) + } + + #[inline] + pub fn hir_docs<'db>(&self, db: &'db dyn HirDatabase) -> Option<&'db Docs> { + match self.owner { + AttrsOwner::AttrDef(it) => AttrFlags::docs(db, it).as_deref(), + AttrsOwner::Field(it) => AttrFlags::field_docs(db, it), + AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => None, + } + } +} + +pub trait HasAttrs: Sized { + #[inline] + fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { + match self.attr_id(db) { + AttrsOwner::AttrDef(it) => AttrsWithOwner::new(db, it), + AttrsOwner::Field(it) => AttrsWithOwner::new_field(db, it), + AttrsOwner::LifetimeParam(it) => AttrsWithOwner::new_lifetime_param(db, it), + AttrsOwner::TypeOrConstParam(it) => AttrsWithOwner::new_type_or_const_param(db, it), + } + } + #[doc(hidden)] - fn attr_id(self) -> AttrDefId; + fn attr_id(self, db: &dyn HirDatabase) -> AttrsOwner; + + #[inline] + fn hir_docs(self, db: &dyn HirDatabase) -> Option<&Docs> { + match self.attr_id(db) { + AttrsOwner::AttrDef(it) => AttrFlags::docs(db, it).as_deref(), + AttrsOwner::Field(it) => AttrFlags::field_docs(db, it), + AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => None, + } + } } macro_rules! impl_has_attrs { ($(($def:ident, $def_id:ident),)*) => {$( impl HasAttrs for $def { - fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { - let def = AttrDefId::$def_id(self.into()); - AttrsWithOwner::new(db, def) - } - fn attr_id(self) -> AttrDefId { - AttrDefId::$def_id(self.into()) + #[inline] + fn attr_id(self, _db: &dyn HirDatabase) -> AttrsOwner { + AttrsOwner::AttrDef(AttrDefId::$def_id(self.into())) } } )*}; } impl_has_attrs![ - (Field, FieldId), (Variant, EnumVariantId), (Static, StaticId), (Const, ConstId), @@ -52,8 +189,6 @@ impl_has_attrs![ (Macro, MacroId), (Function, FunctionId), (Adt, AdtId), - (Module, ModuleId), - (GenericParam, GenericParamId), (Impl, ImplId), (ExternCrateDecl, ExternCrateId), ]; @@ -61,11 +196,9 @@ impl_has_attrs![ macro_rules! impl_has_attrs_enum { ($($variant:ident),* for $enum:ident) => {$( impl HasAttrs for $variant { - fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { - $enum::$variant(self).attrs(db) - } - fn attr_id(self) -> AttrDefId { - $enum::$variant(self).attr_id() + #[inline] + fn attr_id(self, db: &dyn HirDatabase) -> AttrsOwner { + $enum::$variant(self).attr_id(db) } } )*}; @@ -74,30 +207,46 @@ macro_rules! impl_has_attrs_enum { impl_has_attrs_enum![Struct, Union, Enum for Adt]; impl_has_attrs_enum![TypeParam, ConstParam, LifetimeParam for GenericParam]; -impl HasAttrs for AssocItem { - fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { +impl HasAttrs for Module { + #[inline] + fn attr_id(self, db: &dyn HirDatabase) -> AttrsOwner { + AttrsOwner::AttrDef(AttrDefId::ModuleId(InternedModuleId::new(db, self.id))) + } +} + +impl HasAttrs for GenericParam { + #[inline] + fn attr_id(self, _db: &dyn HirDatabase) -> AttrsOwner { match self { - AssocItem::Function(it) => it.attrs(db), - AssocItem::Const(it) => it.attrs(db), - AssocItem::TypeAlias(it) => it.attrs(db), + GenericParam::TypeParam(it) => AttrsOwner::TypeOrConstParam(it.merge().into()), + GenericParam::ConstParam(it) => AttrsOwner::TypeOrConstParam(it.merge().into()), + GenericParam::LifetimeParam(it) => AttrsOwner::LifetimeParam(it.into()), } } - fn attr_id(self) -> AttrDefId { +} + +impl HasAttrs for AssocItem { + #[inline] + fn attr_id(self, db: &dyn HirDatabase) -> AttrsOwner { match self { - AssocItem::Function(it) => it.attr_id(), - AssocItem::Const(it) => it.attr_id(), - AssocItem::TypeAlias(it) => it.attr_id(), + AssocItem::Function(it) => it.attr_id(db), + AssocItem::Const(it) => it.attr_id(db), + AssocItem::TypeAlias(it) => it.attr_id(db), } } } impl HasAttrs for crate::Crate { - fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { - let def = AttrDefId::ModuleId(self.root_module().id); - AttrsWithOwner::new(db, def) + #[inline] + fn attr_id(self, db: &dyn HirDatabase) -> AttrsOwner { + self.root_module().attr_id(db) } - fn attr_id(self) -> AttrDefId { - AttrDefId::ModuleId(self.root_module().id) +} + +impl HasAttrs for Field { + #[inline] + fn attr_id(self, _db: &dyn HirDatabase) -> AttrsOwner { + AttrsOwner::Field(self.into()) } } @@ -107,21 +256,22 @@ pub fn resolve_doc_path_on( def: impl HasAttrs + Copy, link: &str, ns: Option, - is_inner_doc: bool, + is_inner_doc: IsInnerDoc, ) -> Option { - resolve_doc_path_on_(db, link, def.attr_id(), ns, is_inner_doc) + resolve_doc_path_on_(db, link, def.attr_id(db), ns, is_inner_doc) } fn resolve_doc_path_on_( db: &dyn HirDatabase, link: &str, - attr_id: AttrDefId, + attr_id: AttrsOwner, ns: Option, - is_inner_doc: bool, + is_inner_doc: IsInnerDoc, ) -> Option { let resolver = match attr_id { - AttrDefId::ModuleId(it) => { - if is_inner_doc { + AttrsOwner::AttrDef(AttrDefId::ModuleId(it)) => { + let it = it.loc(db); + if is_inner_doc.yes() { it.resolver(db) } else if let Some(parent) = Module::from(it).parent(db) { parent.id.resolver(db) @@ -129,20 +279,20 @@ fn resolve_doc_path_on_( it.resolver(db) } } - AttrDefId::FieldId(it) => it.parent.resolver(db), - AttrDefId::AdtId(it) => it.resolver(db), - AttrDefId::FunctionId(it) => it.resolver(db), - AttrDefId::EnumVariantId(it) => it.resolver(db), - AttrDefId::StaticId(it) => it.resolver(db), - AttrDefId::ConstId(it) => it.resolver(db), - AttrDefId::TraitId(it) => it.resolver(db), - AttrDefId::TypeAliasId(it) => it.resolver(db), - AttrDefId::ImplId(it) => it.resolver(db), - AttrDefId::ExternBlockId(it) => it.resolver(db), - AttrDefId::UseId(it) => it.resolver(db), - AttrDefId::MacroId(it) => it.resolver(db), - AttrDefId::ExternCrateId(it) => it.resolver(db), - AttrDefId::GenericParamId(_) => return None, + AttrsOwner::AttrDef(AttrDefId::AdtId(it)) => it.resolver(db), + AttrsOwner::AttrDef(AttrDefId::FunctionId(it)) => it.resolver(db), + AttrsOwner::AttrDef(AttrDefId::EnumVariantId(it)) => it.resolver(db), + AttrsOwner::AttrDef(AttrDefId::StaticId(it)) => it.resolver(db), + AttrsOwner::AttrDef(AttrDefId::ConstId(it)) => it.resolver(db), + AttrsOwner::AttrDef(AttrDefId::TraitId(it)) => it.resolver(db), + AttrsOwner::AttrDef(AttrDefId::TypeAliasId(it)) => it.resolver(db), + AttrsOwner::AttrDef(AttrDefId::ImplId(it)) => it.resolver(db), + AttrsOwner::AttrDef(AttrDefId::ExternBlockId(it)) => it.resolver(db), + AttrsOwner::AttrDef(AttrDefId::UseId(it)) => it.resolver(db), + AttrsOwner::AttrDef(AttrDefId::MacroId(it)) => it.resolver(db), + AttrsOwner::AttrDef(AttrDefId::ExternCrateId(it)) => it.resolver(db), + AttrsOwner::Field(it) => it.parent.resolver(db), + AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return None, }; let mut modpath = doc_modpath_from_str(link)?; diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index a6d67e8fb4fb..6ef6ea272e58 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -153,8 +153,7 @@ pub struct UnresolvedImport { #[derive(Debug, Clone, Eq, PartialEq)] pub struct UnresolvedMacroCall { - pub macro_call: InFile, - pub precise_location: Option, + pub range: InFile, pub path: ModPath, pub is_bang: bool, } @@ -185,8 +184,7 @@ pub struct InactiveCode { #[derive(Debug, Clone, Eq, PartialEq)] pub struct MacroError { - pub node: InFile, - pub precise_location: Option, + pub range: InFile, pub message: String, pub error: bool, pub kind: &'static str, @@ -194,8 +192,7 @@ pub struct MacroError { #[derive(Debug, Clone, Eq, PartialEq)] pub struct MacroExpansionParseError { - pub node: InFile, - pub precise_location: Option, + pub range: InFile, pub errors: Arc<[SyntaxError]>, } @@ -213,12 +210,12 @@ pub struct UnimplementedBuiltinMacro { #[derive(Debug)] pub struct InvalidDeriveTarget { - pub node: InFile, + pub range: InFile, } #[derive(Debug)] pub struct MalformedDerive { - pub node: InFile, + pub range: InFile, } #[derive(Debug)] diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 48eafb0bd4c6..e912de6fe761 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -45,11 +45,12 @@ use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateOrigin, LangCrateOrigin}; use either::Either; use hir_def::{ - AdtId, AssocItemId, AssocItemLoc, AttrDefId, CallableDefId, ConstId, ConstParamId, - CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, - FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, + AdtId, AssocItemId, AssocItemLoc, CallableDefId, ConstId, ConstParamId, CrateRootModuleId, + DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, + GenericParamId, HasModule, ImplId, InternedModuleId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, SyntheticSyntax, TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, + attrs::AttrFlags, expr_store::{ExpressionStoreDiagnostics, ExpressionStoreSourceMap}, hir::{ BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat, @@ -63,13 +64,12 @@ use hir_def::{ }, per_ns::PerNs, resolver::{HasResolver, Resolver}, - signatures::{ImplFlags, StaticFlags, StructFlags, TraitFlags, VariantFields}, + signatures::{EnumSignature, ImplFlags, StaticFlags, StructFlags, TraitFlags, VariantFields}, src::HasSource as _, visibility::visibility_from_ast, }; use hir_expand::{ - AstId, MacroCallKind, RenderedExpandError, ValueResult, attrs::collect_attrs, - proc_macro::ProcMacroKind, + AstId, MacroCallKind, RenderedExpandError, ValueResult, proc_macro::ProcMacroKind, }; use hir_ty::{ TraitEnvironment, TyDefId, TyLoweringDiagnostic, ValueTyDefId, all_super_traits, autoderef, @@ -98,8 +98,8 @@ use smallvec::SmallVec; use span::{AstIdNode, Edition, FileId}; use stdx::{format_to, impl_from, never}; use syntax::{ - AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, T, TextRange, ToSmolStr, - ast::{self, HasAttrs as _, HasName, HasVisibility as _}, + AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, ToSmolStr, + ast::{self, HasName, HasVisibility as _}, format_smolstr, }; use triomphe::{Arc, ThinArc}; @@ -107,7 +107,7 @@ use triomphe::{Arc, ThinArc}; use crate::db::{DefDatabase, HirDatabase}; pub use crate::{ - attrs::{HasAttrs, resolve_doc_path_on}, + attrs::{AttrsWithOwner, HasAttrs, resolve_doc_path_on}, diagnostics::*, has_source::HasSource, semantics::{ @@ -130,7 +130,7 @@ pub use { hir_def::{ Complete, FindPathConfig, - attr::{AttrSourceMap, Attrs, AttrsWithOwner}, + attrs::{Docs, IsInnerDoc}, find_path::PrefixKind, import_map, lang_item::LangItem, @@ -144,7 +144,6 @@ pub use { }, hir_expand::{ EditionedFileId, ExpandResult, HirFileId, MacroCallId, MacroKind, - attrs::{Attr, AttrId}, change::ChangeWithProcMacros, files::{ FilePosition, FilePositionWrapper, FileRange, FileRangeWrapper, HirFilePosition, @@ -290,11 +289,10 @@ impl Crate { } /// Try to get the root URL of the documentation of a crate. - pub fn get_html_root_url(self: &Crate, db: &dyn HirDatabase) -> Option { + pub fn get_html_root_url(self, db: &dyn HirDatabase) -> Option { // Look for #![doc(html_root_url = "...")] - let attrs = db.attrs(AttrDefId::ModuleId(self.root_module().into())); - let doc_url = attrs.by_key(sym::doc).find_string_value_in_tt(sym::html_root_url); - doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") + let doc_url = AttrFlags::doc_html_root_url(db, self.id); + doc_url.as_ref().map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") } pub fn cfg<'db>(&self, db: &'db dyn HirDatabase) -> &'db CfgOptions { @@ -639,7 +637,7 @@ impl Module { // FIXME: This is accidentally quadratic. continue; } - emit_def_diagnostic(db, acc, diag, edition); + emit_def_diagnostic(db, acc, diag, edition, def_map.krate()); } if !self.id.is_block_module() { @@ -658,8 +656,9 @@ impl Module { acc.extend(def.diagnostics(db, style_lints)) } ModuleDef::Trait(t) => { + let krate = t.krate(db); for diag in TraitItems::query_with_diagnostics(db, t.id).1.iter() { - emit_def_diagnostic(db, acc, diag, edition); + emit_def_diagnostic(db, acc, diag, edition, krate.id); } for item in t.items(db) { @@ -777,7 +776,7 @@ impl Module { let ast_id_map = db.ast_id_map(file_id); for diag in impl_def.id.impl_items_with_diagnostics(db).1.iter() { - emit_def_diagnostic(db, acc, diag, edition); + emit_def_diagnostic(db, acc, diag, edition, loc.container.krate()); } if inherent_impls.invalid_impls().contains(&impl_def.id) { @@ -808,21 +807,10 @@ impl Module { return None; } let parent = impl_def.id.into(); - let generic_params = db.generic_params(parent); - let lifetime_params = generic_params.iter_lt().map(|(local_id, _)| { - GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id }) - }); - let type_params = generic_params - .iter_type_or_consts() - .filter(|(_, it)| it.type_param().is_some()) - .map(|(local_id, _)| { - GenericParamId::TypeParamId(TypeParamId::from_unchecked( - TypeOrConstParamId { parent, local_id }, - )) - }); - let res = type_params.chain(lifetime_params).any(|p| { - db.attrs(AttrDefId::GenericParamId(p)).by_key(sym::may_dangle).exists() - }); + let (lifetimes_attrs, type_and_consts_attrs) = + AttrFlags::query_generic_params(db, parent); + let res = lifetimes_attrs.values().any(|it| it.contains(AttrFlags::MAY_DANGLE)) + || type_and_consts_attrs.values().any(|it| it.contains(AttrFlags::MAY_DANGLE)); Some(res) })() .unwrap_or(false); @@ -983,6 +971,17 @@ impl Module { ) -> Option { hir_def::find_path::find_path(db, item.into().into(), self.into(), prefix_kind, true, cfg) } + + #[inline] + pub fn doc_keyword(self, db: &dyn HirDatabase) -> Option { + AttrFlags::doc_keyword(db, InternedModuleId::new(db, self.id)) + } + + /// Whether it has `#[path = "..."]` attribute. + #[inline] + pub fn has_path(&self, db: &dyn HirDatabase) -> bool { + self.attrs(db).attrs.contains(AttrFlags::HAS_PATH) + } } fn macro_call_diagnostics<'db>( @@ -997,31 +996,19 @@ fn macro_call_diagnostics<'db>( if let Some(err) = err { let loc = db.lookup_intern_macro_call(macro_call_id); let file_id = loc.kind.file_id(); - let node = - InFile::new(file_id, db.ast_id_map(file_id).get_erased(loc.kind.erased_ast_id())); + let mut range = precise_macro_call_location(&loc.kind, db, loc.krate); let RenderedExpandError { message, error, kind } = err.render_to_string(db); - let editioned_file_id = EditionedFileId::from_span(db, err.span().anchor.file_id); - let precise_location = if editioned_file_id == file_id { - Some( - err.span().range - + db.ast_id_map(editioned_file_id.into()) - .get_erased(err.span().anchor.ast_id) - .text_range() - .start(), - ) - } else { - None - }; - acc.push(MacroError { node, precise_location, message, error, kind }.into()); + if Some(err.span().anchor.file_id) == file_id.file_id().map(|it| it.editioned_file_id(db)) { + range.value = err.span().range + + db.ast_id_map(file_id).get_erased(err.span().anchor.ast_id).text_range().start(); + } + acc.push(MacroError { range, message, error, kind }.into()); } if !parse_errors.is_empty() { let loc = db.lookup_intern_macro_call(macro_call_id); - let (node, precise_location) = precise_macro_call_location(&loc.kind, db); - acc.push( - MacroExpansionParseError { node, precise_location, errors: parse_errors.clone() } - .into(), - ) + let range = precise_macro_call_location(&loc.kind, db, loc.krate); + acc.push(MacroExpansionParseError { range, errors: parse_errors.clone() }.into()) } } @@ -1045,6 +1032,7 @@ fn emit_macro_def_diagnostics<'db>( acc, &DefDiagnosticKind::MacroDefError { ast, message: e.to_string() }, edition, + m.krate(db).id, ); } } @@ -1054,8 +1042,9 @@ fn emit_def_diagnostic<'db>( acc: &mut Vec>, diag: &DefDiagnostic, edition: Edition, + krate: base_db::Crate, ) { - emit_def_diagnostic_(db, acc, &diag.kind, edition) + emit_def_diagnostic_(db, acc, &diag.kind, edition, krate) } fn emit_def_diagnostic_<'db>( @@ -1063,6 +1052,7 @@ fn emit_def_diagnostic_<'db>( acc: &mut Vec>, diag: &DefDiagnosticKind, edition: Edition, + krate: base_db::Crate, ) { match diag { DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => { @@ -1085,8 +1075,7 @@ fn emit_def_diagnostic_<'db>( let RenderedExpandError { message, error, kind } = err.render_to_string(db); acc.push( MacroError { - node: InFile::new(ast.file_id, item.syntax_node_ptr()), - precise_location: None, + range: InFile::new(ast.file_id, item.text_range()), message: format!("{}: {message}", path.display(db, edition)), error, kind, @@ -1116,11 +1105,10 @@ fn emit_def_diagnostic_<'db>( ); } DefDiagnosticKind::UnresolvedMacroCall { ast, path } => { - let (node, precise_location) = precise_macro_call_location(ast, db); + let location = precise_macro_call_location(ast, db, krate); acc.push( UnresolvedMacroCall { - macro_call: node, - precise_location, + range: location, path: path.clone(), is_bang: matches!(ast, MacroCallKind::FnLike { .. }), } @@ -1139,34 +1127,12 @@ fn emit_def_diagnostic_<'db>( ); } DefDiagnosticKind::InvalidDeriveTarget { ast, id } => { - let node = ast.to_node(db); - let derive = node.attrs().nth(*id); - match derive { - Some(derive) => { - acc.push( - InvalidDeriveTarget { - node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&derive))), - } - .into(), - ); - } - None => stdx::never!("derive diagnostic on item without derive attribute"), - } + let derive = id.find_attr_range(db, krate, *ast).3.path_range(); + acc.push(InvalidDeriveTarget { range: ast.with_value(derive) }.into()); } DefDiagnosticKind::MalformedDerive { ast, id } => { - let node = ast.to_node(db); - let derive = node.attrs().nth(*id); - match derive { - Some(derive) => { - acc.push( - MalformedDerive { - node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&derive))), - } - .into(), - ); - } - None => stdx::never!("derive diagnostic on item without derive attribute"), - } + let derive = id.find_attr_range(db, krate, *ast).2; + acc.push(MalformedDerive { range: ast.with_value(derive) }.into()); } DefDiagnosticKind::MacroDefError { ast, message } => { let node = ast.to_node(db); @@ -1185,61 +1151,28 @@ fn emit_def_diagnostic_<'db>( fn precise_macro_call_location( ast: &MacroCallKind, db: &dyn HirDatabase, -) -> (InFile, Option) { + krate: base_db::Crate, +) -> InFile { // FIXME: maybe we actually want slightly different ranges for the different macro diagnostics // - e.g. the full attribute for macro errors, but only the name for name resolution match ast { MacroCallKind::FnLike { ast_id, .. } => { let node = ast_id.to_node(db); - ( - ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))), - node.path() - .and_then(|it| it.segment()) - .and_then(|it| it.name_ref()) - .map(|it| it.syntax().text_range()), - ) + let range = node + .path() + .and_then(|it| it.segment()) + .and_then(|it| it.name_ref()) + .map(|it| it.syntax().text_range()); + let range = range.unwrap_or_else(|| node.syntax().text_range()); + ast_id.with_value(range) } MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => { - let node = ast_id.to_node(db); - // Compute the precise location of the macro name's token in the derive - // list. - let token = (|| { - let derive_attr = collect_attrs(&node) - .nth(derive_attr_index.ast_index()) - .and_then(|x| Either::left(x.1))?; - let token_tree = derive_attr.meta()?.token_tree()?; - let chunk_by = token_tree - .syntax() - .children_with_tokens() - .filter_map(|elem| match elem { - syntax::NodeOrToken::Token(tok) => Some(tok), - _ => None, - }) - .chunk_by(|t| t.kind() == T![,]); - let (_, mut group) = chunk_by - .into_iter() - .filter(|&(comma, _)| !comma) - .nth(*derive_index as usize)?; - group.find(|t| t.kind() == T![ident]) - })(); - ( - ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))), - token.as_ref().map(|tok| tok.text_range()), - ) + let range = derive_attr_index.find_derive_range(db, krate, *ast_id, *derive_index); + ast_id.with_value(range) } - MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { - let node = ast_id.to_node(db); - let attr = collect_attrs(&node) - .nth(invoc_attr_index.ast_index()) - .and_then(|x| Either::left(x.1)) - .unwrap_or_else(|| { - panic!("cannot find attribute #{}", invoc_attr_index.ast_index()) - }); - - ( - ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&attr))), - Some(attr.syntax().text_range()), - ) + MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { + let attr_range = attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).2; + ast_id.with_value(attr_range) } } } @@ -1437,7 +1370,7 @@ impl Struct { } pub fn repr(self, db: &dyn HirDatabase) -> Option { - db.struct_signature(self.id).repr + AttrFlags::repr(db, self.id.into()) } pub fn kind(self, db: &dyn HirDatabase) -> StructKind { @@ -1453,7 +1386,7 @@ impl Struct { } pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { - db.attrs(self.id.into()).is_unstable() + AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) } pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedStruct<'db> { @@ -1542,7 +1475,7 @@ impl Union { .collect() } pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { - db.attrs(self.id.into()).is_unstable() + AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) } } @@ -1577,7 +1510,7 @@ impl Enum { } pub fn repr(self, db: &dyn HirDatabase) -> Option { - db.enum_signature(self.id).repr + AttrFlags::repr(db, self.id.into()) } pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { @@ -1593,7 +1526,7 @@ impl Enum { let interner = DbInterner::new_with(db, None, None); Type::new_for_crate( self.id.lookup(db).container.krate(), - match db.enum_signature(self.id).variant_body_type() { + match EnumSignature::variant_body_type(db, self.id) { layout::IntegerType::Pointer(sign) => match sign { true => Ty::new_int(interner, rustc_type_ir::IntTy::Isize), false => Ty::new_uint(interner, rustc_type_ir::UintTy::Usize), @@ -1634,7 +1567,7 @@ impl Enum { } pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { - db.attrs(self.id.into()).is_unstable() + AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) } } @@ -1735,7 +1668,7 @@ impl Variant { } pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { - db.attrs(self.id.into()).is_unstable() + AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) } pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedVariant<'db> { @@ -2220,8 +2153,7 @@ fn expr_store_diagnostics<'db>( InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into() } ExpressionStoreDiagnostics::UnresolvedMacroCall { node, path } => UnresolvedMacroCall { - macro_call: (*node).map(|ast_ptr| ast_ptr.into()), - precise_location: None, + range: node.map(|ptr| ptr.text_range()), path: path.clone(), is_bang: true, } @@ -2446,33 +2378,33 @@ impl Function { /// Does this function have `#[test]` attribute? pub fn is_test(self, db: &dyn HirDatabase) -> bool { - db.attrs(self.id.into()).is_test() + self.attrs(db).is_test() } /// is this a `fn main` or a function with an `export_name` of `main`? pub fn is_main(self, db: &dyn HirDatabase) -> bool { - db.attrs(self.id.into()).export_name() == Some(&sym::main) + self.exported_main(db) || self.module(db).is_crate_root() && db.function_signature(self.id).name == sym::main } /// Is this a function with an `export_name` of `main`? pub fn exported_main(self, db: &dyn HirDatabase) -> bool { - db.attrs(self.id.into()).export_name() == Some(&sym::main) + AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_EXPORT_NAME_MAIN) } /// Does this function have the ignore attribute? pub fn is_ignore(self, db: &dyn HirDatabase) -> bool { - db.attrs(self.id.into()).is_ignore() + AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_IGNORE) } /// Does this function have `#[bench]` attribute? pub fn is_bench(self, db: &dyn HirDatabase) -> bool { - db.attrs(self.id.into()).is_bench() + AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_BENCH) } /// Is this function marked as unstable with `#[feature]` attribute? pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { - db.attrs(self.id.into()).is_unstable() + AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) } pub fn is_unsafe_to_call( @@ -2483,8 +2415,7 @@ impl Function { ) -> bool { let (target_features, target_feature_is_safe_in_target) = caller .map(|caller| { - let target_features = - hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into())); + let target_features = hir_ty::TargetFeatures::from_fn(db, caller.id); let target_feature_is_safe_in_target = match &caller.krate(db).id.workspace_data(db).target { Ok(target) => hir_ty::target_feature_is_safe_in_target(target), @@ -2515,14 +2446,6 @@ impl Function { } pub fn as_proc_macro(self, db: &dyn HirDatabase) -> Option { - let attrs = db.attrs(self.id.into()); - // FIXME: Store this in FunctionData flags? - if !(attrs.is_proc_macro() - || attrs.is_proc_macro_attribute() - || attrs.is_proc_macro_derive()) - { - return None; - } let def_map = crate_def_map(db, HasModule::krate(&self.id, db)); def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() }) } @@ -2975,7 +2898,7 @@ impl Trait { /// `#[rust_analyzer::completions(...)]` mode. pub fn complete(self, db: &dyn HirDatabase) -> Complete { - Complete::extract(true, &self.attrs(db)) + Complete::extract(true, self.attrs(db).attrs) } } @@ -3146,10 +3069,10 @@ impl Macro { let loc = id.lookup(db); let source = loc.source(db); match loc.kind { - ProcMacroKind::CustomDerive => db - .attrs(id.into()) - .parse_proc_macro_derive() - .map_or_else(|| as_name_opt(source.value.name()), |(it, _)| it), + ProcMacroKind::CustomDerive => AttrFlags::derive_info(db, self.id).map_or_else( + || as_name_opt(source.value.name()), + |info| Name::new_symbol_root(info.trait_name.clone()), + ), ProcMacroKind::Bang | ProcMacroKind::Attr => as_name_opt(source.value.name()), } } @@ -3157,7 +3080,7 @@ impl Macro { } pub fn is_macro_export(self, db: &dyn HirDatabase) -> bool { - matches!(self.id, MacroId::MacroRulesId(_) if db.attrs(self.id.into()).by_key(sym::macro_export).exists()) + matches!(self.id, MacroId::MacroRulesId(_) if AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_MACRO_EXPORT)) } pub fn is_proc_macro(self) -> bool { @@ -3981,18 +3904,10 @@ impl DeriveHelper { } pub fn name(&self, db: &dyn HirDatabase) -> Name { - match self.derive { - makro @ MacroId::Macro2Id(_) => db - .attrs(makro.into()) - .parse_rustc_builtin_macro() - .and_then(|(_, helpers)| helpers.get(self.idx as usize).cloned()), - MacroId::MacroRulesId(_) => None, - makro @ MacroId::ProcMacroId(_) => db - .attrs(makro.into()) - .parse_proc_macro_derive() - .and_then(|(_, helpers)| helpers.get(self.idx as usize).cloned()), - } - .unwrap_or_else(Name::missing) + AttrFlags::derive_info(db, self.derive) + .and_then(|it| it.helpers.get(self.idx as usize)) + .map(|helper| Name::new_symbol_root(helper.clone())) + .unwrap_or_else(Name::missing) } } @@ -4213,7 +4128,7 @@ impl TypeParam { } pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { - db.attrs(GenericParamId::from(self.id).into()).is_unstable() + self.attrs(db).is_unstable() } } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 62ce3daab75d..8eb1c9725cd2 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -21,7 +21,6 @@ use hir_def::{ }; use hir_expand::{ EditionedFileId, ExpandResult, FileRange, HirFileId, InMacroFile, MacroCallId, - attrs::collect_attrs, builtin::{BuiltinFnLikeExpander, EagerExpander}, db::ExpandDatabase, files::{FileRangeWrapper, HirFileRange, InRealFile}, @@ -36,7 +35,7 @@ use intern::{Interned, Symbol, sym}; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{SmallVec, smallvec}; -use span::{Edition, FileId, SyntaxContext}; +use span::{FileId, SyntaxContext}; use stdx::{TupleExt, always}; use syntax::{ AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, @@ -386,17 +385,14 @@ impl<'db> SemanticsImpl<'db> { } pub fn attach_first_edition(&self, file: FileId) -> Option { - Some(EditionedFileId::new( - self.db, - file, - self.file_to_module_defs(file).next()?.krate().edition(self.db), - )) + let krate = self.file_to_module_defs(file).next()?.krate(); + Some(EditionedFileId::new(self.db, file, krate.edition(self.db), krate.id)) } pub fn parse_guess_edition(&self, file_id: FileId) -> ast::SourceFile { let file_id = self .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::new(self.db, file_id, Edition::CURRENT)); + .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(self.db, file_id)); let tree = self.db.parse(file_id).tree(); self.cache(tree.syntax().clone(), file_id.into()); @@ -1197,33 +1193,34 @@ impl<'db> SemanticsImpl<'db> { .zip(Some(item)) }) .map(|(call_id, item)| { - let attr_id = match db.lookup_intern_macro_call(call_id).kind { + let item_range = item.syntax().text_range(); + let loc = db.lookup_intern_macro_call(call_id); + let text_range = match loc.kind { hir_expand::MacroCallKind::Attr { - invoc_attr_index, .. - } => invoc_attr_index.ast_index(), - _ => 0, + censored_attr_ids: attr_ids, + .. + } => { + // FIXME: here, the attribute's text range is used to strip away all + // entries from the start of the attribute "list" up the invoking + // attribute. But in + // ``` + // mod foo { + // #![inner] + // } + // ``` + // we don't wanna strip away stuff in the `mod foo {` range, that is + // here if the id corresponds to an inner attribute we got strip all + // text ranges of the outer ones, and then all of the inner ones up + // to the invoking attribute so that the inbetween is ignored. + // FIXME: Should cfg_attr be handled differently? + let (attr, _, _, _) = attr_ids + .invoc_attr() + .find_attr_range_with_source(db, loc.krate, &item); + let start = attr.syntax().text_range().start(); + TextRange::new(start, item_range.end()) + } + _ => item_range, }; - // FIXME: here, the attribute's text range is used to strip away all - // entries from the start of the attribute "list" up the invoking - // attribute. But in - // ``` - // mod foo { - // #![inner] - // } - // ``` - // we don't wanna strip away stuff in the `mod foo {` range, that is - // here if the id corresponds to an inner attribute we got strip all - // text ranges of the outer ones, and then all of the inner ones up - // to the invoking attribute so that the inbetween is ignored. - let text_range = item.syntax().text_range(); - let start = collect_attrs(&item) - .nth(attr_id) - .map(|attr| match attr.1 { - Either::Left(it) => it.syntax().text_range().start(), - Either::Right(it) => it.syntax().text_range().start(), - }) - .unwrap_or_else(|| text_range.start()); - let text_range = TextRange::new(start, text_range.end()); filter_duplicates(tokens, text_range); process_expansion_for_token(ctx, &mut stack, call_id) }) @@ -1473,6 +1470,14 @@ impl<'db> SemanticsImpl<'db> { FileRangeWrapper { file_id: file_id.file_id(self.db), range } } + pub fn diagnostics_display_range_for_range( + &self, + src: InFile, + ) -> FileRangeWrapper { + let FileRange { file_id, range } = src.original_node_file_range_rooted(self.db); + FileRangeWrapper { file_id: file_id.file_id(self.db), range } + } + fn token_ancestors_with_macros( &self, token: SyntaxToken, diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index 5019a5987e51..165ac7e4a08d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -5,7 +5,7 @@ //! node for a *child*, and get its hir. use either::Either; -use hir_expand::{HirFileId, attrs::collect_attrs}; +use hir_expand::HirFileId; use span::AstIdNode; use syntax::{AstPtr, ast}; @@ -94,6 +94,7 @@ impl ChildBySource for ModuleId { impl ChildBySource for ItemScope { fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) { + let krate = file_id.krate(db); self.declarations().for_each(|item| add_module_def(db, res, file_id, item)); self.impls().for_each(|imp| insert_item_loc(db, res, file_id, imp, keys::IMPL)); self.extern_blocks().for_each(|extern_block| { @@ -123,12 +124,10 @@ impl ChildBySource for ItemScope { |(ast_id, calls)| { let adt = ast_id.to_node(db); calls.for_each(|(attr_id, call_id, calls)| { - if let Some((_, Either::Left(attr))) = - collect_attrs(&adt).nth(attr_id.ast_index()) - { - res[keys::DERIVE_MACRO_CALL] - .insert(AstPtr::new(&attr), (attr_id, call_id, calls.into())); - } + // FIXME: Fix cfg_attr handling. + let (attr, _, _, _) = attr_id.find_attr_range_with_source(db, krate, &adt); + res[keys::DERIVE_MACRO_CALL] + .insert(AstPtr::new(&attr), (attr_id, call_id, calls.into())); }); }, ); diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index d8c624e5c689..9059c88ad66a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -392,12 +392,12 @@ impl<'a> SymbolCollector<'a> { let mut do_not_complete = Complete::Yes; if let Some(attrs) = def.attrs(self.db) { - do_not_complete = Complete::extract(matches!(def, ModuleDef::Trait(_)), &attrs); + do_not_complete = Complete::extract(matches!(def, ModuleDef::Trait(_)), attrs.attrs); if let Some(trait_do_not_complete) = trait_do_not_complete { do_not_complete = Complete::for_trait_item(trait_do_not_complete, do_not_complete); } - for alias in attrs.doc_aliases() { + for alias in attrs.doc_aliases(self.db) { self.symbols.insert(FileSymbol { name: alias.clone(), def, @@ -441,9 +441,9 @@ impl<'a> SymbolCollector<'a> { let mut do_not_complete = Complete::Yes; if let Some(attrs) = def.attrs(self.db) { - do_not_complete = Complete::extract(matches!(def, ModuleDef::Trait(_)), &attrs); + do_not_complete = Complete::extract(matches!(def, ModuleDef::Trait(_)), attrs.attrs); - for alias in attrs.doc_aliases() { + for alias in attrs.doc_aliases(self.db) { self.symbols.insert(FileSymbol { name: alias.clone(), def, 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 7843ab9e8f25..e06c534e3c51 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 @@ -1,7 +1,7 @@ use std::iter::{self, Peekable}; use either::Either; -use hir::{Adt, AsAssocItem, Crate, FindPathConfig, HasAttrs, ModuleDef, Semantics, sym}; +use hir::{Adt, AsAssocItem, Crate, FindPathConfig, HasAttrs, ModuleDef, Semantics}; use ide_db::RootDatabase; use ide_db::assists::ExprFillDefaultMode; use ide_db::syntax_helpers::suggest_name; @@ -401,7 +401,7 @@ impl ExtendedVariant { fn should_be_hidden(self, db: &RootDatabase, krate: Crate) -> bool { match self { ExtendedVariant::Variant { variant: var, .. } => { - var.attrs(db).has_doc_hidden() && var.module(db).krate() != krate + var.attrs(db).is_doc_hidden() && var.module(db).krate() != krate } _ => false, } @@ -424,7 +424,7 @@ impl ExtendedEnum { fn is_non_exhaustive(&self, db: &RootDatabase, krate: Crate) -> bool { match self { ExtendedEnum::Enum { enum_: e, .. } => { - e.attrs(db).by_key(sym::non_exhaustive).exists() && e.module(db).krate() != krate + e.attrs(db).is_non_exhaustive() && e.module(db).krate() != krate } _ => false, } 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 8b24d33bf996..46f210804da3 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 @@ -1,4 +1,4 @@ -use hir::{HasVisibility, sym}; +use hir::HasVisibility; use ide_db::{ FxHashMap, FxHashSet, assists::AssistId, @@ -93,7 +93,7 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option) -> let mut buf = String::from("./"); let db = ctx.db(); match parent_module.name(db) { - Some(name) - if !parent_module.is_mod_rs(db) - && parent_module - .attrs(db) - .by_key(sym::path) - .string_value_unescape() - .is_none() => - { + Some(name) if !parent_module.is_mod_rs(db) && !parent_module.has_path(db) => { format_to!(buf, "{}/", name.as_str()) } _ => (), 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 2977f8b8c2e7..eb7553222a68 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -68,7 +68,7 @@ pub mod utils; use hir::Semantics; use ide_db::{EditionedFileId, RootDatabase}; -use syntax::{Edition, TextRange}; +use syntax::TextRange; pub(crate) use crate::assist_context::{AssistContext, Assists}; @@ -90,7 +90,7 @@ pub fn assists( let sema = Semantics::new(db); let file_id = sema .attach_first_edition(range.file_id) - .unwrap_or_else(|| EditionedFileId::new(db, range.file_id, Edition::CURRENT)); + .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, range.file_id)); let ctx = AssistContext::new(sema, config, hir::FileRange { file_id, range: range.range }); let mut acc = Assists::new(&ctx, resolve); handlers::all().iter().for_each(|handler| { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index ade60691b57b..2e220b129fe1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -321,11 +321,13 @@ fn check_with_config( let _tracing = setup_tracing(); let (mut db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before); db.enable_proc_attr_macros(); + let sema = Semantics::new(&db); + let file_with_caret_id = + sema.attach_first_edition(file_with_caret_id.file_id(&db)).unwrap_or(file_with_caret_id); let text_without_caret = db.file_text(file_with_caret_id.file_id(&db)).text(&db).to_string(); let frange = hir::FileRange { file_id: file_with_caret_id, range: range_or_offset.into() }; - let sema = Semantics::new(&db); let ctx = AssistContext::new(sema, &config, frange); let resolve = match expected { ExpectedResult::Unresolved => AssistResolveStrategy::None, 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 5a3c5a39dac7..7a86339c1c9c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -93,16 +93,7 @@ pub fn test_related_attribute_syn(fn_def: &ast::Fn) -> Option { } pub fn has_test_related_attribute(attrs: &hir::AttrsWithOwner) -> bool { - attrs.iter().any(|attr| { - let path = attr.path(); - (|| { - Some( - path.segments().first()?.as_str().starts_with("test") - || path.segments().last()?.as_str().ends_with("test"), - ) - })() - .unwrap_or_default() - }) + attrs.is_test() } #[derive(Clone, Copy, PartialEq)] @@ -128,7 +119,7 @@ pub fn filter_assoc_items( .copied() .filter(|assoc_item| { if ignore_items == IgnoreAssocItems::DocHiddenAttrPresent - && assoc_item.attrs(sema.db).has_doc_hidden() + && assoc_item.attrs(sema.db).is_doc_hidden() { if let hir::AssocItem::Function(f) = assoc_item && !f.has_body(sema.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 c87c46d98127..df577b8ed02e 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 @@ -56,7 +56,7 @@ pub(super) fn complete_lint( }; let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label, ctx.edition); - item.documentation(Documentation::new(description.to_owned())); + item.documentation(Documentation::new_owned(description.to_owned())); item.add_to(acc, ctx.db) } } 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 d1e05a4359f1..20d01485a45a 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 @@ -266,7 +266,7 @@ fn import_on_the_fly( let original_item = &import.original_item; !ctx.is_item_hidden(&import.item_to_import) && !ctx.is_item_hidden(original_item) - && ctx.check_stability(original_item.attrs(ctx.db).as_deref()) + && ctx.check_stability(original_item.attrs(ctx.db).as_ref()) }) .filter(|import| filter_excluded_flyimport(ctx, import)) .sorted_by(|a, b| { @@ -313,7 +313,7 @@ fn import_on_the_fly_pat_( let original_item = &import.original_item; !ctx.is_item_hidden(&import.item_to_import) && !ctx.is_item_hidden(original_item) - && ctx.check_stability(original_item.attrs(ctx.db).as_deref()) + && ctx.check_stability(original_item.attrs(ctx.db).as_ref()) }) .sorted_by(|a, b| { let key = |import_path| { 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 73cbe3f0aaab..4474d6181c20 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 @@ -440,7 +440,7 @@ fn add_custom_postfix_completions( let body = snippet.postfix_snippet(receiver_text); let mut builder = postfix_snippet(trigger, snippet.description.as_deref().unwrap_or_default(), &body); - builder.documentation(Documentation::new(format!("```rust\n{body}\n```"))); + builder.documentation(Documentation::new_owned(format!("```rust\n{body}\n```"))); for import in imports.into_iter() { builder.add_import(import); } 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 ead9852eff53..04450aea75bf 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 @@ -139,7 +139,7 @@ fn add_custom_completions( }; let body = snip.snippet(); let mut builder = snippet(ctx, cap, trigger, &body); - builder.documentation(Documentation::new(format!("```rust\n{body}\n```"))); + builder.documentation(Documentation::new_owned(format!("```rust\n{body}\n```"))); for import in imports.into_iter() { builder.add_import(import); } 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 fc2cc3b796ec..c95b83ef8a02 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -558,7 +558,7 @@ impl CompletionContext<'_> { I: hir::HasAttrs + Copy, { let attrs = item.attrs(self.db); - attrs.doc_aliases().map(|it| it.as_str().into()).collect() + attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect() } /// Check if an item is `#[doc(hidden)]`. @@ -572,7 +572,7 @@ impl CompletionContext<'_> { } /// Checks whether this item should be listed in regards to stability. Returns `true` if we should. - pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool { + pub(crate) fn check_stability(&self, attrs: Option<&hir::AttrsWithOwner>) -> bool { let Some(attrs) = attrs else { return true; }; @@ -590,15 +590,15 @@ impl CompletionContext<'_> { /// Whether the given trait is an operator trait or not. pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool { - match trait_.attrs(self.db).lang() { - Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()), + match trait_.attrs(self.db).lang(self.db) { + Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang), None => false, } } /// Whether the given trait has `#[doc(notable_trait)]` pub(crate) fn is_doc_notable_trait(&self, trait_: hir::Trait) -> bool { - trait_.attrs(self.db).has_doc_notable_trait() + trait_.attrs(self.db).is_doc_notable_trait() } /// Returns the traits in scope, with the [`Drop`] trait removed. @@ -662,7 +662,7 @@ impl CompletionContext<'_> { fn is_visible_impl( &self, vis: &hir::Visibility, - attrs: &hir::Attrs, + attrs: &hir::AttrsWithOwner, defining_crate: hir::Crate, ) -> Visible { if !self.check_stability(Some(attrs)) { @@ -684,14 +684,18 @@ impl CompletionContext<'_> { if self.is_doc_hidden(attrs, defining_crate) { Visible::No } else { Visible::Yes } } - pub(crate) fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool { + pub(crate) fn is_doc_hidden( + &self, + attrs: &hir::AttrsWithOwner, + defining_crate: hir::Crate, + ) -> bool { // `doc(hidden)` items are only completed within the defining crate. - self.krate != defining_crate && attrs.has_doc_hidden() + self.krate != defining_crate && attrs.is_doc_hidden() } pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec { if let Some(attrs) = scope_def.attrs(self.db) { - attrs.doc_aliases().map(|it| it.as_str().into()).collect() + attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect() } else { vec![] } 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 303c71230d60..c526c7f070bf 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -57,7 +57,8 @@ pub struct CompletionItem { /// Additional info to show in the UI pop up. pub detail: Option, - pub documentation: Option, + // FIXME: Make this with `'db` lifetime. + pub documentation: Option>, /// Whether this item is marked as deprecated pub deprecated: bool, @@ -488,7 +489,8 @@ pub(crate) struct Builder { insert_text: Option, is_snippet: bool, detail: Option, - documentation: Option, + // FIXME: Make this with `'db` lifetime. + documentation: Option>, lookup: Option, kind: CompletionItemKind, text_edit: Option, @@ -644,11 +646,11 @@ impl Builder { self } #[allow(unused)] - pub(crate) fn documentation(&mut self, docs: Documentation) -> &mut Builder { + pub(crate) fn documentation(&mut self, docs: Documentation<'_>) -> &mut Builder { self.set_documentation(Some(docs)) } - pub(crate) fn set_documentation(&mut self, docs: Option) -> &mut Builder { - self.documentation = docs; + pub(crate) fn set_documentation(&mut self, docs: Option>) -> &mut Builder { + self.documentation = docs.map(Documentation::into_owned); self } pub(crate) fn set_deprecated(&mut self, deprecated: bool) -> &mut Builder { 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 094e679501fc..77a2a3a3a9a0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -10,7 +10,7 @@ pub(crate) mod type_alias; pub(crate) mod union_literal; pub(crate) mod variant; -use hir::{AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type, sym}; +use hir::{AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type}; use ide_db::text_edit::TextEdit; use ide_db::{ RootDatabase, SnippetCap, SymbolKind, @@ -91,8 +91,7 @@ impl<'a> RenderContext<'a> { } fn is_deprecated(&self, def: impl HasAttrs) -> bool { - let attrs = def.attrs(self.db()); - attrs.by_key(sym::deprecated).exists() + def.attrs(self.db()).is_deprecated() } fn is_deprecated_assoc_item(&self, as_assoc_item: impl AsAssocItem) -> bool { @@ -115,7 +114,7 @@ impl<'a> RenderContext<'a> { } // FIXME: remove this - fn docs(&self, def: impl HasDocs) -> Option { + fn docs(&self, def: impl HasDocs) -> Option> { def.docs(self.db()) } } @@ -321,7 +320,9 @@ pub(crate) fn render_expr( ); let edit = TextEdit::replace(source_range, snippet); item.snippet_edit(ctx.config.snippet_cap?, edit); - item.documentation(Documentation::new(String::from("Autogenerated expression by term search"))); + item.documentation(Documentation::new_owned(String::from( + "Autogenerated expression by term search", + ))); item.set_relevance(crate::CompletionRelevance { type_match: compute_type_match(ctx, &expr.ty(ctx.db)), ..Default::default() @@ -554,7 +555,7 @@ fn res_to_kind(resolution: ScopeDef) -> CompletionItemKind { } } -fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option { +fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option> { use hir::ModuleDef::*; match resolution { ScopeDef::ModuleDef(Module(it)) => it.docs(db), 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 6c89e49f94e8..8b14f05b72b2 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 @@ -180,7 +180,7 @@ impl Variant { } } - fn docs(self, db: &dyn HirDatabase) -> Option { + fn docs(self, db: &dyn HirDatabase) -> Option> { match self { Variant::Struct(it) => it.docs(db), Variant::EnumVariant(it) => it.docs(db), 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 312d3bd426f9..60474a31b4d3 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 @@ -108,7 +108,7 @@ fn build_completion( label: SmolStr, lookup: SmolStr, pat: String, - def: impl HasDocs + Copy, + def: impl HasDocs, adt_ty: hir::Type<'_>, // Missing in context of match statement completions is_variant_missing: bool, 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 42324b4290a7..967b9091b5ca 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 @@ -1,7 +1,7 @@ //! Code common to structs, unions, and enum variants. use crate::context::CompletionContext; -use hir::{HasAttrs, HasCrate, HasVisibility, HirDisplay, StructKind, sym}; +use hir::{HasAttrs, HasCrate, HasVisibility, HirDisplay, StructKind}; use ide_db::SnippetCap; use itertools::Itertools; use syntax::SmolStr; @@ -96,8 +96,8 @@ pub(crate) fn visible_fields( .copied() .collect::>(); let has_invisible_field = n_fields - fields.len() > 0; - let is_foreign_non_exhaustive = item.attrs(ctx.db).by_key(sym::non_exhaustive).exists() - && item.krate(ctx.db) != module.krate(); + let is_foreign_non_exhaustive = + item.attrs(ctx.db).is_non_exhaustive() && item.krate(ctx.db) != module.krate(); let fields_omitted = has_invisible_field || is_foreign_non_exhaustive; Some((fields, fields_omitted)) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index b32a89545726..36d739455030 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -160,12 +160,12 @@ pub(crate) fn position( #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> (RootDatabase, FilePosition) { let mut database = RootDatabase::default(); - let change_fixture = ChangeFixture::parse(&database, ra_fixture); + let change_fixture = ChangeFixture::parse(ra_fixture); database.enable_proc_attr_macros(); database.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let offset = range_or_offset.expect_offset(); - let position = FilePosition { file_id: file_id.file_id(&database), offset }; + let position = FilePosition { file_id: file_id.file_id(), offset }; (database, position) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index c051fd863de6..9ce85b2bf330 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -5,8 +5,10 @@ // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). +use std::borrow::Cow; + use crate::RootDatabase; -use crate::documentation::{DocsRangeMap, Documentation, HasDocs}; +use crate::documentation::{Documentation, HasDocs}; use crate::famous_defs::FamousDefs; use arrayvec::ArrayVec; use either::Either; @@ -21,7 +23,7 @@ use hir::{ use span::Edition; use stdx::{format_to, impl_from}; use syntax::{ - SyntaxKind, SyntaxNode, SyntaxToken, TextSize, + SyntaxKind, SyntaxNode, SyntaxToken, ast::{self, AstNode}, match_ast, }; @@ -199,21 +201,25 @@ impl Definition { Some(name) } - pub fn docs( + pub fn docs<'db>( &self, - db: &RootDatabase, + db: &'db RootDatabase, famous_defs: Option<&FamousDefs<'_, '_>>, display_target: DisplayTarget, - ) -> Option { - self.docs_with_rangemap(db, famous_defs, display_target).map(|(docs, _)| docs) + ) -> Option> { + self.docs_with_rangemap(db, famous_defs, display_target).map(|docs| match docs { + Either::Left(Cow::Borrowed(docs)) => Documentation::new_borrowed(docs.docs()), + Either::Left(Cow::Owned(docs)) => Documentation::new_owned(docs.into_docs()), + Either::Right(docs) => docs, + }) } - pub fn docs_with_rangemap( + pub fn docs_with_rangemap<'db>( &self, - db: &RootDatabase, + db: &'db RootDatabase, famous_defs: Option<&FamousDefs<'_, '_>>, display_target: DisplayTarget, - ) -> Option<(Documentation, Option)> { + ) -> Option, Documentation<'db>>> { let docs = match self { Definition::Macro(it) => it.docs_with_rangemap(db), Definition::Field(it) => it.docs_with_rangemap(db), @@ -229,15 +235,13 @@ impl Definition { it.docs_with_rangemap(db).or_else(|| { // docs are missing, try to fall back to the docs of the aliased item. let adt = it.ty(db).as_adt()?; - let (docs, range_map) = adt.docs_with_rangemap(db)?; + let mut docs = adt.docs_with_rangemap(db)?.into_owned(); let header_docs = format!( "*This is the documentation for* `{}`\n\n", adt.display(db, display_target) ); - let offset = TextSize::new(header_docs.len() as u32); - let range_map = range_map.shift_docstring_line_range(offset); - let docs = header_docs + docs.as_str(); - Some((Documentation::new(docs), range_map)) + docs.prepend_str(&header_docs); + Some(Cow::Owned(docs)) }) } Definition::BuiltinType(it) => { @@ -246,7 +250,7 @@ impl Definition { let primitive_mod = format!("prim_{}", it.name().display(fd.0.db, display_target.edition)); let doc_owner = find_std_module(fd, &primitive_mod, display_target.edition)?; - doc_owner.docs_with_rangemap(fd.0.db) + doc_owner.docs_with_rangemap(db) }) } Definition::BuiltinLifetime(StaticLifetime) => None, @@ -282,7 +286,7 @@ impl Definition { ); } - return Some((Documentation::new(docs.replace('*', "\\*")), None)); + return Some(Either::Right(Documentation::new_owned(docs.replace('*', "\\*")))); } Definition::ToolModule(_) => None, Definition::DeriveHelper(_) => None, @@ -299,7 +303,7 @@ impl Definition { let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?; item.docs_with_rangemap(db) }) - .map(|(docs, range_map)| (docs, Some(range_map))) + .map(Either::Left) } pub fn label(&self, db: &RootDatabase, display_target: DisplayTarget) -> String { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs index cab19aadfd01..4c4691cca2ca 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs @@ -1,337 +1,100 @@ //! Documentation attribute related utilities. -use either::Either; -use hir::{ - AttrId, AttrSourceMap, AttrsWithOwner, HasAttrs, InFile, - db::{DefDatabase, HirDatabase}, - resolve_doc_path_on, sym, -}; -use itertools::Itertools; -use span::{TextRange, TextSize}; -use syntax::{ - AstToken, - ast::{self, IsString}, -}; +use std::borrow::Cow; + +use hir::{HasAttrs, db::HirDatabase, resolve_doc_path_on}; /// Holds documentation #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Documentation(String); +pub struct Documentation<'db>(Cow<'db, str>); -impl Documentation { - pub fn new(s: String) -> Self { - Documentation(s) +impl<'db> Documentation<'db> { + #[inline] + pub fn new_owned(s: String) -> Self { + Documentation(Cow::Owned(s)) } + #[inline] + pub fn new_borrowed(s: &'db str) -> Self { + Documentation(Cow::Borrowed(s)) + } + + #[inline] + pub fn into_owned(self) -> Documentation<'static> { + Documentation::new_owned(self.0.into_owned()) + } + + #[inline] pub fn as_str(&self) -> &str { &self.0 } } -impl From for String { - fn from(Documentation(string): Documentation) -> Self { - string +pub trait HasDocs: HasAttrs + Copy { + fn docs(self, db: &dyn HirDatabase) -> Option> { + let docs = match self.docs_with_rangemap(db)? { + Cow::Borrowed(docs) => Documentation::new_borrowed(docs.docs()), + Cow::Owned(docs) => Documentation::new_owned(docs.into_docs()), + }; + Some(docs) } -} - -pub trait HasDocs: HasAttrs { - fn docs(self, db: &dyn HirDatabase) -> Option; - fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)>; - fn resolve_doc_path( - self, - db: &dyn HirDatabase, - link: &str, - ns: Option, - is_inner_doc: bool, - ) -> Option; -} -/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree. -#[derive(Debug)] -pub struct DocsRangeMap { - source_map: AttrSourceMap, - // (docstring-line-range, attr_index, attr-string-range) - // a mapping from the text range of a line of the [`Documentation`] to the attribute index and - // the original (untrimmed) syntax doc line - mapping: Vec<(TextRange, AttrId, TextRange)>, -} - -impl DocsRangeMap { - /// Maps a [`TextRange`] relative to the documentation string back to its AST range - pub fn map(&self, range: TextRange) -> Option<(InFile, AttrId)> { - let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?; - let (line_docs_range, idx, original_line_src_range) = self.mapping[found]; - if !line_docs_range.contains_range(range) { - return None; - } - - let relative_range = range - line_docs_range.start(); - - let InFile { file_id, value: source } = self.source_map.source_of_id(idx); - match source { - Either::Left(attr) => { - let string = get_doc_string_in_attr(attr)?; - let text_range = string.open_quote_text_range()?; - let range = TextRange::at( - text_range.end() + original_line_src_range.start() + relative_range.start(), - string.syntax().text_range().len().min(range.len()), - ); - Some((InFile { file_id, value: range }, idx)) - } - Either::Right(comment) => { - let text_range = comment.syntax().text_range(); - let range = TextRange::at( - text_range.start() - + TextSize::try_from(comment.prefix().len()).ok()? - + original_line_src_range.start() - + relative_range.start(), - text_range.len().min(range.len()), - ); - Some((InFile { file_id, value: range }, idx)) - } - } - } - - pub fn shift_docstring_line_range(self, offset: TextSize) -> DocsRangeMap { - let mapping = self - .mapping - .into_iter() - .map(|(buf_offset, id, base_offset)| { - let buf_offset = buf_offset.checked_add(offset).unwrap(); - (buf_offset, id, base_offset) - }) - .collect_vec(); - DocsRangeMap { source_map: self.source_map, mapping } - } -} - -pub fn docs_with_rangemap( - db: &dyn DefDatabase, - attrs: &AttrsWithOwner, -) -> Option<(Documentation, DocsRangeMap)> { - let docs = attrs - .by_key(sym::doc) - .attrs() - .filter_map(|attr| attr.string_value_unescape().map(|s| (s, attr.id))); - let indent = doc_indent(attrs); - let mut buf = String::new(); - let mut mapping = Vec::new(); - for (doc, idx) in docs { - if !doc.is_empty() { - let mut base_offset = 0; - for raw_line in doc.split('\n') { - let line = raw_line.trim_end(); - let line_len = line.len(); - let (offset, line) = match line.char_indices().nth(indent) { - Some((offset, _)) => (offset, &line[offset..]), - None => (0, line), - }; - let buf_offset = buf.len(); - buf.push_str(line); - mapping.push(( - TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?), - idx, - TextRange::at( - (base_offset + offset).try_into().ok()?, - line_len.try_into().ok()?, - ), - )); - buf.push('\n'); - base_offset += raw_line.len() + 1; - } - } else { - buf.push('\n'); - } - } - buf.pop(); - if buf.is_empty() { - None - } else { - Some((Documentation(buf), DocsRangeMap { mapping, source_map: attrs.source_map(db) })) - } -} - -pub fn docs_from_attrs(attrs: &hir::Attrs) -> Option { - let docs = attrs.by_key(sym::doc).attrs().filter_map(|attr| attr.string_value_unescape()); - let indent = doc_indent(attrs); - let mut buf = String::new(); - for doc in docs { - // str::lines doesn't yield anything for the empty string - if !doc.is_empty() { - // We don't trim trailing whitespace from doc comments as multiple trailing spaces - // indicates a hard line break in Markdown. - let lines = doc.lines().map(|line| { - line.char_indices().nth(indent).map_or(line, |(offset, _)| &line[offset..]) - }); - - buf.extend(Itertools::intersperse(lines, "\n")); - } - buf.push('\n'); - } - buf.pop(); - if buf.is_empty() { None } else { Some(buf) } -} - -macro_rules! impl_has_docs { - ($($def:ident,)*) => {$( - impl HasDocs for hir::$def { - fn docs(self, db: &dyn HirDatabase) -> Option { - docs_from_attrs(&self.attrs(db)).map(Documentation) - } - fn docs_with_rangemap( - self, - db: &dyn HirDatabase, - ) -> Option<(Documentation, DocsRangeMap)> { - docs_with_rangemap(db, &self.attrs(db)) - } - fn resolve_doc_path( - self, - db: &dyn HirDatabase, - link: &str, - ns: Option, - is_inner_doc: bool, - ) -> Option { - resolve_doc_path_on(db, self, link, ns, is_inner_doc) - } - } - )*}; -} - -impl_has_docs![ - Variant, Field, Static, Const, Trait, TypeAlias, Macro, Function, Adt, Module, Impl, Crate, -]; - -macro_rules! impl_has_docs_enum { - ($($variant:ident),* for $enum:ident) => {$( - impl HasDocs for hir::$variant { - fn docs(self, db: &dyn HirDatabase) -> Option { - hir::$enum::$variant(self).docs(db) - } - - fn docs_with_rangemap( - self, - db: &dyn HirDatabase, - ) -> Option<(Documentation, DocsRangeMap)> { - hir::$enum::$variant(self).docs_with_rangemap(db) - } - fn resolve_doc_path( - self, - db: &dyn HirDatabase, - link: &str, - ns: Option, - is_inner_doc: bool, - ) -> Option { - hir::$enum::$variant(self).resolve_doc_path(db, link, ns, is_inner_doc) - } - } - )*}; -} - -impl_has_docs_enum![Struct, Union, Enum for Adt]; - -impl HasDocs for hir::AssocItem { - fn docs(self, db: &dyn HirDatabase) -> Option { - match self { - hir::AssocItem::Function(it) => it.docs(db), - hir::AssocItem::Const(it) => it.docs(db), - hir::AssocItem::TypeAlias(it) => it.docs(db), - } - } - - fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)> { - match self { - hir::AssocItem::Function(it) => it.docs_with_rangemap(db), - hir::AssocItem::Const(it) => it.docs_with_rangemap(db), - hir::AssocItem::TypeAlias(it) => it.docs_with_rangemap(db), - } - } - - fn resolve_doc_path( - self, - db: &dyn HirDatabase, - link: &str, - ns: Option, - is_inner_doc: bool, - ) -> Option { - match self { - hir::AssocItem::Function(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), - hir::AssocItem::Const(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), - hir::AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), - } - } -} - -impl HasDocs for hir::ExternCrateDecl { - fn docs(self, db: &dyn HirDatabase) -> Option { - let crate_docs = docs_from_attrs(&self.resolved_crate(db)?.root_module().attrs(db)); - let decl_docs = docs_from_attrs(&self.attrs(db)); - match (decl_docs, crate_docs) { - (None, None) => None, - (Some(decl_docs), None) => Some(decl_docs), - (None, Some(crate_docs)) => Some(crate_docs), - (Some(mut decl_docs), Some(crate_docs)) => { - decl_docs.push('\n'); - decl_docs.push('\n'); - decl_docs += &crate_docs; - Some(decl_docs) - } - } - .map(Documentation::new) - } - - fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)> { - let crate_docs = docs_with_rangemap(db, &self.resolved_crate(db)?.root_module().attrs(db)); - let decl_docs = docs_with_rangemap(db, &self.attrs(db)); - match (decl_docs, crate_docs) { - (None, None) => None, - (Some(decl_docs), None) => Some(decl_docs), - (None, Some(crate_docs)) => Some(crate_docs), - ( - Some((Documentation(mut decl_docs), mut decl_range_map)), - Some((Documentation(crate_docs), crate_range_map)), - ) => { - decl_docs.push('\n'); - decl_docs.push('\n'); - let offset = TextSize::new(decl_docs.len() as u32); - decl_docs += &crate_docs; - let crate_range_map = crate_range_map.shift_docstring_line_range(offset); - decl_range_map.mapping.extend(crate_range_map.mapping); - Some((Documentation(decl_docs), decl_range_map)) - } - } + fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option> { + self.hir_docs(db).map(Cow::Borrowed) } fn resolve_doc_path( self, db: &dyn HirDatabase, link: &str, ns: Option, - is_inner_doc: bool, + is_inner_doc: hir::IsInnerDoc, ) -> Option { resolve_doc_path_on(db, self, link, ns, is_inner_doc) } } -fn get_doc_string_in_attr(it: &ast::Attr) -> Option { - match it.expr() { - // #[doc = lit] - Some(ast::Expr::Literal(lit)) => match lit.kind() { - ast::LiteralKind::String(it) => Some(it), - _ => None, - }, - // #[cfg_attr(..., doc = "", ...)] - None => { - // FIXME: See highlight injection for what to do here - None - } - _ => None, - } +macro_rules! impl_has_docs { + ($($def:ident,)*) => {$( + impl HasDocs for hir::$def {} + )*}; } -fn doc_indent(attrs: &hir::Attrs) -> usize { - let mut min = !0; - for val in attrs.by_key(sym::doc).attrs().filter_map(|attr| attr.string_value_unescape()) { - if let Some(m) = - val.lines().filter_map(|line| line.chars().position(|c| !c.is_whitespace())).min() - { - min = min.min(m); +impl_has_docs![ + Variant, Field, Static, Const, Trait, TypeAlias, Macro, Function, Adt, Module, Impl, Crate, + AssocItem, Struct, Union, Enum, +]; + +impl HasDocs for hir::ExternCrateDecl { + fn docs(self, db: &dyn HirDatabase) -> Option> { + let crate_docs = self.resolved_crate(db)?.hir_docs(db); + let decl_docs = self.hir_docs(db); + match (decl_docs, crate_docs) { + (None, None) => None, + (Some(docs), None) | (None, Some(docs)) => { + Some(Documentation::new_borrowed(docs.docs())) + } + (Some(decl_docs), Some(crate_docs)) => { + let mut docs = String::with_capacity( + decl_docs.docs().len() + "\n\n".len() + crate_docs.docs().len(), + ); + docs.push_str(decl_docs.docs()); + docs.push_str("\n\n"); + docs.push_str(crate_docs.docs()); + Some(Documentation::new_owned(docs)) + } + } + } + + fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option> { + let crate_docs = self.resolved_crate(db)?.hir_docs(db); + let decl_docs = self.hir_docs(db); + match (decl_docs, crate_docs) { + (None, None) => None, + (Some(docs), None) | (None, Some(docs)) => Some(Cow::Borrowed(docs)), + (Some(decl_docs), Some(crate_docs)) => { + let mut docs = decl_docs.clone(); + docs.append_str("\n\n"); + docs.append(crate_docs); + Some(Cow::Owned(docs)) + } } } - min } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs b/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs index 1f056a835bc6..cd86e7765196 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs @@ -25,18 +25,14 @@ impl RootDatabase { // We don't want a mistake in the fixture to crash r-a, so we wrap this in `catch_unwind()`. std::panic::catch_unwind(|| { let mut db = RootDatabase::default(); - let fixture = test_fixture::ChangeFixture::parse_with_proc_macros( - &db, - text, - minicore.0, - Vec::new(), - ); + let fixture = + test_fixture::ChangeFixture::parse_with_proc_macros(text, minicore.0, Vec::new()); db.apply_change(fixture.change); let files = fixture .files .into_iter() .zip(fixture.file_lines) - .map(|(file_id, range)| (file_id.file_id(&db), range)) + .map(|(file_id, range)| (file_id.file_id(), range)) .collect(); (db, files, fixture.sysroot_files) }) @@ -525,7 +521,7 @@ impl_empty_upmap_from_ra_fixture!( &str, String, SmolStr, - Documentation, + Documentation<'_>, SymbolKind, CfgExpr, ReferenceCategory, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs b/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs index eacd9b9b4d2f..36a6938af6b8 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs @@ -33,7 +33,7 @@ pub fn is_rust_fence(s: &str) -> bool { const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"]; -pub fn format_docs(src: &Documentation) -> String { +pub fn format_docs(src: &Documentation<'_>) -> String { format_docs_(src.as_str()) } 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 f1d076e874d5..8b53cea7e6d3 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -12,7 +12,7 @@ use either::Either; use hir::{ Adt, AsAssocItem, DefWithBody, EditionedFileId, FileRange, FileRangeWrapper, HasAttrs, HasContainer, HasSource, InFile, InFileWrapper, InRealFile, InlineAsmOperand, ItemContainer, - ModuleSource, PathResolution, Semantics, Visibility, sym, + ModuleSource, PathResolution, Semantics, Visibility, }; use memchr::memmem::Finder; use parser::SyntaxKind; @@ -169,7 +169,7 @@ impl SearchScope { entries.extend( source_root .iter() - .map(|id| (EditionedFileId::new(db, id, crate_data.edition), None)), + .map(|id| (EditionedFileId::new(db, id, crate_data.edition, krate), None)), ); } SearchScope { entries } @@ -183,11 +183,9 @@ impl SearchScope { let source_root = db.file_source_root(root_file).source_root_id(db); let source_root = db.source_root(source_root).source_root(db); - entries.extend( - source_root - .iter() - .map(|id| (EditionedFileId::new(db, id, rev_dep.edition(db)), None)), - ); + entries.extend(source_root.iter().map(|id| { + (EditionedFileId::new(db, id, rev_dep.edition(db), rev_dep.into()), None) + })); } SearchScope { entries } } @@ -201,7 +199,7 @@ impl SearchScope { SearchScope { entries: source_root .iter() - .map(|id| (EditionedFileId::new(db, id, of.edition(db)), None)) + .map(|id| (EditionedFileId::new(db, id, of.edition(db), of.into()), None)) .collect(), } } @@ -368,7 +366,7 @@ impl Definition { if let Definition::Macro(macro_def) = self { return match macro_def.kind(db) { hir::MacroKind::Declarative => { - if macro_def.attrs(db).by_key(sym::macro_export).exists() { + if macro_def.attrs(db).is_macro_export() { SearchScope::reverse_dependencies(db, module.krate()) } else { SearchScope::krate(db, module.krate()) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt index 30d1df4f8e55..427a51055948 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt @@ -3,7 +3,7 @@ Module { id: ModuleId { krate: Crate( - Id(3000), + Id(2c00), ), block: None, local_id: Idx::(0), @@ -16,7 +16,7 @@ Struct( Struct { id: StructId( - 3401, + 3801, ), }, ), @@ -24,7 +24,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -50,7 +50,7 @@ Struct( Struct { id: StructId( - 3400, + 3800, ), }, ), @@ -58,7 +58,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -84,7 +84,7 @@ Struct( Struct { id: StructId( - 3400, + 3800, ), }, ), @@ -92,7 +92,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -118,7 +118,7 @@ Struct( Struct { id: StructId( - 3400, + 3800, ), }, ), @@ -126,7 +126,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -152,7 +152,7 @@ Struct( Struct { id: StructId( - 3400, + 3800, ), }, ), @@ -160,7 +160,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -186,7 +186,7 @@ Struct( Struct { id: StructId( - 3401, + 3801, ), }, ), @@ -194,7 +194,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -220,7 +220,7 @@ Struct( Struct { id: StructId( - 3400, + 3800, ), }, ), @@ -228,7 +228,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 973256c470f3..ce93fa59e258 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -3,7 +3,7 @@ Module { id: ModuleId { krate: Crate( - Id(3000), + Id(2c00), ), block: None, local_id: Idx::(0), @@ -22,7 +22,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -49,14 +49,14 @@ def: TypeAlias( TypeAlias { id: TypeAliasId( - 6800, + 6c00, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -88,7 +88,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -115,14 +115,14 @@ def: Const( Const { id: ConstId( - 6000, + 6400, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -147,14 +147,14 @@ def: Const( Const { id: ConstId( - 6002, + 6402, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -180,7 +180,7 @@ Enum( Enum { id: EnumId( - 4c00, + 5000, ), }, ), @@ -188,7 +188,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -214,7 +214,7 @@ Macro { id: Macro2Id( Macro2Id( - 4800, + 4c00, ), ), }, @@ -222,7 +222,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -248,7 +248,7 @@ Macro { id: Macro2Id( Macro2Id( - 4800, + 4c00, ), ), }, @@ -256,7 +256,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -281,14 +281,14 @@ def: Static( Static { id: StaticId( - 6400, + 6800, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -314,7 +314,7 @@ Struct( Struct { id: StructId( - 4401, + 4801, ), }, ), @@ -322,7 +322,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -348,7 +348,7 @@ Struct( Struct { id: StructId( - 4400, + 4800, ), }, ), @@ -356,7 +356,7 @@ loc: DeclarationLocation { hir_file_id: MacroFile( MacroCallId( - Id(3800), + Id(3c00), ), ), ptr: SyntaxNodePtr { @@ -382,7 +382,7 @@ Struct( Struct { id: StructId( - 4405, + 4805, ), }, ), @@ -390,7 +390,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -418,7 +418,7 @@ Struct( Struct { id: StructId( - 4406, + 4806, ), }, ), @@ -426,7 +426,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -454,7 +454,7 @@ Struct( Struct { id: StructId( - 4407, + 4807, ), }, ), @@ -462,7 +462,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -488,7 +488,7 @@ Struct( Struct { id: StructId( - 4402, + 4802, ), }, ), @@ -496,7 +496,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -521,14 +521,14 @@ def: Trait( Trait { id: TraitId( - 5800, + 5c00, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -554,7 +554,7 @@ Macro { id: Macro2Id( Macro2Id( - 4800, + 4c00, ), ), }, @@ -562,7 +562,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -588,7 +588,7 @@ Union( Union { id: UnionId( - 5000, + 5400, ), }, ), @@ -596,7 +596,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -622,7 +622,7 @@ Module { id: ModuleId { krate: Crate( - Id(3000), + Id(2c00), ), block: None, local_id: Idx::(1), @@ -632,7 +632,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -658,7 +658,7 @@ Module { id: ModuleId { krate: Crate( - Id(3000), + Id(2c00), ), block: None, local_id: Idx::(2), @@ -668,7 +668,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -694,7 +694,7 @@ Macro { id: MacroRulesId( MacroRulesId( - 3401, + 3801, ), ), }, @@ -702,7 +702,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -727,14 +727,14 @@ def: Function( Function { id: FunctionId( - 5c02, + 6002, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -761,14 +761,14 @@ def: Function( Function { id: FunctionId( - 5c01, + 6001, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -796,7 +796,7 @@ Macro { id: MacroRulesId( MacroRulesId( - 3400, + 3800, ), ), }, @@ -804,7 +804,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -829,14 +829,14 @@ def: Function( Function { id: FunctionId( - 5c00, + 6000, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -862,7 +862,7 @@ Macro { id: MacroRulesId( MacroRulesId( - 3401, + 3801, ), ), }, @@ -870,7 +870,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -895,14 +895,14 @@ def: Function( Function { id: FunctionId( - 5c03, + 6003, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -930,7 +930,7 @@ Module { id: ModuleId { krate: Crate( - Id(3000), + Id(2c00), ), block: None, local_id: Idx::(1), @@ -943,7 +943,7 @@ Struct( Struct { id: StructId( - 4403, + 4803, ), }, ), @@ -951,7 +951,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { @@ -977,7 +977,7 @@ Module { id: ModuleId { krate: Crate( - Id(3000), + Id(2c00), ), block: None, local_id: Idx::(2), @@ -989,14 +989,14 @@ def: Trait( Trait { id: TraitId( - 5800, + 5c00, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2001), + Id(3001), ), ), ptr: SyntaxNodePtr { @@ -1022,7 +1022,7 @@ Macro { id: Macro2Id( Macro2Id( - 4800, + 4c00, ), ), }, @@ -1030,7 +1030,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2001), + Id(3001), ), ), ptr: SyntaxNodePtr { @@ -1056,7 +1056,7 @@ Struct( Struct { id: StructId( - 4404, + 4804, ), }, ), @@ -1064,7 +1064,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2001), + Id(3001), ), ), ptr: SyntaxNodePtr { @@ -1090,7 +1090,7 @@ Macro { id: Macro2Id( Macro2Id( - 4800, + 4c00, ), ), }, @@ -1098,7 +1098,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2001), + Id(3001), ), ), ptr: SyntaxNodePtr { @@ -1124,7 +1124,7 @@ Struct( Struct { id: StructId( - 4404, + 4804, ), }, ), @@ -1132,7 +1132,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2001), + Id(3001), ), ), ptr: SyntaxNodePtr { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt index 22872b577f71..3ab837aa613f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt @@ -13,7 +13,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2001), + Id(3001), ), ), ptr: SyntaxNodePtr { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt index 9f98bf87e2e8..a6a808d616a7 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt @@ -13,7 +13,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2001), + Id(3001), ), ), ptr: SyntaxNodePtr { @@ -47,7 +47,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2000), + Id(3000), ), ), ptr: SyntaxNodePtr { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs index 61e28386d072..7b9fdb1e1cf3 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs @@ -114,8 +114,7 @@ fn assoc_item_of_trait( #[cfg(test)] mod tests { use expect_test::{Expect, expect}; - use hir::FilePosition; - use hir::Semantics; + use hir::{EditionedFileId, FilePosition, Semantics}; use span::Edition; use syntax::ast::{self, AstNode}; use test_fixture::ChangeFixture; @@ -127,10 +126,11 @@ mod tests { #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> (RootDatabase, FilePosition) { let mut database = RootDatabase::default(); - let change_fixture = ChangeFixture::parse(&database, ra_fixture); + let change_fixture = ChangeFixture::parse(ra_fixture); database.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); + let file_id = EditionedFileId::from_span_guess_origin(&database, file_id); let offset = range_or_offset.expect_offset(); (database, FilePosition { file_id, offset }) } 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 8611ef653b02..dfa9639f6eb9 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 @@ -95,7 +95,7 @@ fn f() { //^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled #[cfg(no)] #[cfg(no2)] mod m; - //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no and no2 are disabled + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled #[cfg(all(not(a), b))] enum E {} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: b is disabled @@ -130,7 +130,6 @@ trait Bar { /// Tests that `cfg` attributes behind `cfg_attr` is handled properly. #[test] fn inactive_via_cfg_attr() { - cov_mark::check!(cfg_attr_active); check( r#" #[cfg_attr(not(never), cfg(no))] fn f() {} 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 8b708f229d00..9aa7aed16964 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 @@ -8,7 +8,7 @@ pub(crate) fn invalid_derive_target( ctx: &DiagnosticsContext<'_>, d: &hir::InvalidDeriveTarget, ) -> Diagnostic { - let display_range = ctx.sema.diagnostics_display_range(d.node); + let display_range = ctx.sema.diagnostics_display_range_for_range(d.range); Diagnostic::new( DiagnosticCode::RustcHardError("E0774"), @@ -29,7 +29,7 @@ mod tests { //- minicore:derive mod __ { #[derive()] - //^^^^^^^^^^^ error: `derive` may only be applied to `struct`s, `enum`s and `union`s + // ^^^^^^ error: `derive` may only be applied to `struct`s, `enum`s and `union`s fn main() {} } "#, 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 6a1ecae65150..a44b043f433c 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 @@ -13,7 +13,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // 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 { // Use more accurate position if available. - let display_range = ctx.resolve_precise_location(&d.node, d.precise_location); + let display_range = ctx.sema.diagnostics_display_range_for_range(d.range); Diagnostic::new( DiagnosticCode::Ra(d.kind, if d.error { Severity::Error } else { Severity::WeakWarning }), d.message.clone(), @@ -27,8 +27,10 @@ pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> // This diagnostic is shown for macro expansion errors. pub(crate) fn macro_def_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroDefError) -> Diagnostic { // Use more accurate position if available. - let display_range = - ctx.resolve_precise_location(&d.node.map(|it| it.syntax_node_ptr()), d.name); + let display_range = match d.name { + Some(name) => ctx.sema.diagnostics_display_range_for_range(d.node.with_value(name)), + None => ctx.sema.diagnostics_display_range(d.node.map(|it| it.syntax_node_ptr())), + }; Diagnostic::new( DiagnosticCode::Ra("macro-def-error", Severity::Error), d.message.clone(), @@ -135,10 +137,12 @@ macro_rules! env { () => {} } #[rustc_builtin_macro] macro_rules! concat { () => {} } - include!(concat!(env!("OUT_DIR"), "/out.rs")); - //^^^^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run - //^^^^^^^^^^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run - //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run + include!(concat!( + // ^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run + env!( + //^^^ error: `OUT_DIR` not set, build scripts may have failed to run + "OUT_DIR"), "/out.rs")); + //^^^^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run "#, ); } @@ -182,7 +186,7 @@ fn main() { //^^^^^^^^^^^^^^^^ error: failed to load file `does not exist` include!(concat!("does ", "not ", "exist")); - //^^^^^^^^^^^^^^^^^^^^^^^^^^ error: failed to load file `does not exist` + // ^^^^^^^^^^^^^^^^^^^^^^^^ error: failed to load file `does not exist` env!(invalid); //^^^^^^^ error: expected string literal @@ -289,7 +293,7 @@ include!("include-me.rs"); //- /include-me.rs /// long doc that pushes the diagnostic range beyond the first file's text length #[err] -//^^^^^^error: unresolved macro `err` + // ^^^ error: unresolved macro `err` mod prim_never {} "#, ); 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 701b30b9b593..7d0c71f4fa7c 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 @@ -7,7 +7,7 @@ pub(crate) fn malformed_derive( ctx: &DiagnosticsContext<'_>, d: &hir::MalformedDerive, ) -> Diagnostic { - let display_range = ctx.sema.diagnostics_display_range(d.node); + let display_range = ctx.sema.diagnostics_display_range_for_range(d.range); Diagnostic::new( DiagnosticCode::RustcHardError("E0777"), @@ -28,7 +28,7 @@ mod tests { //- minicore:derive mod __ { #[derive = "aaaa"] - //^^^^^^^^^^^^^^^^^^ error: malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]` + // ^^^^^^^^^^^^^^^ error: malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]` struct Foo; } "#, 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 a87b8c42ac1d..030c82ca0ba7 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 @@ -8,8 +8,7 @@ pub(crate) fn unresolved_macro_call( ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMacroCall, ) -> Diagnostic { - // Use more accurate position if available. - let display_range = ctx.resolve_precise_location(&d.macro_call, d.precise_location); + let display_range = ctx.sema.diagnostics_display_range_for_range(d.range); let bang = if d.is_bang { "!" } else { "" }; Diagnostic::new( DiagnosticCode::RustcHardError("unresolved-macro-call"), @@ -76,7 +75,7 @@ self::m!(); self::m2!(); r#" mod _test_inner { #![empty_attr] - //^^^^^^^^^^^^^^ error: unresolved macro `empty_attr` + // ^^^^^^^^^^ error: unresolved macro `empty_attr` } "#, ); 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 1530e6465246..5c8f030de4de 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -102,7 +102,7 @@ use ide_db::{ use itertools::Itertools; use syntax::{ AstPtr, Edition, NodeOrToken, SmolStr, SyntaxKind, SyntaxNode, SyntaxNodePtr, T, TextRange, - ast::{self, AstNode, HasAttrs}, + ast::{self, AstNode}, }; // FIXME: Make this an enum @@ -277,31 +277,6 @@ struct DiagnosticsContext<'a> { is_nightly: bool, } -impl DiagnosticsContext<'_> { - fn resolve_precise_location( - &self, - node: &InFile, - precise_location: Option, - ) -> FileRange { - let sema = &self.sema; - (|| { - let precise_location = precise_location?; - let root = sema.parse_or_expand(node.file_id); - match root.covering_element(precise_location) { - syntax::NodeOrToken::Node(it) => Some(sema.original_range(&it)), - syntax::NodeOrToken::Token(it) => { - node.with_value(it).original_file_range_opt(sema.db) - } - } - })() - .map(|frange| ide_db::FileRange { - file_id: frange.file_id.file_id(self.sema.db), - range: frange.range, - }) - .unwrap_or_else(|| sema.diagnostics_display_range(*node)) - } -} - /// Request parser level diagnostics for the given [`FileId`]. pub fn syntax_diagnostics( db: &RootDatabase, @@ -317,7 +292,7 @@ pub fn syntax_diagnostics( let sema = Semantics::new(db); let editioned_file_id = sema .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id)); + .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, file_id)); let (file_id, _) = editioned_file_id.unpack(db); @@ -348,7 +323,7 @@ pub fn semantic_diagnostics( let sema = Semantics::new(db); let editioned_file_id = sema .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id)); + .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, file_id)); let (file_id, edition) = editioned_file_id.unpack(db); let mut res = Vec::new(); @@ -426,7 +401,7 @@ pub fn semantic_diagnostics( Diagnostic::new( DiagnosticCode::SyntaxError, format!("Syntax Error in Expansion: {err}"), - ctx.resolve_precise_location(&d.node.clone(), d.precise_location), + ctx.sema.diagnostics_display_range_for_range(d.range), ) })); continue; @@ -677,7 +652,7 @@ fn find_outline_mod_lint_severity( let lint_groups = lint_groups(&diag.code, edition); lint_attrs( sema, - ast::AnyHasAttrs::cast(module_source_file.value).expect("SourceFile always has attrs"), + &ast::AnyHasAttrs::cast(module_source_file.value).expect("SourceFile always has attrs"), edition, ) .for_each(|(lint, severity)| { @@ -698,7 +673,7 @@ fn lint_severity_at( .ancestors() .filter_map(ast::AnyHasAttrs::cast) .find_map(|ancestor| { - lint_attrs(sema, ancestor, edition) + lint_attrs(sema, &ancestor, edition) .find_map(|(lint, severity)| lint_groups.contains(&lint).then_some(severity)) }) .or_else(|| { @@ -706,13 +681,13 @@ fn lint_severity_at( }) } +// FIXME: Switch this to analysis' `expand_cfg_attr`. fn lint_attrs<'a>( sema: &'a Semantics<'a, RootDatabase>, - ancestor: ast::AnyHasAttrs, + ancestor: &'a ast::AnyHasAttrs, edition: Edition, ) -> impl Iterator + 'a { - ancestor - .attrs_including_inner() + ast::attrs_including_inner(ancestor) .filter_map(|attr| { attr.as_simple_call().and_then(|(name, value)| match &*name { "allow" | "expect" => Some(Either::Left(iter::once((Severity::Allow, value)))), diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs index 181cc74a51d4..de26879c2959 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs @@ -17,7 +17,7 @@ pub fn ssr_from_comment( frange: FileRange, ) -> Option<(MatchFinder<'_>, TextRange)> { let comment = { - let file_id = EditionedFileId::current_edition(db, frange.file_id); + let file_id = EditionedFileId::current_edition_guess_origin(db, frange.file_id); let file = db.parse(file_id); file.tree().syntax().token_at_offset(frange.range.start()).find_map(ast::Comment::cast) diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs index 43ad12c1f699..7b2142a9f348 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs @@ -125,9 +125,9 @@ impl<'db> MatchFinder<'db> { ) -> Result, SsrError> { restrict_ranges.retain(|range| !range.range.is_empty()); let sema = Semantics::new(db); - let file_id = sema - .attach_first_edition(lookup_context.file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(db, lookup_context.file_id)); + let file_id = sema.attach_first_edition(lookup_context.file_id).unwrap_or_else(|| { + EditionedFileId::current_edition_guess_origin(db, lookup_context.file_id) + }); let resolution_scope = resolving::ResolutionScope::new( &sema, hir::FilePosition { file_id, offset: lookup_context.offset }, diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs index 72f857ceda90..d23d22b4e898 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs @@ -135,11 +135,9 @@ impl<'db> MatchFinder<'db> { // seems to get put into a single source root. let mut files = Vec::new(); self.search_files_do(|file_id| { - files.push( - self.sema - .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(self.sema.db, file_id)), - ); + files.push(self.sema.attach_first_edition(file_id).unwrap_or_else(|| { + EditionedFileId::current_edition_guess_origin(self.sema.db, file_id) + })); }); SearchScope::files(&files) } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index c197d559aa89..0ed91cf7f588 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -13,13 +13,13 @@ use stdx::format_to; use url::Url; use hir::{ - Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrsWithOwner, HasAttrs, db::HirDatabase, sym, + Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrsWithOwner, HasAttrs, db::HirDatabase, }; use ide_db::{ RootDatabase, base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, RootQueryDb}, defs::{Definition, NameClass, NameRefClass}, - documentation::{DocsRangeMap, Documentation, HasDocs, docs_with_rangemap}, + documentation::{Documentation, HasDocs}, helpers::pick_best_token, }; use syntax::{ @@ -54,7 +54,7 @@ pub(crate) fn rewrite_links( db: &RootDatabase, markdown: &str, definition: Definition, - range_map: Option, + range_map: Option<&hir::Docs>, ) -> String { let mut cb = broken_link_clone_cb; let doc = Parser::new_with_broken_link_callback(markdown, MARKDOWN_OPTIONS, Some(&mut cb)) @@ -74,9 +74,9 @@ pub(crate) fn rewrite_links( TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap()); let is_inner_doc = range_map .as_ref() - .and_then(|range_map| range_map.map(text_range)) - .map(|(_, attr_id)| attr_id.is_inner_attr()) - .unwrap_or(false); + .and_then(|range_map| range_map.find_ast_range(text_range)) + .map(|(_, is_inner)| is_inner) + .unwrap_or(hir::IsInnerDoc::No); if let Some((target, title)) = rewrite_intra_doc_link(db, definition, target, title, is_inner_doc, link_type) { @@ -187,7 +187,7 @@ pub(crate) fn external_docs( /// Extracts all links from a given markdown text returning the definition text range, link-text /// and the namespace if known. pub(crate) fn extract_definitions_from_docs( - docs: &Documentation, + docs: &Documentation<'_>, ) -> Vec<(TextRange, String, Option)> { Parser::new_with_broken_link_callback( docs.as_str(), @@ -214,7 +214,7 @@ pub(crate) fn resolve_doc_path_for_def( def: Definition, link: &str, ns: Option, - is_inner_doc: bool, + is_inner_doc: hir::IsInnerDoc, ) -> Option { match def { Definition::Module(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), @@ -324,11 +324,11 @@ impl DocCommentToken { let token_start = t.text_range().start(); let abs_in_expansion_offset = token_start + relative_comment_offset + descended_prefix_len; let (attributes, def) = Self::doc_attributes(sema, &node, is_inner)?; - let (docs, doc_mapping) = docs_with_rangemap(sema.db, &attributes)?; + let doc_mapping = attributes.hir_docs(sema.db)?; let (in_expansion_range, link, ns, is_inner) = - extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| { - let (mapped, idx) = doc_mapping.map(range)?; - (mapped.value.contains(abs_in_expansion_offset)).then_some((mapped.value, link, ns, idx.is_inner_attr())) + extract_definitions_from_docs(&Documentation::new_borrowed(doc_mapping.docs())).into_iter().find_map(|(range, link, ns)| { + let (mapped, is_inner) = doc_mapping.find_ast_range(range)?; + (mapped.value.contains(abs_in_expansion_offset)).then_some((mapped.value, link, ns, is_inner)) })?; // get the relative range to the doc/attribute in the expansion let in_expansion_relative_range = in_expansion_range - descended_prefix_len - token_start; @@ -416,7 +416,7 @@ fn rewrite_intra_doc_link( def: Definition, target: &str, title: &str, - is_inner_doc: bool, + is_inner_doc: hir::IsInnerDoc, link_type: LinkType, ) -> Option<(String, String)> { let (link, ns) = parse_intra_doc_link(target); @@ -659,14 +659,12 @@ fn filename_and_frag_for_def( Definition::Crate(_) => String::from("index.html"), Definition::Module(m) => match m.name(db) { // `#[doc(keyword = "...")]` is internal used only by rust compiler - Some(name) => { - match m.attrs(db).by_key(sym::doc).find_string_value_in_tt(sym::keyword) { - Some(kw) => { - format!("keyword.{kw}.html") - } - None => format!("{}/index.html", name.as_str()), + Some(name) => match m.doc_keyword(db) { + Some(kw) => { + format!("keyword.{kw}.html") } - } + None => format!("{}/index.html", name.as_str()), + }, None => String::from("index.html"), }, Definition::Trait(t) => { diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 3fd885535a23..34ffc11c4b5f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -1,11 +1,11 @@ -use std::iter; +use std::{borrow::Cow, iter}; use expect_test::{Expect, expect}; use hir::Semantics; use ide_db::{ FilePosition, FileRange, RootDatabase, defs::Definition, - documentation::{DocsRangeMap, Documentation, HasDocs}, + documentation::{Documentation, HasDocs}, }; use itertools::Itertools; use syntax::{AstNode, SyntaxNode, ast, match_ast}; @@ -45,9 +45,9 @@ fn check_external_docs( fn check_rewrite(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let sema = &Semantics::new(&analysis.db); - let (cursor_def, docs, range) = def_under_cursor(sema, &position); + let (cursor_def, docs) = def_under_cursor(sema, &position); let res = - hir::attach_db(sema.db, || rewrite_links(sema.db, docs.as_str(), cursor_def, Some(range))); + hir::attach_db(sema.db, || rewrite_links(sema.db, docs.docs(), cursor_def, Some(&docs))); expect.assert_eq(&res) } @@ -57,33 +57,36 @@ fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let (analysis, position, mut expected) = fixture::annotations(ra_fixture); expected.sort_by_key(key_fn); let sema = &Semantics::new(&analysis.db); - let (cursor_def, docs, range) = def_under_cursor(sema, &position); - let defs = extract_definitions_from_docs(&docs); - let actual: Vec<_> = defs - .into_iter() - .flat_map(|(text_range, link, ns)| { - let attr = range.map(text_range); - let is_inner_attr = attr.map(|(_file, attr)| attr.is_inner_attr()).unwrap_or(false); - let def = hir::attach_db(sema.db, || { - resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr) - .unwrap_or_else(|| panic!("Failed to resolve {link}")) - }); - def.try_to_nav(sema).unwrap().into_iter().zip(iter::repeat(link)) - }) - .map(|(nav_target, link)| { - let range = - FileRange { file_id: nav_target.file_id, range: nav_target.focus_or_full_range() }; - (range, link) - }) - .sorted_by_key(key_fn) - .collect(); - assert_eq!(expected, actual); + hir::attach_db(sema.db, || { + let (cursor_def, docs) = def_under_cursor(sema, &position); + let defs = extract_definitions_from_docs(&Documentation::new_borrowed(docs.docs())); + let actual: Vec<_> = defs + .into_iter() + .flat_map(|(text_range, link, ns)| { + let attr = docs.find_ast_range(text_range); + let is_inner_attr = + attr.map(|(_file, is_inner)| is_inner).unwrap_or(hir::IsInnerDoc::No); + let def = resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr) + .unwrap_or_else(|| panic!("Failed to resolve {link}")); + def.try_to_nav(sema).unwrap().into_iter().zip(iter::repeat(link)) + }) + .map(|(nav_target, link)| { + let range = FileRange { + file_id: nav_target.file_id, + range: nav_target.focus_or_full_range(), + }; + (range, link) + }) + .sorted_by_key(key_fn) + .collect(); + assert_eq!(expected, actual); + }); } -fn def_under_cursor( - sema: &Semantics<'_, RootDatabase>, +fn def_under_cursor<'db>( + sema: &Semantics<'db, RootDatabase>, position: &FilePosition, -) -> (Definition, Documentation, DocsRangeMap) { +) -> (Definition, Cow<'db, hir::Docs>) { let (docs, def) = sema .parse_guess_edition(position.file_id) .syntax() @@ -94,14 +97,14 @@ fn def_under_cursor( .find_map(|it| node_to_def(sema, &it)) .expect("no def found") .unwrap(); - let (docs, range) = docs.expect("no docs found for cursor def"); - (def, docs, range) + let docs = docs.expect("no docs found for cursor def"); + (def, docs) } -fn node_to_def( - sema: &Semantics<'_, RootDatabase>, +fn node_to_def<'db>( + sema: &Semantics<'db, RootDatabase>, node: &SyntaxNode, -) -> Option, Definition)>> { +) -> Option>, Definition)>> { Some(match_ast! { match node { ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Module(def))), diff --git a/src/tools/rust-analyzer/crates/ide/src/fixture.rs b/src/tools/rust-analyzer/crates/ide/src/fixture.rs index fbf89042fae1..1a8591d25dca 100644 --- a/src/tools/rust-analyzer/crates/ide/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/ide/src/fixture.rs @@ -7,10 +7,10 @@ use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; /// Creates analysis for a single file. pub(crate) fn file(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Analysis, FileId) { let mut host = AnalysisHost::default(); - let change_fixture = ChangeFixture::parse(&host.db, ra_fixture); + let change_fixture = ChangeFixture::parse(ra_fixture); host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); - (host.analysis(), change_fixture.files[0].file_id(&host.db)) + (host.analysis(), change_fixture.files[0].file_id()) } /// Creates analysis from a multi-file fixture, returns positions marked with $0. @@ -18,23 +18,23 @@ pub(crate) fn position( #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> (Analysis, FilePosition) { let mut host = AnalysisHost::default(); - let change_fixture = ChangeFixture::parse(&host.db, ra_fixture); + let change_fixture = ChangeFixture::parse(ra_fixture); host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let offset = range_or_offset.expect_offset(); - (host.analysis(), FilePosition { file_id: file_id.file_id(&host.db), offset }) + (host.analysis(), FilePosition { file_id: file_id.file_id(), offset }) } /// Creates analysis for a single file, returns range marked with a pair of $0. pub(crate) fn range(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Analysis, FileRange) { let mut host = AnalysisHost::default(); - let change_fixture = ChangeFixture::parse(&host.db, ra_fixture); + let change_fixture = ChangeFixture::parse(ra_fixture); host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let range = range_or_offset.expect_range(); - (host.analysis(), FileRange { file_id: file_id.file_id(&host.db), range }) + (host.analysis(), FileRange { file_id: file_id.file_id(), range }) } /// Creates analysis for a single file, returns range marked with a pair of $0 or a position marked with $0. @@ -42,11 +42,11 @@ pub(crate) fn range_or_position( #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> (Analysis, FileId, RangeOrOffset) { let mut host = AnalysisHost::default(); - let change_fixture = ChangeFixture::parse(&host.db, ra_fixture); + let change_fixture = ChangeFixture::parse(ra_fixture); host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); - (host.analysis(), file_id.file_id(&host.db), range_or_offset) + (host.analysis(), file_id.file_id(), range_or_offset) } /// Creates analysis from a multi-file fixture, returns positions marked with $0. @@ -54,25 +54,24 @@ pub(crate) fn annotations( #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> (Analysis, FilePosition, Vec<(FileRange, String)>) { let mut host = AnalysisHost::default(); - let change_fixture = ChangeFixture::parse(&host.db, ra_fixture); + let change_fixture = ChangeFixture::parse(ra_fixture); host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let offset = range_or_offset.expect_offset(); - let db = &host.db; let annotations = change_fixture .files .iter() .flat_map(|&file_id| { - let file_text = host.analysis().file_text(file_id.file_id(&host.db)).unwrap(); + let file_text = host.analysis().file_text(file_id.file_id()).unwrap(); let annotations = extract_annotations(&file_text); annotations .into_iter() - .map(move |(range, data)| (FileRange { file_id: file_id.file_id(db), range }, data)) + .map(move |(range, data)| (FileRange { file_id: file_id.file_id(), range }, data)) }) .collect(); - (host.analysis(), FilePosition { file_id: file_id.file_id(&host.db), offset }, annotations) + (host.analysis(), FilePosition { file_id: file_id.file_id(), offset }, annotations) } /// Creates analysis from a multi-file fixture with annotations without $0 @@ -80,20 +79,19 @@ pub(crate) fn annotations_without_marker( #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> (Analysis, Vec<(FileRange, String)>) { let mut host = AnalysisHost::default(); - let change_fixture = ChangeFixture::parse(&host.db, ra_fixture); + let change_fixture = ChangeFixture::parse(ra_fixture); host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); - let db = &host.db; let annotations = change_fixture .files .iter() .flat_map(|&file_id| { - let file_text = host.analysis().file_text(file_id.file_id(db)).unwrap(); + let file_text = host.analysis().file_text(file_id.file_id()).unwrap(); let annotations = extract_annotations(&file_text); annotations .into_iter() - .map(move |(range, data)| (FileRange { file_id: file_id.file_id(db), range }, data)) + .map(move |(range, data)| (FileRange { file_id: file_id.file_id(), range }, data)) }) .collect(); (host.analysis(), annotations) diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs index 875403c4e32a..cc333d66caf3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -355,7 +355,7 @@ trait Bar {} fn test() { #[derive(Copy)] - //^^^^^^^^^^^^^^^ + // ^^^^^^^^^^^^ struct Foo$0; impl Foo {} 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 04ce5a7567f3..f7870032ea28 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -62,7 +62,7 @@ pub(crate) fn highlight_related( let _p = tracing::info_span!("highlight_related").entered(); let file_id = sema .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(sema.db, file_id)); + .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(sema.db, file_id)); let syntax = sema.parse(file_id).syntax().clone(); let token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind { 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 a1eff3aaee78..5bdfb5735658 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -1,5 +1,5 @@ //! Logic for rendering the different hover messages -use std::{env, mem, ops::Not}; +use std::{borrow::Cow, env, mem, ops::Not}; use either::Either; use hir::{ @@ -11,7 +11,7 @@ use hir::{ use ide_db::{ RootDatabase, defs::{Definition, find_std_module}, - documentation::{DocsRangeMap, HasDocs}, + documentation::{Documentation, HasDocs}, famous_defs::FamousDefs, generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, syntax_helpers::prettify_macro_expansion, @@ -278,9 +278,9 @@ pub(super) fn keyword( keyword_hints(sema, token, parent, edition, display_target); let doc_owner = find_std_module(&famous_defs, &keyword_mod, edition)?; - let (docs, range_map) = doc_owner.docs_with_rangemap(sema.db)?; + let docs = doc_owner.docs_with_rangemap(sema.db)?; let (markup, range_map) = - markup(Some(docs.into()), Some(range_map), description, None, None, String::new()); + markup(Some(Either::Left(docs)), description, None, None, String::new()); let markup = process_markup(sema.db, Definition::Module(doc_owner), &markup, range_map, config); Some(HoverResult { markup, actions }) } @@ -370,12 +370,12 @@ pub(super) fn process_markup( db: &RootDatabase, def: Definition, markup: &Markup, - markup_range_map: Option, + markup_range_map: Option, config: &HoverConfig<'_>, ) -> Markup { let markup = markup.as_str(); let markup = if config.links_in_hover { - rewrite_links(db, markup, def, markup_range_map) + rewrite_links(db, markup, def, markup_range_map.as_ref()) } else { remove_links(markup) }; @@ -484,7 +484,7 @@ pub(super) fn definition( config: &HoverConfig<'_>, edition: Edition, display_target: DisplayTarget, -) -> (Markup, Option) { +) -> (Markup, Option) { let mod_path = definition_path(db, &def, edition); let label = match def { Definition::Trait(trait_) => trait_ @@ -520,12 +520,7 @@ pub(super) fn definition( } _ => def.label(db, display_target), }; - let (docs, range_map) = - if let Some((docs, doc_range)) = def.docs_with_rangemap(db, famous_defs, display_target) { - (Some(docs), doc_range) - } else { - (None, None) - }; + let docs = def.docs_with_rangemap(db, famous_defs, display_target); let value = || match def { Definition::Variant(it) => { if !it.parent_enum(db).is_data_carrying(db) { @@ -842,14 +837,7 @@ pub(super) fn definition( } }; - markup( - docs.map(Into::into), - range_map, - desc, - extra.is_empty().not().then_some(extra), - mod_path, - subst_types, - ) + markup(docs, desc, extra.is_empty().not().then_some(extra), mod_path, subst_types) } #[derive(Debug)] @@ -1124,13 +1112,12 @@ fn definition_path(db: &RootDatabase, &def: &Definition, edition: Edition) -> Op } fn markup( - docs: Option, - range_map: Option, + docs: Option, Documentation<'_>>>, rust: String, extra: Option, mod_path: Option, subst_types: String, -) -> (Markup, Option) { +) -> (Markup, Option) { let mut buf = String::new(); if let Some(mod_path) = mod_path @@ -1151,10 +1138,21 @@ fn markup( if let Some(doc) = docs { format_to!(buf, "\n___\n\n"); let offset = TextSize::new(buf.len() as u32); - let buf_range_map = range_map.map(|range_map| range_map.shift_docstring_line_range(offset)); - format_to!(buf, "{}", doc); + let docs_str = match &doc { + Either::Left(docs) => docs.docs(), + Either::Right(docs) => docs.as_str(), + }; + format_to!(buf, "{}", docs_str); + let range_map = match doc { + Either::Left(range_map) => { + let mut range_map = range_map.into_owned(); + range_map.shift_by(offset); + Some(range_map) + } + Either::Right(_) => None, + }; - (buf.into(), buf_range_map) + (buf.into(), range_map) } else { (buf.into(), None) } 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 21550d5e6665..d474e50d3c2c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -90,7 +90,7 @@ pub(crate) fn inlay_hints( let sema = Semantics::new(db); let file_id = sema .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id)); + .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, file_id)); let file = sema.parse(file_id); let file = file.syntax(); @@ -143,7 +143,7 @@ pub(crate) fn inlay_hints_resolve( let sema = Semantics::new(db); let file_id = sema .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id)); + .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, file_id)); let file = sema.parse(file_id); let file = file.syntax(); diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 857252832ffe..a633877adb4e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -331,7 +331,8 @@ impl Analysis { pub fn parse(&self, file_id: FileId) -> Cancellable { // FIXME edition self.with_db(|db| { - let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id); + let editioned_file_id_wrapper = + EditionedFileId::current_edition_guess_origin(&self.db, file_id); db.parse(editioned_file_id_wrapper).tree() }) @@ -360,7 +361,7 @@ impl Analysis { /// supported). pub fn matching_brace(&self, position: FilePosition) -> Cancellable> { self.with_db(|db| { - let file_id = EditionedFileId::current_edition(&self.db, position.file_id); + let file_id = EditionedFileId::current_edition_guess_origin(&self.db, position.file_id); let parse = db.parse(file_id); let file = parse.tree(); matching_brace::matching_brace(&file, position.offset) @@ -421,7 +422,7 @@ impl Analysis { pub fn join_lines(&self, config: &JoinLinesConfig, frange: FileRange) -> Cancellable { self.with_db(|db| { let editioned_file_id_wrapper = - EditionedFileId::current_edition(&self.db, frange.file_id); + EditionedFileId::current_edition_guess_origin(&self.db, frange.file_id); let parse = db.parse(editioned_file_id_wrapper); join_lines::join_lines(config, &parse.tree(), frange.range) }) @@ -462,7 +463,8 @@ impl Analysis { ) -> Cancellable> { // FIXME: Edition self.with_db(|db| { - let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id); + let editioned_file_id_wrapper = + EditionedFileId::current_edition_guess_origin(&self.db, file_id); let source_file = db.parse(editioned_file_id_wrapper).tree(); file_structure::file_structure(&source_file, config) }) @@ -493,7 +495,8 @@ impl Analysis { /// Returns the set of folding ranges. pub fn folding_ranges(&self, file_id: FileId) -> Cancellable> { self.with_db(|db| { - let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id); + let editioned_file_id_wrapper = + EditionedFileId::current_edition_guess_origin(&self.db, file_id); folding_ranges::folding_ranges(&db.parse(editioned_file_id_wrapper).tree()) }) 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 b222ff3eec0b..8e73ddf8bfc3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -54,7 +54,8 @@ pub struct NavigationTarget { // FIXME: Symbol pub container_name: Option, pub description: Option, - pub docs: Option, + // FIXME: Use the database lifetime here. + pub docs: Option>, /// In addition to a `name` field, a `NavigationTarget` may also be aliased /// In such cases we want a `NavigationTarget` to be accessible by its alias // FIXME: Symbol @@ -163,7 +164,7 @@ impl NavigationTarget { full_range, SymbolKind::Module, ); - res.docs = module.docs(db); + res.docs = module.docs(db).map(Documentation::into_owned); res.description = Some( module.display(db, module.krate().to_display_target(db)).to_string(), ); @@ -437,7 +438,7 @@ where D::KIND, ) .map(|mut res| { - res.docs = self.docs(db); + res.docs = self.docs(db).map(Documentation::into_owned); res.description = hir::attach_db(db, || { Some(self.display(db, self.krate(db).to_display_target(db)).to_string()) }); @@ -536,7 +537,7 @@ impl TryToNav for hir::ExternCrateDecl { SymbolKind::Module, ); - res.docs = self.docs(db); + res.docs = self.docs(db).map(Documentation::into_owned); res.description = Some(self.display(db, krate.to_display_target(db)).to_string()); res.container_name = container_name(db, *self, edition); res @@ -558,10 +559,9 @@ impl TryToNav for hir::Field { FieldSource::Named(it) => { NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field).map( |mut res| { - res.docs = self.docs(db); - res.description = hir::attach_db(db, || { - Some(self.display(db, krate.to_display_target(db)).to_string()) - }); + res.docs = self.docs(db).map(Documentation::into_owned); + res.description = + Some(self.display(db, krate.to_display_target(db)).to_string()); res }, ) @@ -600,7 +600,7 @@ impl TryToNav for hir::Macro { self.kind(db).into(), ) .map(|mut res| { - res.docs = self.docs(db); + res.docs = self.docs(db).map(Documentation::into_owned); res }), ) @@ -939,7 +939,7 @@ pub(crate) fn orig_range_with_focus_r( ) -> UpmappingResult<(FileRange, Option)> { let Some(name) = focus_range else { return orig_range_r(db, hir_file, value) }; - let call_kind = || db.lookup_intern_macro_call(hir_file.macro_file().unwrap()).kind; + let call = || db.lookup_intern_macro_call(hir_file.macro_file().unwrap()); let def_range = || db.lookup_intern_macro_call(hir_file.macro_file().unwrap()).def.definition_range(db); @@ -965,7 +965,8 @@ pub(crate) fn orig_range_with_focus_r( // name lies outside the node, so instead point to the macro call which // *should* contain the name _ => { - let kind = call_kind(); + let call = call(); + let kind = call.kind; let range = kind.clone().original_call_range_with_input(db); //If the focus range is in the attribute/derive body, we // need to point the call site to the entire body, if not, fall back @@ -977,7 +978,7 @@ pub(crate) fn orig_range_with_focus_r( { range } else { - kind.original_call_range(db) + kind.original_call_range(db, call.krate) } } }, @@ -1006,11 +1007,14 @@ pub(crate) fn orig_range_with_focus_r( }, ), // node is in macro def, just show the focus - _ => ( - // show the macro call - (call_kind().original_call_range(db), None), - Some((focus_range, Some(focus_range))), - ), + _ => { + let call = call(); + ( + // show the macro call + (call.kind.original_call_range(db, call.krate), None), + Some((focus_range, Some(focus_range))), + ) + } } } // lost name? can't happen for single tokens diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index a53a19299727..c4dcd588d693 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -1124,7 +1124,10 @@ pub(super) struct Foo$0 { check_with_scope( code, Some(&mut |db| { - SearchScope::single_file(EditionedFileId::current_edition(db, FileId::from_raw(2))) + SearchScope::single_file(EditionedFileId::current_edition_guess_origin( + db, + FileId::from_raw(2), + )) }), expect![[r#" quux Function FileId(0) 19..35 26..30 diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 494701d97def..4b475dac87b5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -3,17 +3,13 @@ use std::{fmt, sync::OnceLock}; use arrayvec::ArrayVec; use ast::HasName; use cfg::{CfgAtom, CfgExpr}; -use hir::{ - AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, Semantics, Symbol, db::HirDatabase, - sym, -}; +use hir::{AsAssocItem, HasAttrs, HasCrate, HasSource, Semantics, Symbol, db::HirDatabase, sym}; use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; use ide_db::impl_empty_upmap_from_ra_fixture; use ide_db::{ FilePosition, FxHashMap, FxIndexMap, FxIndexSet, RootDatabase, SymbolKind, base_db::RootQueryDb, defs::Definition, - documentation::docs_from_attrs, helpers::visit_file_defs, search::{FileReferenceNode, SearchScope}, }; @@ -323,7 +319,7 @@ pub(crate) fn runnable_fn( def: hir::Function, ) -> Option { let edition = def.krate(sema.db).edition(sema.db); - let under_cfg_test = has_cfg_test(def.module(sema.db).attrs(sema.db)); + let under_cfg_test = has_cfg_test(def.module(sema.db).attrs(sema.db).cfgs(sema.db)); let kind = if !under_cfg_test && def.is_main(sema.db) { RunnableKind::Bin } else { @@ -358,7 +354,7 @@ pub(crate) fn runnable_fn( let file_range = fn_source.syntax().original_file_range_with_macro_call_input(sema.db); let update_test = UpdateTest::find_snapshot_macro(sema, file_range); - let cfg = def.attrs(sema.db).cfg(); + let cfg = def.attrs(sema.db).cfgs(sema.db).cloned(); Some(Runnable { use_name_in_title: false, nav, kind, cfg, update_test }) } @@ -366,8 +362,8 @@ pub(crate) fn runnable_mod( sema: &Semantics<'_, RootDatabase>, def: hir::Module, ) -> Option { - if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(def.attrs(sema.db))) - { + let cfg = def.attrs(sema.db).cfgs(sema.db); + if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(cfg)) { return None; } let path = def @@ -381,8 +377,7 @@ pub(crate) fn runnable_mod( }) .join("::"); - let attrs = def.attrs(sema.db); - let cfg = attrs.cfg(); + let cfg = cfg.cloned(); let nav = NavigationTarget::from_module_to_decl(sema.db, def).call_site(); let module_source = sema.module_definition_node(def); @@ -409,10 +404,10 @@ pub(crate) fn runnable_impl( let display_target = def.module(sema.db).krate().to_display_target(sema.db); let edition = display_target.edition; let attrs = def.attrs(sema.db); - if !has_runnable_doc_test(&attrs) { + if !has_runnable_doc_test(sema.db, &attrs) { return None; } - let cfg = attrs.cfg(); + let cfg = attrs.cfgs(sema.db).cloned(); let nav = def.try_to_nav(sema)?.call_site(); let ty = def.self_ty(sema.db); let adt_name = ty.as_adt()?.name(sema.db); @@ -442,8 +437,16 @@ pub(crate) fn runnable_impl( }) } -fn has_cfg_test(attrs: AttrsWithOwner) -> bool { - attrs.cfgs().any(|cfg| matches!(&cfg, CfgExpr::Atom(CfgAtom::Flag(s)) if *s == sym::test)) +fn has_cfg_test(cfg: Option<&CfgExpr>) -> bool { + return cfg.is_some_and(has_cfg_test_impl); + + fn has_cfg_test_impl(cfg: &CfgExpr) -> bool { + match cfg { + CfgExpr::Atom(CfgAtom::Flag(s)) => *s == sym::test, + CfgExpr::Any(cfgs) | CfgExpr::All(cfgs) => cfgs.iter().any(has_cfg_test_impl), + _ => false, + } + } } /// Creates a test mod runnable for outline modules at the top of their definition. @@ -453,8 +456,8 @@ fn runnable_mod_outline_definition( ) -> Option { def.as_source_file_id(sema.db)?; - if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(def.attrs(sema.db))) - { + let cfg = def.attrs(sema.db).cfgs(sema.db); + if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(cfg)) { return None; } let path = def @@ -468,8 +471,7 @@ fn runnable_mod_outline_definition( }) .join("::"); - let attrs = def.attrs(sema.db); - let cfg = attrs.cfg(); + let cfg = cfg.cloned(); let mod_source = sema.module_definition_node(def); let mod_syntax = mod_source.file_syntax(sema.db); @@ -508,7 +510,7 @@ fn module_def_doctest(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Op let display_target = krate .unwrap_or_else(|| (*db.all_crates().last().expect("no crate graph present")).into()) .to_display_target(db); - if !has_runnable_doc_test(&attrs) { + if !has_runnable_doc_test(db, &attrs) { return None; } let def_name = def.name(db)?; @@ -554,7 +556,7 @@ fn module_def_doctest(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Op use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, - cfg: attrs.cfg(), + cfg: attrs.cfgs(db).cloned(), update_test: UpdateTest::default(), }; Some(res) @@ -571,15 +573,15 @@ impl TestAttr { } } -fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool { +fn has_runnable_doc_test(db: &RootDatabase, attrs: &hir::AttrsWithOwner) -> bool { const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"]; const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = &["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"]; - docs_from_attrs(attrs).is_some_and(|doc| { + attrs.hir_docs(db).is_some_and(|doc| { let mut in_code_block = false; - for line in doc.lines() { + for line in doc.docs().lines() { if let Some(header) = RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence)) { diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 5f7e12cf53f8..a8fc57a431b4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -31,7 +31,7 @@ use crate::RootDatabase; /// edited. #[derive(Debug)] pub struct SignatureHelp { - pub doc: Option, + pub doc: Option>, pub signature: String, pub active_parameter: Option, parameters: Vec, @@ -174,7 +174,7 @@ fn signature_help_for_call( let mut fn_params = None; match callable.kind() { hir::CallableKind::Function(func) => { - res.doc = func.docs(db); + res.doc = func.docs(db).map(Documentation::into_owned); format_to!(res.signature, "fn {}", func.name(db).display(db, edition)); let generic_params = GenericDef::Function(func) @@ -196,7 +196,7 @@ fn signature_help_for_call( }); } hir::CallableKind::TupleStruct(strukt) => { - res.doc = strukt.docs(db); + res.doc = strukt.docs(db).map(Documentation::into_owned); format_to!(res.signature, "struct {}", strukt.name(db).display(db, edition)); let generic_params = GenericDef::Adt(strukt.into()) @@ -209,7 +209,7 @@ fn signature_help_for_call( } } hir::CallableKind::TupleEnumVariant(variant) => { - res.doc = variant.docs(db); + res.doc = variant.docs(db).map(Documentation::into_owned); format_to!( res.signature, "enum {}", @@ -314,33 +314,33 @@ fn signature_help_for_generics( let db = sema.db; match generics_def { hir::GenericDef::Function(it) => { - res.doc = it.docs(db); + res.doc = it.docs(db).map(Documentation::into_owned); format_to!(res.signature, "fn {}", it.name(db).display(db, edition)); } hir::GenericDef::Adt(hir::Adt::Enum(it)) => { - res.doc = it.docs(db); + res.doc = it.docs(db).map(Documentation::into_owned); format_to!(res.signature, "enum {}", it.name(db).display(db, edition)); if let Some(variant) = variant { // In paths, generics of an enum can be specified *after* one of its variants. // eg. `None::` // We'll use the signature of the enum, but include the docs of the variant. - res.doc = variant.docs(db); + res.doc = variant.docs(db).map(Documentation::into_owned); } } hir::GenericDef::Adt(hir::Adt::Struct(it)) => { - res.doc = it.docs(db); + res.doc = it.docs(db).map(Documentation::into_owned); format_to!(res.signature, "struct {}", it.name(db).display(db, edition)); } hir::GenericDef::Adt(hir::Adt::Union(it)) => { - res.doc = it.docs(db); + res.doc = it.docs(db).map(Documentation::into_owned); format_to!(res.signature, "union {}", it.name(db).display(db, edition)); } hir::GenericDef::Trait(it) => { - res.doc = it.docs(db); + res.doc = it.docs(db).map(Documentation::into_owned); format_to!(res.signature, "trait {}", it.name(db).display(db, edition)); } hir::GenericDef::TypeAlias(it) => { - res.doc = it.docs(db); + res.doc = it.docs(db).map(Documentation::into_owned); format_to!(res.signature, "type {}", it.name(db).display(db, edition)); } // These don't have generic args that can be specified @@ -495,7 +495,7 @@ fn signature_help_for_tuple_struct_pat( let fields: Vec<_> = if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res { let en = variant.parent_enum(db); - res.doc = en.docs(db); + res.doc = en.docs(db).map(Documentation::into_owned); format_to!( res.signature, "enum {}::{} (", @@ -512,7 +512,7 @@ fn signature_help_for_tuple_struct_pat( match adt { hir::Adt::Struct(it) => { - res.doc = it.docs(db); + res.doc = it.docs(db).map(Documentation::into_owned); format_to!(res.signature, "struct {} (", it.name(db).display(db, edition)); it.fields(db) } @@ -622,7 +622,7 @@ fn signature_help_for_record_<'db>( fields = variant.fields(db); let en = variant.parent_enum(db); - res.doc = en.docs(db); + res.doc = en.docs(db).map(Documentation::into_owned); format_to!( res.signature, "enum {}::{} {{ ", @@ -639,12 +639,12 @@ fn signature_help_for_record_<'db>( match adt { hir::Adt::Struct(it) => { fields = it.fields(db); - res.doc = it.docs(db); + res.doc = it.docs(db).map(Documentation::into_owned); format_to!(res.signature, "struct {} {{ ", it.name(db).display(db, edition)); } hir::Adt::Union(it) => { fields = it.fields(db); - res.doc = it.docs(db); + res.doc = it.docs(db).map(Documentation::into_owned); format_to!(res.signature, "union {} {{ ", it.name(db).display(db, edition)); } _ => return None, @@ -740,12 +740,12 @@ mod tests { #[rust_analyzer::rust_fixture] ra_fixture: &str, ) -> (RootDatabase, FilePosition) { let mut database = RootDatabase::default(); - let change_fixture = ChangeFixture::parse(&database, ra_fixture); + let change_fixture = ChangeFixture::parse(ra_fixture); database.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let offset = range_or_offset.expect_offset(); - let position = FilePosition { file_id: file_id.file_id(&database), offset }; + let position = FilePosition { file_id: file_id.file_id(), offset }; (database, position) } diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index e261928c413f..ec8292968dbf 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -42,7 +42,8 @@ pub struct ReferenceData { #[derive(Debug)] pub struct TokenStaticData { - pub documentation: Option, + // FIXME: Make this have the lifetime of the database. + pub documentation: Option>, pub hover: Option, pub definition: Option, pub references: Vec, @@ -109,7 +110,7 @@ fn documentation_for_definition( sema: &Semantics<'_, RootDatabase>, def: Definition, scope_node: &SyntaxNode, -) -> Option { +) -> Option> { let famous_defs = match &def { Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(scope_node)?.krate())), _ => None, @@ -124,6 +125,7 @@ fn documentation_for_definition( }) .to_display_target(sema.db), ) + .map(Documentation::into_owned) } // FIXME: This is a weird function diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index 66895cb0b053..782a73d20ca3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -199,7 +199,7 @@ pub(crate) fn highlight( let sema = Semantics::new(db); let file_id = sema .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id)); + .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, file_id)); // Determine the root based on the given range. let (root, range_to_highlight) = { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs index 75e46b8ebfde..597550b482cd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs @@ -20,7 +20,7 @@ pub(crate) fn highlight_as_html_with_config( let sema = Semantics::new(db); let file_id = sema .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id)); + .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, file_id)); let file = sema.parse(file_id); let file = file.syntax(); fn rainbowify(seed: u64) -> String { 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 7955f5ac0de9..26d2bb5e0288 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 @@ -1,16 +1,13 @@ //! "Recursive" Syntax highlighting for code in doctests and fixtures. -use std::mem; - -use either::Either; -use hir::{EditionedFileId, HirFileId, InFile, Semantics, sym}; -use ide_db::range_mapper::RangeMapper; +use hir::{EditionedFileId, HirFileId, InFile, Semantics}; use ide_db::{ - SymbolKind, defs::Definition, documentation::docs_with_rangemap, rust_doc::is_rust_fence, + SymbolKind, defs::Definition, documentation::Documentation, range_mapper::RangeMapper, + rust_doc::is_rust_fence, }; use syntax::{ - AstToken, NodeOrToken, SyntaxNode, TextRange, TextSize, - ast::{self, AstNode, IsString, QuoteOffsets}, + SyntaxNode, TextRange, TextSize, + ast::{self, IsString}, }; use crate::{ @@ -96,118 +93,79 @@ pub(super) fn doc_comment( None => return, }; let src_file_id: HirFileId = src_file_id.into(); + let Some(docs) = attributes.hir_docs(sema.db) else { return }; // Extract intra-doc links and emit highlights for them. - if let Some((docs, doc_mapping)) = docs_with_rangemap(sema.db, &attributes) { - extract_definitions_from_docs(&docs) - .into_iter() - .filter_map(|(range, link, ns)| { - doc_mapping - .map(range) - .filter(|(mapping, _)| mapping.file_id == src_file_id) - .and_then(|(InFile { value: mapped_range, .. }, attr_id)| { - Some(mapped_range).zip(resolve_doc_path_for_def( - sema.db, - def, - &link, - ns, - attr_id.is_inner_attr(), - )) - }) - }) - .for_each(|(range, def)| { - hl.add(HlRange { - range, - highlight: module_def_to_hl_tag(def) - | HlMod::Documentation - | HlMod::Injected - | HlMod::IntraDocLink, - binding_hash: None, + extract_definitions_from_docs(&Documentation::new_borrowed(docs.docs())) + .into_iter() + .filter_map(|(range, link, ns)| { + docs.find_ast_range(range) + .filter(|(mapping, _)| mapping.file_id == src_file_id) + .and_then(|(InFile { value: mapped_range, .. }, is_inner)| { + Some(mapped_range) + .zip(resolve_doc_path_for_def(sema.db, def, &link, ns, is_inner)) }) + }) + .for_each(|(range, def)| { + hl.add(HlRange { + range, + highlight: module_def_to_hl_tag(def) + | HlMod::Documentation + | HlMod::Injected + | HlMod::IntraDocLink, + binding_hash: None, }) - } + }); // Extract doc-test sources from the docs and calculate highlighting for them. let mut inj = RangeMapper::default(); inj.add_unmapped("fn doctest() {\n"); - let attrs_source_map = attributes.source_map(sema.db); - let mut is_codeblock = false; let mut is_doctest = false; - let mut new_comments = Vec::new(); - let mut string; + let mut has_doctests = false; - for attr in attributes.by_key(sym::doc).attrs() { - let InFile { file_id, value: src } = attrs_source_map.source_of(attr); + let mut docs_offset = TextSize::new(0); + for mut line in docs.docs().split('\n') { + let mut line_docs_offset = docs_offset; + docs_offset += TextSize::of(line) + TextSize::of("\n"); + + match RUSTDOC_FENCES.into_iter().find_map(|fence| line.find(fence)) { + Some(idx) => { + is_codeblock = !is_codeblock; + // Check whether code is rust by inspecting fence guards + let guards = &line[idx + RUSTDOC_FENCE_LENGTH..]; + let is_rust = is_rust_fence(guards); + is_doctest = is_codeblock && is_rust; + continue; + } + None if !is_doctest => continue, + None => (), + } + + // lines marked with `#` should be ignored in output, we skip the `#` char + if line.starts_with('#') { + line_docs_offset += TextSize::of("#"); + line = &line["#".len()..]; + } + + let Some((InFile { file_id, value: mapped_range }, _)) = + docs.find_ast_range(TextRange::at(line_docs_offset, TextSize::of(line))) + else { + continue; + }; if file_id != src_file_id { continue; } - let (line, range) = match &src { - Either::Left(it) => { - string = match find_doc_string_in_attr(attr, it) { - Some(it) => it, - None => continue, - }; - let text = string.text(); - let text_range = string.syntax().text_range(); - match string.quote_offsets() { - Some(QuoteOffsets { contents, .. }) => { - (&text[contents - text_range.start()], contents) - } - None => (text, text_range), - } - } - Either::Right(comment) => { - let value = comment.prefix().len(); - let range = comment.syntax().text_range(); - ( - &comment.text()[value..], - TextRange::new(range.start() + TextSize::try_from(value).unwrap(), range.end()), - ) - } - }; - let mut range_start = range.start(); - for line in line.split('\n') { - let line_len = TextSize::from(line.len() as u32); - let prev_range_start = { - let next_range_start = range_start + line_len + TextSize::from(1); - mem::replace(&mut range_start, next_range_start) - }; - let mut pos = TextSize::from(0); - - match RUSTDOC_FENCES.into_iter().find_map(|fence| line.find(fence)) { - Some(idx) => { - is_codeblock = !is_codeblock; - // Check whether code is rust by inspecting fence guards - let guards = &line[idx + RUSTDOC_FENCE_LENGTH..]; - let is_rust = is_rust_fence(guards); - is_doctest = is_codeblock && is_rust; - continue; - } - None if !is_doctest => continue, - None => (), - } - - // whitespace after comment is ignored - if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) { - pos += TextSize::of(ws); - } - // lines marked with `#` should be ignored in output, we skip the `#` char - if line[pos.into()..].starts_with('#') { - pos += TextSize::of('#'); - } - - new_comments.push(TextRange::at(prev_range_start, pos)); - inj.add(&line[pos.into()..], TextRange::new(pos, line_len) + prev_range_start); - inj.add_unmapped("\n"); - } + has_doctests = true; + inj.add(line, mapped_range); + inj.add_unmapped("\n"); } - if new_comments.is_empty() { + if !has_doctests { return; // no need to run an analysis on an empty file } @@ -240,37 +198,6 @@ pub(super) fn doc_comment( } } } - - for range in new_comments { - hl.add(HlRange { - range, - highlight: HlTag::Comment | HlMod::Documentation, - binding_hash: None, - }); - } -} - -fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option { - match it.expr() { - // #[doc = lit] - Some(ast::Expr::Literal(lit)) => match lit.kind() { - ast::LiteralKind::String(it) => Some(it), - _ => None, - }, - // #[cfg_attr(..., doc = "", ...)] - None => { - // We gotta hunt the string token manually here - let text = attr.string_value()?.as_str(); - // FIXME: We just pick the first string literal that has the same text as the doc attribute - // This means technically we might highlight the wrong one - it.syntax() - .descendants_with_tokens() - .filter_map(NodeOrToken::into_token) - .filter_map(ast::String::cast) - .find(|string| string.text().get(1..string.text().len() - 1) == Some(text)) - } - _ => None, - } } fn module_def_to_hl_tag(def: Definition) -> HlTag { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index d00f279c8299..53750ae0bac0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -42,21 +42,21 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
//! This is a module to test doc injection.
 //! ```
-//! fn test() {}
+//! fn test() {}
 //! ```
 
 //! Syntactic name ref highlighting testing
 //! ```rust
-//! extern crate self;
-//! extern crate other as otter;
-//! extern crate core;
-//! trait T { type Assoc; }
-//! fn f<Arg>() -> use<Arg> where (): T<Assoc = ()> {}
+//! extern crate self;
+//! extern crate other as otter;
+//! extern crate core;
+//! trait T { type Assoc; }
+//! fn f<Arg>() -> use<Arg> where (): T<Assoc = ()> {}
 //! ```
 mod outline_module;
 
 /// ```
-/// let _ = "early doctests should not go boom";
+/// let _ = "early doctests should not go boom";
 /// ```
 struct Foo {
     bar: bool,
@@ -65,15 +65,15 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 /// This is an impl of [`Foo`] with a code block.
 ///
 /// ```
-/// fn foo() {
+/// fn foo() {
 ///
-/// }
+/// }
 /// ```
 impl Foo {
     /// ```
-    /// let _ = "Call me
+    /// let _ = "Call me
     //    KILLER WHALE
-    ///     Ishmael.";
+    ///     Ishmael.";
     /// ```
     pub const bar: bool = true;
 
@@ -82,8 +82,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     /// # Examples
     ///
     /// ```
-    /// # #![allow(unused_mut)]
-    /// let mut foo: Foo = Foo::new();
+    /// # #![allow(unused_mut)]
+    /// let mut foo: Foo = Foo::new();
     /// ```
     pub const fn new() -> Foo {
         Foo { bar: true }
@@ -94,38 +94,38 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     /// # Examples
     ///
     /// ```
-    /// use x::y;
+    /// use x::y;
     ///
-    /// let foo = Foo::new();
+    /// let foo = Foo::new();
     ///
-    /// // calls bar on foo
-    /// assert!(foo.bar());
+    /// // calls bar on foo
+    /// assert!(foo.bar());
     ///
-    /// let bar = foo.bar || Foo::bar;
+    /// let bar = foo.bar || Foo::bar;
     ///
-    /// /* multi-line
-    ///        comment */
+    /// /* multi-line
+    ///        comment */
     ///
-    /// let multi_line_string = "Foo
-    ///   bar\n
-    ///          ";
+    /// let multi_line_string = "Foo
+    ///   bar\n
+    ///          ";
     ///
     /// ```
     ///
     /// ```rust,no_run
-    /// let foobar = Foo::new().bar();
+    /// let foobar = Foo::new().bar();
     /// ```
     ///
     /// ~~~rust,no_run
-    /// // code block with tilde.
-    /// let foobar = Foo::new().bar();
+    /// // code block with tilde.
+    /// let foobar = Foo::new().bar();
     /// ~~~
     ///
     /// ```
-    /// // functions
-    /// fn foo<T, const X: usize>(arg: i32) {
-    ///     let x: T = X;
-    /// }
+    /// // functions
+    /// fn foo<T, const X: usize>(arg: i32) {
+    ///     let x: T = X;
+    /// }
     /// ```
     ///
     /// ```sh
@@ -150,8 +150,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 /// ```
-/// macro_rules! noop { ($expr:expr) => { $expr }}
-/// noop!(1);
+/// macro_rules! noop { ($expr:expr) => { $expr }}
+/// noop!(1);
 /// ```
 macro_rules! noop {
     ($expr:expr) => {
@@ -160,18 +160,18 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 /// ```rust
-/// let _ = example(&[1, 2, 3]);
+/// let _ = example(&[1, 2, 3]);
 /// ```
 ///
 /// ```
-/// loop {}
+/// loop {}
 #[cfg_attr(not(feature = "false"), doc = "loop {}")]
 #[doc = "loop {}"]
 /// ```
 ///
 #[cfg_attr(feature = "alloc", doc = "```rust")]
 #[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
-/// let _ = example(&alloc::vec![1, 2, 3]);
+/// let _ = example(&alloc::vec![1, 2, 3]);
 /// ```
 pub fn mix_and_match() {}
 
@@ -187,7 +187,7 @@ It is beyond me why you'd use these when you got ///
 /**
     Really, I don't get it
     ```rust
-    let _ = example(&[1, 2, 3]);
+    let _ = example(&[1, 2, 3]);
     ```
     [`block_comments`] tests these without indentation
 */
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs
index ed55ac5bf04b..0381865fed45 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs
@@ -75,7 +75,10 @@ pub(crate) fn on_char_typed(
     // FIXME: We are hitting the database here, if we are unlucky this call might block momentarily
     // causing the editor to feel sluggish!
     let edition = Edition::CURRENT_FIXME;
-    let editioned_file_id_wrapper = EditionedFileId::new(db, position.file_id, edition);
+    let editioned_file_id_wrapper = EditionedFileId::from_span_guess_origin(
+        db,
+        span::EditionedFileId::new(position.file_id, edition),
+    );
     let file = &db.parse(editioned_file_id_wrapper);
     let char_matches_position =
         file.tree().syntax().text().char_at(position.offset) == Some(char_typed);
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
index fdc583a15cc7..76a2802d294c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
@@ -51,7 +51,7 @@ use ide_db::text_edit::TextEdit;
 // ![On Enter](https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif)
 pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option {
     let editioned_file_id_wrapper =
-        ide_db::base_db::EditionedFileId::current_edition(db, position.file_id);
+        ide_db::base_db::EditionedFileId::current_edition_guess_origin(db, position.file_id);
     let parse = db.parse(editioned_file_id_wrapper);
     let file = parse.tree();
     let token = file.syntax().token_at_offset(position.offset).left_biased()?;
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
index 2cd751463bdb..c9a2f31696f4 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
@@ -12,6 +12,6 @@ pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String {
     let sema = Semantics::new(db);
     let file_id = sema
         .attach_first_edition(file_id)
-        .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id));
+        .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, file_id));
     db.file_item_tree(file_id.into()).pretty_print(db, file_id.edition(db))
 }
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 717bd230a21e..824cc2ff94e3 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
@@ -145,7 +145,9 @@ impl flags::AnalysisStats {
                     if !source_root.is_library || self.with_deps {
                         let length = db.file_text(file_id).text(db).lines().count();
                         let item_stats = db
-                            .file_item_tree(EditionedFileId::current_edition(db, file_id).into())
+                            .file_item_tree(
+                                EditionedFileId::current_edition_guess_origin(db, file_id).into(),
+                            )
                             .item_tree_stats()
                             .into();
 
@@ -155,7 +157,9 @@ impl flags::AnalysisStats {
                     } else {
                         let length = db.file_text(file_id).text(db).lines().count();
                         let item_stats = db
-                            .file_item_tree(EditionedFileId::current_edition(db, file_id).into())
+                            .file_item_tree(
+                                EditionedFileId::current_edition_guess_origin(db, file_id).into(),
+                            )
                             .item_tree_stats()
                             .into();
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index 37f83f6dee67..92bb2c1ce4fa 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -514,12 +514,12 @@ mod test {
 
     fn position(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (AnalysisHost, FilePosition) {
         let mut host = AnalysisHost::default();
-        let change_fixture = ChangeFixture::parse(host.raw_database(), ra_fixture);
+        let change_fixture = ChangeFixture::parse(ra_fixture);
         host.raw_database_mut().apply_change(change_fixture.change);
         let (file_id, range_or_offset) =
             change_fixture.file_position.expect("expected a marker ()");
         let offset = range_or_offset.expect_offset();
-        let position = FilePosition { file_id: file_id.file_id(host.raw_database()), offset };
+        let position = FilePosition { file_id: file_id.file_id(), offset };
         (host, position)
     }
 
@@ -870,7 +870,7 @@ pub mod example_mod {
         let s = "/// foo\nfn bar() {}";
 
         let mut host = AnalysisHost::default();
-        let change_fixture = ChangeFixture::parse(host.raw_database(), s);
+        let change_fixture = ChangeFixture::parse(s);
         host.raw_database_mut().apply_change(change_fixture.change);
 
         let analysis = host.analysis();
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
index e3e3a143de03..cc2ab0f07ca0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
@@ -73,7 +73,7 @@ impl flags::Search {
                 let sr = db.source_root(root).source_root(db);
                 for file_id in sr.iter() {
                     for debug_info in match_finder.debug_where_text_equal(
-                        EditionedFileId::current_edition(db, file_id),
+                        EditionedFileId::current_edition_guess_origin(db, file_id),
                         debug_snippet,
                     ) {
                         println!("{debug_info:#?}");
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
index 0362e13b88b7..2cb0fe9eefad 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
@@ -141,7 +141,7 @@ fn all_unresolved_references(
 ) -> Vec {
     let file_id = sema
         .attach_first_edition(file_id)
-        .unwrap_or_else(|| EditionedFileId::current_edition(sema.db, file_id));
+        .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(sema.db, file_id));
     let file = sema.parse(file_id);
     let root = file.syntax();
 
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 04b20033062e..5a42cbd933f9 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
@@ -119,7 +119,7 @@ pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSe
     }
 }
 
-pub(crate) fn documentation(documentation: Documentation) -> lsp_types::Documentation {
+pub(crate) fn documentation(documentation: Documentation<'_>) -> lsp_types::Documentation {
     let value = format_docs(&documentation);
     let markup_content = lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value };
     lsp_types::Documentation::MarkupContent(markup_content)
@@ -1970,7 +1970,7 @@ pub(crate) fn markup_content(
         ide::HoverDocFormat::Markdown => lsp_types::MarkupKind::Markdown,
         ide::HoverDocFormat::PlainText => lsp_types::MarkupKind::PlainText,
     };
-    let value = format_docs(&Documentation::new(markup.into()));
+    let value = format_docs(&Documentation::new_owned(markup.into()));
     lsp_types::MarkupContent { kind, value }
 }
 
diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
index 4e525be3fe3c..2d1955d1f651 100644
--- a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
@@ -1,6 +1,6 @@
 //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`].
 
-use std::{fmt, hash::Hash};
+use std::{collections::VecDeque, fmt, hash::Hash};
 
 use intern::Symbol;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -102,26 +102,34 @@ where
     SpanData: Copy + fmt::Debug,
     SpanMap: SpanMapper>,
 {
-    let mut c = Converter::new(node, map, Default::default(), Default::default(), span, mode);
+    let mut c =
+        Converter::new(node, map, Default::default(), Default::default(), span, mode, |_, _| {
+            (true, Vec::new())
+        });
     convert_tokens(&mut c)
 }
 
 /// Converts a syntax tree to a [`tt::Subtree`] using the provided span map to populate the
 /// subtree's spans. Additionally using the append and remove parameters, the additional tokens can
 /// be injected or hidden from the output.
-pub fn syntax_node_to_token_tree_modified(
+pub fn syntax_node_to_token_tree_modified(
     node: &SyntaxNode,
     map: SpanMap,
     append: FxHashMap>>>,
     remove: FxHashSet,
     call_site: SpanData,
     mode: DocCommentDesugarMode,
+    on_enter: OnEvent,
 ) -> tt::TopSubtree>
 where
     SpanMap: SpanMapper>,
     SpanData: Copy + fmt::Debug,
+    OnEvent: FnMut(
+        &mut PreorderWithTokens,
+        &WalkEvent,
+    ) -> (bool, Vec>>),
 {
-    let mut c = Converter::new(node, map, append, remove, call_site, mode);
+    let mut c = Converter::new(node, map, append, remove, call_site, mode, on_enter);
     convert_tokens(&mut c)
 }
 
@@ -624,9 +632,9 @@ where
     }
 }
 
-struct Converter {
+struct Converter {
     current: Option,
-    current_leaves: Vec>,
+    current_leaves: VecDeque>,
     preorder: PreorderWithTokens,
     range: TextRange,
     punct_offset: Option<(SyntaxToken, TextSize)>,
@@ -636,9 +644,13 @@ struct Converter {
     remove: FxHashSet,
     call_site: S,
     mode: DocCommentDesugarMode,
+    on_event: OnEvent,
 }
 
-impl Converter {
+impl Converter
+where
+    OnEvent: FnMut(&mut PreorderWithTokens, &WalkEvent) -> (bool, Vec>),
+{
     fn new(
         node: &SyntaxNode,
         map: SpanMap,
@@ -646,8 +658,9 @@ impl Converter {
         remove: FxHashSet,
         call_site: S,
         mode: DocCommentDesugarMode,
+        on_enter: OnEvent,
     ) -> Self {
-        let mut this = Converter {
+        let mut converter = Converter {
             current: None,
             preorder: node.preorder_with_tokens(),
             range: node.text_range(),
@@ -656,16 +669,21 @@ impl Converter {
             append,
             remove,
             call_site,
-            current_leaves: vec![],
+            current_leaves: VecDeque::new(),
             mode,
+            on_event: on_enter,
         };
-        let first = this.next_token();
-        this.current = first;
-        this
+        converter.current = converter.next_token();
+        converter
     }
 
     fn next_token(&mut self) -> Option {
         while let Some(ev) = self.preorder.next() {
+            let (keep_event, insert_leaves) = (self.on_event)(&mut self.preorder, &ev);
+            self.current_leaves.extend(insert_leaves);
+            if !keep_event {
+                continue;
+            }
             match ev {
                 WalkEvent::Enter(token) => {
                     if self.remove.contains(&token) {
@@ -675,10 +693,9 @@ impl Converter {
                             }
                             node => {
                                 self.preorder.skip_subtree();
-                                if let Some(mut v) = self.append.remove(&node) {
-                                    v.reverse();
+                                if let Some(v) = self.append.remove(&node) {
                                     self.current_leaves.extend(v);
-                                    return None;
+                                    continue;
                                 }
                             }
                         }
@@ -687,10 +704,9 @@ impl Converter {
                     }
                 }
                 WalkEvent::Leave(ele) => {
-                    if let Some(mut v) = self.append.remove(&ele) {
-                        v.reverse();
+                    if let Some(v) = self.append.remove(&ele) {
                         self.current_leaves.extend(v);
-                        return None;
+                        continue;
                     }
                 }
             }
@@ -715,8 +731,8 @@ impl SynToken {
     }
 }
 
-impl SrcToken, S> for SynToken {
-    fn kind(&self, _ctx: &Converter) -> SyntaxKind {
+impl SrcToken, S> for SynToken {
+    fn kind(&self, _ctx: &Converter) -> SyntaxKind {
         match self {
             SynToken::Ordinary(token) => token.kind(),
             SynToken::Punct { token, offset: i } => {
@@ -728,14 +744,14 @@ impl SrcToken, S> for SynToken {
             }
         }
     }
-    fn to_char(&self, _ctx: &Converter) -> Option {
+    fn to_char(&self, _ctx: &Converter) -> Option {
         match self {
             SynToken::Ordinary(_) => None,
             SynToken::Punct { token: it, offset: i } => it.text().chars().nth(*i),
             SynToken::Leaf(_) => None,
         }
     }
-    fn to_text(&self, _ctx: &Converter) -> SmolStr {
+    fn to_text(&self, _ctx: &Converter) -> SmolStr {
         match self {
             SynToken::Ordinary(token) | SynToken::Punct { token, offset: _ } => token.text().into(),
             SynToken::Leaf(_) => {
@@ -752,10 +768,11 @@ impl SrcToken, S> for SynToken {
     }
 }
 
-impl TokenConverter for Converter
+impl TokenConverter for Converter
 where
     S: Copy,
     SpanMap: SpanMapper,
+    OnEvent: FnMut(&mut PreorderWithTokens, &WalkEvent) -> (bool, Vec>),
 {
     type Token = SynToken;
     fn convert_doc_comment(
@@ -781,10 +798,7 @@ where
             ));
         }
 
-        if let Some(leaf) = self.current_leaves.pop() {
-            if self.current_leaves.is_empty() {
-                self.current = self.next_token();
-            }
+        if let Some(leaf) = self.current_leaves.pop_front() {
             return Some((SynToken::Leaf(leaf), TextRange::empty(TextSize::new(0))));
         }
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs
index aea99a4389b9..5d67fd449175 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs
@@ -26,7 +26,8 @@ pub use self::{
     generated::{nodes::*, tokens::*},
     node_ext::{
         AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind,
-        SlicePatComponents, StructKind, TypeBoundKind, TypeOrConstParam, VisibilityKind,
+        SlicePatComponents, StructKind, TokenTreeChildren, TypeBoundKind, TypeOrConstParam,
+        VisibilityKind,
     },
     operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
     token_ext::{
@@ -35,6 +36,7 @@ pub use self::{
     traits::{
         AttrDocCommentIter, DocCommentIter, HasArgList, HasAttrs, HasDocComments, HasGenericArgs,
         HasGenericParams, HasLoopBody, HasModuleItem, HasName, HasTypeBounds, HasVisibility,
+        attrs_including_inner,
     },
 };
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
index af741d100f68..901d17bb1491 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
@@ -10,7 +10,7 @@ use parser::SyntaxKind;
 use rowan::{GreenNodeData, GreenTokenData};
 
 use crate::{
-    NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, T, TokenText,
+    NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, TokenText,
     ast::{
         self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName,
         HasTypeBounds, SyntaxNode, support,
@@ -1114,3 +1114,39 @@ impl ast::OrPat {
             .filter(|it| it.kind() == T![|])
     }
 }
+
+/// An iterator over the elements in an [`ast::TokenTree`].
+///
+/// Does not yield trivia or the delimiters.
+#[derive(Clone)]
+pub struct TokenTreeChildren {
+    iter: SyntaxElementChildren,
+}
+
+impl TokenTreeChildren {
+    #[inline]
+    pub fn new(tt: &ast::TokenTree) -> Self {
+        let mut iter = tt.syntax.children_with_tokens();
+        iter.next(); // Bump the opening delimiter.
+        Self { iter }
+    }
+}
+
+impl Iterator for TokenTreeChildren {
+    type Item = NodeOrToken;
+
+    #[inline]
+    fn next(&mut self) -> Option {
+        self.iter.find_map(|item| match item {
+            NodeOrToken::Node(node) => ast::TokenTree::cast(node).map(NodeOrToken::Node),
+            NodeOrToken::Token(token) => {
+                let kind = token.kind();
+                (!matches!(
+                    kind,
+                    SyntaxKind::WHITESPACE | SyntaxKind::COMMENT | T![')'] | T![']'] | T!['}']
+                ))
+                .then_some(NodeOrToken::Token(token))
+            }
+        })
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
index e1a9f3ac0341..83ab87c1c687 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
@@ -40,8 +40,8 @@ impl ast::Comment {
     }
 
     /// Returns the textual content of a doc comment node as a single string with prefix and suffix
-    /// removed.
-    pub fn doc_comment(&self) -> Option<&str> {
+    /// removed, plus the offset of the returned string from the beginning of the comment.
+    pub fn doc_comment(&self) -> Option<(&str, TextSize)> {
         let kind = self.kind();
         match kind {
             CommentKind { shape, doc: Some(_) } => {
@@ -52,7 +52,7 @@ impl ast::Comment {
                 } else {
                     text
                 };
-                Some(text)
+                Some((text, TextSize::of(prefix)))
             }
             _ => None,
         }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
index 5290f32dd27d..2f4109a2c976 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
@@ -4,8 +4,9 @@
 use either::Either;
 
 use crate::{
-    SyntaxElement, SyntaxToken, T,
+    SyntaxElement, SyntaxNode, SyntaxToken, T,
     ast::{self, AstChildren, AstNode, AstToken, support},
+    match_ast,
     syntax_node::SyntaxElementChildren,
 };
 
@@ -76,34 +77,44 @@ pub trait HasAttrs: AstNode {
         self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom)
     }
 
-    /// Returns all attributes of this node, including inner attributes that may not be directly under this node
-    /// but under a child.
-    fn attrs_including_inner(self) -> impl Iterator
-    where
-        Self: Sized,
-    {
-        let inner_attrs_node = if let Some(it) =
-            support::child::(self.syntax()).and_then(|it| it.stmt_list())
-        {
-            Some(it.syntax)
-        } else if let Some(it) = support::child::(self.syntax()) {
-            Some(it.syntax)
-        } else if let Some(it) = support::child::(self.syntax()) {
-            Some(it.syntax)
-        } else if let Some(it) = support::child::(self.syntax()) {
-            Some(it.syntax)
-        } else if let Some(it) = support::child::(self.syntax()) {
-            Some(it.syntax)
-        } else if let Some(it) = support::child::(self.syntax()) {
-            Some(it.syntax)
-        } else {
-            None
-        };
-
-        self.attrs().chain(inner_attrs_node.into_iter().flat_map(|it| support::children(&it)))
+    /// This may return the same node as called with (with `SourceFile`). The caller has the responsibility
+    /// to avoid duplicate attributes.
+    fn inner_attributes_node(&self) -> Option {
+        let syntax = self.syntax();
+        Some(match_ast! {
+            match syntax {
+                // A `SourceFile` contains the inner attributes of itself.
+                ast::SourceFile(_) => syntax.clone(),
+                ast::ExternBlock(it) => it.extern_item_list()?.syntax().clone(),
+                ast::Fn(it) => it.body()?.stmt_list()?.syntax().clone(),
+                ast::MatchExpr(it) => it.match_arm_list()?.syntax().clone(),
+                ast::Impl(it) => it.assoc_item_list()?.syntax().clone(),
+                ast::Trait(it) => it.assoc_item_list()?.syntax().clone(),
+                ast::Module(it) => it.item_list()?.syntax().clone(),
+                ast::BlockExpr(it) => {
+                    if !it.may_carry_attributes() {
+                        return None;
+                    }
+                    syntax.clone()
+                },
+                _ => return None,
+            }
+        })
     }
 }
 
+/// Returns all attributes of this node, including inner attributes that may not be directly under this node
+/// but under a child.
+pub fn attrs_including_inner(owner: &dyn HasAttrs) -> impl Iterator + Clone {
+    owner.attrs().filter(|attr| attr.kind().is_outer()).chain(
+        owner
+            .inner_attributes_node()
+            .into_iter()
+            .flat_map(|node| support::children::(&node))
+            .filter(|attr| attr.kind().is_inner()),
+    )
+}
+
 pub trait HasDocComments: HasAttrs {
     fn doc_comments(&self) -> DocCommentIter {
         DocCommentIter { iter: self.syntax().children_with_tokens() }
@@ -118,7 +129,7 @@ impl DocCommentIter {
     #[cfg(test)]
     pub fn doc_comment_text(self) -> Option {
         let docs = itertools::Itertools::join(
-            &mut self.filter_map(|comment| comment.doc_comment().map(ToOwned::to_owned)),
+            &mut self.filter_map(|comment| comment.doc_comment().map(|it| it.0.to_owned())),
             "\n",
         );
         if docs.is_empty() { None } else { Some(docs) }
@@ -151,7 +162,7 @@ impl AttrDocCommentIter {
 impl Iterator for AttrDocCommentIter {
     type Item = Either;
     fn next(&mut self) -> Option {
-        self.iter.by_ref().find_map(|el| match el {
+        self.iter.find_map(|el| match el {
             SyntaxElement::Node(node) => ast::Attr::cast(node).map(Either::Left),
             SyntaxElement::Token(tok) => {
                 ast::Comment::cast(tok).filter(ast::Comment::is_doc).map(Either::Right)
diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
index aefe81f83e29..2b05add55216 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
@@ -5,7 +5,7 @@ use base_db::target::TargetData;
 use base_db::{
     Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData,
     DependencyBuilder, Env, FileChange, FileSet, FxIndexMap, LangCrateOrigin, SourceDatabase,
-    SourceRoot, Version, VfsPath, salsa,
+    SourceRoot, Version, VfsPath,
 };
 use cfg::CfgOptions;
 use hir_expand::{
@@ -37,10 +37,11 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static {
         #[rust_analyzer::rust_fixture] ra_fixture: &str,
     ) -> (Self, EditionedFileId) {
         let mut db = Self::default();
-        let fixture = ChangeFixture::parse(&db, ra_fixture);
+        let fixture = ChangeFixture::parse(ra_fixture);
         fixture.change.apply(&mut db);
         assert_eq!(fixture.files.len(), 1, "Multiple file found in the fixture");
-        (db, fixture.files[0])
+        let file = EditionedFileId::from_span_guess_origin(&db, fixture.files[0]);
+        (db, file)
     }
 
     #[track_caller]
@@ -48,16 +49,21 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static {
         #[rust_analyzer::rust_fixture] ra_fixture: &str,
     ) -> (Self, Vec) {
         let mut db = Self::default();
-        let fixture = ChangeFixture::parse(&db, ra_fixture);
+        let fixture = ChangeFixture::parse(ra_fixture);
         fixture.change.apply(&mut db);
         assert!(fixture.file_position.is_none());
-        (db, fixture.files)
+        let files = fixture
+            .files
+            .into_iter()
+            .map(|file| EditionedFileId::from_span_guess_origin(&db, file))
+            .collect();
+        (db, files)
     }
 
     #[track_caller]
     fn with_files(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Self {
         let mut db = Self::default();
-        let fixture = ChangeFixture::parse(&db, ra_fixture);
+        let fixture = ChangeFixture::parse(ra_fixture);
         fixture.change.apply(&mut db);
         assert!(fixture.file_position.is_none());
         db
@@ -69,12 +75,8 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static {
         proc_macros: Vec<(String, ProcMacro)>,
     ) -> Self {
         let mut db = Self::default();
-        let fixture = ChangeFixture::parse_with_proc_macros(
-            &db,
-            ra_fixture,
-            MiniCore::RAW_SOURCE,
-            proc_macros,
-        );
+        let fixture =
+            ChangeFixture::parse_with_proc_macros(ra_fixture, MiniCore::RAW_SOURCE, proc_macros);
         fixture.change.apply(&mut db);
         assert!(fixture.file_position.is_none());
         db
@@ -99,12 +101,13 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static {
         #[rust_analyzer::rust_fixture] ra_fixture: &str,
     ) -> (Self, EditionedFileId, RangeOrOffset) {
         let mut db = Self::default();
-        let fixture = ChangeFixture::parse(&db, ra_fixture);
+        let fixture = ChangeFixture::parse(ra_fixture);
         fixture.change.apply(&mut db);
 
         let (file_id, range_or_offset) = fixture
             .file_position
             .expect("Could not find file position in fixture. Did you forget to add an `$0`?");
+        let file_id = EditionedFileId::from_span_guess_origin(&db, file_id);
         (db, file_id, range_or_offset)
     }
 
@@ -116,9 +119,9 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static {
 impl WithFixture for DB {}
 
 pub struct ChangeFixture {
-    pub file_position: Option<(EditionedFileId, RangeOrOffset)>,
+    pub file_position: Option<(span::EditionedFileId, RangeOrOffset)>,
     pub file_lines: Vec,
-    pub files: Vec,
+    pub files: Vec,
     pub change: ChangeWithProcMacros,
     pub sysroot_files: Vec,
 }
@@ -126,15 +129,11 @@ pub struct ChangeFixture {
 const SOURCE_ROOT_PREFIX: &str = "/";
 
 impl ChangeFixture {
-    pub fn parse(
-        db: &dyn salsa::Database,
-        #[rust_analyzer::rust_fixture] ra_fixture: &str,
-    ) -> ChangeFixture {
-        Self::parse_with_proc_macros(db, ra_fixture, MiniCore::RAW_SOURCE, Vec::new())
+    pub fn parse(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> ChangeFixture {
+        Self::parse_with_proc_macros(ra_fixture, MiniCore::RAW_SOURCE, Vec::new())
     }
 
     pub fn parse_with_proc_macros(
-        db: &dyn salsa::Database,
         #[rust_analyzer::rust_fixture] ra_fixture: &str,
         minicore_raw: &str,
         mut proc_macro_defs: Vec<(String, ProcMacro)>,
@@ -202,7 +201,7 @@ impl ChangeFixture {
             let meta = FileMeta::from_fixture(entry, current_source_root_kind);
             if let Some(range_or_offset) = range_or_offset {
                 file_position =
-                    Some((EditionedFileId::new(db, file_id, meta.edition), range_or_offset));
+                    Some((span::EditionedFileId::new(file_id, meta.edition), range_or_offset));
             }
 
             assert!(meta.path.starts_with(SOURCE_ROOT_PREFIX));
@@ -259,7 +258,7 @@ impl ChangeFixture {
             source_change.change_file(file_id, Some(text));
             let path = VfsPath::new_virtual_path(meta.path);
             file_set.insert(file_id, path);
-            files.push(EditionedFileId::new(db, file_id, meta.edition));
+            files.push(span::EditionedFileId::new(file_id, meta.edition));
             file_id = FileId::from_raw(file_id.index() + 1);
         }
 

From 2ae4ddbecbe69e1076cc3526a38453fa3c7020b4 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Wed, 22 Oct 2025 19:19:13 +0300
Subject: [PATCH 066/170] Revert "internal: Rewrite attribute handling"

---
 src/tools/rust-analyzer/Cargo.lock            |    6 +-
 src/tools/rust-analyzer/Cargo.toml            |    5 +-
 .../crates/base-db/src/editioned_file_id.rs   |  291 ---
 .../rust-analyzer/crates/base-db/src/input.rs |    7 +-
 .../rust-analyzer/crates/base-db/src/lib.rs   |   39 +-
 src/tools/rust-analyzer/crates/cfg/Cargo.toml |    1 -
 .../rust-analyzer/crates/cfg/src/cfg_expr.rs  |   59 -
 .../rust-analyzer/crates/cfg/src/tests.rs     |   42 +-
 .../rust-analyzer/crates/hir-def/Cargo.toml   |    4 +-
 .../rust-analyzer/crates/hir-def/src/attr.rs  |  901 +++++++++
 .../rust-analyzer/crates/hir-def/src/attrs.rs | 1613 -----------------
 .../rust-analyzer/crates/hir-def/src/db.rs    |   71 +-
 .../crates/hir-def/src/expr_store/expander.rs |   14 +-
 .../crates/hir-def/src/expr_store/lower.rs    |   22 +-
 .../crates/hir-def/src/expr_store/pretty.rs   |   19 +-
 .../src/expr_store/tests/body/block.rs        |    4 +-
 .../src/expr_store/tests/signatures.rs        |   14 +-
 .../crates/hir-def/src/import_map.rs          |   34 +-
 .../crates/hir-def/src/item_tree.rs           |   40 +-
 .../crates/hir-def/src/item_tree/attrs.rs     |  220 ---
 .../crates/hir-def/src/item_tree/lower.rs     |   35 +-
 .../crates/hir-def/src/item_tree/pretty.rs    |   12 +-
 .../crates/hir-def/src/item_tree/tests.rs     |    9 +-
 .../crates/hir-def/src/lang_item.rs           |    7 +-
 .../rust-analyzer/crates/hir-def/src/lib.rs   |   94 +-
 .../hir-def/src/macro_expansion_tests/mbe.rs  |   43 +-
 .../hir-def/src/macro_expansion_tests/mod.rs  |   15 -
 .../src/macro_expansion_tests/proc_macros.rs  |   96 +-
 .../crates/hir-def/src/nameres.rs             |   15 +-
 .../crates/hir-def/src/nameres/assoc.rs       |   39 +-
 .../hir-def/src/nameres/attr_resolution.rs    |   10 +-
 .../crates/hir-def/src/nameres/collector.rs   |  179 +-
 .../crates/hir-def/src/nameres/diagnostics.rs |   14 +-
 .../hir-def/src/nameres/mod_resolution.rs     |    5 +-
 .../crates/hir-def/src/nameres/proc_macro.rs  |   24 +-
 .../crates/hir-def/src/signatures.rs          |  130 +-
 .../rust-analyzer/crates/hir-def/src/src.rs   |    9 +-
 .../crates/hir-def/src/test_db.rs             |   33 +-
 .../crates/hir-expand/Cargo.toml              |    2 -
 .../crates/hir-expand/src/attrs.rs            |  822 ++++-----
 .../crates/hir-expand/src/builtin/fn_macro.rs |    2 +-
 .../crates/hir-expand/src/cfg_process.rs      |  682 +++----
 .../rust-analyzer/crates/hir-expand/src/db.rs |  179 +-
 .../crates/hir-expand/src/declarative.rs      |   58 +-
 .../crates/hir-expand/src/files.rs            |   33 +-
 .../crates/hir-expand/src/fixup.rs            |    5 +-
 .../crates/hir-expand/src/lib.rs              |  165 +-
 .../crates/hir-expand/src/mod_path.rs         |   59 +-
 .../crates/hir-expand/src/span_map.rs         |   13 +-
 .../crates/hir-ty/src/consteval.rs            |    3 +-
 .../hir-ty/src/diagnostics/decl_check.rs      |    6 +-
 .../diagnostics/match_check/pat_analysis.rs   |    6 +-
 .../hir-ty/src/diagnostics/unsafe_check.rs    |    4 +-
 .../rust-analyzer/crates/hir-ty/src/infer.rs  |   14 +-
 .../crates/hir-ty/src/infer/coerce.rs         |   14 +-
 .../crates/hir-ty/src/infer/expr.rs           |   14 +-
 .../rust-analyzer/crates/hir-ty/src/layout.rs |    4 +-
 .../crates/hir-ty/src/layout/adt.rs           |   35 +-
 .../crates/hir-ty/src/method_resolution.rs    |    5 +-
 .../crates/hir-ty/src/mir/eval/shim.rs        |   45 +-
 .../crates/hir-ty/src/next_solver/interner.rs |   59 +-
 .../crates/hir-ty/src/target_feature.rs       |   46 +-
 .../crates/hir-ty/src/tests/incremental.rs    |   49 +-
 .../rust-analyzer/crates/hir-ty/src/utils.rs  |    8 +-
 .../rust-analyzer/crates/hir/src/attrs.rs     |  256 +--
 .../crates/hir/src/diagnostics.rs             |   13 +-
 src/tools/rust-analyzer/crates/hir/src/lib.rs |  267 ++-
 .../rust-analyzer/crates/hir/src/semantics.rs |   71 +-
 .../hir/src/semantics/child_by_source.rs      |   13 +-
 .../rust-analyzer/crates/hir/src/symbols.rs   |    8 +-
 .../src/handlers/add_missing_match_arms.rs    |    6 +-
 .../handlers/destructure_struct_binding.rs    |    4 +-
 .../src/handlers/move_module_to_file.rs       |   10 +-
 .../crates/ide-assists/src/lib.rs             |    4 +-
 .../crates/ide-assists/src/tests.rs           |    4 +-
 .../crates/ide-assists/src/utils.rs           |   13 +-
 .../src/completions/attribute/lint.rs         |    2 +-
 .../src/completions/flyimport.rs              |    4 +-
 .../ide-completion/src/completions/postfix.rs |    2 +-
 .../ide-completion/src/completions/snippet.rs |    2 +-
 .../crates/ide-completion/src/context.rs      |   22 +-
 .../crates/ide-completion/src/item.rs         |   12 +-
 .../crates/ide-completion/src/render.rs       |   13 +-
 .../ide-completion/src/render/literal.rs      |    2 +-
 .../ide-completion/src/render/pattern.rs      |    2 +-
 .../ide-completion/src/render/variant.rs      |    6 +-
 .../crates/ide-completion/src/tests.rs        |    4 +-
 .../rust-analyzer/crates/ide-db/src/defs.rs   |   38 +-
 .../crates/ide-db/src/documentation.rs        |  383 +++-
 .../crates/ide-db/src/ra_fixture.rs           |   12 +-
 .../crates/ide-db/src/rust_doc.rs             |    2 +-
 .../rust-analyzer/crates/ide-db/src/search.rs |   16 +-
 .../ide-db/src/test_data/test_doc_alias.txt   |   30 +-
 .../test_symbol_index_collection.txt          |  134 +-
 .../test_symbols_exclude_imports.txt          |    2 +-
 .../test_data/test_symbols_with_imports.txt   |    4 +-
 .../rust-analyzer/crates/ide-db/src/traits.rs |    6 +-
 .../src/handlers/inactive_code.rs             |    3 +-
 .../src/handlers/invalid_derive_target.rs     |    4 +-
 .../src/handlers/macro_error.rs               |   22 +-
 .../src/handlers/malformed_derive.rs          |    4 +-
 .../src/handlers/unresolved_macro_call.rs     |    5 +-
 .../crates/ide-diagnostics/src/lib.rs         |   43 +-
 .../crates/ide-ssr/src/from_comment.rs        |    2 +-
 .../rust-analyzer/crates/ide-ssr/src/lib.rs   |    6 +-
 .../crates/ide-ssr/src/search.rs              |    8 +-
 .../rust-analyzer/crates/ide/src/doc_links.rs |   38 +-
 .../crates/ide/src/doc_links/tests.rs         |   69 +-
 .../rust-analyzer/crates/ide/src/fixture.rs   |   32 +-
 .../crates/ide/src/goto_implementation.rs     |    2 +-
 .../crates/ide/src/highlight_related.rs       |    2 +-
 .../crates/ide/src/hover/render.rs            |   52 +-
 .../crates/ide/src/inlay_hints.rs             |    4 +-
 src/tools/rust-analyzer/crates/ide/src/lib.rs |   13 +-
 .../crates/ide/src/navigation_target.rs       |   38 +-
 .../crates/ide/src/references.rs              |    5 +-
 .../rust-analyzer/crates/ide/src/runnables.rs |   50 +-
 .../crates/ide/src/signature_help.rs          |   36 +-
 .../crates/ide/src/static_index.rs            |    6 +-
 .../crates/ide/src/syntax_highlighting.rs     |    2 +-
 .../ide/src/syntax_highlighting/html.rs       |    2 +-
 .../ide/src/syntax_highlighting/inject.rs     |  191 +-
 .../test_data/highlight_doctest.html          |   72 +-
 .../rust-analyzer/crates/ide/src/typing.rs    |    5 +-
 .../crates/ide/src/typing/on_enter.rs         |    2 +-
 .../crates/ide/src/view_item_tree.rs          |    2 +-
 .../rust-analyzer/src/cli/analysis_stats.rs   |    8 +-
 .../crates/rust-analyzer/src/cli/scip.rs      |    6 +-
 .../crates/rust-analyzer/src/cli/ssr.rs       |    2 +-
 .../src/cli/unresolved_references.rs          |    2 +-
 .../crates/rust-analyzer/src/lsp/to_proto.rs  |    4 +-
 .../crates/syntax-bridge/src/lib.rs           |   68 +-
 .../rust-analyzer/crates/syntax/src/ast.rs    |    4 +-
 .../crates/syntax/src/ast/node_ext.rs         |   38 +-
 .../crates/syntax/src/ast/token_ext.rs        |    6 +-
 .../crates/syntax/src/ast/traits.rs           |   67 +-
 .../crates/test-fixture/src/lib.rs            |   45 +-
 137 files changed, 3848 insertions(+), 4996 deletions(-)
 delete mode 100644 src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs
 create mode 100644 src/tools/rust-analyzer/crates/hir-def/src/attr.rs
 delete mode 100644 src/tools/rust-analyzer/crates/hir-def/src/attrs.rs
 delete mode 100644 src/tools/rust-analyzer/crates/hir-def/src/item_tree/attrs.rs

diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index d31d233dc4b6..ea8d1a781dcc 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -725,7 +725,6 @@ dependencies = [
 name = "hir-expand"
 version = "0.0.0"
 dependencies = [
- "arrayvec",
  "base-db",
  "cfg",
  "cov-mark",
@@ -744,7 +743,6 @@ dependencies = [
  "stdx",
  "syntax",
  "syntax-bridge",
- "thin-vec",
  "tracing",
  "triomphe",
  "tt",
@@ -1993,9 +1991,9 @@ dependencies = [
 
 [[package]]
 name = "rowan"
-version = "0.15.17"
+version = "0.15.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4f1e4a001f863f41ea8d0e6a0c34b356d5b733db50dadab3efef640bafb779b"
+checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49"
 dependencies = [
  "countme",
  "hashbrown 0.14.5",
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 767dbcae9031..8a108974681a 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -52,7 +52,7 @@ debug = 2
 # local crates
 macros = { path = "./crates/macros", version = "0.0.0" }
 base-db = { path = "./crates/base-db", version = "0.0.0" }
-cfg = { path = "./crates/cfg", version = "0.0.0", features = ["tt", "syntax"] }
+cfg = { path = "./crates/cfg", version = "0.0.0", features = ["tt"] }
 hir = { path = "./crates/hir", version = "0.0.0" }
 hir-def = { path = "./crates/hir-def", version = "0.0.0" }
 hir-expand = { path = "./crates/hir-expand", version = "0.0.0" }
@@ -131,7 +131,7 @@ process-wrap = { version = "8.2.1", features = ["std"] }
 pulldown-cmark-to-cmark = "10.0.4"
 pulldown-cmark = { version = "0.9.6", default-features = false }
 rayon = "1.10.0"
-rowan = "=0.15.17"
+rowan = "=0.15.15"
 # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work
 # on impls without it
 salsa = { version = "0.24.0", default-features = true, features = [
@@ -167,7 +167,6 @@ tracing-subscriber = { version = "0.3.20", default-features = false, features =
 triomphe = { version = "0.1.14", default-features = false, features = ["std"] }
 url = "2.5.4"
 xshell = "0.2.7"
-thin-vec = "0.2.14"
 petgraph = { version = "0.8.2", default-features = false }
 
 # We need to freeze the version of the crate, as the raw-api feature is considered unstable
diff --git a/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs b/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs
deleted file mode 100644
index 2f8969c0ea33..000000000000
--- a/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs
+++ /dev/null
@@ -1,291 +0,0 @@
-//! Defines [`EditionedFileId`], an interned wrapper around [`span::EditionedFileId`] that
-//! is interned (so queries can take it) and remembers its crate.
-
-use core::fmt;
-use std::hash::{Hash, Hasher};
-
-use span::Edition;
-use vfs::FileId;
-
-use crate::{Crate, RootQueryDb};
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct EditionedFileId(
-    salsa::Id,
-    std::marker::PhantomData<&'static salsa::plumbing::interned::Value>,
-);
-
-const _: () = {
-    use salsa::plumbing as zalsa_;
-    use zalsa_::interned as zalsa_struct_;
-    type Configuration_ = EditionedFileId;
-
-    #[derive(Debug, Clone, PartialEq, Eq)]
-    pub struct EditionedFileIdData {
-        editioned_file_id: span::EditionedFileId,
-        krate: Crate,
-    }
-
-    /// We like to include the origin crate in an `EditionedFileId` (for use in the item tree),
-    /// but this poses us a problem.
-    ///
-    /// Spans contain `EditionedFileId`s, and we don't want to make them store the crate too
-    /// because that will increase their size, which will increase memory usage significantly.
-    /// Furthermore, things using spans do not generally need the crate: they are using the
-    /// file id for queries like `ast_id_map` or `parse`, which do not care about the crate.
-    ///
-    /// To solve this, we hash **only the `span::EditionedFileId`**, but on still compare
-    /// the crate in equality check. This preserves the invariant of `Hash` and `Eq` -
-    /// although same hashes can be used for different items, same file ids used for multiple
-    /// crates is a rare thing, and different items always have different hashes. Then,
-    /// when we only have a `span::EditionedFileId`, we use the `intern()` method to
-    /// reuse existing file ids, and create new one only if needed. See [`from_span_guess_origin`].
-    ///
-    /// See this for more info: https://rust-lang.zulipchat.com/#narrow/channel/185405-t-compiler.2Frust-analyzer/topic/Letting.20EditionedFileId.20know.20its.20crate/near/530189401
-    ///
-    /// [`from_span_guess_origin`]: EditionedFileId::from_span_guess_origin
-    #[derive(Hash, PartialEq, Eq)]
-    struct WithoutCrate {
-        editioned_file_id: span::EditionedFileId,
-    }
-
-    impl Hash for EditionedFileIdData {
-        #[inline]
-        fn hash(&self, state: &mut H) {
-            let EditionedFileIdData { editioned_file_id, krate: _ } = *self;
-            editioned_file_id.hash(state);
-        }
-    }
-
-    impl zalsa_struct_::HashEqLike for EditionedFileIdData {
-        #[inline]
-        fn hash(&self, state: &mut H) {
-            Hash::hash(self, state);
-        }
-
-        #[inline]
-        fn eq(&self, data: &WithoutCrate) -> bool {
-            let EditionedFileIdData { editioned_file_id, krate: _ } = *self;
-            editioned_file_id == data.editioned_file_id
-        }
-    }
-
-    impl zalsa_::HasJar for EditionedFileId {
-        type Jar = zalsa_struct_::JarImpl;
-        const KIND: zalsa_::JarKind = zalsa_::JarKind::Struct;
-    }
-
-    zalsa_::register_jar! {
-        zalsa_::ErasedJar::erase::()
-    }
-
-    impl zalsa_struct_::Configuration for EditionedFileId {
-        const LOCATION: salsa::plumbing::Location =
-            salsa::plumbing::Location { file: file!(), line: line!() };
-        const DEBUG_NAME: &'static str = "EditionedFileId";
-        const REVISIONS: std::num::NonZeroUsize = std::num::NonZeroUsize::MAX;
-        const PERSIST: bool = false;
-
-        type Fields<'a> = EditionedFileIdData;
-        type Struct<'db> = EditionedFileId;
-
-        fn serialize(_: &Self::Fields<'_>, _: S) -> Result
-        where
-            S: zalsa_::serde::Serializer,
-        {
-            unimplemented!("attempted to serialize value that set `PERSIST` to false")
-        }
-
-        fn deserialize<'de, D>(_: D) -> Result, D::Error>
-        where
-            D: zalsa_::serde::Deserializer<'de>,
-        {
-            unimplemented!("attempted to deserialize value that cannot set `PERSIST` to false");
-        }
-    }
-
-    impl Configuration_ {
-        pub fn ingredient(zalsa: &zalsa_::Zalsa) -> &zalsa_struct_::IngredientImpl {
-            static CACHE: zalsa_::IngredientCache> =
-                zalsa_::IngredientCache::new();
-
-            // SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the only
-            // ingredient created by our jar is the struct ingredient.
-            unsafe {
-                CACHE.get_or_create(zalsa, || {
-                    zalsa.lookup_jar_by_type::>()
-                })
-            }
-        }
-    }
-
-    impl zalsa_::AsId for EditionedFileId {
-        fn as_id(&self) -> salsa::Id {
-            self.0.as_id()
-        }
-    }
-    impl zalsa_::FromId for EditionedFileId {
-        fn from_id(id: salsa::Id) -> Self {
-            Self(::from_id(id), std::marker::PhantomData)
-        }
-    }
-
-    unsafe impl Send for EditionedFileId {}
-    unsafe impl Sync for EditionedFileId {}
-
-    impl std::fmt::Debug for EditionedFileId {
-        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-            Self::default_debug_fmt(*self, f)
-        }
-    }
-
-    impl zalsa_::SalsaStructInDb for EditionedFileId {
-        type MemoIngredientMap = salsa::plumbing::MemoIngredientSingletonIndex;
-
-        fn lookup_ingredient_index(aux: &zalsa_::Zalsa) -> salsa::plumbing::IngredientIndices {
-            aux.lookup_jar_by_type::>().into()
-        }
-
-        fn entries(zalsa: &zalsa_::Zalsa) -> impl Iterator + '_ {
-            let _ingredient_index =
-                zalsa.lookup_jar_by_type::>();
-            ::ingredient(zalsa).entries(zalsa).map(|entry| entry.key())
-        }
-
-        #[inline]
-        fn cast(id: salsa::Id, type_id: std::any::TypeId) -> Option {
-            if type_id == std::any::TypeId::of::() {
-                Some(::from_id(id))
-            } else {
-                None
-            }
-        }
-
-        #[inline]
-        unsafe fn memo_table(
-            zalsa: &zalsa_::Zalsa,
-            id: zalsa_::Id,
-            current_revision: zalsa_::Revision,
-        ) -> zalsa_::MemoTableWithTypes<'_> {
-            // SAFETY: Guaranteed by caller.
-            unsafe {
-                zalsa.table().memos::>(id, current_revision)
-            }
-        }
-    }
-
-    unsafe impl zalsa_::Update for EditionedFileId {
-        unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
-            if unsafe { *old_pointer } != new_value {
-                unsafe { *old_pointer = new_value };
-                true
-            } else {
-                false
-            }
-        }
-    }
-
-    impl EditionedFileId {
-        pub fn from_span(
-            db: &(impl salsa::Database + ?Sized),
-            editioned_file_id: span::EditionedFileId,
-            krate: Crate,
-        ) -> Self {
-            let (zalsa, zalsa_local) = db.zalsas();
-            Configuration_::ingredient(zalsa).intern(
-                zalsa,
-                zalsa_local,
-                EditionedFileIdData { editioned_file_id, krate },
-                |_, data| data,
-            )
-        }
-
-        /// Guesses the crate for the file.
-        ///
-        /// Only use this if you cannot precisely determine the origin. This can happen in one of two cases:
-        ///
-        ///  1. The file is not in the module tree.
-        ///  2. You are latency sensitive and cannot afford calling the def map to precisely compute the origin
-        ///     (e.g. on enter feature, folding, etc.).
-        pub fn from_span_guess_origin(
-            db: &dyn RootQueryDb,
-            editioned_file_id: span::EditionedFileId,
-        ) -> Self {
-            let (zalsa, zalsa_local) = db.zalsas();
-            Configuration_::ingredient(zalsa).intern(
-                zalsa,
-                zalsa_local,
-                WithoutCrate { editioned_file_id },
-                |_, _| {
-                    // FileId not in the database.
-                    let krate = db
-                        .relevant_crates(editioned_file_id.file_id())
-                        .first()
-                        .copied()
-                        .unwrap_or_else(|| db.all_crates()[0]);
-                    EditionedFileIdData { editioned_file_id, krate }
-                },
-            )
-        }
-
-        pub fn editioned_file_id(self, db: &dyn salsa::Database) -> span::EditionedFileId {
-            let zalsa = db.zalsa();
-            let fields = Configuration_::ingredient(zalsa).fields(zalsa, self);
-            fields.editioned_file_id
-        }
-
-        pub fn krate(self, db: &dyn salsa::Database) -> Crate {
-            let zalsa = db.zalsa();
-            let fields = Configuration_::ingredient(zalsa).fields(zalsa, self);
-            fields.krate
-        }
-
-        /// Default debug formatting for this struct (may be useful if you define your own `Debug` impl)
-        pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-            zalsa_::with_attached_database(|db| {
-                let zalsa = db.zalsa();
-                let fields = Configuration_::ingredient(zalsa).fields(zalsa, this);
-                fmt::Debug::fmt(fields, f)
-            })
-            .unwrap_or_else(|| {
-                f.debug_tuple("EditionedFileId").field(&zalsa_::AsId::as_id(&this)).finish()
-            })
-        }
-    }
-};
-
-impl EditionedFileId {
-    #[inline]
-    pub fn new(db: &dyn salsa::Database, file_id: FileId, edition: Edition, krate: Crate) -> Self {
-        EditionedFileId::from_span(db, span::EditionedFileId::new(file_id, edition), krate)
-    }
-
-    /// Attaches the current edition and guesses the crate for the file.
-    ///
-    /// Only use this if you cannot precisely determine the origin. This can happen in one of two cases:
-    ///
-    ///  1. The file is not in the module tree.
-    ///  2. You are latency sensitive and cannot afford calling the def map to precisely compute the origin
-    ///     (e.g. on enter feature, folding, etc.).
-    #[inline]
-    pub fn current_edition_guess_origin(db: &dyn RootQueryDb, file_id: FileId) -> Self {
-        Self::from_span_guess_origin(db, span::EditionedFileId::current_edition(file_id))
-    }
-
-    #[inline]
-    pub fn file_id(self, db: &dyn salsa::Database) -> vfs::FileId {
-        let id = self.editioned_file_id(db);
-        id.file_id()
-    }
-
-    #[inline]
-    pub fn unpack(self, db: &dyn salsa::Database) -> (vfs::FileId, span::Edition) {
-        let id = self.editioned_file_id(db);
-        (id.file_id(), id.edition())
-    }
-
-    #[inline]
-    pub fn edition(self, db: &dyn salsa::Database) -> Edition {
-        self.editioned_file_id(db).edition()
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs
index 28539d59825f..cac74778a26b 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -829,10 +829,9 @@ pub(crate) fn transitive_rev_deps(db: &dyn RootQueryDb, of: Crate) -> FxHashSet<
     rev_deps
 }
 
-impl Crate {
-    pub fn root_file_id(self, db: &dyn salsa::Database) -> EditionedFileId {
-        let data = self.data(db);
-        EditionedFileId::new(db, data.root_file_id, data.edition, self)
+impl BuiltCrateData {
+    pub fn root_file_id(&self, db: &dyn salsa::Database) -> EditionedFileId {
+        EditionedFileId::new(db, self.root_file_id, self.edition)
     }
 }
 
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 32909af5d78d..0e411bcfae60 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
@@ -5,7 +5,6 @@ pub use salsa_macros;
 
 // FIXME: Rename this crate, base db is non descriptive
 mod change;
-mod editioned_file_id;
 mod input;
 pub mod target;
 
@@ -18,7 +17,6 @@ use std::{
 
 pub use crate::{
     change::FileChange,
-    editioned_file_id::EditionedFileId,
     input::{
         BuiltCrateData, BuiltDependency, Crate, CrateBuilder, CrateBuilderId, CrateDataBuilder,
         CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CratesIdMap, CratesMap,
@@ -31,6 +29,7 @@ pub use query_group::{self};
 use rustc_hash::{FxHashSet, FxHasher};
 use salsa::{Durability, Setter};
 pub use semver::{BuildMetadata, Prerelease, Version, VersionReq};
+use span::Edition;
 use syntax::{Parse, SyntaxError, ast};
 use triomphe::Arc;
 pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet};
@@ -176,6 +175,42 @@ impl Files {
     }
 }
 
+#[salsa_macros::interned(no_lifetime, debug, constructor=from_span, revisions = usize::MAX)]
+#[derive(PartialOrd, Ord)]
+pub struct EditionedFileId {
+    pub editioned_file_id: span::EditionedFileId,
+}
+
+impl EditionedFileId {
+    // Salsa already uses the name `new`...
+    #[inline]
+    pub fn new(db: &dyn salsa::Database, file_id: FileId, edition: Edition) -> Self {
+        EditionedFileId::from_span(db, span::EditionedFileId::new(file_id, edition))
+    }
+
+    #[inline]
+    pub fn current_edition(db: &dyn salsa::Database, file_id: FileId) -> Self {
+        EditionedFileId::new(db, file_id, Edition::CURRENT)
+    }
+
+    #[inline]
+    pub fn file_id(self, db: &dyn salsa::Database) -> vfs::FileId {
+        let id = self.editioned_file_id(db);
+        id.file_id()
+    }
+
+    #[inline]
+    pub fn unpack(self, db: &dyn salsa::Database) -> (vfs::FileId, span::Edition) {
+        let id = self.editioned_file_id(db);
+        (id.file_id(), id.edition())
+    }
+
+    #[inline]
+    pub fn edition(self, db: &dyn SourceDatabase) -> Edition {
+        self.editioned_file_id(db).edition()
+    }
+}
+
 #[salsa_macros::input(debug)]
 pub struct FileText {
     #[returns(ref)]
diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml
index 9e2a95dbf32c..e17969bd82d4 100644
--- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml
@@ -18,7 +18,6 @@ tracing.workspace = true
 
 # locals deps
 tt = { workspace = true, optional = true }
-syntax = { workspace = true, optional = true }
 intern.workspace = true
 
 [dev-dependencies]
diff --git a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs
index 76e0aba859e6..7a21015e14be 100644
--- a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs
+++ b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs
@@ -63,8 +63,6 @@ impl From for CfgExpr {
 }
 
 impl CfgExpr {
-    // FIXME: Parsing from `tt` is only used in a handful of places, reconsider
-    // if we should switch them to AST.
     #[cfg(feature = "tt")]
     pub fn parse(tt: &tt::TopSubtree) -> CfgExpr {
         next_cfg_expr(&mut tt.iter()).unwrap_or(CfgExpr::Invalid)
@@ -75,13 +73,6 @@ impl CfgExpr {
         next_cfg_expr(tt).unwrap_or(CfgExpr::Invalid)
     }
 
-    #[cfg(feature = "syntax")]
-    pub fn parse_from_ast(
-        ast: &mut std::iter::Peekable,
-    ) -> CfgExpr {
-        next_cfg_expr_from_ast(ast).unwrap_or(CfgExpr::Invalid)
-    }
-
     /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
     pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option {
         match self {
@@ -98,56 +89,6 @@ impl CfgExpr {
     }
 }
 
-#[cfg(feature = "syntax")]
-fn next_cfg_expr_from_ast(
-    it: &mut std::iter::Peekable,
-) -> Option {
-    use intern::sym;
-    use syntax::{NodeOrToken, SyntaxKind, T, ast};
-
-    let name = match it.next() {
-        None => return None,
-        Some(NodeOrToken::Token(ident)) if ident.kind().is_any_identifier() => {
-            Symbol::intern(ident.text())
-        }
-        Some(_) => return Some(CfgExpr::Invalid),
-    };
-
-    let ret = match it.peek() {
-        Some(NodeOrToken::Token(eq)) if eq.kind() == T![=] => {
-            it.next();
-            if let Some(NodeOrToken::Token(literal)) = it.peek()
-                && matches!(literal.kind(), SyntaxKind::STRING)
-            {
-                let literal = tt::token_to_literal(literal.text(), ()).symbol;
-                it.next();
-                CfgAtom::KeyValue { key: name, value: literal.clone() }.into()
-            } else {
-                return Some(CfgExpr::Invalid);
-            }
-        }
-        Some(NodeOrToken::Node(subtree)) => {
-            let mut subtree_iter = ast::TokenTreeChildren::new(subtree).peekable();
-            it.next();
-            let mut subs = std::iter::from_fn(|| next_cfg_expr_from_ast(&mut subtree_iter));
-            match name {
-                s if s == sym::all => CfgExpr::All(subs.collect()),
-                s if s == sym::any => CfgExpr::Any(subs.collect()),
-                s if s == sym::not => {
-                    CfgExpr::Not(Box::new(subs.next().unwrap_or(CfgExpr::Invalid)))
-                }
-                _ => CfgExpr::Invalid,
-            }
-        }
-        _ => CfgAtom::Flag(name).into(),
-    };
-
-    // Eat comma separator
-    while it.next().is_some_and(|it| it.as_token().is_none_or(|it| it.kind() != T![,])) {}
-
-    Some(ret)
-}
-
 #[cfg(feature = "tt")]
 fn next_cfg_expr(it: &mut tt::iter::TtIter<'_, S>) -> Option {
     use intern::sym;
diff --git a/src/tools/rust-analyzer/crates/cfg/src/tests.rs b/src/tools/rust-analyzer/crates/cfg/src/tests.rs
index 52c581dbbd3a..6766748097f0 100644
--- a/src/tools/rust-analyzer/crates/cfg/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/cfg/src/tests.rs
@@ -1,10 +1,7 @@
 use arbitrary::{Arbitrary, Unstructured};
 use expect_test::{Expect, expect};
 use intern::Symbol;
-use syntax::{
-    AstNode, Edition,
-    ast::{self, TokenTreeChildren},
-};
+use syntax::{AstNode, Edition, ast};
 use syntax_bridge::{
     DocCommentDesugarMode,
     dummy_test_span_utils::{DUMMY, DummyTestSpanMap},
@@ -13,33 +10,24 @@ use syntax_bridge::{
 
 use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
 
-#[track_caller]
-fn parse_ast_cfg(tt: &ast::TokenTree) -> CfgExpr {
-    CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(tt).peekable())
-}
-
-#[track_caller]
 fn assert_parse_result(input: &str, expected: CfgExpr) {
     let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
-    let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+    let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
     let tt = syntax_node_to_token_tree(
-        tt_ast.syntax(),
+        tt.syntax(),
         DummyTestSpanMap,
         DUMMY,
         DocCommentDesugarMode::ProcMacro,
     );
     let cfg = CfgExpr::parse(&tt);
     assert_eq!(cfg, expected);
-    let cfg = parse_ast_cfg(&tt_ast);
-    assert_eq!(cfg, expected);
 }
 
-#[track_caller]
 fn check_dnf(input: &str, expect: Expect) {
     let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
-    let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+    let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
     let tt = syntax_node_to_token_tree(
-        tt_ast.syntax(),
+        tt.syntax(),
         DummyTestSpanMap,
         DUMMY,
         DocCommentDesugarMode::ProcMacro,
@@ -47,17 +35,13 @@ fn check_dnf(input: &str, expect: Expect) {
     let cfg = CfgExpr::parse(&tt);
     let actual = format!("#![cfg({})]", DnfExpr::new(&cfg));
     expect.assert_eq(&actual);
-    let cfg = parse_ast_cfg(&tt_ast);
-    let actual = format!("#![cfg({})]", DnfExpr::new(&cfg));
-    expect.assert_eq(&actual);
 }
 
-#[track_caller]
 fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
     let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
-    let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+    let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
     let tt = syntax_node_to_token_tree(
-        tt_ast.syntax(),
+        tt.syntax(),
         DummyTestSpanMap,
         DUMMY,
         DocCommentDesugarMode::ProcMacro,
@@ -66,18 +50,14 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
     let dnf = DnfExpr::new(&cfg);
     let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
     expect.assert_eq(&why_inactive);
-    let cfg = parse_ast_cfg(&tt_ast);
-    let dnf = DnfExpr::new(&cfg);
-    let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
-    expect.assert_eq(&why_inactive);
 }
 
 #[track_caller]
 fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
     let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
-    let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+    let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
     let tt = syntax_node_to_token_tree(
-        tt_ast.syntax(),
+        tt.syntax(),
         DummyTestSpanMap,
         DUMMY,
         DocCommentDesugarMode::ProcMacro,
@@ -86,10 +66,6 @@ fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
     let dnf = DnfExpr::new(&cfg);
     let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::>();
     assert_eq!(hints, expected_hints);
-    let cfg = parse_ast_cfg(&tt_ast);
-    let dnf = DnfExpr::new(&cfg);
-    let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::>();
-    assert_eq!(hints, expected_hints);
 }
 
 #[test]
diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
index e1f60742d324..abb4819a7672 100644
--- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
@@ -45,8 +45,7 @@ mbe.workspace = true
 cfg.workspace = true
 tt.workspace = true
 span.workspace = true
-thin-vec.workspace = true
-syntax-bridge.workspace = true
+thin-vec = "0.2.14"
 
 [dev-dependencies]
 expect-test.workspace = true
@@ -54,6 +53,7 @@ expect-test.workspace = true
 # local deps
 test-utils.workspace = true
 test-fixture.workspace = true
+syntax-bridge.workspace = true
 
 [features]
 in-rust-tree = ["hir-expand/in-rust-tree"]
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
new file mode 100644
index 000000000000..b4fcfa11aea7
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
@@ -0,0 +1,901 @@
+//! A higher level attributes based on TokenTree, with also some shortcuts.
+
+use std::{borrow::Cow, convert::identity, hash::Hash, ops};
+
+use base_db::Crate;
+use cfg::{CfgExpr, CfgOptions};
+use either::Either;
+use hir_expand::{
+    HirFileId, InFile,
+    attrs::{Attr, AttrId, RawAttrs, collect_attrs},
+    span_map::SpanMapRef,
+};
+use intern::{Symbol, sym};
+use la_arena::{ArenaMap, Idx, RawIdx};
+use mbe::DelimiterKind;
+use rustc_abi::ReprOptions;
+use span::AstIdNode;
+use syntax::{
+    AstPtr,
+    ast::{self, HasAttrs},
+};
+use triomphe::Arc;
+use tt::iter::{TtElement, TtIter};
+
+use crate::{
+    AdtId, AstIdLoc, AttrDefId, GenericParamId, HasModule, LocalFieldId, Lookup, MacroId,
+    VariantId,
+    db::DefDatabase,
+    item_tree::block_item_tree_query,
+    lang_item::LangItem,
+    nameres::{ModuleOrigin, ModuleSource},
+    src::{HasChildSource, HasSource},
+};
+
+/// Desugared attributes of an item post `cfg_attr` expansion.
+#[derive(Default, Debug, Clone, PartialEq, Eq)]
+pub struct Attrs(RawAttrs);
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct AttrsWithOwner {
+    attrs: Attrs,
+    owner: AttrDefId,
+}
+
+impl Attrs {
+    pub fn new(
+        db: &dyn DefDatabase,
+        owner: &dyn ast::HasAttrs,
+        span_map: SpanMapRef<'_>,
+        cfg_options: &CfgOptions,
+    ) -> Self {
+        Attrs(RawAttrs::new_expanded(db, owner, span_map, cfg_options))
+    }
+
+    pub fn get(&self, id: AttrId) -> Option<&Attr> {
+        (**self).iter().find(|attr| attr.id == id)
+    }
+
+    pub(crate) fn expand_cfg_attr(
+        db: &dyn DefDatabase,
+        krate: Crate,
+        raw_attrs: RawAttrs,
+    ) -> Attrs {
+        Attrs(raw_attrs.expand_cfg_attr(db, krate))
+    }
+
+    pub(crate) fn is_cfg_enabled_for(
+        db: &dyn DefDatabase,
+        owner: &dyn ast::HasAttrs,
+        span_map: SpanMapRef<'_>,
+        cfg_options: &CfgOptions,
+    ) -> Result<(), CfgExpr> {
+        RawAttrs::attrs_iter_expanded::(db, owner, span_map, cfg_options)
+            .filter_map(|attr| attr.cfg())
+            .find_map(|cfg| match cfg_options.check(&cfg).is_none_or(identity) {
+                true => None,
+                false => Some(cfg),
+            })
+            .map_or(Ok(()), Err)
+    }
+}
+
+impl ops::Deref for Attrs {
+    type Target = [Attr];
+
+    fn deref(&self) -> &[Attr] {
+        &self.0
+    }
+}
+
+impl ops::Deref for AttrsWithOwner {
+    type Target = Attrs;
+
+    fn deref(&self) -> &Attrs {
+        &self.attrs
+    }
+}
+
+impl Attrs {
+    pub const EMPTY: Self = Self(RawAttrs::EMPTY);
+
+    pub(crate) fn fields_attrs_query(
+        db: &dyn DefDatabase,
+        v: VariantId,
+    ) -> Arc> {
+        let _p = tracing::info_span!("fields_attrs_query").entered();
+        let mut res = ArenaMap::default();
+        let (fields, file_id, krate) = match v {
+            VariantId::EnumVariantId(it) => {
+                let loc = it.lookup(db);
+                let krate = loc.parent.lookup(db).container.krate;
+                let source = loc.source(db);
+                (source.value.field_list(), source.file_id, krate)
+            }
+            VariantId::StructId(it) => {
+                let loc = it.lookup(db);
+                let krate = loc.container.krate;
+                let source = loc.source(db);
+                (source.value.field_list(), source.file_id, krate)
+            }
+            VariantId::UnionId(it) => {
+                let loc = it.lookup(db);
+                let krate = loc.container.krate;
+                let source = loc.source(db);
+                (
+                    source.value.record_field_list().map(ast::FieldList::RecordFieldList),
+                    source.file_id,
+                    krate,
+                )
+            }
+        };
+        let Some(fields) = fields else {
+            return Arc::new(res);
+        };
+
+        let cfg_options = krate.cfg_options(db);
+        let span_map = db.span_map(file_id);
+
+        match fields {
+            ast::FieldList::RecordFieldList(fields) => {
+                let mut idx = 0;
+                for field in fields.fields() {
+                    let attrs =
+                        Attrs(RawAttrs::new_expanded(db, &field, span_map.as_ref(), cfg_options));
+                    if attrs.is_cfg_enabled(cfg_options).is_ok() {
+                        res.insert(Idx::from_raw(RawIdx::from(idx)), attrs);
+                        idx += 1;
+                    }
+                }
+            }
+            ast::FieldList::TupleFieldList(fields) => {
+                let mut idx = 0;
+                for field in fields.fields() {
+                    let attrs =
+                        Attrs(RawAttrs::new_expanded(db, &field, span_map.as_ref(), cfg_options));
+                    if attrs.is_cfg_enabled(cfg_options).is_ok() {
+                        res.insert(Idx::from_raw(RawIdx::from(idx)), attrs);
+                        idx += 1;
+                    }
+                }
+            }
+        }
+
+        res.shrink_to_fit();
+        Arc::new(res)
+    }
+}
+
+impl Attrs {
+    #[inline]
+    pub fn by_key(&self, key: Symbol) -> AttrQuery<'_> {
+        AttrQuery { attrs: self, key }
+    }
+
+    #[inline]
+    pub fn rust_analyzer_tool(&self) -> impl Iterator {
+        self.iter()
+            .filter(|&attr| attr.path.segments().first().is_some_and(|s| *s == sym::rust_analyzer))
+    }
+
+    #[inline]
+    pub fn cfg(&self) -> Option {
+        let mut cfgs = self.by_key(sym::cfg).tt_values().map(CfgExpr::parse);
+        let first = cfgs.next()?;
+        match cfgs.next() {
+            Some(second) => {
+                let cfgs = [first, second].into_iter().chain(cfgs);
+                Some(CfgExpr::All(cfgs.collect()))
+            }
+            None => Some(first),
+        }
+    }
+
+    #[inline]
+    pub fn cfgs(&self) -> impl Iterator + '_ {
+        self.by_key(sym::cfg).tt_values().map(CfgExpr::parse)
+    }
+
+    #[inline]
+    pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Result<(), CfgExpr> {
+        self.cfgs().try_for_each(|cfg| {
+            if cfg_options.check(&cfg) != Some(false) { Ok(()) } else { Err(cfg) }
+        })
+    }
+
+    #[inline]
+    pub fn lang(&self) -> Option<&Symbol> {
+        self.by_key(sym::lang).string_value()
+    }
+
+    #[inline]
+    pub fn lang_item(&self) -> Option {
+        self.by_key(sym::lang).string_value().and_then(LangItem::from_symbol)
+    }
+
+    #[inline]
+    pub fn has_doc_hidden(&self) -> bool {
+        self.by_key(sym::doc).tt_values().any(|tt| {
+            tt.top_subtree().delimiter.kind == DelimiterKind::Parenthesis &&
+                matches!(tt.token_trees().flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::hidden)
+        })
+    }
+
+    #[inline]
+    pub fn has_doc_notable_trait(&self) -> bool {
+        self.by_key(sym::doc).tt_values().any(|tt| {
+            tt.top_subtree().delimiter.kind == DelimiterKind::Parenthesis &&
+                matches!(tt.token_trees().flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::notable_trait)
+        })
+    }
+
+    #[inline]
+    pub fn doc_exprs(&self) -> impl Iterator + '_ {
+        self.by_key(sym::doc).tt_values().map(DocExpr::parse)
+    }
+
+    #[inline]
+    pub fn doc_aliases(&self) -> impl Iterator + '_ {
+        self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec())
+    }
+
+    #[inline]
+    pub fn export_name(&self) -> Option<&Symbol> {
+        self.by_key(sym::export_name).string_value()
+    }
+
+    #[inline]
+    pub fn is_proc_macro(&self) -> bool {
+        self.by_key(sym::proc_macro).exists()
+    }
+
+    #[inline]
+    pub fn is_proc_macro_attribute(&self) -> bool {
+        self.by_key(sym::proc_macro_attribute).exists()
+    }
+
+    #[inline]
+    pub fn is_proc_macro_derive(&self) -> bool {
+        self.by_key(sym::proc_macro_derive).exists()
+    }
+
+    #[inline]
+    pub fn is_test(&self) -> bool {
+        self.iter().any(|it| {
+            it.path()
+                .segments()
+                .iter()
+                .rev()
+                .zip([sym::core, sym::prelude, sym::v1, sym::test].iter().rev())
+                .all(|it| it.0 == it.1)
+        })
+    }
+
+    #[inline]
+    pub fn is_ignore(&self) -> bool {
+        self.by_key(sym::ignore).exists()
+    }
+
+    #[inline]
+    pub fn is_bench(&self) -> bool {
+        self.by_key(sym::bench).exists()
+    }
+
+    #[inline]
+    pub fn is_unstable(&self) -> bool {
+        self.by_key(sym::unstable).exists()
+    }
+
+    #[inline]
+    pub fn rustc_legacy_const_generics(&self) -> Option>> {
+        self.by_key(sym::rustc_legacy_const_generics)
+            .tt_values()
+            .next()
+            .map(parse_rustc_legacy_const_generics)
+            .filter(|it| !it.is_empty())
+            .map(Box::new)
+    }
+
+    #[inline]
+    pub fn repr(&self) -> Option {
+        self.by_key(sym::repr).tt_values().filter_map(parse_repr_tt).fold(None, |acc, repr| {
+            acc.map_or(Some(repr), |mut acc| {
+                merge_repr(&mut acc, repr);
+                Some(acc)
+            })
+        })
+    }
+}
+
+fn parse_rustc_legacy_const_generics(tt: &crate::tt::TopSubtree) -> Box<[u32]> {
+    let mut indices = Vec::new();
+    let mut iter = tt.iter();
+    while let (Some(first), second) = (iter.next(), iter.next()) {
+        match first {
+            TtElement::Leaf(tt::Leaf::Literal(lit)) => match lit.symbol.as_str().parse() {
+                Ok(index) => indices.push(index),
+                Err(_) => break,
+            },
+            _ => break,
+        }
+
+        if let Some(comma) = second {
+            match comma {
+                TtElement::Leaf(tt::Leaf::Punct(punct)) if punct.char == ',' => {}
+                _ => break,
+            }
+        }
+    }
+
+    indices.into_boxed_slice()
+}
+
+fn merge_repr(this: &mut ReprOptions, other: ReprOptions) {
+    let ReprOptions { int, align, pack, flags, field_shuffle_seed: _ } = this;
+    flags.insert(other.flags);
+    *align = (*align).max(other.align);
+    *pack = match (*pack, other.pack) {
+        (Some(pack), None) | (None, Some(pack)) => Some(pack),
+        _ => (*pack).min(other.pack),
+    };
+    if other.int.is_some() {
+        *int = other.int;
+    }
+}
+
+fn parse_repr_tt(tt: &crate::tt::TopSubtree) -> Option {
+    use crate::builtin_type::{BuiltinInt, BuiltinUint};
+    use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
+
+    match tt.top_subtree().delimiter {
+        tt::Delimiter { kind: DelimiterKind::Parenthesis, .. } => {}
+        _ => return None,
+    }
+
+    let mut acc = ReprOptions::default();
+    let mut tts = tt.iter();
+    while let Some(tt) = tts.next() {
+        let TtElement::Leaf(tt::Leaf::Ident(ident)) = tt else {
+            continue;
+        };
+        let repr = match &ident.sym {
+            s if *s == sym::packed => {
+                let pack = if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() {
+                    tts.next();
+                    if let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = tt_iter.next() {
+                        lit.symbol.as_str().parse().unwrap_or_default()
+                    } else {
+                        0
+                    }
+                } else {
+                    0
+                };
+                let pack = Some(Align::from_bytes(pack).unwrap_or(Align::ONE));
+                ReprOptions { pack, ..Default::default() }
+            }
+            s if *s == sym::align => {
+                let mut align = None;
+                if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() {
+                    tts.next();
+                    if let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = tt_iter.next()
+                        && let Ok(a) = lit.symbol.as_str().parse()
+                    {
+                        align = Align::from_bytes(a).ok();
+                    }
+                }
+                ReprOptions { align, ..Default::default() }
+            }
+            s if *s == sym::C => ReprOptions { flags: ReprFlags::IS_C, ..Default::default() },
+            s if *s == sym::transparent => {
+                ReprOptions { flags: ReprFlags::IS_TRANSPARENT, ..Default::default() }
+            }
+            s if *s == sym::simd => ReprOptions { flags: ReprFlags::IS_SIMD, ..Default::default() },
+            repr => {
+                let mut int = None;
+                if let Some(builtin) = BuiltinInt::from_suffix_sym(repr)
+                    .map(Either::Left)
+                    .or_else(|| BuiltinUint::from_suffix_sym(repr).map(Either::Right))
+                {
+                    int = Some(match builtin {
+                        Either::Left(bi) => match bi {
+                            BuiltinInt::Isize => IntegerType::Pointer(true),
+                            BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true),
+                            BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true),
+                            BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true),
+                            BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true),
+                            BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true),
+                        },
+                        Either::Right(bu) => match bu {
+                            BuiltinUint::Usize => IntegerType::Pointer(false),
+                            BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false),
+                            BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false),
+                            BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false),
+                            BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false),
+                            BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false),
+                        },
+                    });
+                }
+                ReprOptions { int, ..Default::default() }
+            }
+        };
+        merge_repr(&mut acc, repr);
+    }
+
+    Some(acc)
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum DocAtom {
+    /// eg. `#[doc(hidden)]`
+    Flag(Symbol),
+    /// eg. `#[doc(alias = "it")]`
+    ///
+    /// Note that a key can have multiple values that are all considered "active" at the same time.
+    /// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`.
+    KeyValue { key: Symbol, value: Symbol },
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum DocExpr {
+    Invalid,
+    /// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]`
+    Atom(DocAtom),
+    /// eg. `#[doc(alias("x", "y"))]`
+    Alias(Vec),
+}
+
+impl From for DocExpr {
+    fn from(atom: DocAtom) -> Self {
+        DocExpr::Atom(atom)
+    }
+}
+
+impl DocExpr {
+    fn parse(tt: &tt::TopSubtree) -> DocExpr {
+        next_doc_expr(tt.iter()).unwrap_or(DocExpr::Invalid)
+    }
+
+    pub fn aliases(&self) -> &[Symbol] {
+        match self {
+            DocExpr::Atom(DocAtom::KeyValue { key, value }) if *key == sym::alias => {
+                std::slice::from_ref(value)
+            }
+            DocExpr::Alias(aliases) => aliases,
+            _ => &[],
+        }
+    }
+}
+
+fn next_doc_expr(mut it: TtIter<'_, S>) -> Option {
+    let name = match it.next() {
+        None => return None,
+        Some(TtElement::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(),
+        Some(_) => return Some(DocExpr::Invalid),
+    };
+
+    // Peek
+    let ret = match it.peek() {
+        Some(TtElement::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => {
+            it.next();
+            match it.next() {
+                Some(TtElement::Leaf(tt::Leaf::Literal(tt::Literal {
+                    symbol: text,
+                    kind: tt::LitKind::Str,
+                    ..
+                }))) => DocAtom::KeyValue { key: name, value: text.clone() }.into(),
+                _ => return Some(DocExpr::Invalid),
+            }
+        }
+        Some(TtElement::Subtree(_, subtree_iter)) => {
+            it.next();
+            let subs = parse_comma_sep(subtree_iter);
+            match &name {
+                s if *s == sym::alias => DocExpr::Alias(subs),
+                _ => DocExpr::Invalid,
+            }
+        }
+        _ => DocAtom::Flag(name).into(),
+    };
+    Some(ret)
+}
+
+fn parse_comma_sep(iter: TtIter<'_, S>) -> Vec {
+    iter.filter_map(|tt| match tt {
+        TtElement::Leaf(tt::Leaf::Literal(tt::Literal {
+            kind: tt::LitKind::Str, symbol, ..
+        })) => Some(symbol.clone()),
+        _ => None,
+    })
+    .collect()
+}
+
+impl AttrsWithOwner {
+    pub fn new(db: &dyn DefDatabase, owner: AttrDefId) -> Self {
+        Self { attrs: db.attrs(owner), owner }
+    }
+
+    pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
+        let _p = tracing::info_span!("attrs_query").entered();
+        // FIXME: this should use `Trace` to avoid duplication in `source_map` below
+        match def {
+            AttrDefId::ModuleId(module) => {
+                let def_map = module.def_map(db);
+                let mod_data = &def_map[module.local_id];
+
+                let raw_attrs = match mod_data.origin {
+                    ModuleOrigin::File { definition, declaration_tree_id, declaration, .. } => {
+                        let decl_attrs = declaration_tree_id
+                            .item_tree(db)
+                            .raw_attrs(declaration.upcast())
+                            .clone();
+                        let tree = db.file_item_tree(definition.into());
+                        let def_attrs = tree.top_level_raw_attrs().clone();
+                        decl_attrs.merge(def_attrs)
+                    }
+                    ModuleOrigin::CrateRoot { definition } => {
+                        let tree = db.file_item_tree(definition.into());
+                        tree.top_level_raw_attrs().clone()
+                    }
+                    ModuleOrigin::Inline { definition_tree_id, definition } => {
+                        definition_tree_id.item_tree(db).raw_attrs(definition.upcast()).clone()
+                    }
+                    ModuleOrigin::BlockExpr { id, .. } => {
+                        let tree = block_item_tree_query(db, id);
+                        tree.top_level_raw_attrs().clone()
+                    }
+                };
+                Attrs::expand_cfg_attr(db, module.krate, raw_attrs)
+            }
+            AttrDefId::FieldId(it) => db.fields_attrs(it.parent)[it.local_id].clone(),
+            AttrDefId::EnumVariantId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::AdtId(it) => match it {
+                AdtId::StructId(it) => attrs_from_ast_id_loc(db, it),
+                AdtId::EnumId(it) => attrs_from_ast_id_loc(db, it),
+                AdtId::UnionId(it) => attrs_from_ast_id_loc(db, it),
+            },
+            AttrDefId::TraitId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::MacroId(it) => match it {
+                MacroId::Macro2Id(it) => attrs_from_ast_id_loc(db, it),
+                MacroId::MacroRulesId(it) => attrs_from_ast_id_loc(db, it),
+                MacroId::ProcMacroId(it) => attrs_from_ast_id_loc(db, it),
+            },
+            AttrDefId::ImplId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::ConstId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::StaticId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::FunctionId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::TypeAliasId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::GenericParamId(it) => match it {
+                GenericParamId::ConstParamId(it) => {
+                    let src = it.parent().child_source(db);
+                    // FIXME: We should be never getting `None` here.
+                    Attrs(match src.value.get(it.local_id()) {
+                        Some(val) => RawAttrs::new_expanded(
+                            db,
+                            val,
+                            db.span_map(src.file_id).as_ref(),
+                            def.krate(db).cfg_options(db),
+                        ),
+                        None => RawAttrs::EMPTY,
+                    })
+                }
+                GenericParamId::TypeParamId(it) => {
+                    let src = it.parent().child_source(db);
+                    // FIXME: We should be never getting `None` here.
+                    Attrs(match src.value.get(it.local_id()) {
+                        Some(val) => RawAttrs::new_expanded(
+                            db,
+                            val,
+                            db.span_map(src.file_id).as_ref(),
+                            def.krate(db).cfg_options(db),
+                        ),
+                        None => RawAttrs::EMPTY,
+                    })
+                }
+                GenericParamId::LifetimeParamId(it) => {
+                    let src = it.parent.child_source(db);
+                    // FIXME: We should be never getting `None` here.
+                    Attrs(match src.value.get(it.local_id) {
+                        Some(val) => RawAttrs::new_expanded(
+                            db,
+                            val,
+                            db.span_map(src.file_id).as_ref(),
+                            def.krate(db).cfg_options(db),
+                        ),
+                        None => RawAttrs::EMPTY,
+                    })
+                }
+            },
+            AttrDefId::ExternBlockId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::ExternCrateId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::UseId(it) => attrs_from_ast_id_loc(db, it),
+        }
+    }
+
+    pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap {
+        let owner = match self.owner {
+            AttrDefId::ModuleId(module) => {
+                // Modules can have 2 attribute owners (the `mod x;` item, and the module file itself).
+
+                let def_map = module.def_map(db);
+                let mod_data = &def_map[module.local_id];
+                match mod_data.declaration_source(db) {
+                    Some(it) => {
+                        let mut map = AttrSourceMap::new(InFile::new(it.file_id, &it.value));
+                        if let InFile { file_id, value: ModuleSource::SourceFile(file) } =
+                            mod_data.definition_source(db)
+                        {
+                            map.append_module_inline_attrs(AttrSourceMap::new(InFile::new(
+                                file_id, &file,
+                            )));
+                        }
+                        return map;
+                    }
+                    None => {
+                        let InFile { file_id, value } = mod_data.definition_source(db);
+                        let attrs_owner = match &value {
+                            ModuleSource::SourceFile(file) => file as &dyn ast::HasAttrs,
+                            ModuleSource::Module(module) => module as &dyn ast::HasAttrs,
+                            ModuleSource::BlockExpr(block) => block as &dyn ast::HasAttrs,
+                        };
+                        return AttrSourceMap::new(InFile::new(file_id, attrs_owner));
+                    }
+                }
+            }
+            AttrDefId::FieldId(id) => {
+                let map = db.fields_attrs_source_map(id.parent);
+                let file_id = id.parent.file_id(db);
+                let root = db.parse_or_expand(file_id);
+                let owner = ast::AnyHasAttrs::new(map[id.local_id].to_node(&root));
+                InFile::new(file_id, owner)
+            }
+            AttrDefId::AdtId(adt) => match adt {
+                AdtId::StructId(id) => any_has_attrs(db, id),
+                AdtId::UnionId(id) => any_has_attrs(db, id),
+                AdtId::EnumId(id) => any_has_attrs(db, id),
+            },
+            AttrDefId::FunctionId(id) => any_has_attrs(db, id),
+            AttrDefId::EnumVariantId(id) => any_has_attrs(db, id),
+            AttrDefId::StaticId(id) => any_has_attrs(db, id),
+            AttrDefId::ConstId(id) => any_has_attrs(db, id),
+            AttrDefId::TraitId(id) => any_has_attrs(db, id),
+            AttrDefId::TypeAliasId(id) => any_has_attrs(db, id),
+            AttrDefId::MacroId(id) => match id {
+                MacroId::Macro2Id(id) => any_has_attrs(db, id),
+                MacroId::MacroRulesId(id) => any_has_attrs(db, id),
+                MacroId::ProcMacroId(id) => any_has_attrs(db, id),
+            },
+            AttrDefId::ImplId(id) => any_has_attrs(db, id),
+            AttrDefId::GenericParamId(id) => match id {
+                GenericParamId::ConstParamId(id) => id
+                    .parent()
+                    .child_source(db)
+                    .map(|source| ast::AnyHasAttrs::new(source[id.local_id()].clone())),
+                GenericParamId::TypeParamId(id) => id
+                    .parent()
+                    .child_source(db)
+                    .map(|source| ast::AnyHasAttrs::new(source[id.local_id()].clone())),
+                GenericParamId::LifetimeParamId(id) => id
+                    .parent
+                    .child_source(db)
+                    .map(|source| ast::AnyHasAttrs::new(source[id.local_id].clone())),
+            },
+            AttrDefId::ExternBlockId(id) => any_has_attrs(db, id),
+            AttrDefId::ExternCrateId(id) => any_has_attrs(db, id),
+            AttrDefId::UseId(id) => any_has_attrs(db, id),
+        };
+
+        AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs))
+    }
+}
+
+#[derive(Debug)]
+pub struct AttrSourceMap {
+    source: Vec>,
+    file_id: HirFileId,
+    /// If this map is for a module, this will be the [`HirFileId`] of the module's definition site,
+    /// while `file_id` will be the one of the module declaration site.
+    /// The usize is the index into `source` from which point on the entries reside in the def site
+    /// file.
+    mod_def_site_file_id: Option<(HirFileId, usize)>,
+}
+
+impl AttrSourceMap {
+    fn new(owner: InFile<&dyn ast::HasAttrs>) -> Self {
+        Self {
+            source: collect_attrs(owner.value).map(|(_, it)| it).collect(),
+            file_id: owner.file_id,
+            mod_def_site_file_id: None,
+        }
+    }
+
+    /// Append a second source map to this one, this is required for modules, whose outline and inline
+    /// attributes can reside in different files
+    fn append_module_inline_attrs(&mut self, other: Self) {
+        assert!(self.mod_def_site_file_id.is_none() && other.mod_def_site_file_id.is_none());
+        let len = self.source.len();
+        self.source.extend(other.source);
+        if other.file_id != self.file_id {
+            self.mod_def_site_file_id = Some((other.file_id, len));
+        }
+    }
+
+    /// Maps the lowered `Attr` back to its original syntax node.
+    ///
+    /// `attr` must come from the `owner` used for AttrSourceMap
+    ///
+    /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
+    /// the attribute represented by `Attr`.
+    pub fn source_of(&self, attr: &Attr) -> InFile<&Either> {
+        self.source_of_id(attr.id)
+    }
+
+    pub fn source_of_id(&self, id: AttrId) -> InFile<&Either> {
+        let ast_idx = id.ast_index();
+        let file_id = match self.mod_def_site_file_id {
+            Some((file_id, def_site_cut)) if def_site_cut <= ast_idx => file_id,
+            _ => self.file_id,
+        };
+
+        self.source
+            .get(ast_idx)
+            .map(|it| InFile::new(file_id, it))
+            .unwrap_or_else(|| panic!("cannot find attr at index {id:?}"))
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct AttrQuery<'attr> {
+    attrs: &'attr Attrs,
+    key: Symbol,
+}
+
+impl<'attr> AttrQuery<'attr> {
+    #[inline]
+    pub fn tt_values(self) -> impl Iterator {
+        self.attrs().filter_map(|attr| attr.token_tree_value())
+    }
+
+    #[inline]
+    pub fn string_value(self) -> Option<&'attr Symbol> {
+        self.attrs().find_map(|attr| attr.string_value())
+    }
+
+    #[inline]
+    pub fn string_value_with_span(self) -> Option<(&'attr Symbol, span::Span)> {
+        self.attrs().find_map(|attr| attr.string_value_with_span())
+    }
+
+    #[inline]
+    pub fn string_value_unescape(self) -> Option> {
+        self.attrs().find_map(|attr| attr.string_value_unescape())
+    }
+
+    #[inline]
+    pub fn exists(self) -> bool {
+        self.attrs().next().is_some()
+    }
+
+    #[inline]
+    pub fn attrs(self) -> impl Iterator + Clone {
+        let key = self.key;
+        self.attrs.iter().filter(move |attr| attr.path.as_ident().is_some_and(|s| *s == key))
+    }
+
+    /// Find string value for a specific key inside token tree
+    ///
+    /// ```ignore
+    /// #[doc(html_root_url = "url")]
+    ///       ^^^^^^^^^^^^^ key
+    /// ```
+    #[inline]
+    pub fn find_string_value_in_tt(self, key: Symbol) -> Option<&'attr str> {
+        self.tt_values().find_map(|tt| {
+            let name = tt.iter()
+                .skip_while(|tt| !matches!(tt, TtElement::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == key))
+                .nth(2);
+
+            match name {
+                Some(TtElement::Leaf(tt::Leaf::Literal(tt::Literal{  symbol: text, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_) , ..}))) => Some(text.as_str()),
+                _ => None
+            }
+        })
+    }
+}
+
+fn any_has_attrs<'db>(
+    db: &(dyn DefDatabase + 'db),
+    id: impl Lookup>,
+) -> InFile {
+    id.lookup(db).source(db).map(ast::AnyHasAttrs::new)
+}
+
+fn attrs_from_ast_id_loc<'db, N: AstIdNode + HasAttrs>(
+    db: &(dyn DefDatabase + 'db),
+    lookup: impl Lookup + HasModule>,
+) -> Attrs {
+    let loc = lookup.lookup(db);
+    let source = loc.source(db);
+    let span_map = db.span_map(source.file_id);
+    let cfg_options = loc.krate(db).cfg_options(db);
+    Attrs(RawAttrs::new_expanded(db, &source.value, span_map.as_ref(), cfg_options))
+}
+
+pub(crate) fn fields_attrs_source_map(
+    db: &dyn DefDatabase,
+    def: VariantId,
+) -> Arc>>> {
+    let mut res = ArenaMap::default();
+    let child_source = def.child_source(db);
+
+    for (idx, variant) in child_source.value.iter() {
+        res.insert(
+            idx,
+            variant
+                .as_ref()
+                .either(|l| AstPtr::new(l).wrap_left(), |r| AstPtr::new(r).wrap_right()),
+        );
+    }
+
+    Arc::new(res)
+}
+
+#[cfg(test)]
+mod tests {
+    //! This module contains tests for doc-expression parsing.
+    //! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`.
+
+    use intern::Symbol;
+    use span::EditionedFileId;
+    use triomphe::Arc;
+
+    use hir_expand::span_map::{RealSpanMap, SpanMap};
+    use span::FileId;
+    use syntax::{AstNode, TextRange, ast};
+    use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree};
+
+    use crate::attr::{DocAtom, DocExpr};
+
+    fn assert_parse_result(input: &str, expected: DocExpr) {
+        let source_file = ast::SourceFile::parse(input, span::Edition::CURRENT).ok().unwrap();
+        let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+        let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(
+            EditionedFileId::current_edition(FileId::from_raw(0)),
+        )));
+        let tt = syntax_node_to_token_tree(
+            tt.syntax(),
+            map.as_ref(),
+            map.span_for_range(TextRange::empty(0.into())),
+            DocCommentDesugarMode::ProcMacro,
+        );
+        let cfg = DocExpr::parse(&tt);
+        assert_eq!(cfg, expected);
+    }
+
+    #[test]
+    fn test_doc_expr_parser() {
+        assert_parse_result("#![doc(hidden)]", DocAtom::Flag(Symbol::intern("hidden")).into());
+
+        assert_parse_result(
+            r#"#![doc(alias = "foo")]"#,
+            DocAtom::KeyValue { key: Symbol::intern("alias"), value: Symbol::intern("foo") }.into(),
+        );
+
+        assert_parse_result(
+            r#"#![doc(alias("foo"))]"#,
+            DocExpr::Alias([Symbol::intern("foo")].into()),
+        );
+        assert_parse_result(
+            r#"#![doc(alias("foo", "bar", "baz"))]"#,
+            DocExpr::Alias(
+                [Symbol::intern("foo"), Symbol::intern("bar"), Symbol::intern("baz")].into(),
+            ),
+        );
+
+        assert_parse_result(
+            r#"
+        #[doc(alias("Bar", "Qux"))]
+        struct Foo;"#,
+            DocExpr::Alias([Symbol::intern("Bar"), Symbol::intern("Qux")].into()),
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs
deleted file mode 100644
index 1897cb5205aa..000000000000
--- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs
+++ /dev/null
@@ -1,1613 +0,0 @@
-//! Attributes for anything that is not name resolution.
-//!
-//! The fundamental idea of this module stems from the observation that most "interesting"
-//! attributes have a more memory-compact form than storing their full syntax, and
-//! that most of the attributes are flags, and those that are not are rare. Therefore,
-//! this module defines [`AttrFlags`], which is a bitflag enum that contains only a yes/no
-//! answer to whether an attribute is present on an item. For most attributes, that's all
-//! that is interesting us; for the rest of them, we define another query that extracts
-//! their data. A key part is that every one of those queries will have a wrapper method
-//! that queries (or is given) the `AttrFlags` and checks for the presence of the attribute;
-//! if it is not present, we do not call the query, to prevent Salsa from needing to record
-//! its value. This way, queries are only called on items that have the attribute, which is
-//! usually only a few.
-//!
-//! An exception to this model that is also defined in this module is documentation (doc
-//! comments and `#[doc = "..."]` attributes). But it also has a more compact form than
-//! the attribute: a concatenated string of the full docs as well as a source map
-//! to map it back to AST (which is needed for things like resolving links in doc comments
-//! and highlight injection). The lowering and upmapping of doc comments is a bit complicated,
-//! but it is encapsulated in the [`Docs`] struct.
-
-use std::{
-    convert::Infallible,
-    iter::Peekable,
-    ops::{ControlFlow, Range},
-};
-
-use base_db::Crate;
-use cfg::{CfgExpr, CfgOptions};
-use either::Either;
-use hir_expand::{
-    HirFileId, InFile, Lookup,
-    attrs::{Meta, expand_cfg_attr, expand_cfg_attr_with_doc_comments},
-};
-use intern::Symbol;
-use itertools::Itertools;
-use la_arena::ArenaMap;
-use rustc_abi::ReprOptions;
-use rustc_hash::FxHashSet;
-use smallvec::SmallVec;
-use syntax::{
-    AstNode, AstToken, NodeOrToken, SmolStr, SyntaxNode, SyntaxToken, T,
-    ast::{self, AttrDocCommentIter, HasAttrs, IsString, TokenTreeChildren},
-};
-use tt::{TextRange, TextSize};
-
-use crate::{
-    AdtId, AstIdLoc, AttrDefId, FieldId, FunctionId, GenericDefId, HasModule, InternedModuleId,
-    LifetimeParamId, LocalFieldId, MacroId, TypeOrConstParamId, VariantId,
-    db::DefDatabase,
-    hir::generics::{GenericParams, LocalLifetimeParamId, LocalTypeOrConstParamId},
-    lang_item::LangItem,
-    nameres::ModuleOrigin,
-    src::{HasChildSource, HasSource},
-};
-
-#[inline]
-fn attrs_from_ast_id_loc>(
-    db: &dyn DefDatabase,
-    lookup: impl Lookup + HasModule>,
-) -> (InFile, Crate) {
-    let loc = lookup.lookup(db);
-    let source = loc.source(db);
-    let krate = loc.krate(db);
-    (source.map(|it| it.into()), krate)
-}
-
-#[inline]
-fn extract_doc_tt_attr(attr_flags: &mut AttrFlags, tt: ast::TokenTree) {
-    for atom in DocAtom::parse(tt) {
-        match atom {
-            DocAtom::Flag(flag) => match &*flag {
-                "notable_trait" => attr_flags.insert(AttrFlags::IS_DOC_NOTABLE_TRAIT),
-                "hidden" => attr_flags.insert(AttrFlags::IS_DOC_HIDDEN),
-                _ => {}
-            },
-            DocAtom::KeyValue { key, value: _ } => match &*key {
-                "alias" => attr_flags.insert(AttrFlags::HAS_DOC_ALIASES),
-                "keyword" => attr_flags.insert(AttrFlags::HAS_DOC_KEYWORD),
-                _ => {}
-            },
-            DocAtom::Alias(_) => attr_flags.insert(AttrFlags::HAS_DOC_ALIASES),
-        }
-    }
-}
-
-fn extract_ra_completions(attr_flags: &mut AttrFlags, tt: ast::TokenTree) {
-    let tt = TokenTreeChildren::new(&tt);
-    if let Ok(NodeOrToken::Token(option)) = tt.exactly_one()
-        && option.kind().is_any_identifier()
-    {
-        match option.text() {
-            "ignore_flyimport" => attr_flags.insert(AttrFlags::COMPLETE_IGNORE_FLYIMPORT),
-            "ignore_methods" => attr_flags.insert(AttrFlags::COMPLETE_IGNORE_METHODS),
-            "ignore_flyimport_methods" => {
-                attr_flags.insert(AttrFlags::COMPLETE_IGNORE_FLYIMPORT_METHODS)
-            }
-            _ => {}
-        }
-    }
-}
-
-fn extract_rustc_skip_during_method_dispatch(attr_flags: &mut AttrFlags, tt: ast::TokenTree) {
-    let iter = TokenTreeChildren::new(&tt);
-    for kind in iter {
-        if let NodeOrToken::Token(kind) = kind
-            && kind.kind().is_any_identifier()
-        {
-            match kind.text() {
-                "array" => attr_flags.insert(AttrFlags::RUSTC_SKIP_ARRAY_DURING_METHOD_DISPATCH),
-                "boxed_slice" => {
-                    attr_flags.insert(AttrFlags::RUSTC_SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH)
-                }
-                _ => {}
-            }
-        }
-    }
-}
-
-#[inline]
-fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow {
-    match attr {
-        Meta::NamedKeyValue { name: Some(name), value, .. } => match name.text() {
-            "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
-            "lang" => attr_flags.insert(AttrFlags::LANG_ITEM),
-            "path" => attr_flags.insert(AttrFlags::HAS_PATH),
-            "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE),
-            "export_name" => {
-                if let Some(value) = value
-                    && let Some(value) = ast::String::cast(value)
-                    && let Ok(value) = value.value()
-                    && *value == *"main"
-                {
-                    attr_flags.insert(AttrFlags::IS_EXPORT_NAME_MAIN);
-                }
-            }
-            _ => {}
-        },
-        Meta::TokenTree { path, tt } => match path.segments.len() {
-            1 => match path.segments[0].text() {
-                "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
-                "cfg" => attr_flags.insert(AttrFlags::HAS_CFG),
-                "doc" => extract_doc_tt_attr(attr_flags, tt),
-                "repr" => attr_flags.insert(AttrFlags::HAS_REPR),
-                "target_feature" => attr_flags.insert(AttrFlags::HAS_TARGET_FEATURE),
-                "proc_macro_derive" | "rustc_builtin_macro" => {
-                    attr_flags.insert(AttrFlags::IS_DERIVE_OR_BUILTIN_MACRO)
-                }
-                "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE),
-                "rustc_layout_scalar_valid_range_start" | "rustc_layout_scalar_valid_range_end" => {
-                    attr_flags.insert(AttrFlags::RUSTC_LAYOUT_SCALAR_VALID_RANGE)
-                }
-                "rustc_legacy_const_generics" => {
-                    attr_flags.insert(AttrFlags::HAS_LEGACY_CONST_GENERICS)
-                }
-                "rustc_skip_during_method_dispatch" => {
-                    extract_rustc_skip_during_method_dispatch(attr_flags, tt)
-                }
-                _ => {}
-            },
-            2 => match path.segments[0].text() {
-                "rust_analyzer" => match path.segments[1].text() {
-                    "completions" => extract_ra_completions(attr_flags, tt),
-                    _ => {}
-                },
-                _ => {}
-            },
-            _ => {}
-        },
-        Meta::Path { path } => {
-            match path.segments.len() {
-                1 => match path.segments[0].text() {
-                    "rustc_has_incoherent_inherent_impls" => {
-                        attr_flags.insert(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS)
-                    }
-                    "rustc_allow_incoherent_impl" => {
-                        attr_flags.insert(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL)
-                    }
-                    "fundamental" => attr_flags.insert(AttrFlags::FUNDAMENTAL),
-                    "no_std" => attr_flags.insert(AttrFlags::IS_NO_STD),
-                    "may_dangle" => attr_flags.insert(AttrFlags::MAY_DANGLE),
-                    "rustc_paren_sugar" => attr_flags.insert(AttrFlags::RUSTC_PAREN_SUGAR),
-                    "rustc_coinductive" => attr_flags.insert(AttrFlags::RUSTC_COINDUCTIVE),
-                    "rustc_force_inline" => attr_flags.insert(AttrFlags::RUSTC_FORCE_INLINE),
-                    "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE),
-                    "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
-                    "macro_export" => attr_flags.insert(AttrFlags::IS_MACRO_EXPORT),
-                    "no_mangle" => attr_flags.insert(AttrFlags::NO_MANGLE),
-                    "non_exhaustive" => attr_flags.insert(AttrFlags::NON_EXHAUSTIVE),
-                    "ignore" => attr_flags.insert(AttrFlags::IS_IGNORE),
-                    "bench" => attr_flags.insert(AttrFlags::IS_BENCH),
-                    "rustc_const_panic_str" => attr_flags.insert(AttrFlags::RUSTC_CONST_PANIC_STR),
-                    "rustc_intrinsic" => attr_flags.insert(AttrFlags::RUSTC_INTRINSIC),
-                    "rustc_safe_intrinsic" => attr_flags.insert(AttrFlags::RUSTC_SAFE_INTRINSIC),
-                    "rustc_intrinsic_must_be_overridden" => {
-                        attr_flags.insert(AttrFlags::RUSTC_INTRINSIC_MUST_BE_OVERRIDDEN)
-                    }
-                    "rustc_allocator" => attr_flags.insert(AttrFlags::RUSTC_ALLOCATOR),
-                    "rustc_deallocator" => attr_flags.insert(AttrFlags::RUSTC_DEALLOCATOR),
-                    "rustc_reallocator" => attr_flags.insert(AttrFlags::RUSTC_REALLOCATOR),
-                    "rustc_allocator_zeroed" => {
-                        attr_flags.insert(AttrFlags::RUSTC_ALLOCATOR_ZEROED)
-                    }
-                    "rustc_reservation_impl" => {
-                        attr_flags.insert(AttrFlags::RUSTC_RESERVATION_IMPL)
-                    }
-                    "rustc_deprecated_safe_2024" => {
-                        attr_flags.insert(AttrFlags::RUSTC_DEPRECATED_SAFE_2024)
-                    }
-                    "rustc_skip_array_during_method_dispatch" => {
-                        attr_flags.insert(AttrFlags::RUSTC_SKIP_ARRAY_DURING_METHOD_DISPATCH)
-                    }
-                    _ => {}
-                },
-                2 => match path.segments[0].text() {
-                    "rust_analyzer" => match path.segments[1].text() {
-                        "skip" => attr_flags.insert(AttrFlags::RUST_ANALYZER_SKIP),
-                        _ => {}
-                    },
-                    _ => {}
-                },
-                _ => {}
-            }
-
-            if path.is_test {
-                attr_flags.insert(AttrFlags::IS_TEST);
-            }
-        }
-        _ => {}
-    };
-    ControlFlow::Continue(())
-}
-
-bitflags::bitflags! {
-    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-    pub struct AttrFlags: u64 {
-        const RUST_ANALYZER_SKIP = 1 << 0;
-
-        const LANG_ITEM = 1 << 1;
-
-        const HAS_DOC_ALIASES = 1 << 2;
-        const HAS_DOC_KEYWORD = 1 << 3;
-        const IS_DOC_NOTABLE_TRAIT = 1 << 4;
-        const IS_DOC_HIDDEN = 1 << 5;
-
-        const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 6;
-        const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
-        const FUNDAMENTAL = 1 << 8;
-        const RUSTC_SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 9;
-        const RUSTC_SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 10;
-        const HAS_REPR = 1 << 11;
-        const HAS_TARGET_FEATURE = 1 << 12;
-        const RUSTC_DEPRECATED_SAFE_2024 = 1 << 13;
-        const HAS_LEGACY_CONST_GENERICS = 1 << 14;
-        const NO_MANGLE = 1 << 15;
-        const NON_EXHAUSTIVE = 1 << 16;
-        const RUSTC_RESERVATION_IMPL = 1 << 17;
-        const RUSTC_CONST_PANIC_STR = 1 << 18;
-        const MAY_DANGLE = 1 << 19;
-
-        const RUSTC_INTRINSIC = 1 << 20;
-        const RUSTC_SAFE_INTRINSIC = 1 << 21;
-        const RUSTC_INTRINSIC_MUST_BE_OVERRIDDEN = 1 << 22;
-        const RUSTC_ALLOCATOR = 1 << 23;
-        const RUSTC_DEALLOCATOR = 1 << 24;
-        const RUSTC_REALLOCATOR = 1 << 25;
-        const RUSTC_ALLOCATOR_ZEROED = 1 << 26;
-
-        const IS_UNSTABLE = 1 << 27;
-        const IS_IGNORE = 1 << 28;
-        // FIXME: `IS_TEST` and `IS_BENCH` should be based on semantic information, not textual match.
-        const IS_BENCH = 1 << 29;
-        const IS_TEST = 1 << 30;
-        const IS_EXPORT_NAME_MAIN = 1 << 31;
-        const IS_MACRO_EXPORT = 1 << 32;
-        const IS_NO_STD = 1 << 33;
-        const IS_DERIVE_OR_BUILTIN_MACRO = 1 << 34;
-        const IS_DEPRECATED = 1 << 35;
-        const HAS_PATH = 1 << 36;
-        const HAS_CFG = 1 << 37;
-
-        const COMPLETE_IGNORE_FLYIMPORT = 1 << 38;
-        const COMPLETE_IGNORE_FLYIMPORT_METHODS = 1 << 39;
-        const COMPLETE_IGNORE_METHODS = 1 << 40;
-
-        const RUSTC_LAYOUT_SCALAR_VALID_RANGE = 1 << 41;
-        const RUSTC_PAREN_SUGAR = 1 << 42;
-        const RUSTC_COINDUCTIVE = 1 << 43;
-        const RUSTC_FORCE_INLINE = 1 << 44;
-    }
-}
-
-fn attrs_source(
-    db: &dyn DefDatabase,
-    owner: AttrDefId,
-) -> (InFile, Option>, Crate) {
-    let (owner, krate) = match owner {
-        AttrDefId::ModuleId(id) => {
-            let id = id.loc(db);
-            let def_map = id.def_map(db);
-            let (definition, declaration) = match def_map[id.local_id].origin {
-                ModuleOrigin::CrateRoot { definition } => {
-                    let file = db.parse(definition).tree();
-                    (InFile::new(definition.into(), ast::AnyHasAttrs::from(file)), None)
-                }
-                ModuleOrigin::File { declaration, declaration_tree_id, definition, .. } => {
-                    let declaration = InFile::new(declaration_tree_id.file_id(), declaration);
-                    let declaration = declaration.with_value(declaration.to_node(db));
-                    let definition_source = db.parse(definition).tree();
-                    (InFile::new(definition.into(), definition_source.into()), Some(declaration))
-                }
-                ModuleOrigin::Inline { definition_tree_id, definition } => {
-                    let definition = InFile::new(definition_tree_id.file_id(), definition);
-                    let definition = definition.with_value(definition.to_node(db).into());
-                    (definition, None)
-                }
-                ModuleOrigin::BlockExpr { block, .. } => {
-                    let definition = block.to_node(db);
-                    (block.with_value(definition.into()), None)
-                }
-            };
-            return (definition, declaration, id.krate);
-        }
-        AttrDefId::AdtId(AdtId::StructId(it)) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::AdtId(AdtId::UnionId(it)) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::AdtId(AdtId::EnumId(it)) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::FunctionId(it) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::EnumVariantId(it) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::StaticId(it) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::ConstId(it) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::TraitId(it) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::TypeAliasId(it) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::MacroId(MacroId::MacroRulesId(it)) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::MacroId(MacroId::Macro2Id(it)) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::MacroId(MacroId::ProcMacroId(it)) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::ImplId(it) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::ExternBlockId(it) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::ExternCrateId(it) => attrs_from_ast_id_loc(db, it),
-        AttrDefId::UseId(it) => attrs_from_ast_id_loc(db, it),
-    };
-    (owner, None, krate)
-}
-
-fn collect_attrs(
-    db: &dyn DefDatabase,
-    owner: AttrDefId,
-    mut callback: impl FnMut(Meta) -> ControlFlow,
-) -> Option {
-    let (source, outer_mod_decl, krate) = attrs_source(db, owner);
-
-    let mut cfg_options = None;
-    expand_cfg_attr(
-        outer_mod_decl
-            .into_iter()
-            .flat_map(|it| it.value.attrs())
-            .chain(ast::attrs_including_inner(&source.value)),
-        || cfg_options.get_or_insert_with(|| krate.cfg_options(db)),
-        move |meta, _, _, _| callback(meta),
-    )
-}
-
-fn collect_field_attrs(
-    db: &dyn DefDatabase,
-    variant: VariantId,
-    mut field_attrs: impl FnMut(&CfgOptions, InFile) -> T,
-) -> ArenaMap {
-    let (variant_syntax, krate) = match variant {
-        VariantId::EnumVariantId(it) => attrs_from_ast_id_loc(db, it),
-        VariantId::StructId(it) => attrs_from_ast_id_loc(db, it),
-        VariantId::UnionId(it) => attrs_from_ast_id_loc(db, it),
-    };
-    let cfg_options = krate.cfg_options(db);
-    let variant_syntax = variant_syntax
-        .with_value(ast::VariantDef::cast(variant_syntax.value.syntax().clone()).unwrap());
-    let fields = match &variant_syntax.value {
-        ast::VariantDef::Struct(it) => it.field_list(),
-        ast::VariantDef::Union(it) => it.record_field_list().map(ast::FieldList::RecordFieldList),
-        ast::VariantDef::Variant(it) => it.field_list(),
-    };
-    let Some(fields) = fields else {
-        return ArenaMap::new();
-    };
-
-    let mut result = ArenaMap::new();
-    let mut idx = 0;
-    match fields {
-        ast::FieldList::RecordFieldList(fields) => {
-            for field in fields.fields() {
-                if AttrFlags::is_cfg_enabled_for(&field, cfg_options).is_ok() {
-                    result.insert(
-                        la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(idx)),
-                        field_attrs(cfg_options, variant_syntax.with_value(field.into())),
-                    );
-                    idx += 1;
-                }
-            }
-        }
-        ast::FieldList::TupleFieldList(fields) => {
-            for field in fields.fields() {
-                if AttrFlags::is_cfg_enabled_for(&field, cfg_options).is_ok() {
-                    result.insert(
-                        la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(idx)),
-                        field_attrs(cfg_options, variant_syntax.with_value(field.into())),
-                    );
-                    idx += 1;
-                }
-            }
-        }
-    }
-    result.shrink_to_fit();
-    result
-}
-
-#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct RustcLayoutScalarValidRange {
-    pub start: Option,
-    pub end: Option,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-struct DocsSourceMapLine {
-    /// The offset in [`Docs::docs`].
-    string_offset: TextSize,
-    /// The offset in the AST of the text.
-    ast_offset: TextSize,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Docs {
-    /// The concatenated string of all `#[doc = "..."]` attributes and documentation comments.
-    docs: String,
-    /// A sorted map from an offset in `docs` to an offset in the source code.
-    docs_source_map: Vec,
-    /// If the item is an outlined module (`mod foo;`), `docs_source_map` store the concatenated
-    /// list of the outline and inline docs (outline first). Then, this field contains the [`HirFileId`]
-    /// of the outline declaration, and the index in `docs` from which the inline docs
-    /// begin.
-    outline_mod: Option<(HirFileId, usize)>,
-    inline_file: HirFileId,
-    /// The size the prepended prefix, which does not map to real doc comments.
-    prefix_len: TextSize,
-    /// The offset in `docs` from which the docs are inner attributes/comments.
-    inline_inner_docs_start: Option,
-    /// Like `inline_inner_docs_start`, but for `outline_mod`. This can happen only when merging `Docs`
-    /// (as outline modules don't have inner attributes).
-    outline_inner_docs_start: Option,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum IsInnerDoc {
-    No,
-    Yes,
-}
-
-impl IsInnerDoc {
-    #[inline]
-    pub fn yes(self) -> bool {
-        self == IsInnerDoc::Yes
-    }
-}
-
-impl Docs {
-    #[inline]
-    pub fn docs(&self) -> &str {
-        &self.docs
-    }
-
-    #[inline]
-    pub fn into_docs(self) -> String {
-        self.docs
-    }
-
-    pub fn find_ast_range(
-        &self,
-        mut string_range: TextRange,
-    ) -> Option<(InFile, IsInnerDoc)> {
-        if string_range.start() < self.prefix_len {
-            return None;
-        }
-        string_range -= self.prefix_len;
-
-        let mut file = self.inline_file;
-        let mut inner_docs_start = self.inline_inner_docs_start;
-        // Check whether the range is from the outline, the inline, or both.
-        let source_map = if let Some((outline_mod_file, outline_mod_end)) = self.outline_mod {
-            if let Some(first_inline) = self.docs_source_map.get(outline_mod_end) {
-                if string_range.end() <= first_inline.string_offset {
-                    // The range is completely in the outline.
-                    file = outline_mod_file;
-                    inner_docs_start = self.outline_inner_docs_start;
-                    &self.docs_source_map[..outline_mod_end]
-                } else if string_range.start() >= first_inline.string_offset {
-                    // The range is completely in the inline.
-                    &self.docs_source_map[outline_mod_end..]
-                } else {
-                    // The range is combined from the outline and the inline - cannot map it back.
-                    return None;
-                }
-            } else {
-                // There is no inline.
-                file = outline_mod_file;
-                inner_docs_start = self.outline_inner_docs_start;
-                &self.docs_source_map
-            }
-        } else {
-            // There is no outline.
-            &self.docs_source_map
-        };
-
-        let after_range =
-            source_map.partition_point(|line| line.string_offset <= string_range.start()) - 1;
-        let after_range = &source_map[after_range..];
-        let line = after_range.first()?;
-        if after_range.get(1).is_some_and(|next_line| next_line.string_offset < string_range.end())
-        {
-            // The range is combined from two lines - cannot map it back.
-            return None;
-        }
-        let ast_range = string_range - line.string_offset + line.ast_offset;
-        let is_inner = if inner_docs_start
-            .is_some_and(|inner_docs_start| string_range.start() >= inner_docs_start)
-        {
-            IsInnerDoc::Yes
-        } else {
-            IsInnerDoc::No
-        };
-        Some((InFile::new(file, ast_range), is_inner))
-    }
-
-    #[inline]
-    pub fn shift_by(&mut self, offset: TextSize) {
-        self.prefix_len += offset;
-    }
-
-    pub fn prepend_str(&mut self, s: &str) {
-        self.prefix_len += TextSize::of(s);
-        self.docs.insert_str(0, s);
-    }
-
-    pub fn append_str(&mut self, s: &str) {
-        self.docs.push_str(s);
-    }
-
-    pub fn append(&mut self, other: &Docs) {
-        let other_offset = TextSize::of(&self.docs);
-
-        assert!(
-            self.outline_mod.is_none() && other.outline_mod.is_none(),
-            "cannot merge `Docs` that have `outline_mod` set"
-        );
-        self.outline_mod = Some((self.inline_file, self.docs_source_map.len()));
-        self.inline_file = other.inline_file;
-        self.outline_inner_docs_start = self.inline_inner_docs_start;
-        self.inline_inner_docs_start = other.inline_inner_docs_start.map(|it| it + other_offset);
-
-        self.docs.push_str(&other.docs);
-        self.docs_source_map.extend(other.docs_source_map.iter().map(
-            |&DocsSourceMapLine { string_offset, ast_offset }| DocsSourceMapLine {
-                ast_offset,
-                string_offset: string_offset + other_offset,
-            },
-        ));
-    }
-
-    fn extend_with_doc_comment(&mut self, comment: ast::Comment, indent: &mut usize) {
-        let Some((doc, offset)) = comment.doc_comment() else { return };
-        self.extend_with_doc_str(doc, comment.syntax().text_range().start() + offset, indent);
-    }
-
-    fn extend_with_doc_attr(&mut self, value: SyntaxToken, indent: &mut usize) {
-        let Some(value) = ast::String::cast(value) else { return };
-        let Some(value_offset) = value.text_range_between_quotes() else { return };
-        let value_offset = value_offset.start();
-        let Ok(value) = value.value() else { return };
-        // FIXME: Handle source maps for escaped text.
-        self.extend_with_doc_str(&value, value_offset, indent);
-    }
-
-    fn extend_with_doc_str(&mut self, doc: &str, mut offset_in_ast: TextSize, indent: &mut usize) {
-        for line in doc.split('\n') {
-            self.docs_source_map.push(DocsSourceMapLine {
-                string_offset: TextSize::of(&self.docs),
-                ast_offset: offset_in_ast,
-            });
-            offset_in_ast += TextSize::of(line) + TextSize::of("\n");
-
-            let line = line.trim_end();
-            if let Some(line_indent) = line.chars().position(|ch| !ch.is_whitespace()) {
-                // Empty lines are handled because `position()` returns `None` for them.
-                *indent = std::cmp::min(*indent, line_indent);
-            }
-            self.docs.push_str(line);
-            self.docs.push('\n');
-        }
-    }
-
-    fn remove_indent(&mut self, indent: usize, start_source_map_index: usize) {
-        /// In case of panics, we want to avoid corrupted UTF-8 in `self.docs`, so we clear it.
-        struct Guard<'a>(&'a mut Docs);
-        impl Drop for Guard<'_> {
-            fn drop(&mut self) {
-                let Docs {
-                    docs,
-                    docs_source_map,
-                    outline_mod,
-                    inline_file: _,
-                    prefix_len: _,
-                    inline_inner_docs_start: _,
-                    outline_inner_docs_start: _,
-                } = self.0;
-                // Don't use `String::clear()` here because it's not guaranteed to not do UTF-8-dependent things,
-                // and we may have temporarily broken the string's encoding.
-                unsafe { docs.as_mut_vec() }.clear();
-                // This is just to avoid panics down the road.
-                docs_source_map.clear();
-                *outline_mod = None;
-            }
-        }
-
-        if self.docs.is_empty() {
-            return;
-        }
-
-        let guard = Guard(self);
-        let source_map = &mut guard.0.docs_source_map[start_source_map_index..];
-        let Some(&DocsSourceMapLine { string_offset: mut copy_into, .. }) = source_map.first()
-        else {
-            return;
-        };
-        // We basically want to remove multiple ranges from a string. Doing this efficiently (without O(N^2)
-        // or allocations) requires unsafe. Basically, for each line, we copy the line minus the indent into
-        // consecutive to the previous line (which may have moved). Then at the end we truncate.
-        let mut accumulated_offset = TextSize::new(0);
-        for idx in 0..source_map.len() {
-            let string_end_offset = source_map
-                .get(idx + 1)
-                .map_or_else(|| TextSize::of(&guard.0.docs), |next_attr| next_attr.string_offset);
-            let line_source = &mut source_map[idx];
-            let line_docs =
-                &guard.0.docs[TextRange::new(line_source.string_offset, string_end_offset)];
-            let line_docs_len = TextSize::of(line_docs);
-            let indent_size = line_docs.char_indices().nth(indent).map_or_else(
-                || TextSize::of(line_docs) - TextSize::of("\n"),
-                |(offset, _)| TextSize::new(offset as u32),
-            );
-            unsafe { guard.0.docs.as_bytes_mut() }.copy_within(
-                Range::::from(TextRange::new(
-                    line_source.string_offset + indent_size,
-                    string_end_offset,
-                )),
-                copy_into.into(),
-            );
-            copy_into += line_docs_len - indent_size;
-
-            if let Some(inner_attrs_start) = &mut guard.0.inline_inner_docs_start
-                && *inner_attrs_start == line_source.string_offset
-            {
-                *inner_attrs_start -= accumulated_offset;
-            }
-            // The removals in the string accumulate, but in the AST not, because it already points
-            // to the beginning of each attribute.
-            // Also, we need to shift the AST offset of every line, but the string offset of the first
-            // line should not get shifted (in general, the shift for the string offset is by the
-            // number of lines until the current one, excluding the current one).
-            line_source.string_offset -= accumulated_offset;
-            line_source.ast_offset += indent_size;
-
-            accumulated_offset += indent_size;
-        }
-        // Don't use `String::truncate()` here because it's not guaranteed to not do UTF-8-dependent things,
-        // and we may have temporarily broken the string's encoding.
-        unsafe { guard.0.docs.as_mut_vec() }.truncate(copy_into.into());
-
-        std::mem::forget(guard);
-    }
-
-    fn remove_last_newline(&mut self) {
-        self.docs.truncate(self.docs.len().saturating_sub(1));
-    }
-
-    fn shrink_to_fit(&mut self) {
-        let Docs {
-            docs,
-            docs_source_map,
-            outline_mod: _,
-            inline_file: _,
-            prefix_len: _,
-            inline_inner_docs_start: _,
-            outline_inner_docs_start: _,
-        } = self;
-        docs.shrink_to_fit();
-        docs_source_map.shrink_to_fit();
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Hash)]
-pub struct DeriveInfo {
-    pub trait_name: Symbol,
-    pub helpers: Box<[Symbol]>,
-}
-
-fn extract_doc_aliases(result: &mut Vec, attr: Meta) -> ControlFlow {
-    if let Meta::TokenTree { path, tt } = attr
-        && path.is1("doc")
-    {
-        for atom in DocAtom::parse(tt) {
-            match atom {
-                DocAtom::Alias(aliases) => {
-                    result.extend(aliases.into_iter().map(|alias| Symbol::intern(&alias)))
-                }
-                DocAtom::KeyValue { key, value } if key == "alias" => {
-                    result.push(Symbol::intern(&value))
-                }
-                _ => {}
-            }
-        }
-    }
-    ControlFlow::Continue(())
-}
-
-fn extract_cfgs(result: &mut Vec, attr: Meta) -> ControlFlow {
-    if let Meta::TokenTree { path, tt } = attr
-        && path.is1("cfg")
-    {
-        result.push(CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(&tt).peekable()));
-    }
-    ControlFlow::Continue(())
-}
-
-fn extract_docs<'a>(
-    get_cfg_options: &dyn Fn() -> &'a CfgOptions,
-    source: InFile,
-    outer_mod_decl: Option>,
-    inner_attrs_node: Option,
-) -> Option> {
-    let mut result = Docs {
-        docs: String::new(),
-        docs_source_map: Vec::new(),
-        outline_mod: None,
-        inline_file: source.file_id,
-        prefix_len: TextSize::new(0),
-        inline_inner_docs_start: None,
-        outline_inner_docs_start: None,
-    };
-
-    let mut cfg_options = None;
-    let mut extend_with_attrs =
-        |result: &mut Docs, node: &SyntaxNode, expect_inner_attrs, indent: &mut usize| {
-            expand_cfg_attr_with_doc_comments::<_, Infallible>(
-                AttrDocCommentIter::from_syntax_node(node).filter(|attr| match attr {
-                    Either::Left(attr) => attr.kind().is_inner() == expect_inner_attrs,
-                    Either::Right(comment) => comment.kind().doc.is_some_and(|kind| {
-                        (kind == ast::CommentPlacement::Inner) == expect_inner_attrs
-                    }),
-                }),
-                || cfg_options.get_or_insert_with(get_cfg_options),
-                |attr| {
-                    match attr {
-                        Either::Right(doc_comment) => {
-                            result.extend_with_doc_comment(doc_comment, indent)
-                        }
-                        Either::Left((attr, _, _, _)) => match attr {
-                            // FIXME: Handle macros: `#[doc = concat!("foo", "bar")]`.
-                            Meta::NamedKeyValue {
-                                name: Some(name), value: Some(value), ..
-                            } if name.text() == "doc" => {
-                                result.extend_with_doc_attr(value, indent);
-                            }
-                            _ => {}
-                        },
-                    }
-                    ControlFlow::Continue(())
-                },
-            );
-        };
-
-    if let Some(outer_mod_decl) = outer_mod_decl {
-        let mut indent = usize::MAX;
-        extend_with_attrs(&mut result, outer_mod_decl.value.syntax(), false, &mut indent);
-        result.remove_indent(indent, 0);
-        result.outline_mod = Some((outer_mod_decl.file_id, result.docs_source_map.len()));
-    }
-
-    let inline_source_map_start = result.docs_source_map.len();
-    let mut indent = usize::MAX;
-    extend_with_attrs(&mut result, source.value.syntax(), false, &mut indent);
-    if let Some(inner_attrs_node) = &inner_attrs_node {
-        result.inline_inner_docs_start = Some(TextSize::of(&result.docs));
-        extend_with_attrs(&mut result, inner_attrs_node, true, &mut indent);
-    }
-    result.remove_indent(indent, inline_source_map_start);
-
-    result.remove_last_newline();
-
-    result.shrink_to_fit();
-
-    if result.docs.is_empty() { None } else { Some(Box::new(result)) }
-}
-
-#[salsa::tracked]
-impl AttrFlags {
-    #[salsa::tracked]
-    pub fn query(db: &dyn DefDatabase, owner: AttrDefId) -> AttrFlags {
-        let mut attr_flags = AttrFlags::empty();
-        collect_attrs(db, owner, |attr| match_attr_flags(&mut attr_flags, attr));
-        attr_flags
-    }
-
-    #[inline]
-    pub fn query_field(db: &dyn DefDatabase, field: FieldId) -> AttrFlags {
-        return field_attr_flags(db, field.parent)
-            .get(field.local_id)
-            .copied()
-            .unwrap_or_else(AttrFlags::empty);
-
-        #[salsa::tracked(returns(ref))]
-        fn field_attr_flags(
-            db: &dyn DefDatabase,
-            variant: VariantId,
-        ) -> ArenaMap {
-            collect_field_attrs(db, variant, |cfg_options, field| {
-                let mut attr_flags = AttrFlags::empty();
-                expand_cfg_attr(
-                    field.value.attrs(),
-                    || cfg_options,
-                    |attr, _, _, _| match_attr_flags(&mut attr_flags, attr),
-                );
-                attr_flags
-            })
-        }
-    }
-
-    #[inline]
-    pub fn query_generic_params(
-        db: &dyn DefDatabase,
-        def: GenericDefId,
-    ) -> &(ArenaMap, ArenaMap)
-    {
-        let generic_params = GenericParams::new(db, def);
-        let params_count_excluding_self =
-            generic_params.len() - usize::from(generic_params.trait_self_param().is_some());
-        if params_count_excluding_self == 0 {
-            return const { &(ArenaMap::new(), ArenaMap::new()) };
-        }
-        return generic_params_attr_flags(db, def);
-
-        #[salsa::tracked(returns(ref))]
-        fn generic_params_attr_flags(
-            db: &dyn DefDatabase,
-            def: GenericDefId,
-        ) -> (ArenaMap, ArenaMap)
-        {
-            let mut lifetimes = ArenaMap::new();
-            let mut type_and_consts = ArenaMap::new();
-
-            let mut cfg_options = None;
-            let mut cfg_options =
-                || *cfg_options.get_or_insert_with(|| def.krate(db).cfg_options(db));
-
-            let lifetimes_source = HasChildSource::::child_source(&def, db);
-            for (lifetime_id, lifetime) in lifetimes_source.value.iter() {
-                let mut attr_flags = AttrFlags::empty();
-                expand_cfg_attr(lifetime.attrs(), &mut cfg_options, |attr, _, _, _| {
-                    match_attr_flags(&mut attr_flags, attr)
-                });
-                if !attr_flags.is_empty() {
-                    lifetimes.insert(lifetime_id, attr_flags);
-                }
-            }
-
-            let type_and_consts_source =
-                HasChildSource::::child_source(&def, db);
-            for (type_or_const_id, type_or_const) in type_and_consts_source.value.iter() {
-                let mut attr_flags = AttrFlags::empty();
-                expand_cfg_attr(type_or_const.attrs(), &mut cfg_options, |attr, _, _, _| {
-                    match_attr_flags(&mut attr_flags, attr)
-                });
-                if !attr_flags.is_empty() {
-                    type_and_consts.insert(type_or_const_id, attr_flags);
-                }
-            }
-
-            lifetimes.shrink_to_fit();
-            type_and_consts.shrink_to_fit();
-            (lifetimes, type_and_consts)
-        }
-    }
-
-    #[inline]
-    pub fn query_lifetime_param(db: &dyn DefDatabase, owner: LifetimeParamId) -> AttrFlags {
-        AttrFlags::query_generic_params(db, owner.parent)
-            .0
-            .get(owner.local_id)
-            .copied()
-            .unwrap_or_else(AttrFlags::empty)
-    }
-    #[inline]
-    pub fn query_type_or_const_param(db: &dyn DefDatabase, owner: TypeOrConstParamId) -> AttrFlags {
-        AttrFlags::query_generic_params(db, owner.parent)
-            .1
-            .get(owner.local_id)
-            .copied()
-            .unwrap_or_else(AttrFlags::empty)
-    }
-
-    pub(crate) fn is_cfg_enabled_for(
-        owner: &dyn HasAttrs,
-        cfg_options: &CfgOptions,
-    ) -> Result<(), CfgExpr> {
-        let attrs = ast::attrs_including_inner(owner);
-        let result = expand_cfg_attr(
-            attrs,
-            || cfg_options,
-            |attr, _, _, _| {
-                if let Meta::TokenTree { path, tt } = attr
-                    && path.is1("cfg")
-                    && let cfg =
-                        CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(&tt).peekable())
-                    && cfg_options.check(&cfg) == Some(false)
-                {
-                    ControlFlow::Break(cfg)
-                } else {
-                    ControlFlow::Continue(())
-                }
-            },
-        );
-        match result {
-            Some(cfg) => Err(cfg),
-            None => Ok(()),
-        }
-    }
-
-    #[inline]
-    pub fn lang_item(db: &dyn DefDatabase, owner: AttrDefId) -> Option {
-        AttrFlags::query(db, owner).lang_item_with_attrs(db, owner)
-    }
-
-    #[inline]
-    pub fn lang_item_with_attrs(self, db: &dyn DefDatabase, owner: AttrDefId) -> Option {
-        if !self.contains(AttrFlags::LANG_ITEM) {
-            // Don't create the query in case this is not a lang item, this wastes memory.
-            return None;
-        }
-
-        return lang_item(db, owner);
-
-        #[salsa::tracked]
-        fn lang_item(db: &dyn DefDatabase, owner: AttrDefId) -> Option {
-            collect_attrs(db, owner, |attr| {
-                if let Meta::NamedKeyValue { name: Some(name), value: Some(value), .. } = attr
-                    && name.text() == "lang"
-                    && let Some(value) = ast::String::cast(value)
-                    && let Ok(value) = value.value()
-                    && let symbol = Symbol::intern(&value)
-                    && let Some(lang_item) = LangItem::from_symbol(&symbol)
-                {
-                    ControlFlow::Break(lang_item)
-                } else {
-                    ControlFlow::Continue(())
-                }
-            })
-        }
-    }
-
-    #[inline]
-    pub fn repr(db: &dyn DefDatabase, owner: AdtId) -> Option {
-        if !AttrFlags::query(db, owner.into()).contains(AttrFlags::HAS_REPR) {
-            // Don't create the query in case this has no repr, this wastes memory.
-            return None;
-        }
-
-        return repr(db, owner);
-
-        #[salsa::tracked]
-        fn repr(db: &dyn DefDatabase, owner: AdtId) -> Option {
-            let mut result = None;
-            collect_attrs::(db, owner.into(), |attr| {
-                if let Meta::TokenTree { path, tt } = attr
-                    && path.is1("repr")
-                    && let Some(repr) = parse_repr_tt(&tt)
-                {
-                    match &mut result {
-                        Some(existing) => merge_repr(existing, repr),
-                        None => result = Some(repr),
-                    }
-                }
-                ControlFlow::Continue(())
-            });
-            result
-        }
-    }
-
-    /// Call this only if there are legacy const generics, to save memory.
-    #[salsa::tracked(returns(ref))]
-    pub(crate) fn legacy_const_generic_indices(
-        db: &dyn DefDatabase,
-        owner: FunctionId,
-    ) -> Option> {
-        let result = collect_attrs(db, owner.into(), |attr| {
-            if let Meta::TokenTree { path, tt } = attr
-                && path.is1("rustc_legacy_const_generics")
-            {
-                let result = parse_rustc_legacy_const_generics(tt);
-                ControlFlow::Break(result)
-            } else {
-                ControlFlow::Continue(())
-            }
-        });
-        result.filter(|it| !it.is_empty())
-    }
-
-    // There aren't typically many crates, so it's okay to always make this a query without a flag.
-    #[salsa::tracked(returns(ref))]
-    pub fn doc_html_root_url(db: &dyn DefDatabase, krate: Crate) -> Option {
-        let root_file_id = krate.root_file_id(db);
-        let syntax = db.parse(root_file_id).tree();
-
-        let mut cfg_options = None;
-        expand_cfg_attr(
-            syntax.attrs(),
-            || cfg_options.get_or_insert(krate.cfg_options(db)),
-            |attr, _, _, _| {
-                if let Meta::TokenTree { path, tt } = attr
-                    && path.is1("doc")
-                    && let Some(result) = DocAtom::parse(tt).into_iter().find_map(|atom| {
-                        if let DocAtom::KeyValue { key, value } = atom
-                            && key == "html_root_url"
-                        {
-                            Some(value)
-                        } else {
-                            None
-                        }
-                    })
-                {
-                    ControlFlow::Break(result)
-                } else {
-                    ControlFlow::Continue(())
-                }
-            },
-        )
-    }
-
-    #[inline]
-    pub fn target_features(db: &dyn DefDatabase, owner: FunctionId) -> &FxHashSet {
-        if !AttrFlags::query(db, owner.into()).contains(AttrFlags::HAS_TARGET_FEATURE) {
-            return const { &FxHashSet::with_hasher(rustc_hash::FxBuildHasher) };
-        }
-
-        return target_features(db, owner);
-
-        #[salsa::tracked(returns(ref))]
-        fn target_features(db: &dyn DefDatabase, owner: FunctionId) -> FxHashSet {
-            let mut result = FxHashSet::default();
-            collect_attrs::(db, owner.into(), |attr| {
-                if let Meta::TokenTree { path, tt } = attr
-                    && path.is1("target_feature")
-                    && let mut tt = TokenTreeChildren::new(&tt)
-                    && let Some(NodeOrToken::Token(enable_ident)) = tt.next()
-                    && enable_ident.text() == "enable"
-                    && let Some(NodeOrToken::Token(eq_token)) = tt.next()
-                    && eq_token.kind() == T![=]
-                    && let Some(NodeOrToken::Token(features)) = tt.next()
-                    && let Some(features) = ast::String::cast(features)
-                    && let Ok(features) = features.value()
-                    && tt.next().is_none()
-                {
-                    result.extend(features.split(',').map(Symbol::intern));
-                }
-                ControlFlow::Continue(())
-            });
-            result.shrink_to_fit();
-            result
-        }
-    }
-
-    #[inline]
-    pub fn rustc_layout_scalar_valid_range(
-        db: &dyn DefDatabase,
-        owner: AdtId,
-    ) -> RustcLayoutScalarValidRange {
-        if !AttrFlags::query(db, owner.into()).contains(AttrFlags::RUSTC_LAYOUT_SCALAR_VALID_RANGE)
-        {
-            return RustcLayoutScalarValidRange::default();
-        }
-
-        return rustc_layout_scalar_valid_range(db, owner);
-
-        #[salsa::tracked]
-        fn rustc_layout_scalar_valid_range(
-            db: &dyn DefDatabase,
-            owner: AdtId,
-        ) -> RustcLayoutScalarValidRange {
-            let mut result = RustcLayoutScalarValidRange::default();
-            collect_attrs::(db, owner.into(), |attr| {
-                if let Meta::TokenTree { path, tt } = attr
-                    && (path.is1("rustc_layout_scalar_valid_range_start")
-                        || path.is1("rustc_layout_scalar_valid_range_end"))
-                    && let tt = TokenTreeChildren::new(&tt)
-                    && let Ok(NodeOrToken::Token(value)) = tt.exactly_one()
-                    && let Some(value) = ast::IntNumber::cast(value)
-                    && let Ok(value) = value.value()
-                {
-                    if path.is1("rustc_layout_scalar_valid_range_start") {
-                        result.start = Some(value)
-                    } else {
-                        result.end = Some(value);
-                    }
-                }
-                ControlFlow::Continue(())
-            });
-            result
-        }
-    }
-
-    #[inline]
-    pub fn doc_aliases(self, db: &dyn DefDatabase, owner: Either) -> &[Symbol] {
-        if !self.contains(AttrFlags::HAS_DOC_ALIASES) {
-            return &[];
-        }
-        return match owner {
-            Either::Left(it) => doc_aliases(db, it),
-            Either::Right(field) => fields_doc_aliases(db, field.parent)
-                .get(field.local_id)
-                .map(|it| &**it)
-                .unwrap_or_default(),
-        };
-
-        #[salsa::tracked(returns(ref))]
-        fn doc_aliases(db: &dyn DefDatabase, owner: AttrDefId) -> Box<[Symbol]> {
-            let mut result = Vec::new();
-            collect_attrs::(db, owner, |attr| extract_doc_aliases(&mut result, attr));
-            result.into_boxed_slice()
-        }
-
-        #[salsa::tracked(returns(ref))]
-        fn fields_doc_aliases(
-            db: &dyn DefDatabase,
-            variant: VariantId,
-        ) -> ArenaMap> {
-            collect_field_attrs(db, variant, |cfg_options, field| {
-                let mut result = Vec::new();
-                expand_cfg_attr(
-                    field.value.attrs(),
-                    || cfg_options,
-                    |attr, _, _, _| extract_doc_aliases(&mut result, attr),
-                );
-                result.into_boxed_slice()
-            })
-        }
-    }
-
-    #[inline]
-    pub fn cfgs(self, db: &dyn DefDatabase, owner: Either) -> Option<&CfgExpr> {
-        if !self.contains(AttrFlags::HAS_CFG) {
-            return None;
-        }
-        return match owner {
-            Either::Left(it) => cfgs(db, it).as_ref(),
-            Either::Right(field) => {
-                fields_cfgs(db, field.parent).get(field.local_id).and_then(|it| it.as_ref())
-            }
-        };
-
-        // We LRU this query because it is only used by IDE.
-        #[salsa::tracked(returns(ref), lru = 250)]
-        fn cfgs(db: &dyn DefDatabase, owner: AttrDefId) -> Option {
-            let mut result = Vec::new();
-            collect_attrs::(db, owner, |attr| extract_cfgs(&mut result, attr));
-            match result.len() {
-                0 => None,
-                1 => result.into_iter().next(),
-                _ => Some(CfgExpr::All(result.into_boxed_slice())),
-            }
-        }
-
-        // We LRU this query because it is only used by IDE.
-        #[salsa::tracked(returns(ref), lru = 50)]
-        fn fields_cfgs(
-            db: &dyn DefDatabase,
-            variant: VariantId,
-        ) -> ArenaMap> {
-            collect_field_attrs(db, variant, |cfg_options, field| {
-                let mut result = Vec::new();
-                expand_cfg_attr(
-                    field.value.attrs(),
-                    || cfg_options,
-                    |attr, _, _, _| extract_cfgs(&mut result, attr),
-                );
-                match result.len() {
-                    0 => None,
-                    1 => result.into_iter().next(),
-                    _ => Some(CfgExpr::All(result.into_boxed_slice())),
-                }
-            })
-        }
-    }
-
-    #[inline]
-    pub fn doc_keyword(db: &dyn DefDatabase, owner: InternedModuleId) -> Option {
-        if !AttrFlags::query(db, AttrDefId::ModuleId(owner)).contains(AttrFlags::HAS_DOC_KEYWORD) {
-            return None;
-        }
-        return doc_keyword(db, owner);
-
-        #[salsa::tracked]
-        fn doc_keyword(db: &dyn DefDatabase, owner: InternedModuleId) -> Option {
-            collect_attrs(db, AttrDefId::ModuleId(owner), |attr| {
-                if let Meta::TokenTree { path, tt } = attr
-                    && path.is1("doc")
-                {
-                    for atom in DocAtom::parse(tt) {
-                        if let DocAtom::KeyValue { key, value } = atom
-                            && key == "keyword"
-                        {
-                            return ControlFlow::Break(Symbol::intern(&value));
-                        }
-                    }
-                }
-                ControlFlow::Continue(())
-            })
-        }
-    }
-
-    // We LRU this query because it is only used by IDE.
-    #[salsa::tracked(returns(ref), lru = 250)]
-    pub fn docs(db: &dyn DefDatabase, owner: AttrDefId) -> Option> {
-        let (source, outer_mod_decl, krate) = attrs_source(db, owner);
-        let inner_attrs_node = source.value.inner_attributes_node();
-        extract_docs(&|| krate.cfg_options(db), source, outer_mod_decl, inner_attrs_node)
-    }
-
-    #[inline]
-    pub fn field_docs(db: &dyn DefDatabase, field: FieldId) -> Option<&Docs> {
-        return fields_docs(db, field.parent).get(field.local_id).and_then(|it| it.as_deref());
-
-        // We LRU this query because it is only used by IDE.
-        #[salsa::tracked(returns(ref), lru = 50)]
-        pub fn fields_docs(
-            db: &dyn DefDatabase,
-            variant: VariantId,
-        ) -> ArenaMap>> {
-            collect_field_attrs(db, variant, |cfg_options, field| {
-                extract_docs(&|| cfg_options, field, None, None)
-            })
-        }
-    }
-
-    #[inline]
-    pub fn derive_info(db: &dyn DefDatabase, owner: MacroId) -> Option<&DeriveInfo> {
-        if !AttrFlags::query(db, owner.into()).contains(AttrFlags::IS_DERIVE_OR_BUILTIN_MACRO) {
-            return None;
-        }
-
-        return derive_info(db, owner).as_ref();
-
-        #[salsa::tracked(returns(ref))]
-        fn derive_info(db: &dyn DefDatabase, owner: MacroId) -> Option {
-            collect_attrs(db, owner.into(), |attr| {
-                if let Meta::TokenTree { path, tt } = attr
-                    && path.segments.len() == 1
-                    && matches!(
-                        path.segments[0].text(),
-                        "proc_macro_derive" | "rustc_builtin_macro"
-                    )
-                    && let mut tt = TokenTreeChildren::new(&tt)
-                    && let Some(NodeOrToken::Token(trait_name)) = tt.next()
-                    && trait_name.kind().is_any_identifier()
-                {
-                    let trait_name = Symbol::intern(trait_name.text());
-
-                    let helpers = if let Some(NodeOrToken::Token(comma)) = tt.next()
-                        && comma.kind() == T![,]
-                        && let Some(NodeOrToken::Token(attributes)) = tt.next()
-                        && attributes.text() == "attributes"
-                        && let Some(NodeOrToken::Node(attributes)) = tt.next()
-                    {
-                        attributes
-                            .syntax()
-                            .children_with_tokens()
-                            .filter_map(NodeOrToken::into_token)
-                            .filter(|it| it.kind().is_any_identifier())
-                            .map(|it| Symbol::intern(it.text()))
-                            .collect::>()
-                    } else {
-                        Box::new([])
-                    };
-
-                    ControlFlow::Break(DeriveInfo { trait_name, helpers })
-                } else {
-                    ControlFlow::Continue(())
-                }
-            })
-        }
-    }
-}
-
-fn merge_repr(this: &mut ReprOptions, other: ReprOptions) {
-    let ReprOptions { int, align, pack, flags, field_shuffle_seed: _ } = this;
-    flags.insert(other.flags);
-    *align = (*align).max(other.align);
-    *pack = match (*pack, other.pack) {
-        (Some(pack), None) | (None, Some(pack)) => Some(pack),
-        _ => (*pack).min(other.pack),
-    };
-    if other.int.is_some() {
-        *int = other.int;
-    }
-}
-
-fn parse_repr_tt(tt: &ast::TokenTree) -> Option {
-    use crate::builtin_type::{BuiltinInt, BuiltinUint};
-    use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
-
-    let mut tts = TokenTreeChildren::new(tt).peekable();
-
-    let mut acc = ReprOptions::default();
-    while let Some(tt) = tts.next() {
-        let NodeOrToken::Token(ident) = tt else {
-            continue;
-        };
-        if !ident.kind().is_any_identifier() {
-            continue;
-        }
-        let repr = match ident.text() {
-            "packed" => {
-                let pack = if let Some(NodeOrToken::Node(tt)) = tts.peek() {
-                    let tt = tt.clone();
-                    tts.next();
-                    let mut tt_iter = TokenTreeChildren::new(&tt);
-                    if let Some(NodeOrToken::Token(lit)) = tt_iter.next()
-                        && let Some(lit) = ast::IntNumber::cast(lit)
-                        && let Ok(lit) = lit.value()
-                        && let Ok(lit) = lit.try_into()
-                    {
-                        lit
-                    } else {
-                        0
-                    }
-                } else {
-                    0
-                };
-                let pack = Some(Align::from_bytes(pack).unwrap_or(Align::ONE));
-                ReprOptions { pack, ..Default::default() }
-            }
-            "align" => {
-                let mut align = None;
-                if let Some(NodeOrToken::Node(tt)) = tts.peek() {
-                    let tt = tt.clone();
-                    tts.next();
-                    let mut tt_iter = TokenTreeChildren::new(&tt);
-                    if let Some(NodeOrToken::Token(lit)) = tt_iter.next()
-                        && let Some(lit) = ast::IntNumber::cast(lit)
-                        && let Ok(lit) = lit.value()
-                        && let Ok(lit) = lit.try_into()
-                    {
-                        align = Align::from_bytes(lit).ok();
-                    }
-                }
-                ReprOptions { align, ..Default::default() }
-            }
-            "C" => ReprOptions { flags: ReprFlags::IS_C, ..Default::default() },
-            "transparent" => ReprOptions { flags: ReprFlags::IS_TRANSPARENT, ..Default::default() },
-            "simd" => ReprOptions { flags: ReprFlags::IS_SIMD, ..Default::default() },
-            repr => {
-                let mut int = None;
-                if let Some(builtin) = BuiltinInt::from_suffix(repr)
-                    .map(Either::Left)
-                    .or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right))
-                {
-                    int = Some(match builtin {
-                        Either::Left(bi) => match bi {
-                            BuiltinInt::Isize => IntegerType::Pointer(true),
-                            BuiltinInt::I8 => IntegerType::Fixed(Integer::I8, true),
-                            BuiltinInt::I16 => IntegerType::Fixed(Integer::I16, true),
-                            BuiltinInt::I32 => IntegerType::Fixed(Integer::I32, true),
-                            BuiltinInt::I64 => IntegerType::Fixed(Integer::I64, true),
-                            BuiltinInt::I128 => IntegerType::Fixed(Integer::I128, true),
-                        },
-                        Either::Right(bu) => match bu {
-                            BuiltinUint::Usize => IntegerType::Pointer(false),
-                            BuiltinUint::U8 => IntegerType::Fixed(Integer::I8, false),
-                            BuiltinUint::U16 => IntegerType::Fixed(Integer::I16, false),
-                            BuiltinUint::U32 => IntegerType::Fixed(Integer::I32, false),
-                            BuiltinUint::U64 => IntegerType::Fixed(Integer::I64, false),
-                            BuiltinUint::U128 => IntegerType::Fixed(Integer::I128, false),
-                        },
-                    });
-                }
-                ReprOptions { int, ..Default::default() }
-            }
-        };
-        merge_repr(&mut acc, repr);
-    }
-
-    Some(acc)
-}
-
-fn parse_rustc_legacy_const_generics(tt: ast::TokenTree) -> Box<[u32]> {
-    TokenTreeChildren::new(&tt)
-        .filter_map(|param| {
-            ast::IntNumber::cast(param.into_token()?)?.value().ok()?.try_into().ok()
-        })
-        .collect()
-}
-
-#[derive(Debug)]
-enum DocAtom {
-    /// eg. `#[doc(hidden)]`
-    Flag(SmolStr),
-    /// eg. `#[doc(alias = "it")]`
-    ///
-    /// Note that a key can have multiple values that are all considered "active" at the same time.
-    /// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`.
-    KeyValue { key: SmolStr, value: SmolStr },
-    /// eg. `#[doc(alias("x", "y"))]`
-    Alias(Vec),
-}
-
-impl DocAtom {
-    fn parse(tt: ast::TokenTree) -> SmallVec<[DocAtom; 1]> {
-        let mut iter = TokenTreeChildren::new(&tt).peekable();
-        let mut result = SmallVec::new();
-        while iter.peek().is_some() {
-            if let Some(expr) = next_doc_expr(&mut iter) {
-                result.push(expr);
-            }
-        }
-        result
-    }
-}
-
-fn next_doc_expr(it: &mut Peekable) -> Option {
-    let name = match it.next() {
-        Some(NodeOrToken::Token(token)) if token.kind().is_any_identifier() => {
-            SmolStr::new(token.text())
-        }
-        _ => return None,
-    };
-
-    let ret = match it.peek() {
-        Some(NodeOrToken::Token(eq)) if eq.kind() == T![=] => {
-            it.next();
-            if let Some(NodeOrToken::Token(value)) = it.next()
-                && let Some(value) = ast::String::cast(value)
-                && let Ok(value) = value.value()
-            {
-                DocAtom::KeyValue { key: name, value: SmolStr::new(&*value) }
-            } else {
-                return None;
-            }
-        }
-        Some(NodeOrToken::Node(subtree)) => {
-            if name != "alias" {
-                return None;
-            }
-            let aliases = TokenTreeChildren::new(subtree)
-                .filter_map(|alias| {
-                    Some(SmolStr::new(&*ast::String::cast(alias.into_token()?)?.value().ok()?))
-                })
-                .collect();
-            it.next();
-            DocAtom::Alias(aliases)
-        }
-        _ => DocAtom::Flag(name),
-    };
-    Some(ret)
-}
-
-#[cfg(test)]
-mod tests {
-    use expect_test::expect;
-    use hir_expand::InFile;
-    use test_fixture::WithFixture;
-    use tt::{TextRange, TextSize};
-
-    use crate::attrs::IsInnerDoc;
-    use crate::{attrs::Docs, test_db::TestDB};
-
-    #[test]
-    fn docs() {
-        let (_db, file_id) = TestDB::with_single_file("");
-        let mut docs = Docs {
-            docs: String::new(),
-            docs_source_map: Vec::new(),
-            outline_mod: None,
-            inline_file: file_id.into(),
-            prefix_len: TextSize::new(0),
-            inline_inner_docs_start: None,
-            outline_inner_docs_start: None,
-        };
-        let mut indent = usize::MAX;
-
-        let outer = " foo\n\tbar  baz";
-        let mut ast_offset = TextSize::new(123);
-        for line in outer.split('\n') {
-            docs.extend_with_doc_str(line, ast_offset, &mut indent);
-            ast_offset += TextSize::of(line) + TextSize::of("\n");
-        }
-
-        docs.inline_inner_docs_start = Some(TextSize::of(&docs.docs));
-        ast_offset += TextSize::new(123);
-        let inner = " bar \n baz";
-        for line in inner.split('\n') {
-            docs.extend_with_doc_str(line, ast_offset, &mut indent);
-            ast_offset += TextSize::of(line) + TextSize::of("\n");
-        }
-
-        assert_eq!(indent, 1);
-        expect![[r#"
-            [
-                DocsSourceMapLine {
-                    string_offset: 0,
-                    ast_offset: 123,
-                },
-                DocsSourceMapLine {
-                    string_offset: 5,
-                    ast_offset: 128,
-                },
-                DocsSourceMapLine {
-                    string_offset: 15,
-                    ast_offset: 261,
-                },
-                DocsSourceMapLine {
-                    string_offset: 20,
-                    ast_offset: 267,
-                },
-            ]
-        "#]]
-        .assert_debug_eq(&docs.docs_source_map);
-
-        docs.remove_indent(indent, 0);
-
-        assert_eq!(docs.inline_inner_docs_start, Some(TextSize::new(13)));
-
-        assert_eq!(docs.docs, "foo\nbar  baz\nbar\nbaz\n");
-        expect![[r#"
-            [
-                DocsSourceMapLine {
-                    string_offset: 0,
-                    ast_offset: 124,
-                },
-                DocsSourceMapLine {
-                    string_offset: 4,
-                    ast_offset: 129,
-                },
-                DocsSourceMapLine {
-                    string_offset: 13,
-                    ast_offset: 262,
-                },
-                DocsSourceMapLine {
-                    string_offset: 17,
-                    ast_offset: 268,
-                },
-            ]
-        "#]]
-        .assert_debug_eq(&docs.docs_source_map);
-
-        docs.append(&docs.clone());
-        docs.prepend_str("prefix---");
-        assert_eq!(docs.docs, "prefix---foo\nbar  baz\nbar\nbaz\nfoo\nbar  baz\nbar\nbaz\n");
-        expect![[r#"
-            [
-                DocsSourceMapLine {
-                    string_offset: 0,
-                    ast_offset: 124,
-                },
-                DocsSourceMapLine {
-                    string_offset: 4,
-                    ast_offset: 129,
-                },
-                DocsSourceMapLine {
-                    string_offset: 13,
-                    ast_offset: 262,
-                },
-                DocsSourceMapLine {
-                    string_offset: 17,
-                    ast_offset: 268,
-                },
-                DocsSourceMapLine {
-                    string_offset: 21,
-                    ast_offset: 124,
-                },
-                DocsSourceMapLine {
-                    string_offset: 25,
-                    ast_offset: 129,
-                },
-                DocsSourceMapLine {
-                    string_offset: 34,
-                    ast_offset: 262,
-                },
-                DocsSourceMapLine {
-                    string_offset: 38,
-                    ast_offset: 268,
-                },
-            ]
-        "#]]
-        .assert_debug_eq(&docs.docs_source_map);
-
-        let range = |start, end| TextRange::new(TextSize::new(start), TextSize::new(end));
-        let in_file = |range| InFile::new(file_id.into(), range);
-        assert_eq!(docs.find_ast_range(range(0, 2)), None);
-        assert_eq!(docs.find_ast_range(range(8, 10)), None);
-        assert_eq!(
-            docs.find_ast_range(range(9, 10)),
-            Some((in_file(range(124, 125)), IsInnerDoc::No))
-        );
-        assert_eq!(docs.find_ast_range(range(20, 23)), None);
-        assert_eq!(
-            docs.find_ast_range(range(23, 25)),
-            Some((in_file(range(263, 265)), IsInnerDoc::Yes))
-        );
-    }
-}
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 ad2990087672..4e1d598623ab 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -1,21 +1,23 @@
 //! Defines database & queries for name resolution.
 use base_db::{Crate, RootQueryDb, SourceDatabase};
+use either::Either;
 use hir_expand::{
     EditionedFileId, HirFileId, InFile, Lookup, MacroCallId, MacroDefId, MacroDefKind,
     db::ExpandDatabase,
 };
+use intern::sym;
 use la_arena::ArenaMap;
+use syntax::{AstPtr, ast};
 use triomphe::Arc;
 
 use crate::{
-    AssocItemId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, CrateRootModuleId, DefWithBodyId,
-    EnumId, EnumLoc, EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId,
-    ExternCrateLoc, FunctionId, FunctionLoc, GenericDefId, HasModule, ImplId, ImplLoc,
-    InternedModuleId, LocalFieldId, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId,
-    MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId,
-    StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc,
-    VariantId,
-    attrs::AttrFlags,
+    AssocItemId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc,
+    EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc,
+    FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalFieldId, Macro2Id, Macro2Loc,
+    MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId,
+    ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId,
+    TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId,
+    attr::{Attrs, AttrsWithOwner},
     expr_store::{
         Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, scope::ExprScopes,
     },
@@ -28,6 +30,7 @@ use crate::{
         ConstSignature, EnumSignature, FunctionSignature, ImplSignature, StaticSignature,
         StructSignature, TraitSignature, TypeAliasSignature, UnionSignature,
     },
+    tt,
     visibility::{self, Visibility},
 };
 
@@ -235,11 +238,28 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase {
         def: GenericDefId,
     ) -> (Arc, Arc, Arc);
 
-    // FIXME: Get rid of this, call `AttrFlags::lang_item()` directly.
+    // region:attrs
+
+    #[salsa::invoke(Attrs::fields_attrs_query)]
+    fn fields_attrs(&self, def: VariantId) -> Arc>;
+
+    // should this really be a query?
+    #[salsa::invoke(crate::attr::fields_attrs_source_map)]
+    fn fields_attrs_source_map(
+        &self,
+        def: VariantId,
+    ) -> Arc>>>;
+
+    // FIXME: Make this a non-interned query.
+    #[salsa::invoke_interned(AttrsWithOwner::attrs_query)]
+    fn attrs(&self, def: AttrDefId) -> Attrs;
+
     #[salsa::transparent]
     #[salsa::invoke(lang_item::lang_attr)]
     fn lang_attr(&self, def: AttrDefId) -> Option;
 
+    // endregion:attrs
+
     #[salsa::invoke(ImportMap::import_map_query)]
     fn import_map(&self, krate: Crate) -> Arc;
 
@@ -283,9 +303,36 @@ fn include_macro_invoc(
 }
 
 fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: Crate) -> bool {
-    let root_module = CrateRootModuleId::from(crate_id).module(db);
-    let attrs = AttrFlags::query(db, AttrDefId::ModuleId(InternedModuleId::new(db, root_module)));
-    attrs.contains(AttrFlags::IS_NO_STD)
+    let file = crate_id.data(db).root_file_id(db);
+    let item_tree = db.file_item_tree(file.into());
+    let attrs = item_tree.top_level_raw_attrs();
+    for attr in &**attrs {
+        match attr.path().as_ident() {
+            Some(ident) if *ident == sym::no_std => return true,
+            Some(ident) if *ident == sym::cfg_attr => {}
+            _ => continue,
+        }
+
+        // This is a `cfg_attr`; check if it could possibly expand to `no_std`.
+        // Syntax is: `#[cfg_attr(condition(cfg, style), attr0, attr1, <...>)]`
+        let tt = match attr.token_tree_value() {
+            Some(tt) => tt.token_trees(),
+            None => continue,
+        };
+
+        let segments =
+            tt.split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(p)) if p.char == ','));
+        for output in segments.skip(1) {
+            match output.flat_tokens() {
+                [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::no_std => {
+                    return true;
+                }
+                _ => {}
+            }
+        }
+    }
+
+    false
 }
 
 fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId {
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 6a2f06b0a6f6..23b9712d1e6c 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
@@ -17,10 +17,11 @@ use syntax::{AstNode, Parse, ast};
 use triomphe::Arc;
 use tt::TextRange;
 
-use crate::{
-    MacroId, UnresolvedMacro, attrs::AttrFlags, db::DefDatabase, expr_store::HygieneId,
-    macro_call_as_call_id, nameres::DefMap,
-};
+use crate::attr::Attrs;
+use crate::expr_store::HygieneId;
+use crate::macro_call_as_call_id;
+use crate::nameres::DefMap;
+use crate::{MacroId, UnresolvedMacro, db::DefDatabase};
 
 #[derive(Debug)]
 pub(super) struct Expander {
@@ -69,10 +70,11 @@ impl Expander {
 
     pub(super) fn is_cfg_enabled(
         &self,
-        owner: &dyn HasAttrs,
+        db: &dyn DefDatabase,
+        has_attrs: &dyn HasAttrs,
         cfg_options: &CfgOptions,
     ) -> Result<(), cfg::CfgExpr> {
-        AttrFlags::is_cfg_enabled_for(owner, cfg_options)
+        Attrs::is_cfg_enabled_for(db, has_attrs, self.span_map.as_ref(), cfg_options)
     }
 
     pub(super) fn call_syntax_ctx(&self) -> SyntaxContext {
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 fbe0b1ab9596..3794cb18e936 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
@@ -12,6 +12,7 @@ use cfg::CfgOptions;
 use either::Either;
 use hir_expand::{
     HirFileId, InFile, MacroDefId,
+    mod_path::tool_path,
     name::{AsName, Name},
     span_map::SpanMapRef,
 };
@@ -33,7 +34,6 @@ use tt::TextRange;
 use crate::{
     AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId,
     ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro,
-    attrs::AttrFlags,
     builtin_type::BuiltinUint,
     db::DefDatabase,
     expr_store::{
@@ -87,16 +87,14 @@ pub(super) fn lower_body(
     let mut params = vec![];
     let mut collector = ExprCollector::new(db, module, current_file_id);
 
-    let skip_body = AttrFlags::query(
-        db,
-        match owner {
-            DefWithBodyId::FunctionId(it) => it.into(),
-            DefWithBodyId::StaticId(it) => it.into(),
-            DefWithBodyId::ConstId(it) => it.into(),
-            DefWithBodyId::VariantId(it) => it.into(),
-        },
-    )
-    .contains(AttrFlags::RUST_ANALYZER_SKIP);
+    let skip_body = match owner {
+        DefWithBodyId::FunctionId(it) => db.attrs(it.into()),
+        DefWithBodyId::StaticId(it) => db.attrs(it.into()),
+        DefWithBodyId::ConstId(it) => db.attrs(it.into()),
+        DefWithBodyId::VariantId(it) => db.attrs(it.into()),
+    }
+    .rust_analyzer_tool()
+    .any(|attr| *attr.path() == tool_path![skip]);
     // If #[rust_analyzer::skip] annotated, only construct enough information for the signature
     // and skip the body.
     if skip_body {
@@ -2487,7 +2485,7 @@ impl ExprCollector<'_> {
     /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
     /// not.
     fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> bool {
-        let enabled = self.expander.is_cfg_enabled(owner, self.cfg_options);
+        let enabled = self.expander.is_cfg_enabled(self.db, owner, self.cfg_options);
         match enabled {
             Ok(()) => true,
             Err(cfg) => {
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 e386e8d0c596..5b9da3c5e668 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
@@ -12,8 +12,7 @@ use span::Edition;
 use syntax::ast::HasName;
 
 use crate::{
-    AdtId, DefWithBodyId, FunctionId, GenericDefId, StructId, TypeParamId, VariantId,
-    attrs::AttrFlags,
+    AdtId, DefWithBodyId, GenericDefId, TypeParamId, VariantId,
     expr_store::path::{GenericArg, GenericArgs},
     hir::{
         Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, Statement,
@@ -168,7 +167,7 @@ pub fn print_signature(db: &dyn DefDatabase, owner: GenericDefId, edition: Editi
         GenericDefId::AdtId(id) => match id {
             AdtId::StructId(id) => {
                 let signature = db.struct_signature(id);
-                print_struct(db, id, &signature, edition)
+                print_struct(db, &signature, edition)
             }
             AdtId::UnionId(id) => {
                 format!("unimplemented {id:?}")
@@ -180,7 +179,7 @@ pub fn print_signature(db: &dyn DefDatabase, owner: GenericDefId, edition: Editi
         GenericDefId::ConstId(id) => format!("unimplemented {id:?}"),
         GenericDefId::FunctionId(id) => {
             let signature = db.function_signature(id);
-            print_function(db, id, &signature, edition)
+            print_function(db, &signature, edition)
         }
         GenericDefId::ImplId(id) => format!("unimplemented {id:?}"),
         GenericDefId::StaticId(id) => format!("unimplemented {id:?}"),
@@ -209,8 +208,7 @@ pub fn print_path(
 
 pub fn print_struct(
     db: &dyn DefDatabase,
-    id: StructId,
-    StructSignature { name, generic_params, store, flags, shape }: &StructSignature,
+    StructSignature { name, generic_params, store, flags, shape, repr }: &StructSignature,
     edition: Edition,
 ) -> String {
     let mut p = Printer {
@@ -221,7 +219,7 @@ pub fn print_struct(
         line_format: LineFormat::Newline,
         edition,
     };
-    if let Some(repr) = AttrFlags::repr(db, id.into()) {
+    if let Some(repr) = repr {
         if repr.c() {
             wln!(p, "#[repr(C)]");
         }
@@ -257,8 +255,7 @@ pub fn print_struct(
 
 pub fn print_function(
     db: &dyn DefDatabase,
-    id: FunctionId,
-    signature @ FunctionSignature {
+    FunctionSignature {
         name,
         generic_params,
         store,
@@ -266,10 +263,10 @@ pub fn print_function(
         ret_type,
         abi,
         flags,
+        legacy_const_generics_indices,
     }: &FunctionSignature,
     edition: Edition,
 ) -> String {
-    let legacy_const_generics_indices = signature.legacy_const_generics_indices(db, id);
     let mut p = Printer {
         db,
         store,
@@ -301,7 +298,7 @@ pub fn print_function(
         if i != 0 {
             w!(p, ", ");
         }
-        if legacy_const_generics_indices.is_some_and(|idx| idx.contains(&(i as u32))) {
+        if legacy_const_generics_indices.as_ref().is_some_and(|idx| idx.contains(&(i as u32))) {
             w!(p, "const: ");
         }
         p.print_type_ref(*param);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs
index 0cb9325b502e..c7707378a5b3 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs
@@ -189,8 +189,8 @@ fn f() {
 }
     "#,
         expect![[r#"
-            BlockId(3801) in BlockRelativeModuleId { block: Some(BlockId(3800)), local_id: Idx::(1) }
-            BlockId(3800) in BlockRelativeModuleId { block: None, local_id: Idx::(0) }
+            BlockId(3c01) in BlockRelativeModuleId { block: Some(BlockId(3c00)), local_id: Idx::(1) }
+            BlockId(3c00) in BlockRelativeModuleId { block: None, local_id: Idx::(0) }
             crate scope
         "#]],
     );
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 2dac4e7fc84b..b68674c7a74f 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
@@ -38,24 +38,14 @@ fn lower_and_print(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expe
         match def {
             GenericDefId::AdtId(adt_id) => match adt_id {
                 crate::AdtId::StructId(struct_id) => {
-                    out += &print_struct(
-                        &db,
-                        struct_id,
-                        &db.struct_signature(struct_id),
-                        Edition::CURRENT,
-                    );
+                    out += &print_struct(&db, &db.struct_signature(struct_id), Edition::CURRENT);
                 }
                 crate::AdtId::UnionId(_id) => (),
                 crate::AdtId::EnumId(_id) => (),
             },
             GenericDefId::ConstId(_id) => (),
             GenericDefId::FunctionId(function_id) => {
-                out += &print_function(
-                    &db,
-                    function_id,
-                    &db.function_signature(function_id),
-                    Edition::CURRENT,
-                )
+                out += &print_function(&db, &db.function_signature(function_id), Edition::CURRENT)
             }
 
             GenericDefId::ImplId(_id) => (),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
index 67cf466276c5..f31f355cfa5d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
@@ -13,8 +13,7 @@ use stdx::format_to;
 use triomphe::Arc;
 
 use crate::{
-    AssocItemId, AttrDefId, Complete, FxIndexMap, InternedModuleId, ModuleDefId, ModuleId, TraitId,
-    attrs::AttrFlags,
+    AssocItemId, AttrDefId, Complete, FxIndexMap, ModuleDefId, ModuleId, TraitId,
     db::DefDatabase,
     item_scope::{ImportOrExternCrate, ItemInNs},
     nameres::{DefMap, assoc::TraitItems, crate_def_map},
@@ -166,34 +165,17 @@ impl ImportMap {
                         }
                     } else {
                         match item {
-                            ItemInNs::Types(id) | ItemInNs::Values(id) => match id {
-                                ModuleDefId::ModuleId(it) => {
-                                    Some(AttrDefId::ModuleId(InternedModuleId::new(db, it)))
-                                }
-                                ModuleDefId::FunctionId(it) => Some(it.into()),
-                                ModuleDefId::AdtId(it) => Some(it.into()),
-                                ModuleDefId::EnumVariantId(it) => Some(it.into()),
-                                ModuleDefId::ConstId(it) => Some(it.into()),
-                                ModuleDefId::StaticId(it) => Some(it.into()),
-                                ModuleDefId::TraitId(it) => Some(it.into()),
-                                ModuleDefId::TypeAliasId(it) => Some(it.into()),
-                                ModuleDefId::MacroId(it) => Some(it.into()),
-                                ModuleDefId::BuiltinType(_) => None,
-                            },
+                            ItemInNs::Types(id) | ItemInNs::Values(id) => id.try_into().ok(),
                             ItemInNs::Macros(id) => Some(id.into()),
                         }
                     };
                     let (is_doc_hidden, is_unstable, do_not_complete) = match attr_id {
                         None => (false, false, Complete::Yes),
                         Some(attr_id) => {
-                            let attrs = AttrFlags::query(db, attr_id);
+                            let attrs = db.attrs(attr_id);
                             let do_not_complete =
-                                Complete::extract(matches!(attr_id, AttrDefId::TraitId(_)), attrs);
-                            (
-                                attrs.contains(AttrFlags::IS_DOC_HIDDEN),
-                                attrs.contains(AttrFlags::IS_UNSTABLE),
-                                do_not_complete,
-                            )
+                                Complete::extract(matches!(attr_id, AttrDefId::TraitId(_)), &attrs);
+                            (attrs.has_doc_hidden(), attrs.is_unstable(), do_not_complete)
                         }
                     };
 
@@ -257,15 +239,15 @@ impl ImportMap {
             };
 
             let attr_id = item.into();
-            let attrs = AttrFlags::query(db, attr_id);
+            let attrs = &db.attrs(attr_id);
             let item_do_not_complete = Complete::extract(false, attrs);
             let do_not_complete =
                 Complete::for_trait_item(trait_import_info.complete, item_do_not_complete);
             let assoc_item_info = ImportInfo {
                 container: trait_import_info.container,
                 name: assoc_item_name.clone(),
-                is_doc_hidden: attrs.contains(AttrFlags::IS_DOC_HIDDEN),
-                is_unstable: attrs.contains(AttrFlags::IS_UNSTABLE),
+                is_doc_hidden: attrs.has_doc_hidden(),
+                is_unstable: attrs.is_unstable(),
                 complete: do_not_complete,
             };
 
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 2a104fff2b92..f35df8d3a7e1 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
@@ -30,7 +30,6 @@
 //! In general, any item in the `ItemTree` stores its `AstId`, which allows mapping it back to its
 //! surface syntax.
 
-mod attrs;
 mod lower;
 mod pretty;
 #[cfg(test)]
@@ -44,8 +43,10 @@ use std::{
 };
 
 use ast::{AstNode, StructKind};
+use base_db::Crate;
 use hir_expand::{
     ExpandTo, HirFileId,
+    attrs::RawAttrs,
     mod_path::{ModPath, PathKind},
     name::Name,
 };
@@ -58,12 +59,9 @@ use syntax::{SyntaxKind, ast, match_ast};
 use thin_vec::ThinVec;
 use triomphe::Arc;
 
-use crate::{BlockId, Lookup, db::DefDatabase};
+use crate::{BlockId, Lookup, attr::Attrs, db::DefDatabase};
 
-pub(crate) use crate::item_tree::{
-    attrs::*,
-    lower::{lower_use_tree, visibility_from_ast},
-};
+pub(crate) use crate::item_tree::lower::{lower_use_tree, visibility_from_ast};
 
 #[derive(Copy, Clone, Eq, PartialEq)]
 pub(crate) struct RawVisibilityId(u32);
@@ -98,7 +96,7 @@ pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) ->
     let mut item_tree = match_ast! {
         match syntax {
             ast::SourceFile(file) => {
-                let top_attrs = ctx.lower_attrs(&file);
+                let top_attrs = RawAttrs::new(db, &file, ctx.span_map());
                 let mut item_tree = ctx.lower_module_items(&file);
                 item_tree.top_attrs = top_attrs;
                 item_tree
@@ -134,7 +132,7 @@ pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) ->
                     attrs: FxHashMap::default(),
                     small_data: FxHashMap::default(),
                     big_data: FxHashMap::default(),
-                    top_attrs: AttrsOrCfg::empty(),
+                    top_attrs: RawAttrs::EMPTY,
                     vis: ItemVisibilities { arena: ThinVec::new() },
                 })
             })
@@ -170,7 +168,7 @@ pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc
                     attrs: FxHashMap::default(),
                     small_data: FxHashMap::default(),
                     big_data: FxHashMap::default(),
-                    top_attrs: AttrsOrCfg::empty(),
+                    top_attrs: RawAttrs::EMPTY,
                     vis: ItemVisibilities { arena: ThinVec::new() },
                 })
             })
@@ -184,8 +182,8 @@ pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc
 #[derive(Debug, Default, Eq, PartialEq)]
 pub struct ItemTree {
     top_level: Box<[ModItemId]>,
-    top_attrs: AttrsOrCfg,
-    attrs: FxHashMap, AttrsOrCfg>,
+    top_attrs: RawAttrs,
+    attrs: FxHashMap, RawAttrs>,
     vis: ItemVisibilities,
     big_data: FxHashMap, BigModItem>,
     small_data: FxHashMap, SmallModItem>,
@@ -199,12 +197,26 @@ impl ItemTree {
     }
 
     /// Returns the inner attributes of the source file.
-    pub(crate) fn top_level_attrs(&self) -> &AttrsOrCfg {
+    pub(crate) fn top_level_raw_attrs(&self) -> &RawAttrs {
         &self.top_attrs
     }
 
-    pub(crate) fn attrs(&self, of: FileAstId) -> Option<&AttrsOrCfg> {
-        self.attrs.get(&of)
+    /// Returns the inner attributes of the source file.
+    pub(crate) fn top_level_attrs(&self, db: &dyn DefDatabase, krate: Crate) -> Attrs {
+        Attrs::expand_cfg_attr(db, krate, self.top_attrs.clone())
+    }
+
+    pub(crate) fn raw_attrs(&self, of: FileAstId) -> &RawAttrs {
+        self.attrs.get(&of).unwrap_or(&RawAttrs::EMPTY)
+    }
+
+    pub(crate) fn attrs(
+        &self,
+        db: &dyn DefDatabase,
+        krate: Crate,
+        of: FileAstId,
+    ) -> Attrs {
+        Attrs::expand_cfg_attr(db, krate, self.raw_attrs(of).clone())
     }
 
     /// Returns a count of a few, expensive items.
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/attrs.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/attrs.rs
deleted file mode 100644
index 5c635a4b3831..000000000000
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/attrs.rs
+++ /dev/null
@@ -1,220 +0,0 @@
-//! Defines attribute helpers for name resolution.
-//!
-//! Notice we don't preserve all attributes for name resolution, to save space:
-//! for example, we skip doc comments (desugared to `#[doc = "..."]` attributes)
-//! and `#[inline]`. The filtered attributes are listed in [`hir_expand::attrs`].
-
-use std::{
-    borrow::Cow,
-    convert::Infallible,
-    ops::{self, ControlFlow},
-};
-
-use cfg::{CfgExpr, CfgOptions};
-use either::Either;
-use hir_expand::{
-    attrs::{Attr, AttrId, AttrInput, Meta, collect_item_tree_attrs},
-    mod_path::ModPath,
-    name::Name,
-    span_map::SpanMapRef,
-};
-use intern::{Interned, Symbol, sym};
-use syntax::{AstNode, T, ast};
-use syntax_bridge::DocCommentDesugarMode;
-use tt::token_to_literal;
-
-use crate::{db::DefDatabase, item_tree::lower::Ctx};
-
-#[derive(Debug, PartialEq, Eq)]
-pub(crate) enum AttrsOrCfg {
-    Enabled {
-        attrs: AttrsOwned,
-    },
-    /// This only collects the attributes up to the disabled `cfg` (this is what needed for crate-level attributes.)
-    CfgDisabled(Box<(CfgExpr, AttrsOwned)>),
-}
-
-impl Default for AttrsOrCfg {
-    #[inline]
-    fn default() -> Self {
-        AttrsOrCfg::Enabled { attrs: AttrsOwned(Box::new([])) }
-    }
-}
-
-impl AttrsOrCfg {
-    pub(crate) fn lower<'a>(
-        db: &dyn DefDatabase,
-        owner: &dyn ast::HasAttrs,
-        cfg_options: &dyn Fn() -> &'a CfgOptions,
-        span_map: SpanMapRef<'_>,
-    ) -> AttrsOrCfg {
-        let mut attrs = Vec::new();
-        let result =
-            collect_item_tree_attrs::(owner, cfg_options, |meta, container, _, _| {
-                // NOTE: We cannot early return from this function, *every* attribute must be pushed, otherwise we'll mess the `AttrId`
-                // tracking.
-                let (span, path_range, input) = match meta {
-                    Meta::NamedKeyValue { path_range, name: _, value } => {
-                        let span = span_map.span_for_range(path_range);
-                        let input = value.map(|value| {
-                            Box::new(AttrInput::Literal(token_to_literal(
-                                value.text(),
-                                span_map.span_for_range(value.text_range()),
-                            )))
-                        });
-                        (span, path_range, input)
-                    }
-                    Meta::TokenTree { path, tt } => {
-                        let span = span_map.span_for_range(path.range);
-                        let tt = syntax_bridge::syntax_node_to_token_tree(
-                            tt.syntax(),
-                            span_map,
-                            span,
-                            DocCommentDesugarMode::ProcMacro,
-                        );
-                        let input = Some(Box::new(AttrInput::TokenTree(tt)));
-                        (span, path.range, input)
-                    }
-                    Meta::Path { path } => {
-                        let span = span_map.span_for_range(path.range);
-                        (span, path.range, None)
-                    }
-                };
-
-                let path = container.token_at_offset(path_range.start()).right_biased().and_then(
-                    |first_path_token| {
-                        let is_abs = matches!(first_path_token.kind(), T![:] | T![::]);
-                        let segments =
-                            std::iter::successors(Some(first_path_token), |it| it.next_token())
-                                .take_while(|it| it.text_range().end() <= path_range.end())
-                                .filter(|it| it.kind().is_any_identifier());
-                        ModPath::from_tokens(
-                            db,
-                            &mut |range| span_map.span_for_range(range).ctx,
-                            is_abs,
-                            segments,
-                        )
-                    },
-                );
-                let path = path.unwrap_or_else(|| Name::missing().into());
-
-                attrs.push(Attr { path: Interned::new(path), input, ctxt: span.ctx });
-                ControlFlow::Continue(())
-            });
-        let attrs = AttrsOwned(attrs.into_boxed_slice());
-        match result {
-            Some(Either::Right(cfg)) => AttrsOrCfg::CfgDisabled(Box::new((cfg, attrs))),
-            None => AttrsOrCfg::Enabled { attrs },
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub(crate) struct AttrsOwned(Box<[Attr]>);
-
-#[derive(Debug, Clone, Copy)]
-pub(crate) struct Attrs<'a>(&'a [Attr]);
-
-impl ops::Deref for Attrs<'_> {
-    type Target = [Attr];
-
-    #[inline]
-    fn deref(&self) -> &Self::Target {
-        self.0
-    }
-}
-
-impl Ctx<'_> {
-    #[inline]
-    pub(super) fn lower_attrs(&self, owner: &dyn ast::HasAttrs) -> AttrsOrCfg {
-        AttrsOrCfg::lower(self.db, owner, &|| self.cfg_options(), self.span_map())
-    }
-}
-
-impl AttrsOwned {
-    #[inline]
-    pub(crate) fn as_ref(&self) -> Attrs<'_> {
-        Attrs(&self.0)
-    }
-}
-
-impl<'a> Attrs<'a> {
-    pub(crate) const EMPTY: Self = Attrs(&[]);
-
-    #[inline]
-    pub(crate) fn by_key(self, key: Symbol) -> AttrQuery<'a> {
-        AttrQuery { attrs: self, key }
-    }
-
-    #[inline]
-    pub(crate) fn iter(self) -> impl Iterator {
-        self.0.iter().enumerate().map(|(id, attr)| (AttrId::from_item_tree_index(id as u32), attr))
-    }
-
-    #[inline]
-    pub(crate) fn iter_after(
-        self,
-        after: Option,
-    ) -> impl Iterator {
-        let skip = after.map_or(0, |after| after.item_tree_index() + 1);
-        self.0[skip as usize..]
-            .iter()
-            .enumerate()
-            .map(move |(id, attr)| (AttrId::from_item_tree_index(id as u32 + skip), attr))
-    }
-
-    #[inline]
-    pub(crate) fn is_proc_macro(&self) -> bool {
-        self.by_key(sym::proc_macro).exists()
-    }
-
-    #[inline]
-    pub(crate) fn is_proc_macro_attribute(&self) -> bool {
-        self.by_key(sym::proc_macro_attribute).exists()
-    }
-}
-#[derive(Debug, Clone)]
-pub(crate) struct AttrQuery<'attr> {
-    attrs: Attrs<'attr>,
-    key: Symbol,
-}
-
-impl<'attr> AttrQuery<'attr> {
-    #[inline]
-    pub(crate) fn tt_values(self) -> impl Iterator {
-        self.attrs().filter_map(|attr| attr.token_tree_value())
-    }
-
-    #[inline]
-    pub(crate) fn string_value_with_span(self) -> Option<(&'attr Symbol, span::Span)> {
-        self.attrs().find_map(|attr| attr.string_value_with_span())
-    }
-
-    #[inline]
-    pub(crate) fn string_value_unescape(self) -> Option> {
-        self.attrs().find_map(|attr| attr.string_value_unescape())
-    }
-
-    #[inline]
-    pub(crate) fn exists(self) -> bool {
-        self.attrs().next().is_some()
-    }
-
-    #[inline]
-    pub(crate) fn attrs(self) -> impl Iterator + Clone {
-        let key = self.key;
-        self.attrs.0.iter().filter(move |attr| attr.path.as_ident().is_some_and(|s| *s == key))
-    }
-}
-
-impl AttrsOrCfg {
-    #[inline]
-    pub(super) fn empty() -> Self {
-        AttrsOrCfg::Enabled { attrs: AttrsOwned(Box::new([])) }
-    }
-
-    #[inline]
-    pub(super) fn is_empty(&self) -> bool {
-        matches!(self, AttrsOrCfg::Enabled { attrs } if attrs.as_ref().is_empty())
-    }
-}
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 b50a75169158..454e06399583 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
@@ -1,9 +1,8 @@
 //! AST -> `ItemTree` lowering code.
 
-use std::cell::OnceCell;
+use std::{cell::OnceCell, collections::hash_map::Entry};
 
 use base_db::FxIndexSet;
-use cfg::CfgOptions;
 use hir_expand::{
     HirFileId,
     mod_path::PathKind,
@@ -23,19 +22,18 @@ use crate::{
     item_tree::{
         BigModItem, Const, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl,
         ImportAlias, Interned, ItemTree, ItemTreeAstId, Macro2, MacroCall, MacroRules, Mod,
-        ModItemId, ModKind, ModPath, RawVisibility, RawVisibilityId, SmallModItem, Static, Struct,
-        StructKind, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, VisibilityExplicitness,
-        attrs::AttrsOrCfg,
+        ModItemId, ModKind, ModPath, RawAttrs, RawVisibility, RawVisibilityId, SmallModItem,
+        Static, Struct, StructKind, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind,
+        VisibilityExplicitness,
     },
 };
 
 pub(super) struct Ctx<'a> {
-    pub(super) db: &'a dyn DefDatabase,
+    db: &'a dyn DefDatabase,
     tree: ItemTree,
     source_ast_id_map: Arc,
     span_map: OnceCell,
     file: HirFileId,
-    cfg_options: OnceCell<&'a CfgOptions>,
     top_level: Vec,
     visibilities: FxIndexSet,
 }
@@ -47,18 +45,12 @@ impl<'a> Ctx<'a> {
             tree: ItemTree::default(),
             source_ast_id_map: db.ast_id_map(file),
             file,
-            cfg_options: OnceCell::new(),
             span_map: OnceCell::new(),
             visibilities: FxIndexSet::default(),
             top_level: Vec::new(),
         }
     }
 
-    #[inline]
-    pub(super) fn cfg_options(&self) -> &'a CfgOptions {
-        self.cfg_options.get_or_init(|| self.file.krate(self.db).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()
     }
@@ -106,7 +98,7 @@ impl<'a> Ctx<'a> {
     }
 
     pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree {
-        self.tree.top_attrs = self.lower_attrs(block);
+        self.tree.top_attrs = RawAttrs::new(self.db, block, self.span_map());
         self.top_level = block
             .statements()
             .filter_map(|stmt| match stmt {
@@ -152,15 +144,22 @@ impl<'a> Ctx<'a> {
             // FIXME: Handle `global_asm!()`.
             ast::Item::AsmExpr(_) => return None,
         };
-        let attrs = self.lower_attrs(item);
+        let attrs = RawAttrs::new(self.db, item, self.span_map());
         self.add_attrs(mod_item.ast_id(), attrs);
 
         Some(mod_item)
     }
 
-    fn add_attrs(&mut self, item: FileAstId, attrs: AttrsOrCfg) {
+    fn add_attrs(&mut self, item: FileAstId, attrs: RawAttrs) {
         if !attrs.is_empty() {
-            self.tree.attrs.insert(item, attrs);
+            match self.tree.attrs.entry(item) {
+                Entry::Occupied(mut entry) => {
+                    *entry.get_mut() = entry.get().merge(attrs);
+                }
+                Entry::Vacant(entry) => {
+                    entry.insert(attrs);
+                }
+            }
         }
     }
 
@@ -353,7 +352,7 @@ impl<'a> Ctx<'a> {
                         ast::ExternItem::TypeAlias(ty) => self.lower_type_alias(ty)?.into(),
                         ast::ExternItem::MacroCall(call) => self.lower_macro_call(call)?.into(),
                     };
-                    let attrs = self.lower_attrs(&item);
+                    let attrs = RawAttrs::new(self.db, &item, self.span_map());
                     self.add_attrs(mod_item.ast_id(), attrs);
                     Some(mod_item)
                 })
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
index 66a2d14a734f..94a6cce3ce33 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
@@ -7,8 +7,8 @@ use span::{Edition, ErasedFileAstId};
 use crate::{
     item_tree::{
         Const, DefDatabase, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl, ItemTree,
-        Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, RawVisibilityId, Static, Struct,
-        Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, attrs::AttrsOrCfg,
+        Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, RawAttrs, RawVisibilityId, Static,
+        Struct, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind,
     },
     visibility::RawVisibility,
 };
@@ -85,13 +85,9 @@ impl Printer<'_> {
         }
     }
 
-    fn print_attrs(&mut self, attrs: &AttrsOrCfg, inner: bool, separated_by: &str) {
-        let AttrsOrCfg::Enabled { attrs } = attrs else {
-            w!(self, "#[cfg(false)]{separated_by}");
-            return;
-        };
+    fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool, separated_by: &str) {
         let inner = if inner { "!" } else { "" };
-        for attr in &*attrs.as_ref() {
+        for attr in &**attrs {
             w!(
                 self,
                 "#{}[{}{}]{}",
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
index a57432f33c3d..91b42bef8f79 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
@@ -30,8 +30,10 @@ use crate::{A, B};
 
 use a::{c, d::{e}};
         "#,
-        expect![[r#"
+        expect![[r##"
+            #![doc = " file comment"]
             #![no_std]
+            #![doc = " another file comment"]
 
             // AstId: ExternCrate[070B, 0]
             pub(self) extern crate self as renamed;
@@ -45,12 +47,13 @@ use a::{c, d::{e}};
             // AstId: Use[0000, 1]
             pub(self) use globs::*;
 
+            #[doc = " docs on import"]
             // AstId: Use[0000, 2]
             pub(self) use crate::{A, B};
 
             // AstId: Use[0000, 3]
             pub(self) use a::{c, d::{e}};
-        "#]],
+        "##]],
     );
 }
 
@@ -192,6 +195,8 @@ mod inline {
 mod outline;
         "#,
         expect![[r##"
+            #[doc = " outer"]
+            #[doc = " inner"]
             // AstId: Module[03AE, 0]
             pub(self) mod inline {
                 // AstId: Use[0000, 0]
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 4f97baadd183..df0705bf90cb 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
@@ -10,7 +10,6 @@ use triomphe::Arc;
 use crate::{
     AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, ModuleDefId,
     StaticId, StructId, TraitId, TypeAliasId, UnionId,
-    attrs::AttrFlags,
     db::DefDatabase,
     expr_store::path::Path,
     nameres::{assoc::TraitItems, crate_def_map, crate_local_def_map},
@@ -214,14 +213,14 @@ impl LangItems {
         T: Into + Copy,
     {
         let _p = tracing::info_span!("collect_lang_item").entered();
-        if let Some(lang_item) = AttrFlags::lang_item(db, item.into()) {
+        if let Some(lang_item) = lang_attr(db, item.into()) {
             self.items.entry(lang_item).or_insert_with(|| constructor(item));
         }
     }
 }
 
 pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option {
-    AttrFlags::lang_item(db, item)
+    db.attrs(item).lang_item()
 }
 
 pub(crate) fn notable_traits_in_deps(db: &dyn DefDatabase, krate: Crate) -> Arc<[Arc<[TraitId]>]> {
@@ -241,7 +240,7 @@ pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option
     for (_, module_data) in crate_def_map.modules() {
         for def in module_data.scope.declarations() {
             if let ModuleDefId::TraitId(trait_) = def
-                && AttrFlags::query(db, trait_.into()).contains(AttrFlags::IS_DOC_NOTABLE_TRAIT)
+                && db.attrs(trait_.into()).has_doc_notable_trait()
             {
                 traits.push(trait_);
             }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index c3c9fc75252d..e5c213ca937c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -19,7 +19,7 @@ extern crate ra_ap_rustc_abi as rustc_abi;
 
 pub mod db;
 
-pub mod attrs;
+pub mod attr;
 pub mod builtin_type;
 pub mod item_scope;
 pub mod per_ns;
@@ -45,7 +45,7 @@ pub mod find_path;
 pub mod import_map;
 pub mod visibility;
 
-use intern::{Interned, Symbol};
+use intern::{Interned, Symbol, sym};
 pub use rustc_abi as layout;
 use thin_vec::ThinVec;
 use triomphe::Arc;
@@ -80,7 +80,7 @@ use syntax::{AstNode, ast};
 pub use hir_expand::{Intern, Lookup, tt};
 
 use crate::{
-    attrs::AttrFlags,
+    attr::Attrs,
     builtin_type::BuiltinType,
     db::DefDatabase,
     expr_store::ExpressionStoreSourceMap,
@@ -956,16 +956,10 @@ impl CallableDefId {
     }
 }
 
-// FIXME: We probably should use this in more places.
-/// This is used to avoid interning the whole `AttrDefId`, so we intern just modules and not everything.
-#[salsa_macros::interned(debug, no_lifetime)]
-pub struct InternedModuleId {
-    pub loc: ModuleId,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, salsa_macros::Supertype)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub enum AttrDefId {
-    ModuleId(InternedModuleId),
+    ModuleId(ModuleId),
+    FieldId(FieldId),
     AdtId(AdtId),
     FunctionId(FunctionId),
     EnumVariantId(EnumVariantId),
@@ -975,12 +969,15 @@ pub enum AttrDefId {
     TypeAliasId(TypeAliasId),
     MacroId(MacroId),
     ImplId(ImplId),
+    GenericParamId(GenericParamId),
     ExternBlockId(ExternBlockId),
     ExternCrateId(ExternCrateId),
     UseId(UseId),
 }
 
 impl_from!(
+    ModuleId,
+    FieldId,
     AdtId(StructId, EnumId, UnionId),
     EnumVariantId,
     StaticId,
@@ -990,11 +987,41 @@ impl_from!(
     TypeAliasId,
     MacroId(Macro2Id, MacroRulesId, ProcMacroId),
     ImplId,
+    GenericParamId,
     ExternCrateId,
     UseId
     for AttrDefId
 );
 
+impl TryFrom for AttrDefId {
+    type Error = ();
+
+    fn try_from(value: ModuleDefId) -> Result {
+        match value {
+            ModuleDefId::ModuleId(it) => Ok(it.into()),
+            ModuleDefId::FunctionId(it) => Ok(it.into()),
+            ModuleDefId::AdtId(it) => Ok(it.into()),
+            ModuleDefId::EnumVariantId(it) => Ok(it.into()),
+            ModuleDefId::ConstId(it) => Ok(it.into()),
+            ModuleDefId::StaticId(it) => Ok(it.into()),
+            ModuleDefId::TraitId(it) => Ok(it.into()),
+            ModuleDefId::TypeAliasId(it) => Ok(it.into()),
+            ModuleDefId::MacroId(id) => Ok(id.into()),
+            ModuleDefId::BuiltinType(_) => Err(()),
+        }
+    }
+}
+
+impl From for AttrDefId {
+    fn from(acid: ItemContainerId) -> Self {
+        match acid {
+            ItemContainerId::ModuleId(mid) => AttrDefId::ModuleId(mid),
+            ItemContainerId::ImplId(iid) => AttrDefId::ImplId(iid),
+            ItemContainerId::TraitId(tid) => AttrDefId::TraitId(tid),
+            ItemContainerId::ExternBlockId(id) => AttrDefId::ExternBlockId(id),
+        }
+    }
+}
 impl From for AttrDefId {
     fn from(assoc: AssocItemId) -> Self {
         match assoc {
@@ -1235,7 +1262,8 @@ impl HasModule for GenericDefId {
 impl HasModule for AttrDefId {
     fn module(&self, db: &dyn DefDatabase) -> ModuleId {
         match self {
-            AttrDefId::ModuleId(it) => it.loc(db),
+            AttrDefId::ModuleId(it) => *it,
+            AttrDefId::FieldId(it) => it.parent.module(db),
             AttrDefId::AdtId(it) => it.module(db),
             AttrDefId::FunctionId(it) => it.module(db),
             AttrDefId::EnumVariantId(it) => it.module(db),
@@ -1245,6 +1273,12 @@ impl HasModule for AttrDefId {
             AttrDefId::TypeAliasId(it) => it.module(db),
             AttrDefId::ImplId(it) => it.module(db),
             AttrDefId::ExternBlockId(it) => it.module(db),
+            AttrDefId::GenericParamId(it) => match it {
+                GenericParamId::TypeParamId(it) => it.parent(),
+                GenericParamId::ConstParamId(it) => it.parent(),
+                GenericParamId::LifetimeParamId(it) => it.parent,
+            }
+            .module(db),
             AttrDefId::MacroId(it) => it.module(db),
             AttrDefId::ExternCrateId(it) => it.module(db),
             AttrDefId::UseId(it) => it.module(db),
@@ -1368,18 +1402,32 @@ pub enum Complete {
 }
 
 impl Complete {
-    #[inline]
-    pub fn extract(is_trait: bool, attrs: AttrFlags) -> Complete {
-        if attrs.contains(AttrFlags::COMPLETE_IGNORE_FLYIMPORT) {
-            return Complete::IgnoreFlyimport;
-        } else if is_trait {
-            if attrs.contains(AttrFlags::COMPLETE_IGNORE_METHODS) {
-                return Complete::IgnoreMethods;
-            } else if attrs.contains(AttrFlags::COMPLETE_IGNORE_FLYIMPORT_METHODS) {
-                return Complete::IgnoreFlyimportMethods;
+    pub fn extract(is_trait: bool, attrs: &Attrs) -> Complete {
+        let mut do_not_complete = Complete::Yes;
+        for ra_attr in attrs.rust_analyzer_tool() {
+            let segments = ra_attr.path.segments();
+            if segments.len() != 2 {
+                continue;
+            }
+            let action = segments[1].symbol();
+            if *action == sym::completions {
+                match ra_attr.token_tree_value().map(|tt| tt.token_trees().flat_tokens()) {
+                    Some([tt::TokenTree::Leaf(tt::Leaf::Ident(ident))]) => {
+                        if ident.sym == sym::ignore_flyimport {
+                            do_not_complete = Complete::IgnoreFlyimport;
+                        } else if is_trait {
+                            if ident.sym == sym::ignore_methods {
+                                do_not_complete = Complete::IgnoreMethods;
+                            } else if ident.sym == sym::ignore_flyimport_methods {
+                                do_not_complete = Complete::IgnoreFlyimportMethods;
+                            }
+                        }
+                    }
+                    _ => {}
+                }
             }
         }
-        Complete::Yes
+        do_not_complete
     }
 
     #[inline]
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 115b487b7ac8..c489c1f7c1da 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -300,21 +300,21 @@ fn match_by_first_token_literally() {
     check(
         r#"
 macro_rules! m {
-    ($i:ident) => ( enum $i {} );
+    ($i:ident) => ( mod $i {} );
     (= $i:ident) => ( fn $i() {} );
     (+ $i:ident) => ( struct $i; )
 }
-m! { Foo }
+m! { foo }
 m! { = bar }
 m! { + Baz }
 "#,
         expect![[r#"
 macro_rules! m {
-    ($i:ident) => ( enum $i {} );
+    ($i:ident) => ( mod $i {} );
     (= $i:ident) => ( fn $i() {} );
     (+ $i:ident) => ( struct $i; )
 }
-enum Foo {}
+mod foo {}
 fn bar() {}
 struct Baz;
 "#]],
@@ -326,21 +326,21 @@ fn match_by_last_token_literally() {
     check(
         r#"
 macro_rules! m {
-    ($i:ident) => ( enum $i {} );
+    ($i:ident) => ( mod $i {} );
     ($i:ident =) => ( fn $i() {} );
     ($i:ident +) => ( struct $i; )
 }
-m! { Foo }
+m! { foo }
 m! { bar = }
 m! { Baz + }
 "#,
         expect![[r#"
 macro_rules! m {
-    ($i:ident) => ( enum $i {} );
+    ($i:ident) => ( mod $i {} );
     ($i:ident =) => ( fn $i() {} );
     ($i:ident +) => ( struct $i; )
 }
-enum Foo {}
+mod foo {}
 fn bar() {}
 struct Baz;
 "#]],
@@ -352,21 +352,21 @@ fn match_by_ident() {
     check(
         r#"
 macro_rules! m {
-    ($i:ident) => ( enum $i {} );
+    ($i:ident) => ( mod $i {} );
     (spam $i:ident) => ( fn $i() {} );
     (eggs $i:ident) => ( struct $i; )
 }
-m! { Foo }
+m! { foo }
 m! { spam bar }
 m! { eggs Baz }
 "#,
         expect![[r#"
 macro_rules! m {
-    ($i:ident) => ( enum $i {} );
+    ($i:ident) => ( mod $i {} );
     (spam $i:ident) => ( fn $i() {} );
     (eggs $i:ident) => ( struct $i; )
 }
-enum Foo {}
+mod foo {}
 fn bar() {}
 struct Baz;
 "#]],
@@ -378,12 +378,12 @@ fn match_by_separator_token() {
     check(
         r#"
 macro_rules! m {
-    ($($i:ident),*) => ($(enum $i {} )*);
+    ($($i:ident),*) => ($(mod $i {} )*);
     ($($i:ident)#*) => ($(fn $i() {} )*);
     ($i:ident ,# $ j:ident) => ( struct $i; struct $ j; )
 }
 
-m! { Baz, Qux }
+m! { foo, bar }
 
 m! { foo# bar }
 
@@ -391,13 +391,13 @@ m! { Foo,# Bar }
 "#,
         expect![[r#"
 macro_rules! m {
-    ($($i:ident),*) => ($(enum $i {} )*);
+    ($($i:ident),*) => ($(mod $i {} )*);
     ($($i:ident)#*) => ($(fn $i() {} )*);
     ($i:ident ,# $ j:ident) => ( struct $i; struct $ j; )
 }
 
-enum Baz {}
-enum Qux {}
+mod foo {}
+mod bar {}
 
 fn foo() {}
 fn bar() {}
@@ -1114,11 +1114,11 @@ fn test_single_item() {
     check(
         r#"
 macro_rules! m { ($i:item) => ( $i ) }
-m! { struct C {} }
+m! { mod c {} }
 "#,
         expect![[r#"
 macro_rules! m { ($i:item) => ( $i ) }
-struct C {}
+mod c {}
 "#]],
     )
 }
@@ -1144,7 +1144,6 @@ m! {
     type T = u8;
 }
 "#,
-        // The modules are counted twice, once because of the module and once because of the macro call.
         expect![[r#"
 macro_rules! m { ($($i:item)*) => ($($i )*) }
 extern crate a;
@@ -1162,9 +1161,7 @@ trait J {}
 fn h() {}
 extern {}
 type T = u8;
-
-mod b;
-mod c {}"#]],
+"#]],
     );
 }
 
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 74393411054e..e8ae499d27b2 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
@@ -245,21 +245,6 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
         }
     }
 
-    for (_, module) in def_map.modules() {
-        let Some(src) = module.declaration_source(&db) else {
-            continue;
-        };
-        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(),
-                false,
-                false,
-            );
-            format_to!(expanded_text, "\n{}", pp)
-        }
-    }
-
     for impl_id in def_map[local_id].scope.impls() {
         let src = impl_id.lookup(&db).source(&db);
         if let Some(macro_file) = src.file_id.macro_file()
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
index 3f0afe61e0b8..6952a9da1013 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
@@ -9,93 +9,37 @@ use crate::macro_expansion_tests::{check, check_errors};
 
 #[test]
 fn attribute_macro_attr_censoring() {
+    cov_mark::check!(attribute_macro_attr_censoring);
     check(
         r#"
 //- proc_macros: identity
-//- minicore: derive
-#[attr1] #[derive()] #[proc_macros::identity] #[attr2]
+#[attr1] #[proc_macros::identity] #[attr2]
 struct S;
-
-/// Foo
-#[cfg_attr(false, doc = "abc...", attr1)]
-mod foo {
-    #![cfg_attr(true, cfg_attr(true, foo, cfg_attr(false, bar), proc_macros::identity))]
-    #![cfg_attr(true, doc = "123...", attr2)]
-    #![attr3]
-
-    #[cfg_attr(true, cfg(false))]
-    fn foo() {}
-
-    #[cfg(true)]
-    fn bar() {}
-}
 "#,
-        expect![[r##"
-#[attr1] #[derive()] #[proc_macros::identity] #[attr2]
+        expect![[r#"
+#[attr1] #[proc_macros::identity] #[attr2]
 struct S;
 
-/// Foo
-#[cfg_attr(false, doc = "abc...", attr1)]
-mod foo {
-    #![cfg_attr(true, cfg_attr(true, foo, cfg_attr(false, bar), proc_macros::identity))]
-    #![cfg_attr(true, doc = "123...", attr2)]
-    #![attr3]
-
-    #[cfg_attr(true, cfg(false))]
-    fn foo() {}
-
-    #[cfg(true)]
-    fn bar() {}
-}
-
 #[attr1]
-#[attr2] struct S;
-#[doc = " Foo"] mod foo {
-    # ![foo]
-    # ![doc = "123..."]
-    # ![attr2]
-    # ![attr3]
-    #[cfg_attr(true , cfg(false ))] fn foo() {}
-    #[cfg(true )] fn bar() {}
-}"##]],
+#[attr2] struct S;"#]],
     );
 }
 
 #[test]
 fn derive_censoring() {
+    cov_mark::check!(derive_censoring);
     check(
         r#"
 //- proc_macros: derive_identity
 //- minicore:derive
-use derive as my_cool_derive;
 #[attr1]
 #[derive(Foo)]
 #[derive(proc_macros::DeriveIdentity)]
 #[derive(Bar)]
 #[attr2]
 struct S;
-
-#[my_cool_derive()]
-#[cfg_attr(true, derive(), attr1, derive(proc_macros::DeriveIdentity))]
-#[my_cool_derive()]
-struct Foo {
-    #[cfg_attr(false, cfg(false), attr2)]
-    v1: i32,
-    #[cfg_attr(true, cfg(false), attr2)]
-    v1: i32,
-    #[cfg_attr(true, attr3)]
-    v2: fn(#[cfg(false)] param: i32, #[cfg_attr(true, attr4)] param2: u32),
-    v3: Foo<{
-        #[cfg(false)]
-        let foo = 123;
-        456
-    }>,
-    #[cfg(false)]
-    v4: bool // No comma here
-}
 "#,
         expect![[r#"
-use derive as my_cool_derive;
 #[attr1]
 #[derive(Foo)]
 #[derive(proc_macros::DeriveIdentity)]
@@ -103,32 +47,6 @@ use derive as my_cool_derive;
 #[attr2]
 struct S;
 
-#[my_cool_derive()]
-#[cfg_attr(true, derive(), attr1, derive(proc_macros::DeriveIdentity))]
-#[my_cool_derive()]
-struct Foo {
-    #[cfg_attr(false, cfg(false), attr2)]
-    v1: i32,
-    #[cfg_attr(true, cfg(false), attr2)]
-    v1: i32,
-    #[cfg_attr(true, attr3)]
-    v2: fn(#[cfg(false)] param: i32, #[cfg_attr(true, attr4)] param2: u32),
-    v3: Foo<{
-        #[cfg(false)]
-        let foo = 123;
-        456
-    }>,
-    #[cfg(false)]
-    v4: bool // No comma here
-}
-
-#[attr1]
-#[my_cool_derive()] struct Foo {
-    v1: i32, #[attr3]v2: fn(#[attr4]param2: u32), v3: Foo< {
-        456
-    }
-    >,
-}
 #[attr1]
 #[derive(Bar)]
 #[attr2] struct S;"#]],
@@ -169,7 +87,7 @@ fn foo() { bar.; blub }
 fn foo() { bar.; blub }
 
 fn foo() {
-    bar.;
+    bar. ;
     blub
 }"#]],
     );
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index e4b95a5a77a5..7d5e627964eb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -391,14 +391,19 @@ pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefM
     )
     .entered();
 
-    let root_file_id = crate_id.root_file_id(db);
-    let module_data =
-        ModuleData::new(ModuleOrigin::CrateRoot { definition: root_file_id }, Visibility::Public);
+    let module_data = ModuleData::new(
+        ModuleOrigin::CrateRoot { definition: krate.root_file_id(db) },
+        Visibility::Public,
+    );
 
     let def_map =
         DefMap::empty(crate_id, Arc::new(DefMapCrateData::new(krate.edition)), module_data, None);
-    let (def_map, local_def_map) =
-        collector::collect_defs(db, def_map, TreeId::new(root_file_id.into(), None), None);
+    let (def_map, local_def_map) = collector::collect_defs(
+        db,
+        def_map,
+        TreeId::new(krate.root_file_id(db).into(), None),
+        None,
+    );
 
     DefMapPair::new(db, def_map, local_def_map)
 }
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 b67853347bde..8d2a386de8ec 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
@@ -4,8 +4,7 @@ use std::mem;
 
 use cfg::CfgOptions;
 use hir_expand::{
-    AstId, AttrMacroAttrIds, ExpandTo, HirFileId, InFile, Intern, Lookup, MacroCallKind,
-    MacroDefKind,
+    AstId, ExpandTo, HirFileId, InFile, Intern, Lookup, MacroCallKind, MacroDefKind,
     mod_path::ModPath,
     name::{AsName, Name},
     span_map::SpanMap,
@@ -22,8 +21,8 @@ use triomphe::Arc;
 use crate::{
     AssocItemId, AstIdWithPath, ConstLoc, FunctionId, FunctionLoc, ImplId, ItemContainerId,
     ItemLoc, MacroCallId, ModuleId, TraitId, TypeAliasId, TypeAliasLoc,
+    attr::Attrs,
     db::DefDatabase,
-    item_tree::AttrsOrCfg,
     macro_call_as_call_id,
     nameres::{
         DefMap, LocalDefMap, MacroSubNs,
@@ -192,22 +191,19 @@ impl<'a> AssocItemCollector<'a> {
 
     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.local_id,
-                        InFile::new(self.file_id, ast_id.erase()),
-                        cfg.0,
-                        self.cfg_options.clone(),
-                    ));
-                    return;
-                }
-            };
+        let attrs = Attrs::new(self.db, &item, self.span_map.as_ref(), self.cfg_options);
+        if let Err(cfg) = attrs.is_cfg_enabled(self.cfg_options) {
+            self.diagnostics.push(DefDiagnostic::unconfigured_code(
+                self.module_id.local_id,
+                InFile::new(self.file_id, ast_id.erase()),
+                cfg,
+                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() {
+        'attrs: for attr in &*attrs {
             let ast_id_with_path = AstIdWithPath { path: attr.path.clone(), ast_id };
 
             match self.def_map.resolve_attr_macro(
@@ -216,7 +212,6 @@ impl<'a> AssocItemCollector<'a> {
                 self.module_id.local_id,
                 ast_id_with_path,
                 attr,
-                attr_id,
             ) {
                 Ok(ResolvedAttr::Macro(call_id)) => {
                     let loc = self.db.lookup_intern_macro_call(call_id);
@@ -245,12 +240,8 @@ impl<'a> AssocItemCollector<'a> {
                 Err(_) => {
                     self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
                         self.module_id.local_id,
-                        MacroCallKind::Attr {
-                            ast_id,
-                            attr_args: None,
-                            censored_attr_ids: AttrMacroAttrIds::from_one(attr_id),
-                        },
-                        (*attr.path).clone(),
+                        MacroCallKind::Attr { ast_id, attr_args: None, invoc_attr_index: attr.id },
+                        attr.path().clone(),
                     ));
                 }
             }
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 fb755026c3e0..2f56d608fcbf 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
@@ -2,7 +2,7 @@
 
 use base_db::Crate;
 use hir_expand::{
-    AttrMacroAttrIds, MacroCallId, MacroCallKind, MacroDefId,
+    MacroCallId, MacroCallKind, MacroDefId,
     attrs::{Attr, AttrId, AttrInput},
     inert_attr_macro::find_builtin_attr_idx,
     mod_path::{ModPath, PathKind},
@@ -28,7 +28,6 @@ pub enum ResolvedAttr {
 }
 
 impl DefMap {
-    /// This cannot be used to resolve items that allow derives.
     pub(crate) fn resolve_attr_macro(
         &self,
         local_def_map: &LocalDefMap,
@@ -36,7 +35,6 @@ impl DefMap {
         original_module: LocalModuleId,
         ast_id: AstIdWithPath,
         attr: &Attr,
-        attr_id: AttrId,
     ) -> Result {
         // NB: does not currently work for derive helpers as they aren't recorded in the `DefMap`
 
@@ -70,9 +68,6 @@ impl DefMap {
             db,
             &ast_id,
             attr,
-            // There aren't any active attributes before this one, because attribute macros
-            // replace their input, and derive macros are not allowed in this function.
-            AttrMacroAttrIds::from_one(attr_id),
             self.krate,
             db.macro_def(def),
         )))
@@ -107,7 +102,6 @@ pub(super) fn attr_macro_as_call_id(
     db: &dyn DefDatabase,
     item_attr: &AstIdWithPath,
     macro_attr: &Attr,
-    censored_attr_ids: AttrMacroAttrIds,
     krate: Crate,
     def: MacroDefId,
 ) -> MacroCallId {
@@ -127,7 +121,7 @@ pub(super) fn attr_macro_as_call_id(
         MacroCallKind::Attr {
             ast_id: item_attr.ast_id,
             attr_args: arg.map(Arc::new),
-            censored_attr_ids,
+            invoc_attr_index: macro_attr.id,
         },
         macro_attr.ctxt,
     )
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 c3b272b403bb..a2ce53835651 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
@@ -3,14 +3,14 @@
 //! `DefCollector::collect` contains the fixed-point iteration loop which
 //! resolves imports and expands macros.
 
-use std::{cmp::Ordering, iter, mem};
+use std::{cmp::Ordering, iter, mem, ops::Not};
 
 use base_db::{BuiltDependency, Crate, CrateOrigin, LangCrateOrigin};
 use cfg::{CfgAtom, CfgExpr, CfgOptions};
 use either::Either;
 use hir_expand::{
-    AttrMacroAttrIds, EditionedFileId, ErasedAstId, ExpandTo, HirFileId, InFile, MacroCallId,
-    MacroCallKind, MacroDefId, MacroDefKind,
+    EditionedFileId, ErasedAstId, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
+    MacroDefId, MacroDefKind,
     attrs::{Attr, AttrId},
     builtin::{find_builtin_attr, find_builtin_derive, find_builtin_macro},
     mod_path::{ModPath, PathKind},
@@ -18,10 +18,9 @@ use hir_expand::{
     proc_macro::CustomProcMacroExpander,
 };
 use intern::{Interned, sym};
-use itertools::izip;
+use itertools::{Itertools, izip};
 use la_arena::Idx;
 use rustc_hash::{FxHashMap, FxHashSet};
-use smallvec::SmallVec;
 use span::{Edition, FileAstId, SyntaxContext};
 use syntax::ast;
 use triomphe::Arc;
@@ -33,11 +32,12 @@ use crate::{
     MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId,
     ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId,
     UseLoc,
+    attr::Attrs,
     db::DefDatabase,
     item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports},
     item_tree::{
-        self, Attrs, AttrsOrCfg, FieldsShape, ImportAlias, ImportKind, ItemTree, ItemTreeAstId,
-        Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, TreeId,
+        self, FieldsShape, ImportAlias, ImportKind, ItemTree, ItemTreeAstId, Macro2, MacroCall,
+        MacroRules, Mod, ModItemId, ModKind, TreeId,
     },
     macro_call_as_call_id,
     nameres::{
@@ -102,7 +102,6 @@ pub(super) fn collect_defs(
         proc_macros,
         from_glob_import: Default::default(),
         skip_attrs: Default::default(),
-        prev_active_attrs: Default::default(),
         unresolved_extern_crates: Default::default(),
         is_proc_macro: krate.is_proc_macro,
     };
@@ -207,7 +206,6 @@ enum MacroDirectiveKind<'db> {
     },
     Attr {
         ast_id: AstIdWithPath,
-        attr_id: AttrId,
         attr: Attr,
         mod_item: ModItemId,
         /* is this needed? */ tree: TreeId,
@@ -248,27 +246,28 @@ struct DefCollector<'db> {
     /// This also stores the attributes to skip when we resolve derive helpers and non-macro
     /// non-builtin attributes in general.
     // FIXME: There has to be a better way to do this
-    skip_attrs: FxHashMap, AttrId>,
-    /// When we expand attributes, we need to censor all previous active attributes
-    /// on the same item. Therefore, this holds all active attributes that we already
-    /// expanded.
-    prev_active_attrs: FxHashMap, SmallVec<[AttrId; 1]>>,
+    skip_attrs: FxHashMap>, AttrId>,
 }
 
 impl<'db> DefCollector<'db> {
     fn seed_with_top_level(&mut self) {
         let _p = tracing::info_span!("seed_with_top_level").entered();
 
-        let file_id = self.def_map.krate.root_file_id(self.db);
+        let file_id = self.def_map.krate.data(self.db).root_file_id(self.db);
         let item_tree = self.db.file_item_tree(file_id.into());
-        let attrs = match item_tree.top_level_attrs() {
-            AttrsOrCfg::Enabled { attrs } => attrs.as_ref(),
-            AttrsOrCfg::CfgDisabled(it) => it.1.as_ref(),
-        };
+        let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
         let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
 
+        let mut process = true;
+
         // Process other crate-level attributes.
         for attr in &*attrs {
+            if let Some(cfg) = attr.cfg()
+                && self.cfg_options.check(&cfg) == Some(false)
+            {
+                process = false;
+                break;
+            }
             let Some(attr_name) = attr.path.as_ident() else { continue };
 
             match () {
@@ -292,7 +291,7 @@ impl<'db> DefCollector<'db> {
                 () if *attr_name == sym::feature => {
                     let features =
                         attr.parse_path_comma_token_tree(self.db).into_iter().flatten().filter_map(
-                            |(feat, _, _)| match feat.segments() {
+                            |(feat, _)| match feat.segments() {
                                 [name] => Some(name.symbol().clone()),
                                 _ => None,
                             },
@@ -345,7 +344,7 @@ impl<'db> DefCollector<'db> {
 
         self.inject_prelude();
 
-        if matches!(item_tree.top_level_attrs(), AttrsOrCfg::CfgDisabled(_)) {
+        if !process {
             return;
         }
 
@@ -363,7 +362,10 @@ impl<'db> DefCollector<'db> {
 
     fn seed_with_inner(&mut self, tree_id: TreeId) {
         let item_tree = tree_id.item_tree(self.db);
-        let is_cfg_enabled = matches!(item_tree.top_level_attrs(), AttrsOrCfg::Enabled { .. });
+        let is_cfg_enabled = item_tree
+            .top_level_attrs(self.db, self.def_map.krate)
+            .cfg()
+            .is_none_or(|cfg| self.cfg_options.check(&cfg) != Some(false));
         if is_cfg_enabled {
             self.inject_prelude();
 
@@ -454,18 +456,18 @@ impl<'db> DefCollector<'db> {
             self.unresolved_macros.iter().enumerate().find_map(|(idx, directive)| match &directive
                 .kind
             {
-                MacroDirectiveKind::Attr { ast_id, mod_item, attr_id, attr, tree, item_tree } => {
+                MacroDirectiveKind::Attr { ast_id, mod_item, attr, tree, item_tree } => {
                     self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
                         directive.module_id,
                         MacroCallKind::Attr {
                             ast_id: ast_id.ast_id,
                             attr_args: None,
-                            censored_attr_ids: AttrMacroAttrIds::from_one(*attr_id),
+                            invoc_attr_index: attr.id,
                         },
-                        (*attr.path).clone(),
+                        attr.path().clone(),
                     ));
 
-                    self.skip_attrs.insert(ast_id.ast_id.with_value(mod_item.ast_id()), *attr_id);
+                    self.skip_attrs.insert(ast_id.ast_id.with_value(mod_item.ast_id()), attr.id);
 
                     Some((idx, directive, *mod_item, *tree, *item_tree))
                 }
@@ -1348,7 +1350,6 @@ impl<'db> DefCollector<'db> {
                 MacroDirectiveKind::Attr {
                     ast_id: file_ast_id,
                     mod_item,
-                    attr_id,
                     attr,
                     tree,
                     item_tree,
@@ -1361,7 +1362,7 @@ impl<'db> DefCollector<'db> {
                         let mod_dir = collector.mod_dirs[&directive.module_id].clone();
                         collector
                             .skip_attrs
-                            .insert(InFile::new(file_id, mod_item.ast_id()), *attr_id);
+                            .insert(InFile::new(file_id, mod_item.ast_id()), attr.id);
 
                         ModCollector {
                             def_collector: collector,
@@ -1397,6 +1398,7 @@ impl<'db> DefCollector<'db> {
                     // being cfg'ed out).
                     // Ideally we will just expand them to nothing here. But we are only collecting macro calls,
                     // not expanding them, so we have no way to do that.
+                    // If you add an ignored attribute here, also add it to `Semantics::might_be_inside_macro_call()`.
                     if matches!(
                         def.kind,
                         MacroDefKind::BuiltInAttr(_, expander)
@@ -1408,18 +1410,8 @@ impl<'db> DefCollector<'db> {
                         }
                     }
 
-                    let mut call_id = || {
-                        let active_attrs = self.prev_active_attrs.entry(ast_id).or_default();
-                        active_attrs.push(*attr_id);
-
-                        attr_macro_as_call_id(
-                            self.db,
-                            file_ast_id,
-                            attr,
-                            AttrMacroAttrIds::from_many(active_attrs),
-                            self.def_map.krate,
-                            def,
-                        )
+                    let call_id = || {
+                        attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def)
                     };
                     if matches!(def,
                         MacroDefId { kind: MacroDefKind::BuiltInAttr(_, exp), .. }
@@ -1437,7 +1429,7 @@ impl<'db> DefCollector<'db> {
                                 let diag = DefDiagnostic::invalid_derive_target(
                                     directive.module_id,
                                     ast_id,
-                                    *attr_id,
+                                    attr.id,
                                 );
                                 self.def_map.diagnostics.push(diag);
                                 return recollect_without(self);
@@ -1450,7 +1442,7 @@ impl<'db> DefCollector<'db> {
                             Some(derive_macros) => {
                                 let call_id = call_id();
                                 let mut len = 0;
-                                for (idx, (path, call_site, _)) in derive_macros.enumerate() {
+                                for (idx, (path, call_site)) in derive_macros.enumerate() {
                                     let ast_id = AstIdWithPath::new(
                                         file_id,
                                         ast_id.value,
@@ -1461,7 +1453,7 @@ impl<'db> DefCollector<'db> {
                                         depth: directive.depth + 1,
                                         kind: MacroDirectiveKind::Derive {
                                             ast_id,
-                                            derive_attr: *attr_id,
+                                            derive_attr: attr.id,
                                             derive_pos: idx,
                                             ctxt: call_site.ctx,
                                             derive_macro_id: call_id,
@@ -1477,13 +1469,13 @@ impl<'db> DefCollector<'db> {
                                 // Check the comment in [`builtin_attr_macro`].
                                 self.def_map.modules[directive.module_id]
                                     .scope
-                                    .init_derive_attribute(ast_id, *attr_id, call_id, len + 1);
+                                    .init_derive_attribute(ast_id, attr.id, call_id, len + 1);
                             }
                             None => {
                                 let diag = DefDiagnostic::malformed_derive(
                                     directive.module_id,
                                     ast_id,
-                                    *attr_id,
+                                    attr.id,
                                 );
                                 self.def_map.diagnostics.push(diag);
                             }
@@ -1720,17 +1712,16 @@ impl ModCollector<'_, '_> {
         };
 
         let mut process_mod_item = |item: ModItemId| {
-            let attrs = match self.item_tree.attrs(item.ast_id()) {
-                Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(),
-                None => Attrs::EMPTY,
-                Some(AttrsOrCfg::CfgDisabled(cfg)) => {
-                    let ast_id = item.ast_id().erase();
-                    self.emit_unconfigured_diagnostic(InFile::new(self.file_id(), ast_id), &cfg.0);
-                    return;
-                }
-            };
+            let attrs = self.item_tree.attrs(db, krate, item.ast_id());
+            if let Some(cfg) = attrs.cfg()
+                && !self.is_cfg_enabled(&cfg)
+            {
+                let ast_id = item.ast_id().erase();
+                self.emit_unconfigured_diagnostic(InFile::new(self.file_id(), ast_id), &cfg);
+                return;
+            }
 
-            if let Err(()) = self.resolve_attributes(attrs, item, container) {
+            if let Err(()) = self.resolve_attributes(&attrs, item, container) {
                 // Do not process the item. It has at least one non-builtin attribute, so the
                 // fixed-point algorithm is required to resolve the rest of them.
                 return;
@@ -1742,7 +1733,7 @@ impl ModCollector<'_, '_> {
                 self.def_collector.crate_local_def_map.unwrap_or(&self.def_collector.local_def_map);
 
             match item {
-                ModItemId::Mod(m) => self.collect_module(m, attrs),
+                ModItemId::Mod(m) => self.collect_module(m, &attrs),
                 ModItemId::Use(item_tree_id) => {
                     let id =
                         UseLoc { container: module, id: InFile::new(self.file_id(), item_tree_id) }
@@ -2015,7 +2006,7 @@ impl ModCollector<'_, '_> {
                 );
                 return;
             };
-            for (path, _, _) in paths {
+            for (path, _) in paths {
                 if let Some(name) = path.as_ident() {
                     single_imports.push(name.clone());
                 }
@@ -2029,7 +2020,7 @@ impl ModCollector<'_, '_> {
         );
     }
 
-    fn collect_module(&mut self, module_ast_id: ItemTreeAstId, attrs: Attrs<'_>) {
+    fn collect_module(&mut self, module_ast_id: ItemTreeAstId, attrs: &Attrs) {
         let path_attr = attrs.by_key(sym::path).string_value_unescape();
         let is_macro_use = attrs.by_key(sym::macro_use).exists();
         let module = &self.item_tree[module_ast_id];
@@ -2070,18 +2061,23 @@ impl ModCollector<'_, '_> {
                     self.file_id(),
                     &module.name,
                     path_attr.as_deref(),
-                    self.def_collector.def_map.krate,
                 ) {
                     Ok((file_id, is_mod_rs, mod_dir)) => {
                         let item_tree = db.file_item_tree(file_id.into());
-                        match item_tree.top_level_attrs() {
-                            AttrsOrCfg::CfgDisabled(cfg) => {
+                        let krate = self.def_collector.def_map.krate;
+                        let is_enabled = item_tree
+                            .top_level_attrs(db, krate)
+                            .cfg()
+                            .and_then(|cfg| self.is_cfg_enabled(&cfg).not().then_some(cfg))
+                            .map_or(Ok(()), Err);
+                        match is_enabled {
+                            Err(cfg) => {
                                 self.emit_unconfigured_diagnostic(
                                     InFile::new(self.file_id(), module_ast_id.erase()),
-                                    &cfg.0,
+                                    &cfg,
                                 );
                             }
-                            AttrsOrCfg::Enabled { attrs } => {
+                            Ok(()) => {
                                 let module_id = self.push_child_module(
                                     module.name.clone(),
                                     ast_id.value,
@@ -2097,8 +2093,11 @@ impl ModCollector<'_, '_> {
                                     mod_dir,
                                 }
                                 .collect_in_top_module(item_tree.top_level_items());
-                                let is_macro_use =
-                                    is_macro_use || attrs.as_ref().by_key(sym::macro_use).exists();
+                                let is_macro_use = is_macro_use
+                                    || item_tree
+                                        .top_level_attrs(db, krate)
+                                        .by_key(sym::macro_use)
+                                        .exists();
                                 if is_macro_use {
                                     self.import_all_legacy_macros(module_id);
                                 }
@@ -2186,16 +2185,36 @@ impl ModCollector<'_, '_> {
     /// assumed to be resolved already.
     fn resolve_attributes(
         &mut self,
-        attrs: Attrs<'_>,
+        attrs: &Attrs,
         mod_item: ModItemId,
         container: ItemContainerId,
     ) -> Result<(), ()> {
-        let ignore_up_to = self
+        let mut ignore_up_to = self
             .def_collector
             .skip_attrs
             .get(&InFile::new(self.file_id(), mod_item.ast_id()))
             .copied();
-        for (attr_id, attr) in attrs.iter_after(ignore_up_to) {
+        let iter = attrs
+            .iter()
+            .dedup_by(|a, b| {
+                // FIXME: this should not be required, all attributes on an item should have a
+                // unique ID!
+                // Still, this occurs because `#[cfg_attr]` can "expand" to multiple attributes:
+                //     #[cfg_attr(not(off), unresolved, unresolved)]
+                //     struct S;
+                // We should come up with a different way to ID attributes.
+                a.id == b.id
+            })
+            .skip_while(|attr| match ignore_up_to {
+                Some(id) if attr.id == id => {
+                    ignore_up_to = None;
+                    true
+                }
+                Some(_) => true,
+                None => false,
+            });
+
+        for attr in iter {
             if self.def_collector.def_map.is_builtin_or_registered_attr(&attr.path) {
                 continue;
             }
@@ -2210,7 +2229,6 @@ impl ModCollector<'_, '_> {
                 depth: self.macro_depth + 1,
                 kind: MacroDirectiveKind::Attr {
                     ast_id,
-                    attr_id,
                     attr: attr.clone(),
                     mod_item,
                     tree: self.tree_id,
@@ -2226,14 +2244,9 @@ impl ModCollector<'_, '_> {
     }
 
     fn collect_macro_rules(&mut self, ast_id: ItemTreeAstId, module: ModuleId) {
+        let krate = self.def_collector.def_map.krate;
         let mac = &self.item_tree[ast_id];
-        let attrs = match self.item_tree.attrs(ast_id.upcast()) {
-            Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(),
-            None => Attrs::EMPTY,
-            Some(AttrsOrCfg::CfgDisabled(_)) => {
-                unreachable!("we only get here if the macro is not cfg'ed out")
-            }
-        };
+        let attrs = self.item_tree.attrs(self.def_collector.db, krate, ast_id.upcast());
         let f_ast_id = InFile::new(self.file_id(), ast_id.upcast());
 
         let export_attr = || attrs.by_key(sym::macro_export);
@@ -2313,14 +2326,9 @@ impl ModCollector<'_, '_> {
     }
 
     fn collect_macro_def(&mut self, ast_id: ItemTreeAstId, module: ModuleId) {
+        let krate = self.def_collector.def_map.krate;
         let mac = &self.item_tree[ast_id];
-        let attrs = match self.item_tree.attrs(ast_id.upcast()) {
-            Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(),
-            None => Attrs::EMPTY,
-            Some(AttrsOrCfg::CfgDisabled(_)) => {
-                unreachable!("we only get here if the macro is not cfg'ed out")
-            }
-        };
+        let attrs = self.item_tree.attrs(self.def_collector.db, krate, ast_id.upcast());
         let f_ast_id = InFile::new(self.file_id(), ast_id.upcast());
 
         // Case 1: builtin macros
@@ -2506,6 +2514,10 @@ impl ModCollector<'_, '_> {
         Some((a, b))
     }
 
+    fn is_cfg_enabled(&self, cfg: &CfgExpr) -> bool {
+        self.def_collector.cfg_options.check(cfg) != Some(false)
+    }
+
     fn emit_unconfigured_diagnostic(&mut self, ast_id: ErasedAstId, cfg: &CfgExpr) {
         self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code(
             self.module_id,
@@ -2545,7 +2557,6 @@ mod tests {
             proc_macros: Default::default(),
             from_glob_import: Default::default(),
             skip_attrs: Default::default(),
-            prev_active_attrs: Default::default(),
             is_proc_macro: false,
             unresolved_extern_crates: Default::default(),
         };
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
index 6a07c56aeebe..c495a0744919 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
@@ -17,8 +17,8 @@ pub enum DefDiagnosticKind {
     UnconfiguredCode { ast_id: ErasedAstId, cfg: CfgExpr, opts: CfgOptions },
     UnresolvedMacroCall { ast: MacroCallKind, path: ModPath },
     UnimplementedBuiltinMacro { ast: AstId },
-    InvalidDeriveTarget { ast: AstId, id: AttrId },
-    MalformedDerive { ast: AstId, id: AttrId },
+    InvalidDeriveTarget { ast: AstId, id: usize },
+    MalformedDerive { ast: AstId, id: usize },
     MacroDefError { ast: AstId, message: String },
     MacroError { ast: AstId, path: ModPath, err: ExpandErrorKind },
 }
@@ -119,7 +119,10 @@ impl DefDiagnostic {
         ast: AstId,
         id: AttrId,
     ) -> Self {
-        Self { in_module: container, kind: DefDiagnosticKind::InvalidDeriveTarget { ast, id } }
+        Self {
+            in_module: container,
+            kind: DefDiagnosticKind::InvalidDeriveTarget { ast, id: id.ast_index() },
+        }
     }
 
     pub(super) fn malformed_derive(
@@ -127,6 +130,9 @@ impl DefDiagnostic {
         ast: AstId,
         id: AttrId,
     ) -> Self {
-        Self { in_module: container, kind: DefDiagnosticKind::MalformedDerive { ast, id } }
+        Self {
+            in_module: container,
+            kind: DefDiagnosticKind::MalformedDerive { ast, id: id.ast_index() },
+        }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
index 140b77ac002f..0c50f13edfb6 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
@@ -1,6 +1,6 @@
 //! This module resolves `mod foo;` declaration to file.
 use arrayvec::ArrayVec;
-use base_db::{AnchoredPath, Crate};
+use base_db::AnchoredPath;
 use hir_expand::{EditionedFileId, name::Name};
 
 use crate::{HirFileId, db::DefDatabase};
@@ -62,7 +62,6 @@ impl ModDir {
         file_id: HirFileId,
         name: &Name,
         attr_path: Option<&str>,
-        krate: Crate,
     ) -> Result<(EditionedFileId, bool, ModDir), Box<[String]>> {
         let name = name.as_str();
 
@@ -92,7 +91,7 @@ impl ModDir {
                 if let Some(mod_dir) = self.child(dir_path, !root_dir_owner) {
                     return Ok((
                         // FIXME: Edition, is this rightr?
-                        EditionedFileId::new(db, file_id, orig_file_id.edition(db), krate),
+                        EditionedFileId::new(db, file_id, orig_file_id.edition(db)),
                         is_mod_rs,
                         mod_dir,
                     ));
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
index cd45afe57d7c..cd8882183bb4 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
@@ -3,10 +3,8 @@
 use hir_expand::name::{AsName, Name};
 use intern::sym;
 
-use crate::{
-    item_tree::Attrs,
-    tt::{Leaf, TokenTree, TopSubtree, TtElement},
-};
+use crate::attr::Attrs;
+use crate::tt::{Leaf, TokenTree, TopSubtree, TtElement};
 
 #[derive(Debug, PartialEq, Eq)]
 pub struct ProcMacroDef {
@@ -31,8 +29,8 @@ impl ProcMacroKind {
     }
 }
 
-impl Attrs<'_> {
-    pub(crate) fn parse_proc_macro_decl(&self, func_name: &Name) -> Option {
+impl Attrs {
+    pub fn parse_proc_macro_decl(&self, func_name: &Name) -> Option {
         if self.is_proc_macro() {
             Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Bang })
         } else if self.is_proc_macro_attribute() {
@@ -53,10 +51,15 @@ impl Attrs<'_> {
         }
     }
 
-    pub(crate) fn parse_proc_macro_derive(&self) -> Option<(Name, Box<[Name]>)> {
+    pub fn parse_proc_macro_derive(&self) -> Option<(Name, Box<[Name]>)> {
         let derive = self.by_key(sym::proc_macro_derive).tt_values().next()?;
         parse_macro_name_and_helper_attrs(derive)
     }
+
+    pub fn parse_rustc_builtin_macro(&self) -> Option<(Name, Box<[Name]>)> {
+        let derive = self.by_key(sym::rustc_builtin_macro).tt_values().next()?;
+        parse_macro_name_and_helper_attrs(derive)
+    }
 }
 
 // This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have
@@ -81,11 +84,14 @@ pub(crate) fn parse_macro_name_and_helper_attrs(tt: &TopSubtree) -> Option<(Name
             let helpers = tt::TokenTreesView::new(&tt.token_trees().flat_tokens()[3..]).try_into_subtree()?;
             let helpers = helpers
                 .iter()
-                .filter_map(|tt| match tt {
+                .filter(
+                    |tt| !matches!(tt, TtElement::Leaf(Leaf::Punct(comma)) if comma.char == ','),
+                )
+                .map(|tt| match tt {
                     TtElement::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()),
                     _ => None,
                 })
-                .collect::>();
+                .collect::>>()?;
 
             Some((trait_name.as_name(), helpers))
         }
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 c9e8955ad68c..ebbf87cad668 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
@@ -21,7 +21,7 @@ use triomphe::Arc;
 use crate::{
     ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, HasModule, ImplId,
     ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId,
-    attrs::AttrFlags,
+    attr::Attrs,
     db::DefDatabase,
     expr_store::{
         ExpressionStore, ExpressionStoreSourceMap,
@@ -48,13 +48,12 @@ pub struct StructSignature {
     pub store: Arc,
     pub flags: StructFlags,
     pub shape: FieldsShape,
+    pub repr: Option,
 }
 
 bitflags! {
     #[derive(Debug, Copy, Clone, PartialEq, Eq)]
     pub struct StructFlags: u8 {
-        /// Indicates whether this struct has `#[repr]`.
-        const HAS_REPR = 1 << 0;
         /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute.
         const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1;
         /// Indicates whether the struct has a `#[fundamental]` attribute.
@@ -76,19 +75,16 @@ impl StructSignature {
     pub fn query(db: &dyn DefDatabase, id: StructId) -> (Arc, Arc) {
         let loc = id.lookup(db);
         let InFile { file_id, value: source } = loc.source(db);
-        let attrs = AttrFlags::query(db, id.into());
+        let attrs = db.attrs(id.into());
 
         let mut flags = StructFlags::empty();
-        if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) {
+        if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
             flags |= StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
         }
-        if attrs.contains(AttrFlags::FUNDAMENTAL) {
+        if attrs.by_key(sym::fundamental).exists() {
             flags |= StructFlags::FUNDAMENTAL;
         }
-        if attrs.contains(AttrFlags::HAS_REPR) {
-            flags |= StructFlags::HAS_REPR;
-        }
-        if let Some(lang) = attrs.lang_item_with_attrs(db, id.into()) {
+        if let Some(lang) = attrs.lang_item() {
             match lang {
                 LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA,
                 LangItem::OwnedBox => flags |= StructFlags::IS_BOX,
@@ -98,6 +94,7 @@ impl StructSignature {
                 _ => (),
             }
         }
+        let repr = attrs.repr();
         let shape = adt_shape(source.kind());
 
         let (store, generic_params, source_map) = lower_generic_params(
@@ -115,19 +112,11 @@ impl StructSignature {
                 flags,
                 shape,
                 name: as_name_opt(source.name()),
+                repr,
             }),
             Arc::new(source_map),
         )
     }
-
-    #[inline]
-    pub fn repr(&self, db: &dyn DefDatabase, id: StructId) -> Option {
-        if self.flags.contains(StructFlags::HAS_REPR) {
-            AttrFlags::repr(db, id.into())
-        } else {
-            None
-        }
-    }
 }
 
 #[inline]
@@ -145,22 +134,22 @@ pub struct UnionSignature {
     pub generic_params: Arc,
     pub store: Arc,
     pub flags: StructFlags,
+    pub repr: Option,
 }
 
 impl UnionSignature {
     pub fn query(db: &dyn DefDatabase, id: UnionId) -> (Arc, Arc) {
         let loc = id.lookup(db);
-        let attrs = AttrFlags::query(db, id.into());
+        let attrs = db.attrs(id.into());
         let mut flags = StructFlags::empty();
-        if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) {
+        if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
             flags |= StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
         }
-        if attrs.contains(AttrFlags::FUNDAMENTAL) {
+        if attrs.by_key(sym::fundamental).exists() {
             flags |= StructFlags::FUNDAMENTAL;
         }
-        if attrs.contains(AttrFlags::HAS_REPR) {
-            flags |= StructFlags::HAS_REPR;
-        }
+
+        let repr = attrs.repr();
 
         let InFile { file_id, value: source } = loc.source(db);
         let (store, generic_params, source_map) = lower_generic_params(
@@ -176,6 +165,7 @@ impl UnionSignature {
                 generic_params,
                 store,
                 flags,
+                repr,
                 name: as_name_opt(source.name()),
             }),
             Arc::new(source_map),
@@ -196,17 +186,20 @@ pub struct EnumSignature {
     pub generic_params: Arc,
     pub store: Arc,
     pub flags: EnumFlags,
+    pub repr: Option,
 }
 
 impl EnumSignature {
     pub fn query(db: &dyn DefDatabase, id: EnumId) -> (Arc, Arc) {
         let loc = id.lookup(db);
-        let attrs = AttrFlags::query(db, id.into());
+        let attrs = db.attrs(id.into());
         let mut flags = EnumFlags::empty();
-        if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) {
+        if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
             flags |= EnumFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
         }
 
+        let repr = attrs.repr();
+
         let InFile { file_id, value: source } = loc.source(db);
         let (store, generic_params, source_map) = lower_generic_params(
             db,
@@ -222,14 +215,15 @@ impl EnumSignature {
                 generic_params,
                 store,
                 flags,
+                repr,
                 name: as_name_opt(source.name()),
             }),
             Arc::new(source_map),
         )
     }
 
-    pub fn variant_body_type(db: &dyn DefDatabase, id: EnumId) -> IntegerType {
-        match AttrFlags::repr(db, id.into()) {
+    pub fn variant_body_type(&self) -> IntegerType {
+        match self.repr {
             Some(ReprOptions { int: Some(builtin), .. }) => builtin,
             _ => IntegerType::Pointer(true),
         }
@@ -257,9 +251,9 @@ impl ConstSignature {
         let loc = id.lookup(db);
 
         let module = loc.container.module(db);
-        let attrs = AttrFlags::query(db, id.into());
+        let attrs = db.attrs(id.into());
         let mut flags = ConstFlags::empty();
-        if attrs.contains(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL) {
+        if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
             flags |= ConstFlags::RUSTC_ALLOW_INCOHERENT_IMPL;
         }
         let source = loc.source(db);
@@ -312,9 +306,9 @@ impl StaticSignature {
         let loc = id.lookup(db);
 
         let module = loc.container.module(db);
-        let attrs = AttrFlags::query(db, id.into());
+        let attrs = db.attrs(id.into());
         let mut flags = StaticFlags::empty();
-        if attrs.contains(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL) {
+        if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
             flags |= StaticFlags::RUSTC_ALLOW_INCOHERENT_IMPL;
         }
 
@@ -439,7 +433,7 @@ impl TraitSignature {
         let loc = id.lookup(db);
 
         let mut flags = TraitFlags::empty();
-        let attrs = AttrFlags::query(db, id.into());
+        let attrs = db.attrs(id.into());
         let source = loc.source(db);
         if source.value.auto_token().is_some() {
             flags.insert(TraitFlags::AUTO);
@@ -450,23 +444,34 @@ impl TraitSignature {
         if source.value.eq_token().is_some() {
             flags.insert(TraitFlags::ALIAS);
         }
-        if attrs.contains(AttrFlags::FUNDAMENTAL) {
+        if attrs.by_key(sym::fundamental).exists() {
             flags |= TraitFlags::FUNDAMENTAL;
         }
-        if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) {
+        if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
             flags |= TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
         }
-        if attrs.contains(AttrFlags::RUSTC_PAREN_SUGAR) {
+        if attrs.by_key(sym::rustc_paren_sugar).exists() {
             flags |= TraitFlags::RUSTC_PAREN_SUGAR;
         }
-        if attrs.contains(AttrFlags::RUSTC_COINDUCTIVE) {
+        if attrs.by_key(sym::rustc_coinductive).exists() {
             flags |= TraitFlags::COINDUCTIVE;
         }
+        let mut skip_array_during_method_dispatch =
+            attrs.by_key(sym::rustc_skip_array_during_method_dispatch).exists();
+        let mut skip_boxed_slice_during_method_dispatch = false;
+        for tt in attrs.by_key(sym::rustc_skip_during_method_dispatch).tt_values() {
+            for tt in tt.iter() {
+                if let tt::iter::TtElement::Leaf(tt::Leaf::Ident(ident)) = tt {
+                    skip_array_during_method_dispatch |= ident.sym == sym::array;
+                    skip_boxed_slice_during_method_dispatch |= ident.sym == sym::boxed_slice;
+                }
+            }
+        }
 
-        if attrs.contains(AttrFlags::RUSTC_SKIP_ARRAY_DURING_METHOD_DISPATCH) {
+        if skip_array_during_method_dispatch {
             flags |= TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH;
         }
-        if attrs.contains(AttrFlags::RUSTC_SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH) {
+        if skip_boxed_slice_during_method_dispatch {
             flags |= TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH;
         }
 
@@ -498,8 +503,7 @@ bitflags! {
         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 RUSTC_INTRINSIC = 1 << 12;
     }
 }
 
@@ -512,6 +516,8 @@ pub struct FunctionSignature {
     pub ret_type: Option,
     pub abi: Option,
     pub flags: FnFlags,
+    // FIXME: we should put this behind a fn flags + query to avoid bloating the struct
+    pub legacy_const_generics_indices: Option>>,
 }
 
 impl FunctionSignature {
@@ -523,26 +529,23 @@ impl FunctionSignature {
         let module = loc.container.module(db);
 
         let mut flags = FnFlags::empty();
-        let attrs = AttrFlags::query(db, id.into());
-        if attrs.contains(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL) {
+        let attrs = db.attrs(id.into());
+        if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
             flags.insert(FnFlags::RUSTC_ALLOW_INCOHERENT_IMPL);
         }
 
-        if attrs.contains(AttrFlags::HAS_TARGET_FEATURE) {
+        if attrs.by_key(sym::target_feature).exists() {
             flags.insert(FnFlags::HAS_TARGET_FEATURE);
         }
-
-        if attrs.contains(AttrFlags::RUSTC_INTRINSIC) {
+        if attrs.by_key(sym::rustc_intrinsic).exists() {
             flags.insert(FnFlags::RUSTC_INTRINSIC);
         }
-        if attrs.contains(AttrFlags::HAS_LEGACY_CONST_GENERICS) {
-            flags.insert(FnFlags::HAS_LEGACY_CONST_GENERICS);
-        }
+        let legacy_const_generics_indices = attrs.rustc_legacy_const_generics();
 
         let source = loc.source(db);
 
         if source.value.unsafe_token().is_some() {
-            if attrs.contains(AttrFlags::RUSTC_DEPRECATED_SAFE_2024) {
+            if attrs.by_key(sym::rustc_deprecated_safe_2024).exists() {
                 flags.insert(FnFlags::DEPRECATED_SAFE_2024);
             } else {
                 flags.insert(FnFlags::UNSAFE);
@@ -584,6 +587,7 @@ impl FunctionSignature {
                 ret_type,
                 abi,
                 flags,
+                legacy_const_generics_indices,
                 name,
             }),
             Arc::new(source_map),
@@ -632,19 +636,6 @@ impl FunctionSignature {
         self.flags.contains(FnFlags::HAS_TARGET_FEATURE)
     }
 
-    #[inline]
-    pub fn legacy_const_generics_indices<'db>(
-        &self,
-        db: &'db dyn DefDatabase,
-        id: FunctionId,
-    ) -> Option<&'db [u32]> {
-        if !self.flags.contains(FnFlags::HAS_LEGACY_CONST_GENERICS) {
-            return None;
-        }
-
-        AttrFlags::legacy_const_generic_indices(db, id).as_deref()
-    }
-
     pub fn is_intrinsic(db: &dyn DefDatabase, id: FunctionId) -> bool {
         let data = db.function_signature(id);
         data.flags.contains(FnFlags::RUSTC_INTRINSIC)
@@ -688,11 +679,11 @@ impl TypeAliasSignature {
         let loc = id.lookup(db);
 
         let mut flags = TypeAliasFlags::empty();
-        let attrs = AttrFlags::query(db, id.into());
-        if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) {
+        let attrs = db.attrs(id.into());
+        if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
             flags.insert(TypeAliasFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPL);
         }
-        if attrs.contains(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL) {
+        if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
             flags.insert(TypeAliasFlags::RUSTC_ALLOW_INCOHERENT_IMPL);
         }
         if matches!(loc.container, ItemContainerId::ExternBlockId(_)) {
@@ -875,7 +866,7 @@ fn lower_fields(
     let mut has_fields = false;
     for (ty, field) in fields.value {
         has_fields = true;
-        match AttrFlags::is_cfg_enabled_for(&field, cfg_options) {
+        match Attrs::is_cfg_enabled_for(db, &field, col.span_map(), cfg_options) {
             Ok(()) => {
                 let type_ref =
                     col.lower_type_ref_opt(ty, &mut ExprCollector::impl_trait_error_allocator);
@@ -937,6 +928,7 @@ impl EnumVariants {
         let loc = e.lookup(db);
         let source = loc.source(db);
         let ast_id_map = db.ast_id_map(source.file_id);
+        let span_map = db.span_map(source.file_id);
 
         let mut diagnostics = ThinVec::new();
         let cfg_options = loc.container.krate.cfg_options(db);
@@ -948,7 +940,7 @@ impl EnumVariants {
             .variants()
             .filter_map(|variant| {
                 let ast_id = ast_id_map.ast_id(&variant);
-                match AttrFlags::is_cfg_enabled_for(&variant, cfg_options) {
+                match Attrs::is_cfg_enabled_for(db, &variant, span_map.as_ref(), cfg_options) {
                     Ok(()) => {
                         let enum_variant =
                             EnumVariantLoc { id: source.with_value(ast_id), parent: e, index }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
index 153fd195f0ad..367b543cf908 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
@@ -7,7 +7,7 @@ use syntax::{AstNode, AstPtr, ast};
 
 use crate::{
     AstIdLoc, GenericDefId, LocalFieldId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup,
-    UseId, VariantId, attrs::AttrFlags, db::DefDatabase,
+    UseId, VariantId, attr::Attrs, db::DefDatabase,
 };
 
 pub trait HasSource {
@@ -145,13 +145,15 @@ impl HasChildSource for VariantId {
                 (lookup.source(db).map(|it| it.kind()), lookup.container)
             }
         };
+        let span_map = db.span_map(src.file_id);
         let mut map = ArenaMap::new();
         match &src.value {
             ast::StructKind::Tuple(fl) => {
                 let cfg_options = container.krate.cfg_options(db);
                 let mut idx = 0;
                 for fd in fl.fields() {
-                    let enabled = AttrFlags::is_cfg_enabled_for(&fd, cfg_options).is_ok();
+                    let enabled =
+                        Attrs::is_cfg_enabled_for(db, &fd, span_map.as_ref(), cfg_options).is_ok();
                     if !enabled {
                         continue;
                     }
@@ -166,7 +168,8 @@ impl HasChildSource for VariantId {
                 let cfg_options = container.krate.cfg_options(db);
                 let mut idx = 0;
                 for fd in fl.fields() {
-                    let enabled = AttrFlags::is_cfg_enabled_for(&fd, cfg_options).is_ok();
+                    let enabled =
+                        Attrs::is_cfg_enabled_for(db, &fd, span_map.as_ref(), cfg_options).is_ok();
                     if !enabled {
                         continue;
                     }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
index 3bb9c361b3c8..12a1c1554cc1 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
@@ -190,15 +190,7 @@ impl TestDB {
         let mut res = DefMap::ROOT;
         for (module, data) in def_map.modules() {
             let src = data.definition_source(self);
-            // We're not comparing the `base_db::EditionedFileId`, but rather the VFS `FileId`, because
-            // `position.file_id` is created before the def map, causing it to have to wrong crate
-            // attached often, which means it won't compare equal. This should not be a problem in real
-            // r-a session, only in tests, because in real r-a we only guess the crate on syntactic-only
-            // (e.g. on-enter) handlers. The rest pick the `EditionedFileId` from the def map.
-            let Some(file_id) = src.file_id.file_id() else {
-                continue;
-            };
-            if file_id.file_id(self) != position.file_id.file_id(self) {
+            if src.file_id != position.file_id {
                 continue;
             }
 
@@ -238,15 +230,7 @@ impl TestDB {
         let mut fn_def = None;
         for (_, module) in def_map.modules() {
             let file_id = module.definition_source(self).file_id;
-            // We're not comparing the `base_db::EditionedFileId`, but rather the VFS `FileId`, because
-            // `position.file_id` is created before the def map, causing it to have to wrong crate
-            // attached often, which means it won't compare equal. This should not be a problem in real
-            // r-a session, only in tests, because in real r-a we only guess the crate on syntactic-only
-            // (e.g. on-enter) handlers. The rest pick the `EditionedFileId` from the def map.
-            let Some(file_id) = file_id.file_id() else {
-                continue;
-            };
-            if file_id.file_id(self) != position.file_id.file_id(self) {
+            if file_id != position.file_id {
                 continue;
             }
             for decl in module.scope.declarations() {
@@ -269,25 +253,26 @@ impl TestDB {
                     };
                     if size != Some(new_size) {
                         size = Some(new_size);
-                        fn_def = Some((it, file_id));
+                        fn_def = Some(it);
                     }
                 }
             }
         }
 
         // Find the innermost block expression that has a `DefMap`.
-        let (def_with_body, file_id) = fn_def?;
-        let def_with_body = def_with_body.into();
+        let def_with_body = fn_def?.into();
         let source_map = self.body_with_source_map(def_with_body).1;
         let scopes = self.expr_scopes(def_with_body);
 
-        let root_syntax_node = self.parse(file_id).syntax_node();
+        let root_syntax_node = self.parse(position.file_id).syntax_node();
         let scope_iter =
             algo::ancestors_at_offset(&root_syntax_node, position.offset).filter_map(|node| {
                 let block = ast::BlockExpr::cast(node)?;
                 let expr = ast::Expr::from(block);
-                let expr_id =
-                    source_map.node_expr(InFile::new(file_id.into(), &expr))?.as_expr().unwrap();
+                let expr_id = source_map
+                    .node_expr(InFile::new(position.file_id.into(), &expr))?
+                    .as_expr()
+                    .unwrap();
                 let scope = scopes.scope_for(expr_id).unwrap();
                 Some(scope)
             });
diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
index 4fa476afb64a..80a3c0848653 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
@@ -23,8 +23,6 @@ triomphe.workspace = true
 query-group.workspace = true
 salsa.workspace = true
 salsa-macros.workspace = true
-arrayvec.workspace = true
-thin-vec.workspace = true
 
 # local deps
 stdx.workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
index e1807cd2e1e9..986f8764f5c9 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
@@ -1,397 +1,200 @@
-//! Defines the basics of attributes lowering.
-//!
-//! The heart and soul of this module is [`expand_cfg_attr()`], alongside its sibling
-//! [`expand_cfg_attr_with_doc_comments()`]. It is used to implement all attribute lowering
-//! in r-a. Its basic job is to list attributes; however, attributes do not necessarily map
-//! into [`ast::Attr`], because `cfg_attr` can map to zero, one, or more attributes
-//! (`#[cfg_attr(predicate, attr1, attr2, ...)]`). To bridge this gap, this module defines
-//! [`Meta`], which represents a desugared attribute. Various bits of r-a need different
-//! things from [`Meta`], therefore it contains many parts. The basic idea is:
-//!
-//!  - There are three kinds of attributes, `path = value`, `path`, and `path(token_tree)`.
-//!  - Most bits of rust-analyzer only need to deal with some paths. Therefore, we keep
-//!    the path only if it has up to 2 segments, or one segment for `path = value`.
-//!    We also only keep the value in `path = value` if it is a literal. However, we always
-//!    save the all relevant ranges of attributes (the path range, and the full attribute range)
-//!    for parts of r-a (e.g. name resolution) that need a faithful representation of the
-//!    attribute.
-//!
-//! [`expand_cfg_attr()`] expands `cfg_attr`s as it goes (as its name implies), to list
-//! all attributes.
-//!
-//! Another thing to note is that we need to be able to map an attribute back to a range
-//! (for diagnostic purposes etc.). This is only ever needed for attributes that participate
-//! in name resolution. An attribute is mapped back by its [`AttrId`], which is just an
-//! index into the item tree attributes list. To minimize the risk of bugs, we have one
-//! place (here) and one function ([`is_item_tree_filtered_attr()`]) that decides whether
-//! an attribute participate in name resolution.
+//! A higher level attributes based on TokenTree, with also some shortcuts.
+use std::iter;
+use std::{borrow::Cow, fmt, ops};
 
-use std::{
-    borrow::Cow, cell::OnceCell, convert::Infallible, fmt, iter::Peekable, ops::ControlFlow,
-};
-
-use ::tt::{TextRange, TextSize};
-use arrayvec::ArrayVec;
 use base_db::Crate;
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
-use intern::{Interned, Symbol};
+use intern::{Interned, Symbol, sym};
+
 use mbe::{DelimiterKind, Punct};
-use parser::T;
-use smallvec::SmallVec;
-use span::{RealSpanMap, Span, SyntaxContext};
-use syntax::{
-    AstNode, NodeOrToken, SyntaxNode, SyntaxToken,
-    ast::{self, TokenTreeChildren},
-    unescape,
-};
-use syntax_bridge::DocCommentDesugarMode;
+use smallvec::{SmallVec, smallvec};
+use span::{Span, SyntaxContext};
+use syntax::unescape;
+use syntax::{AstNode, AstToken, SyntaxNode, ast, match_ast};
+use syntax_bridge::{DocCommentDesugarMode, desugar_doc_comment_text, syntax_node_to_token_tree};
+use triomphe::ThinArc;
 
 use crate::{
-    AstId,
     db::ExpandDatabase,
     mod_path::ModPath,
+    name::Name,
     span_map::SpanMapRef,
-    tt::{self, TopSubtree},
+    tt::{self, TopSubtree, token_to_literal},
 };
 
-#[derive(Debug)]
-pub struct AttrPath {
-    /// This can be empty if the path is not of 1 or 2 segments exactly.
-    pub segments: ArrayVec,
-    pub range: TextRange,
-    // FIXME: This shouldn't be textual, `#[test]` needs name resolution.
-    // And if textual, it shouldn't be here, it should be in hir-def/src/attrs.rs. But some macros
-    // fully qualify `test` as `core::prelude::vX::test`, and this is more than 2 segments, so hir-def
-    // attrs can't find it. But this will mean we have to push every up-to-4-segments path, which
-    // may impact perf. So it was easier to just hack it here.
-    pub is_test: bool,
+/// Syntactical attributes, without filtering of `cfg_attr`s.
+#[derive(Default, Debug, Clone, PartialEq, Eq)]
+pub struct RawAttrs {
+    // FIXME: This can become `Box<[Attr]>` if https://internals.rust-lang.org/t/layout-of-dst-box/21728?u=chrefr is accepted.
+    entries: Option>,
 }
 
-impl AttrPath {
-    #[inline]
-    fn extract(path: &ast::Path) -> Self {
-        let mut is_test = false;
-        let segments = (|| {
-            let mut segments = ArrayVec::new();
-            let segment2 = path.segment()?.name_ref()?.syntax().first_token()?;
-            if segment2.text() == "test" {
-                // `#[test]` or `#[core::prelude::vX::test]`.
-                is_test = true;
-            }
-            let segment1 = path.qualifier();
-            if let Some(segment1) = segment1 {
-                if segment1.qualifier().is_some() {
-                    None
-                } else {
-                    let segment1 = segment1.segment()?.name_ref()?.syntax().first_token()?;
-                    segments.push(segment1);
-                    segments.push(segment2);
-                    Some(segments)
-                }
-            } else {
-                segments.push(segment2);
-                Some(segments)
-            }
-        })();
-        AttrPath {
-            segments: segments.unwrap_or(ArrayVec::new()),
-            range: path.syntax().text_range(),
-            is_test,
-        }
-    }
+impl ops::Deref for RawAttrs {
+    type Target = [Attr];
 
-    #[inline]
-    pub fn is1(&self, segment: &str) -> bool {
-        self.segments.len() == 1 && self.segments[0].text() == segment
+    fn deref(&self) -> &[Attr] {
+        match &self.entries {
+            Some(it) => &it.slice,
+            None => &[],
+        }
     }
 }
 
-#[derive(Debug)]
-pub enum Meta {
-    /// `name` is `None` if not a single token. `value` is a literal or `None`.
-    NamedKeyValue {
-        path_range: TextRange,
-        name: Option,
-        value: Option,
-    },
-    TokenTree {
-        path: AttrPath,
-        tt: ast::TokenTree,
-    },
-    Path {
-        path: AttrPath,
-    },
-}
+impl RawAttrs {
+    pub const EMPTY: Self = Self { entries: None };
 
-impl Meta {
-    #[inline]
-    pub fn path_range(&self) -> TextRange {
-        match self {
-            Meta::NamedKeyValue { path_range, .. } => *path_range,
-            Meta::TokenTree { path, .. } | Meta::Path { path } => path.range,
-        }
-    }
+    pub fn new(
+        db: &dyn ExpandDatabase,
+        owner: &dyn ast::HasAttrs,
+        span_map: SpanMapRef<'_>,
+    ) -> Self {
+        let entries: Vec<_> = Self::attrs_iter::(db, owner, span_map).collect();
 
-    fn extract(iter: &mut Peekable) -> Option<(Self, TextSize)> {
-        let mut start_offset = None;
-        if let Some(NodeOrToken::Token(colon1)) = iter.peek()
-            && colon1.kind() == T![:]
-        {
-            start_offset = Some(colon1.text_range().start());
-            iter.next();
-            iter.next_if(|it| it.as_token().is_some_and(|it| it.kind() == T![:]));
-        }
-        let first_segment = iter
-            .next_if(|it| it.as_token().is_some_and(|it| it.kind().is_any_identifier()))?
-            .into_token()?;
-        let mut is_test = first_segment.text() == "test";
-        let start_offset = start_offset.unwrap_or_else(|| first_segment.text_range().start());
-
-        let mut segments_len = 1;
-        let mut second_segment = None;
-        let mut path_range = first_segment.text_range();
-        while iter.peek().and_then(NodeOrToken::as_token).is_some_and(|it| it.kind() == T![:])
-            && let _ = iter.next()
-            && iter.peek().and_then(NodeOrToken::as_token).is_some_and(|it| it.kind() == T![:])
-            && let _ = iter.next()
-            && let Some(NodeOrToken::Token(segment)) = iter.peek()
-            && segment.kind().is_any_identifier()
-        {
-            segments_len += 1;
-            is_test = segment.text() == "test";
-            second_segment = Some(segment.clone());
-            path_range = TextRange::new(path_range.start(), segment.text_range().end());
-            iter.next();
-        }
-
-        let segments = |first, second| {
-            let mut segments = ArrayVec::new();
-            if segments_len <= 2 {
-                segments.push(first);
-                if let Some(second) = second {
-                    segments.push(second);
-                }
-            }
-            segments
-        };
-        let meta = match iter.peek() {
-            Some(NodeOrToken::Token(eq)) if eq.kind() == T![=] => {
-                iter.next();
-                let value = match iter.peek() {
-                    Some(NodeOrToken::Token(token)) if token.kind().is_literal() => {
-                        // No need to consume it, it will be consumed by `extract_and_eat_comma()`.
-                        Some(token.clone())
-                    }
-                    _ => None,
-                };
-                let name = if second_segment.is_none() { Some(first_segment) } else { None };
-                Meta::NamedKeyValue { path_range, name, value }
-            }
-            Some(NodeOrToken::Node(tt)) => Meta::TokenTree {
-                path: AttrPath {
-                    segments: segments(first_segment, second_segment),
-                    range: path_range,
-                    is_test,
-                },
-                tt: tt.clone(),
-            },
-            _ => Meta::Path {
-                path: AttrPath {
-                    segments: segments(first_segment, second_segment),
-                    range: path_range,
-                    is_test,
-                },
-            },
-        };
-        Some((meta, start_offset))
-    }
-
-    fn extract_possibly_unsafe(
-        iter: &mut Peekable,
-        container: &ast::TokenTree,
-    ) -> Option<(Self, TextRange)> {
-        if iter.peek().is_some_and(|it| it.as_token().is_some_and(|it| it.kind() == T![unsafe])) {
-            iter.next();
-            let tt = iter.next()?.into_node()?;
-            let result = Self::extract(&mut TokenTreeChildren::new(&tt).peekable()).map(
-                |(meta, start_offset)| (meta, TextRange::new(start_offset, tt_end_offset(&tt))),
-            );
-            while iter.next().is_some_and(|it| it.as_token().is_none_or(|it| it.kind() != T![,])) {}
-            result
+        let entries = if entries.is_empty() {
+            None
         } else {
-            Self::extract(iter).map(|(meta, start_offset)| {
-                let end_offset = 'find_end_offset: {
-                    for it in iter {
-                        if let NodeOrToken::Token(it) = it
-                            && it.kind() == T![,]
-                        {
-                            break 'find_end_offset it.text_range().start();
-                        }
-                    }
-                    tt_end_offset(container)
-                };
-                (meta, TextRange::new(start_offset, end_offset))
-            })
+            Some(ThinArc::from_header_and_iter((), entries.into_iter()))
+        };
+
+        RawAttrs { entries }
+    }
+
+    /// A [`RawAttrs`] that has its `#[cfg_attr(...)]` attributes expanded.
+    pub fn new_expanded(
+        db: &dyn ExpandDatabase,
+        owner: &dyn ast::HasAttrs,
+        span_map: SpanMapRef<'_>,
+        cfg_options: &CfgOptions,
+    ) -> Self {
+        let entries: Vec<_> =
+            Self::attrs_iter_expanded::(db, owner, span_map, cfg_options).collect();
+
+        let entries = if entries.is_empty() {
+            None
+        } else {
+            Some(ThinArc::from_header_and_iter((), entries.into_iter()))
+        };
+
+        RawAttrs { entries }
+    }
+
+    pub fn attrs_iter(
+        db: &dyn ExpandDatabase,
+        owner: &dyn ast::HasAttrs,
+        span_map: SpanMapRef<'_>,
+    ) -> impl Iterator {
+        collect_attrs(owner).filter_map(move |(id, attr)| match attr {
+            Either::Left(attr) => {
+                attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id))
+            }
+            Either::Right(comment) if DESUGAR_COMMENTS => comment.doc_comment().map(|doc| {
+                let span = span_map.span_for_range(comment.syntax().text_range());
+                let (text, kind) = desugar_doc_comment_text(doc, DocCommentDesugarMode::ProcMacro);
+                Attr {
+                    id,
+                    input: Some(Box::new(AttrInput::Literal(tt::Literal {
+                        symbol: text,
+                        span,
+                        kind,
+                        suffix: None,
+                    }))),
+                    path: Interned::new(ModPath::from(Name::new_symbol(sym::doc, span.ctx))),
+                    ctxt: span.ctx,
+                }
+            }),
+            Either::Right(_) => None,
+        })
+    }
+
+    pub fn attrs_iter_expanded(
+        db: &dyn ExpandDatabase,
+        owner: &dyn ast::HasAttrs,
+        span_map: SpanMapRef<'_>,
+        cfg_options: &CfgOptions,
+    ) -> impl Iterator {
+        Self::attrs_iter::(db, owner, span_map)
+            .flat_map(|attr| attr.expand_cfg_attr(db, cfg_options))
+    }
+
+    pub fn merge(&self, other: Self) -> Self {
+        match (&self.entries, other.entries) {
+            (None, None) => Self::EMPTY,
+            (None, entries @ Some(_)) => Self { entries },
+            (Some(entries), None) => Self { entries: Some(entries.clone()) },
+            (Some(a), Some(b)) => {
+                let last_ast_index = a.slice.last().map_or(0, |it| it.id.ast_index() + 1);
+                let items = a
+                    .slice
+                    .iter()
+                    .cloned()
+                    .chain(b.slice.iter().map(|it| {
+                        let mut it = it.clone();
+                        let id = it.id.ast_index() + last_ast_index;
+                        it.id = AttrId::new(id, it.id.is_inner_attr());
+                        it
+                    }))
+                    .collect::>();
+                Self { entries: Some(ThinArc::from_header_and_iter((), items.into_iter())) }
+            }
         }
     }
+
+    /// Processes `cfg_attr`s
+    pub fn expand_cfg_attr(self, db: &dyn ExpandDatabase, krate: Crate) -> RawAttrs {
+        let has_cfg_attrs =
+            self.iter().any(|attr| attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr));
+        if !has_cfg_attrs {
+            return self;
+        }
+
+        let cfg_options = krate.cfg_options(db);
+        let new_attrs = self
+            .iter()
+            .cloned()
+            .flat_map(|attr| attr.expand_cfg_attr(db, cfg_options))
+            .collect::>();
+        let entries = if new_attrs.is_empty() {
+            None
+        } else {
+            Some(ThinArc::from_header_and_iter((), new_attrs.into_iter()))
+        };
+        RawAttrs { entries }
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.entries.is_none()
+    }
 }
 
-fn tt_end_offset(tt: &ast::TokenTree) -> TextSize {
-    tt.syntax().last_token().unwrap().text_range().start()
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct AttrId {
+    id: u32,
 }
 
-/// The callback is passed a desugared form of the attribute ([`Meta`]), a [`SyntaxNode`] fully containing it
-/// (note: it may not be the direct parent), the range within the [`SyntaxNode`] bounding the attribute,
-/// and the outermost `ast::Attr`. Note that one node may map to multiple [`Meta`]s due to `cfg_attr`.
-#[inline]
-pub fn expand_cfg_attr<'a, BreakValue>(
-    attrs: impl Iterator,
-    cfg_options: impl FnMut() -> &'a CfgOptions,
-    mut callback: impl FnMut(Meta, &SyntaxNode, TextRange, &ast::Attr) -> ControlFlow,
-) -> Option {
-    expand_cfg_attr_with_doc_comments::(
-        attrs.map(Either::Left),
-        cfg_options,
-        move |Either::Left((meta, container, range, top_attr))| {
-            callback(meta, container, range, top_attr)
-        },
-    )
-}
+// FIXME: This only handles a single level of cfg_attr nesting
+// that is `#[cfg_attr(all(), cfg_attr(all(), cfg(any())))]` breaks again
+impl AttrId {
+    const INNER_ATTR_SET_BIT: u32 = 1 << 31;
 
-#[inline]
-pub fn expand_cfg_attr_with_doc_comments<'a, DocComment, BreakValue>(
-    mut attrs: impl Iterator>,
-    mut cfg_options: impl FnMut() -> &'a CfgOptions,
-    mut callback: impl FnMut(
-        Either<(Meta, &SyntaxNode, TextRange, &ast::Attr), DocComment>,
-    ) -> ControlFlow,
-) -> Option {
-    let mut stack = SmallVec::<[_; 1]>::new();
-    let result = attrs.try_for_each(|top_attr| {
-        let top_attr = match top_attr {
-            Either::Left(it) => it,
-            Either::Right(comment) => return callback(Either::Right(comment)),
-        };
-        if let Some((attr_name, tt)) = top_attr.as_simple_call()
-            && attr_name == "cfg_attr"
-        {
-            let mut tt_iter = TokenTreeChildren::new(&tt).peekable();
-            let cfg = cfg::CfgExpr::parse_from_ast(&mut tt_iter);
-            if cfg_options().check(&cfg) != Some(false) {
-                stack.push((tt_iter, tt));
-                while let Some((tt_iter, tt)) = stack.last_mut() {
-                    let Some((attr, range)) = Meta::extract_possibly_unsafe(tt_iter, tt) else {
-                        stack.pop();
-                        continue;
-                    };
-                    if let Meta::TokenTree { path, tt: nested_tt } = &attr
-                        && path.is1("cfg_attr")
-                    {
-                        let mut nested_tt_iter = TokenTreeChildren::new(nested_tt).peekable();
-                        let cfg = cfg::CfgExpr::parse_from_ast(&mut nested_tt_iter);
-                        if cfg_options().check(&cfg) != Some(false) {
-                            stack.push((nested_tt_iter, nested_tt.clone()));
-                        }
-                    } else {
-                        callback(Either::Left((attr, tt.syntax(), range, &top_attr)))?;
-                    }
-                }
-            }
-        } else if let Some(ast_meta) = top_attr.meta()
-            && let Some(path) = ast_meta.path()
-        {
-            let path = AttrPath::extract(&path);
-            let meta = if let Some(tt) = ast_meta.token_tree() {
-                Meta::TokenTree { path, tt }
-            } else if let Some(value) = ast_meta.expr() {
-                let value =
-                    if let ast::Expr::Literal(value) = value { Some(value.token()) } else { None };
-                let name =
-                    if path.segments.len() == 1 { Some(path.segments[0].clone()) } else { None };
-                Meta::NamedKeyValue { name, value, path_range: path.range }
-            } else {
-                Meta::Path { path }
-            };
-            callback(Either::Left((
-                meta,
-                ast_meta.syntax(),
-                ast_meta.syntax().text_range(),
-                &top_attr,
-            )))?;
-        }
-        ControlFlow::Continue(())
-    });
-    result.break_value()
-}
+    pub fn new(id: usize, is_inner: bool) -> Self {
+        assert!(id <= !Self::INNER_ATTR_SET_BIT as usize);
+        let id = id as u32;
+        Self { id: if is_inner { id | Self::INNER_ATTR_SET_BIT } else { id } }
+    }
 
-#[inline]
-pub(crate) fn is_item_tree_filtered_attr(name: &str) -> bool {
-    matches!(
-        name,
-        "doc"
-            | "stable"
-            | "unstable"
-            | "target_feature"
-            | "allow"
-            | "expect"
-            | "warn"
-            | "deny"
-            | "forbid"
-            | "repr"
-            | "inline"
-            | "track_caller"
-            | "must_use"
-    )
-}
+    pub fn ast_index(&self) -> usize {
+        (self.id & !Self::INNER_ATTR_SET_BIT) as usize
+    }
 
-/// This collects attributes exactly as the item tree needs them. This is used for the item tree,
-/// as well as for resolving [`AttrId`]s.
-pub fn collect_item_tree_attrs<'a, BreakValue>(
-    owner: &dyn ast::HasAttrs,
-    cfg_options: impl Fn() -> &'a CfgOptions,
-    mut on_attr: impl FnMut(Meta, &SyntaxNode, &ast::Attr, TextRange) -> ControlFlow,
-) -> Option> {
-    let attrs = ast::attrs_including_inner(owner);
-    expand_cfg_attr(
-        attrs,
-        || cfg_options(),
-        |attr, container, range, top_attr| {
-            // We filter builtin attributes that we don't need for nameres, because this saves memory.
-            // I only put the most common attributes, but if some attribute becomes common feel free to add it.
-            // Notice, however: for an attribute to be filtered out, it *must* not be shadowable with a macro!
-            let filter = match &attr {
-                Meta::NamedKeyValue { name: Some(name), .. } => {
-                    is_item_tree_filtered_attr(name.text())
-                }
-                Meta::TokenTree { path, tt } if path.segments.len() == 1 => {
-                    let name = path.segments[0].text();
-                    if name == "cfg" {
-                        let cfg =
-                            CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(tt).peekable());
-                        if cfg_options().check(&cfg) == Some(false) {
-                            return ControlFlow::Break(Either::Right(cfg));
-                        }
-                        true
-                    } else {
-                        is_item_tree_filtered_attr(name)
-                    }
-                }
-                Meta::Path { path } => {
-                    path.segments.len() == 1 && is_item_tree_filtered_attr(path.segments[0].text())
-                }
-                _ => false,
-            };
-            if !filter && let ControlFlow::Break(v) = on_attr(attr, container, top_attr, range) {
-                return ControlFlow::Break(Either::Left(v));
-            }
-            ControlFlow::Continue(())
-        },
-    )
+    pub fn is_inner_attr(&self) -> bool {
+        self.id & Self::INNER_ATTR_SET_BIT != 0
+    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Attr {
+    pub id: AttrId,
     pub path: Interned,
     pub input: Option>,
     pub ctxt: SyntaxContext,
@@ -414,6 +217,131 @@ impl fmt::Display for AttrInput {
     }
 }
 
+impl Attr {
+    fn from_src(
+        db: &dyn ExpandDatabase,
+        ast: ast::Meta,
+        span_map: SpanMapRef<'_>,
+        id: AttrId,
+    ) -> Option {
+        let path = ast.path()?;
+        let range = path.syntax().text_range();
+        let path = Interned::new(ModPath::from_src(db, path, &mut |range| {
+            span_map.span_for_range(range).ctx
+        })?);
+        let span = span_map.span_for_range(range);
+        let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
+            let token = lit.token();
+            Some(Box::new(AttrInput::Literal(token_to_literal(token.text(), span))))
+        } else if let Some(tt) = ast.token_tree() {
+            let tree = syntax_node_to_token_tree(
+                tt.syntax(),
+                span_map,
+                span,
+                DocCommentDesugarMode::ProcMacro,
+            );
+            Some(Box::new(AttrInput::TokenTree(tree)))
+        } else {
+            None
+        };
+        Some(Attr { id, path, input, ctxt: span.ctx })
+    }
+
+    fn from_tt(
+        db: &dyn ExpandDatabase,
+        mut tt: tt::TokenTreesView<'_>,
+        id: AttrId,
+    ) -> Option {
+        if matches!(tt.flat_tokens(),
+            [tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, .. })), ..]
+            if *sym == sym::unsafe_
+        ) {
+            match tt.iter().nth(1) {
+                Some(tt::TtElement::Subtree(_, iter)) => tt = iter.remaining(),
+                _ => return None,
+            }
+        }
+        let first = tt.flat_tokens().first()?;
+        let ctxt = first.first_span().ctx;
+        let (path, input) = {
+            let mut iter = tt.iter();
+            let start = iter.savepoint();
+            let mut input = tt::TokenTreesView::new(&[]);
+            let mut path = iter.from_savepoint(start);
+            let mut path_split_savepoint = iter.savepoint();
+            while let Some(tt) = iter.next() {
+                path = iter.from_savepoint(start);
+                if !matches!(
+                    tt,
+                    tt::TtElement::Leaf(
+                        tt::Leaf::Punct(tt::Punct { char: ':' | '$', .. }) | tt::Leaf::Ident(_),
+                    )
+                ) {
+                    input = path_split_savepoint.remaining();
+                    break;
+                }
+                path_split_savepoint = iter.savepoint();
+            }
+            (path, input)
+        };
+
+        let path = Interned::new(ModPath::from_tt(db, path)?);
+
+        let input = match (input.flat_tokens().first(), input.try_into_subtree()) {
+            (_, Some(tree)) => {
+                Some(Box::new(AttrInput::TokenTree(tt::TopSubtree::from_subtree(tree))))
+            }
+            (Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))), _) => {
+                match input.flat_tokens().get(1) {
+                    Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => {
+                        Some(Box::new(AttrInput::Literal(lit.clone())))
+                    }
+                    _ => None,
+                }
+            }
+            _ => None,
+        };
+        Some(Attr { id, path, input, ctxt })
+    }
+
+    pub fn path(&self) -> &ModPath {
+        &self.path
+    }
+
+    pub fn expand_cfg_attr(
+        self,
+        db: &dyn ExpandDatabase,
+        cfg_options: &CfgOptions,
+    ) -> impl IntoIterator {
+        let is_cfg_attr = self.path.as_ident().is_some_and(|name| *name == sym::cfg_attr);
+        if !is_cfg_attr {
+            return smallvec![self];
+        }
+
+        let subtree = match self.token_tree_value() {
+            Some(it) => it,
+            _ => return smallvec![self.clone()],
+        };
+
+        let (cfg, parts) = match parse_cfg_attr_input(subtree) {
+            Some(it) => it,
+            None => return smallvec![self.clone()],
+        };
+        let index = self.id;
+        let attrs = parts.filter_map(|attr| Attr::from_tt(db, attr, index));
+
+        let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg);
+        let cfg = CfgExpr::parse(&cfg);
+        if cfg_options.check(&cfg) == Some(false) {
+            smallvec![]
+        } else {
+            cov_mark::hit!(cfg_attr_active);
+
+            attrs.collect::>()
+        }
+    }
+}
+
 impl Attr {
     /// #[path = "string"]
     pub fn string_value(&self) -> Option<&Symbol> {
@@ -475,26 +403,30 @@ impl Attr {
     pub fn parse_path_comma_token_tree<'a>(
         &'a self,
         db: &'a dyn ExpandDatabase,
-    ) -> Option)> + 'a> {
+    ) -> Option + 'a> {
         let args = self.token_tree_value()?;
 
         if args.top_subtree().delimiter.kind != DelimiterKind::Parenthesis {
             return None;
         }
-        Some(parse_path_comma_token_tree(db, args))
-    }
-}
+        let paths = args
+            .token_trees()
+            .split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))))
+            .filter_map(move |tts| {
+                let span = tts.flat_tokens().first()?.first_span();
+                Some((ModPath::from_tt(db, tts)?, span))
+            });
 
-fn parse_path_comma_token_tree<'a>(
-    db: &'a dyn ExpandDatabase,
-    args: &'a tt::TopSubtree,
-) -> impl Iterator)> {
-    args.token_trees()
-        .split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))))
-        .filter_map(move |tts| {
-            let span = tts.flat_tokens().first()?.first_span();
-            Some((ModPath::from_tt(db, tts)?, span, tts))
-        })
+        Some(paths)
+    }
+
+    pub fn cfg(&self) -> Option {
+        if *self.path.as_ident()? == sym::cfg {
+            self.token_tree_value().map(CfgExpr::parse)
+        } else {
+            None
+        }
+    }
 }
 
 fn unescape(s: &str) -> Option> {
@@ -523,104 +455,58 @@ fn unescape(s: &str) -> Option> {
     }
 }
 
-/// This is an index of an attribute *that always points to the item tree attributes*.
-///
-/// Outer attributes are counted first, then inner attributes. This does not support
-/// out-of-line modules, which may have attributes spread across 2 files!
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct AttrId {
-    id: u32,
+pub fn collect_attrs(
+    owner: &dyn ast::HasAttrs,
+) -> impl Iterator)> {
+    let inner_attrs =
+        inner_attributes(owner.syntax()).into_iter().flatten().zip(iter::repeat(true));
+    let outer_attrs = ast::AttrDocCommentIter::from_syntax_node(owner.syntax())
+        .filter(|el| match el {
+            Either::Left(attr) => attr.kind().is_outer(),
+            Either::Right(comment) => comment.is_outer(),
+        })
+        .zip(iter::repeat(false));
+    outer_attrs
+        .chain(inner_attrs)
+        .enumerate()
+        .map(|(id, (attr, is_inner))| (AttrId::new(id, is_inner), attr))
 }
 
-impl AttrId {
-    #[inline]
-    pub fn from_item_tree_index(id: u32) -> Self {
-        Self { id }
-    }
-
-    #[inline]
-    pub fn item_tree_index(self) -> u32 {
-        self.id
-    }
-
-    /// Returns the containing `ast::Attr` (note that it may contain other attributes as well due
-    /// to `cfg_attr`), a `SyntaxNode` guaranteed to contain the attribute, the full range of the
-    /// attribute, and its desugared [`Meta`].
-    pub fn find_attr_range(
-        self,
-        db: &dyn ExpandDatabase,
-        krate: Crate,
-        owner: AstId,
-    ) -> (ast::Attr, SyntaxNode, TextRange, Meta) {
-        self.find_attr_range_with_source(db, krate, &owner.to_node(db))
-    }
-
-    /// Returns the containing `ast::Attr` (note that it may contain other attributes as well due
-    /// to `cfg_attr`), a `SyntaxNode` guaranteed to contain the attribute, the full range of the
-    /// attribute, and its desugared [`Meta`].
-    pub fn find_attr_range_with_source(
-        self,
-        db: &dyn ExpandDatabase,
-        krate: Crate,
-        owner: &dyn ast::HasAttrs,
-    ) -> (ast::Attr, SyntaxNode, TextRange, Meta) {
-        let cfg_options = OnceCell::new();
-        let mut index = 0;
-        let result = collect_item_tree_attrs(
-            owner,
-            || cfg_options.get_or_init(|| krate.cfg_options(db)),
-            |meta, container, top_attr, range| {
-                if index == self.id {
-                    return ControlFlow::Break((top_attr.clone(), container.clone(), range, meta));
+fn inner_attributes(
+    syntax: &SyntaxNode,
+) -> Option>> {
+    let node = match_ast! {
+        match syntax {
+            ast::SourceFile(_) => syntax.clone(),
+            ast::ExternBlock(it) => it.extern_item_list()?.syntax().clone(),
+            ast::Fn(it) => it.body()?.stmt_list()?.syntax().clone(),
+            ast::Impl(it) => it.assoc_item_list()?.syntax().clone(),
+            ast::Module(it) => it.item_list()?.syntax().clone(),
+            ast::BlockExpr(it) => {
+                if !it.may_carry_attributes() {
+                    return None
                 }
-                index += 1;
-                ControlFlow::Continue(())
+                syntax.clone()
             },
-        );
-        match result {
-            Some(Either::Left(it)) => it,
-            _ => {
-                panic!("used an incorrect `AttrId`; crate={krate:?}, attr_id={self:?}");
-            }
+            _ => return None,
         }
-    }
+    };
 
-    pub fn find_derive_range(
-        self,
-        db: &dyn ExpandDatabase,
-        krate: Crate,
-        owner: AstId,
-        derive_index: u32,
-    ) -> TextRange {
-        let (_, _, derive_attr_range, derive_attr) = self.find_attr_range(db, krate, owner);
-        let Meta::TokenTree { tt, .. } = derive_attr else {
-            return derive_attr_range;
-        };
-        // Fake the span map, as we don't really need spans here, just the offsets of the node in the file.
-        let span_map = RealSpanMap::absolute(span::EditionedFileId::current_edition(
-            span::FileId::from_raw(0),
-        ));
-        let tt = syntax_bridge::syntax_node_to_token_tree(
-            tt.syntax(),
-            SpanMapRef::RealSpanMap(&span_map),
-            span_map.span_for_range(tt.syntax().text_range()),
-            DocCommentDesugarMode::ProcMacro,
-        );
-        let Some((_, _, derive_tts)) =
-            parse_path_comma_token_tree(db, &tt).nth(derive_index as usize)
-        else {
-            return derive_attr_range;
-        };
-        let (Some(first_tt), Some(last_tt)) =
-            (derive_tts.flat_tokens().first(), derive_tts.flat_tokens().last())
-        else {
-            return derive_attr_range;
-        };
-        let start = first_tt.first_span().range.start();
-        let end = match last_tt {
-            tt::TokenTree::Leaf(it) => it.span().range.end(),
-            tt::TokenTree::Subtree(it) => it.delimiter.close.range.end(),
-        };
-        TextRange::new(start, end)
-    }
+    let attrs = ast::AttrDocCommentIter::from_syntax_node(&node).filter(|el| match el {
+        Either::Left(attr) => attr.kind().is_inner(),
+        Either::Right(comment) => comment.is_inner(),
+    });
+    Some(attrs)
+}
+
+// Input subtree is: `(cfg, $(attr),+)`
+// Split it up into a `cfg` subtree and the `attr` subtrees.
+fn parse_cfg_attr_input(
+    subtree: &TopSubtree,
+) -> Option<(tt::TokenTreesView<'_>, impl Iterator>)> {
+    let mut parts = subtree
+        .token_trees()
+        .split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))));
+    let cfg = parts.next()?;
+    Some((cfg, parts.filter(|it| !it.is_empty())))
 }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
index 92bcd378149e..6fe63f249cd4 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
@@ -772,7 +772,7 @@ fn relative_file(
     if res == call_site && !allow_recursion {
         Err(ExpandError::other(err_span, format!("recursive inclusion of `{path_str}`")))
     } else {
-        Ok(EditionedFileId::new(db, res, lookup.krate.data(db).edition, lookup.krate))
+        Ok(EditionedFileId::new(db, res, lookup.krate.data(db).edition))
     }
 }
 
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 8b82671ed4a0..d5ebd6ee19f5 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
@@ -1,343 +1,373 @@
 //! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro
-use std::{cell::OnceCell, ops::ControlFlow};
+use std::iter::Peekable;
 
-use ::tt::TextRange;
 use base_db::Crate;
-use cfg::CfgExpr;
-use parser::T;
-use smallvec::SmallVec;
+use cfg::{CfgAtom, CfgExpr};
+use intern::{Symbol, sym};
+use rustc_hash::FxHashSet;
 use syntax::{
-    AstNode, PreorderWithTokens, SyntaxElement, SyntaxNode, SyntaxToken, WalkEvent,
-    ast::{self, HasAttrs, TokenTreeChildren},
+    AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, T,
+    ast::{self, Attr, HasAttrs, Meta, TokenTree, VariantList},
 };
-use syntax_bridge::DocCommentDesugarMode;
+use tracing::{debug, warn};
 
-use crate::{
-    attrs::{AttrId, Meta, expand_cfg_attr, is_item_tree_filtered_attr},
-    db::ExpandDatabase,
-    fixup::{self, SyntaxFixupUndoInfo},
-    span_map::SpanMapRef,
-    tt::{self, DelimSpan, Span},
-};
+use crate::{MacroCallLoc, MacroDefKind, db::ExpandDatabase, proc_macro::ProcMacroKind};
 
-struct ItemIsCfgedOut;
-
-#[derive(Debug)]
-struct ExpandedAttrToProcess {
-    range: TextRange,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum NextExpandedAttrState {
-    NotStarted,
-    InTheMiddle,
-}
-
-#[derive(Debug)]
-struct AstAttrToProcess {
-    range: TextRange,
-    expanded_attrs: SmallVec<[ExpandedAttrToProcess; 1]>,
-    expanded_attrs_idx: usize,
-    next_expanded_attr: NextExpandedAttrState,
-    pound_span: Span,
-    brackets_span: DelimSpan,
-    /// If `Some`, this is an inner attribute.
-    excl_span: Option,
-}
-
-fn macro_input_callback(
-    db: &dyn ExpandDatabase,
-    is_derive: bool,
-    censor_item_tree_attr_ids: &[AttrId],
-    krate: Crate,
-    default_span: Span,
-    span_map: SpanMapRef<'_>,
-) -> 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));
-
-    let mut should_strip_attr = {
-        let mut item_tree_attr_id = 0;
-        let mut censor_item_tree_attr_ids_index = 0;
-        move || {
-            let mut result = false;
-            if let Some(&next_censor_attr_id) =
-                censor_item_tree_attr_ids.get(censor_item_tree_attr_ids_index)
-                && next_censor_attr_id.item_tree_index() == item_tree_attr_id
-            {
-                censor_item_tree_attr_ids_index += 1;
-                result = true;
-            }
-            item_tree_attr_id += 1;
-            result
-        }
-    };
-
-    let mut attrs = Vec::new();
-    let mut attrs_idx = 0;
-    let mut has_inner_attrs_owner = false;
-    let mut in_attr = false;
-    let mut done_with_attrs = false;
-    let mut did_top_attrs = false;
-    move |preorder, event| {
-        match event {
-            WalkEvent::Enter(SyntaxElement::Node(node)) => {
-                if done_with_attrs {
-                    return (true, Vec::new());
-                }
-
-                if ast::Attr::can_cast(node.kind()) {
-                    in_attr = true;
-                    let node_range = node.text_range();
-                    while attrs
-                        .get(attrs_idx)
-                        .is_some_and(|it: &AstAttrToProcess| it.range != node_range)
-                    {
-                        attrs_idx += 1;
-                    }
-                } else if let Some(has_attrs) = ast::AnyHasAttrs::cast(node.clone()) {
-                    if has_inner_attrs_owner {
-                        has_inner_attrs_owner = false;
-                        return (true, Vec::new());
-                    }
-
-                    if did_top_attrs && !is_derive {
-                        // Derives need all attributes handled, but attribute macros need only the top attributes handled.
-                        done_with_attrs = true;
-                        return (true, Vec::new());
-                    }
-                    did_top_attrs = true;
-
-                    if let Some(inner_attrs_node) = has_attrs.inner_attributes_node()
-                        && inner_attrs_node != *node
-                    {
-                        has_inner_attrs_owner = true;
-                    }
-
-                    let node_attrs = ast::attrs_including_inner(&has_attrs);
-
-                    attrs.clear();
-                    node_attrs.clone().for_each(|attr| {
-                        let span_for = |token: Option| {
-                            token
-                                .map(|token| span_map.span_for_range(token.text_range()))
-                                .unwrap_or(default_span)
-                        };
-                        attrs.push(AstAttrToProcess {
-                            range: attr.syntax().text_range(),
-                            pound_span: span_for(attr.pound_token()),
-                            brackets_span: DelimSpan {
-                                open: span_for(attr.l_brack_token()),
-                                close: span_for(attr.r_brack_token()),
-                            },
-                            excl_span: attr
-                                .excl_token()
-                                .map(|token| span_map.span_for_range(token.text_range())),
-                            expanded_attrs: SmallVec::new(),
-                            expanded_attrs_idx: 0,
-                            next_expanded_attr: NextExpandedAttrState::NotStarted,
-                        });
-                    });
-
-                    attrs_idx = 0;
-                    let strip_current_item = expand_cfg_attr(
-                        node_attrs,
-                        &cfg_options,
-                        |attr, _container, range, top_attr| {
-                            // Find the attr.
-                            while attrs[attrs_idx].range != top_attr.syntax().text_range() {
-                                attrs_idx += 1;
-                            }
-
-                            let mut strip_current_attr = false;
-                            match attr {
-                                Meta::NamedKeyValue { name, .. } => {
-                                    if name
-                                        .is_none_or(|name| !is_item_tree_filtered_attr(name.text()))
-                                    {
-                                        strip_current_attr = should_strip_attr();
-                                    }
-                                }
-                                Meta::TokenTree { path, tt } => {
-                                    if path.segments.len() != 1
-                                        || !is_item_tree_filtered_attr(path.segments[0].text())
-                                    {
-                                        strip_current_attr = should_strip_attr();
-                                    }
-
-                                    if path.segments.len() == 1 {
-                                        let name = path.segments[0].text();
-
-                                        if name == "cfg" {
-                                            let cfg_expr = CfgExpr::parse_from_ast(
-                                                &mut TokenTreeChildren::new(&tt).peekable(),
-                                            );
-                                            if cfg_options().check(&cfg_expr) == Some(false) {
-                                                return ControlFlow::Break(ItemIsCfgedOut);
-                                            }
-                                            strip_current_attr = true;
-                                        }
-                                    }
-                                }
-                                Meta::Path { path } => {
-                                    if path.segments.len() != 1
-                                        || !is_item_tree_filtered_attr(path.segments[0].text())
-                                    {
-                                        strip_current_attr = should_strip_attr();
-                                    }
-                                }
-                            }
-
-                            if !strip_current_attr {
-                                attrs[attrs_idx]
-                                    .expanded_attrs
-                                    .push(ExpandedAttrToProcess { range });
-                            }
-
-                            ControlFlow::Continue(())
-                        },
-                    );
-                    attrs_idx = 0;
-
-                    if strip_current_item.is_some() {
-                        preorder.skip_subtree();
-                        attrs.clear();
-
-                        'eat_comma: {
-                            // If there is a comma after this node, eat it too.
-                            let mut events_until_comma = 0;
-                            for event in preorder.clone() {
-                                match event {
-                                    WalkEvent::Enter(SyntaxElement::Node(_))
-                                    | WalkEvent::Leave(_) => {}
-                                    WalkEvent::Enter(SyntaxElement::Token(token)) => {
-                                        let kind = token.kind();
-                                        if kind == T![,] {
-                                            break;
-                                        } else if !kind.is_trivia() {
-                                            break 'eat_comma;
-                                        }
-                                    }
-                                }
-                                events_until_comma += 1;
-                            }
-                            preorder.nth(events_until_comma);
-                        }
-
-                        return (false, Vec::new());
-                    }
-                }
-            }
-            WalkEvent::Leave(SyntaxElement::Node(node)) => {
-                if ast::Attr::can_cast(node.kind()) {
-                    in_attr = false;
-                    attrs_idx += 1;
-                }
-            }
-            WalkEvent::Enter(SyntaxElement::Token(token)) => {
-                if !in_attr {
-                    return (true, Vec::new());
-                }
-
-                let Some(ast_attr) = attrs.get_mut(attrs_idx) else {
-                    return (true, Vec::new());
-                };
-                let token_range = token.text_range();
-                let Some(expanded_attr) = ast_attr.expanded_attrs.get(ast_attr.expanded_attrs_idx)
-                else {
-                    // No expanded attributes in this `ast::Attr`, or we finished them all already, either way
-                    // the remaining tokens should be discarded.
-                    return (false, Vec::new());
-                };
-                match ast_attr.next_expanded_attr {
-                    NextExpandedAttrState::NotStarted => {
-                        if token_range.start() >= expanded_attr.range.start() {
-                            // We started the next attribute.
-                            let mut insert_tokens = Vec::with_capacity(3);
-                            insert_tokens.push(tt::Leaf::Punct(tt::Punct {
-                                char: '#',
-                                spacing: tt::Spacing::Alone,
-                                span: ast_attr.pound_span,
-                            }));
-                            if let Some(span) = ast_attr.excl_span {
-                                insert_tokens.push(tt::Leaf::Punct(tt::Punct {
-                                    char: '!',
-                                    spacing: tt::Spacing::Alone,
-                                    span,
-                                }));
-                            }
-                            insert_tokens.push(tt::Leaf::Punct(tt::Punct {
-                                char: '[',
-                                spacing: tt::Spacing::Alone,
-                                span: ast_attr.brackets_span.open,
-                            }));
-
-                            ast_attr.next_expanded_attr = NextExpandedAttrState::InTheMiddle;
-
-                            return (true, insert_tokens);
-                        } else {
-                            // Before any attribute or between the attributes.
-                            return (false, Vec::new());
-                        }
-                    }
-                    NextExpandedAttrState::InTheMiddle => {
-                        if token_range.start() >= expanded_attr.range.end() {
-                            // Finished the current attribute.
-                            let insert_tokens = vec![tt::Leaf::Punct(tt::Punct {
-                                char: ']',
-                                spacing: tt::Spacing::Alone,
-                                span: ast_attr.brackets_span.close,
-                            })];
-
-                            ast_attr.next_expanded_attr = NextExpandedAttrState::NotStarted;
-                            ast_attr.expanded_attrs_idx += 1;
-
-                            // It's safe to ignore the current token because between attributes
-                            // there is always at least one token we skip - either the closing bracket
-                            // in `#[]` or the comma in case of multiple attrs in `cfg_attr` expansion.
-                            return (false, insert_tokens);
-                        } else {
-                            // Still in the middle.
-                            return (true, Vec::new());
-                        }
-                    }
-                }
-            }
-            WalkEvent::Leave(SyntaxElement::Token(_)) => {}
-        }
-        (true, Vec::new())
+fn check_cfg(db: &dyn ExpandDatabase, attr: &Attr, krate: Crate) -> Option {
+    if !attr.simple_name().as_deref().map(|v| v == "cfg")? {
+        return None;
     }
+    let cfg = parse_from_attr_token_tree(&attr.meta()?.token_tree()?)?;
+    let enabled = krate.cfg_options(db).check(&cfg) != Some(false);
+    Some(enabled)
 }
 
-pub(crate) fn attr_macro_input_to_token_tree(
-    db: &dyn ExpandDatabase,
-    node: &SyntaxNode,
-    span_map: SpanMapRef<'_>,
-    span: Span,
-    is_derive: bool,
-    censor_item_tree_attr_ids: &[AttrId],
-    krate: Crate,
-) -> (tt::TopSubtree, SyntaxFixupUndoInfo) {
-    let fixups = fixup::fixup_syntax(span_map, node, span, DocCommentDesugarMode::ProcMacro);
-    (
-        syntax_bridge::syntax_node_to_token_tree_modified(
-            node,
-            span_map,
-            fixups.append,
-            fixups.remove,
-            span,
-            DocCommentDesugarMode::ProcMacro,
-            macro_input_callback(db, is_derive, censor_item_tree_attr_ids, krate, span, span_map),
-        ),
-        fixups.undo_info,
-    )
+fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: Crate) -> Option {
+    if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? {
+        return None;
+    }
+    check_cfg_attr_value(db, &attr.token_tree()?, krate)
 }
 
 pub fn check_cfg_attr_value(
     db: &dyn ExpandDatabase,
-    attr: &ast::TokenTree,
+    attr: &TokenTree,
     krate: Crate,
 ) -> Option {
-    let cfg_expr = CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(attr).peekable());
-    krate.cfg_options(db).check(&cfg_expr)
+    let cfg_expr = parse_from_attr_token_tree(attr)?;
+    let enabled = krate.cfg_options(db).check(&cfg_expr) != Some(false);
+    Some(enabled)
+}
+
+fn process_has_attrs_with_possible_comma(
+    db: &dyn ExpandDatabase,
+    items: impl Iterator,
+    krate: Crate,
+    remove: &mut FxHashSet,
+) -> Option<()> {
+    for item in items {
+        let field_attrs = item.attrs();
+        'attrs: for attr in field_attrs {
+            if let Some(enabled) = check_cfg(db, &attr, krate) {
+                if enabled {
+                    debug!("censoring {:?}", attr.syntax());
+                    remove.insert(attr.syntax().clone().into());
+                } else {
+                    debug!("censoring {:?}", item.syntax());
+                    remove.insert(item.syntax().clone().into());
+                    // We need to remove the , as well
+                    remove_possible_comma(&item, remove);
+                    break 'attrs;
+                }
+            }
+
+            if let Some(enabled) = check_cfg_attr(db, &attr, krate) {
+                if enabled {
+                    debug!("Removing cfg_attr tokens {:?}", attr);
+                    let meta = attr.meta()?;
+                    let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?;
+                    remove.extend(removes_from_cfg_attr);
+                } else {
+                    debug!("censoring type cfg_attr {:?}", item.syntax());
+                    remove.insert(attr.syntax().clone().into());
+                }
+            }
+        }
+    }
+    Some(())
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+enum CfgExprStage {
+    /// Stripping the CFGExpr part of the attribute
+    StrippigCfgExpr,
+    /// Found the comma after the CFGExpr. Will keep all tokens until the next comma or the end of the attribute
+    FoundComma,
+    /// Everything following the attribute. This could be another attribute or the end of the attribute.
+    // FIXME: cfg_attr with multiple attributes will not be handled correctly. We will only keep the first attribute
+    // Related Issue: https://github.com/rust-lang/rust-analyzer/issues/10110
+    EverythingElse,
+}
+
+/// This function creates its own set of tokens to remove. To help prevent malformed syntax as input.
+fn remove_tokens_within_cfg_attr(meta: Meta) -> Option> {
+    let mut remove: FxHashSet = FxHashSet::default();
+    debug!("Enabling attribute {}", meta);
+    let meta_path = meta.path()?;
+    debug!("Removing {:?}", meta_path.syntax());
+    remove.insert(meta_path.syntax().clone().into());
+
+    let meta_tt = meta.token_tree()?;
+    debug!("meta_tt {}", meta_tt);
+    let mut stage = CfgExprStage::StrippigCfgExpr;
+    for tt in meta_tt.token_trees_and_tokens() {
+        debug!("Checking {:?}. Stage: {:?}", tt, stage);
+        match (stage, tt) {
+            (CfgExprStage::StrippigCfgExpr, syntax::NodeOrToken::Node(node)) => {
+                remove.insert(node.syntax().clone().into());
+            }
+            (CfgExprStage::StrippigCfgExpr, syntax::NodeOrToken::Token(token)) => {
+                if token.kind() == T![,] {
+                    stage = CfgExprStage::FoundComma;
+                }
+                remove.insert(token.into());
+            }
+            (CfgExprStage::FoundComma, syntax::NodeOrToken::Token(token))
+                if (token.kind() == T![,] || token.kind() == T![')']) =>
+            {
+                // The end of the attribute or separator for the next attribute
+                stage = CfgExprStage::EverythingElse;
+                remove.insert(token.into());
+            }
+            (CfgExprStage::EverythingElse, syntax::NodeOrToken::Node(node)) => {
+                remove.insert(node.syntax().clone().into());
+            }
+            (CfgExprStage::EverythingElse, syntax::NodeOrToken::Token(token)) => {
+                remove.insert(token.into());
+            }
+            // This is an actual attribute
+            _ => {}
+        }
+    }
+    if stage != CfgExprStage::EverythingElse {
+        warn!("Invalid cfg_attr attribute. {:?}", meta_tt);
+        return None;
+    }
+    Some(remove)
+}
+/// Removes a possible comma after the [AstNode]
+fn remove_possible_comma(item: &impl AstNode, res: &mut FxHashSet) {
+    if let Some(comma) = item.syntax().next_sibling_or_token().filter(|it| it.kind() == T![,]) {
+        res.insert(comma);
+    }
+}
+fn process_enum(
+    db: &dyn ExpandDatabase,
+    variants: VariantList,
+    krate: Crate,
+    remove: &mut FxHashSet,
+) -> Option<()> {
+    'variant: for variant in variants.variants() {
+        for attr in variant.attrs() {
+            if let Some(enabled) = check_cfg(db, &attr, krate) {
+                if enabled {
+                    debug!("censoring {:?}", attr.syntax());
+                    remove.insert(attr.syntax().clone().into());
+                } else {
+                    // Rustc does not strip the attribute if it is enabled. So we will leave it
+                    debug!("censoring type {:?}", variant.syntax());
+                    remove.insert(variant.syntax().clone().into());
+                    // We need to remove the , as well
+                    remove_possible_comma(&variant, remove);
+                    continue 'variant;
+                }
+            }
+
+            if let Some(enabled) = check_cfg_attr(db, &attr, krate) {
+                if enabled {
+                    debug!("Removing cfg_attr tokens {:?}", attr);
+                    let meta = attr.meta()?;
+                    let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?;
+                    remove.extend(removes_from_cfg_attr);
+                } else {
+                    debug!("censoring type cfg_attr {:?}", variant.syntax());
+                    remove.insert(attr.syntax().clone().into());
+                }
+            }
+        }
+        if let Some(fields) = variant.field_list() {
+            match fields {
+                ast::FieldList::RecordFieldList(fields) => {
+                    process_has_attrs_with_possible_comma(db, fields.fields(), krate, remove)?;
+                }
+                ast::FieldList::TupleFieldList(fields) => {
+                    process_has_attrs_with_possible_comma(db, fields.fields(), krate, remove)?;
+                }
+            }
+        }
+    }
+    Some(())
+}
+
+pub(crate) fn process_cfg_attrs(
+    db: &dyn ExpandDatabase,
+    node: &SyntaxNode,
+    loc: &MacroCallLoc,
+) -> Option> {
+    // FIXME: #[cfg_eval] is not implemented. But it is not stable yet
+    let is_derive = match loc.def.kind {
+        MacroDefKind::BuiltInDerive(..)
+        | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) => true,
+        MacroDefKind::BuiltInAttr(_, expander) => expander.is_derive(),
+        _ => false,
+    };
+    let mut remove = FxHashSet::default();
+
+    let item = ast::Item::cast(node.clone())?;
+    for attr in item.attrs() {
+        if let Some(enabled) = check_cfg_attr(db, &attr, loc.krate) {
+            if enabled {
+                debug!("Removing cfg_attr tokens {:?}", attr);
+                let meta = attr.meta()?;
+                let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?;
+                remove.extend(removes_from_cfg_attr);
+            } else {
+                debug!("Removing type cfg_attr {:?}", item.syntax());
+                remove.insert(attr.syntax().clone().into());
+            }
+        }
+    }
+
+    if is_derive {
+        // Only derives get their code cfg-clean, normal attribute macros process only the cfg at their level
+        // (cfg_attr is handled above, cfg is handled in the def map).
+        match item {
+            ast::Item::Struct(it) => match it.field_list()? {
+                ast::FieldList::RecordFieldList(fields) => {
+                    process_has_attrs_with_possible_comma(
+                        db,
+                        fields.fields(),
+                        loc.krate,
+                        &mut remove,
+                    )?;
+                }
+                ast::FieldList::TupleFieldList(fields) => {
+                    process_has_attrs_with_possible_comma(
+                        db,
+                        fields.fields(),
+                        loc.krate,
+                        &mut remove,
+                    )?;
+                }
+            },
+            ast::Item::Enum(it) => {
+                process_enum(db, it.variant_list()?, loc.krate, &mut remove)?;
+            }
+            ast::Item::Union(it) => {
+                process_has_attrs_with_possible_comma(
+                    db,
+                    it.record_field_list()?.fields(),
+                    loc.krate,
+                    &mut remove,
+                )?;
+            }
+            // FIXME: Implement for other items if necessary. As we do not support #[cfg_eval] yet, we do not need to implement it for now
+            _ => {}
+        }
+    }
+    Some(remove)
+}
+/// Parses a `cfg` attribute from the meta
+fn parse_from_attr_token_tree(tt: &TokenTree) -> Option {
+    let mut iter = tt
+        .token_trees_and_tokens()
+        .filter(is_not_whitespace)
+        .skip(1)
+        .take_while(is_not_closing_paren)
+        .peekable();
+    next_cfg_expr_from_syntax(&mut iter)
+}
+
+fn is_not_closing_paren(element: &NodeOrToken) -> bool {
+    !matches!(element, NodeOrToken::Token(token) if (token.kind() == syntax::T![')']))
+}
+fn is_not_whitespace(element: &NodeOrToken) -> bool {
+    !matches!(element, NodeOrToken::Token(token) if (token.kind() == SyntaxKind::WHITESPACE))
+}
+
+fn next_cfg_expr_from_syntax(iter: &mut Peekable) -> Option
+where
+    I: Iterator>,
+{
+    let name = match iter.next() {
+        None => return None,
+        Some(NodeOrToken::Token(element)) => match element.kind() {
+            syntax::T![ident] => Symbol::intern(element.text()),
+            _ => return Some(CfgExpr::Invalid),
+        },
+        Some(_) => return Some(CfgExpr::Invalid),
+    };
+    let result = match &name {
+        s if [&sym::all, &sym::any, &sym::not].contains(&s) => {
+            let mut preds = Vec::new();
+            let Some(NodeOrToken::Node(tree)) = iter.next() else {
+                return Some(CfgExpr::Invalid);
+            };
+            let mut tree_iter = tree
+                .token_trees_and_tokens()
+                .filter(is_not_whitespace)
+                .skip(1)
+                .take_while(is_not_closing_paren)
+                .peekable();
+            while tree_iter.peek().is_some() {
+                let pred = next_cfg_expr_from_syntax(&mut tree_iter);
+                if let Some(pred) = pred {
+                    preds.push(pred);
+                }
+            }
+            let group = match &name {
+                s if *s == sym::all => CfgExpr::All(preds.into_boxed_slice()),
+                s if *s == sym::any => CfgExpr::Any(preds.into_boxed_slice()),
+                s if *s == sym::not => {
+                    CfgExpr::Not(Box::new(preds.pop().unwrap_or(CfgExpr::Invalid)))
+                }
+                _ => unreachable!(),
+            };
+            Some(group)
+        }
+        _ => match iter.peek() {
+            Some(NodeOrToken::Token(element)) if (element.kind() == syntax::T![=]) => {
+                iter.next();
+                match iter.next() {
+                    Some(NodeOrToken::Token(value_token))
+                        if (value_token.kind() == syntax::SyntaxKind::STRING) =>
+                    {
+                        let value = value_token.text();
+                        Some(CfgExpr::Atom(CfgAtom::KeyValue {
+                            key: name,
+                            value: Symbol::intern(value.trim_matches('"')),
+                        }))
+                    }
+                    _ => None,
+                }
+            }
+            _ => Some(CfgExpr::Atom(CfgAtom::Flag(name))),
+        },
+    };
+    if let Some(NodeOrToken::Token(element)) = iter.peek()
+        && element.kind() == syntax::T![,]
+    {
+        iter.next();
+    }
+    result
+}
+#[cfg(test)]
+mod tests {
+    use cfg::DnfExpr;
+    use expect_test::{Expect, expect};
+    use syntax::{AstNode, SourceFile, ast::Attr};
+
+    use crate::cfg_process::parse_from_attr_token_tree;
+
+    fn check_dnf_from_syntax(input: &str, expect: Expect) {
+        let parse = SourceFile::parse(input, span::Edition::CURRENT);
+        let node = match parse.tree().syntax().descendants().find_map(Attr::cast) {
+            Some(it) => it,
+            None => {
+                let node = std::any::type_name::();
+                panic!("Failed to make ast node `{node}` from text {input}")
+            }
+        };
+        let node = node.clone_subtree();
+        assert_eq!(node.syntax().text_range().start(), 0.into());
+
+        let cfg = parse_from_attr_token_tree(&node.meta().unwrap().token_tree().unwrap()).unwrap();
+        let actual = format!("#![cfg({})]", DnfExpr::new(&cfg));
+        expect.assert_eq(&actual);
+    }
+    #[test]
+    fn cfg_from_attr() {
+        check_dnf_from_syntax(r#"#[cfg(test)]"#, expect![[r#"#![cfg(test)]"#]]);
+        check_dnf_from_syntax(r#"#[cfg(not(never))]"#, expect![[r#"#![cfg(not(never))]"#]]);
+    }
 }
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 6b5aa39fa6bf..888c1405a6bb 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -1,9 +1,11 @@
 //! Defines database & queries for macro expansion.
 
 use base_db::{Crate, RootQueryDb};
+use either::Either;
 use mbe::MatchedArmIndex;
+use rustc_hash::FxHashSet;
 use span::{AstIdMap, Edition, Span, SyntaxContext};
-use syntax::{AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, ast};
+use syntax::{AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T, ast};
 use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree};
 use triomphe::Arc;
 
@@ -11,9 +13,9 @@ use crate::{
     AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo,
     EagerExpander, EditionedFileId, ExpandError, ExpandResult, ExpandTo, HirFileId, MacroCallId,
     MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
-    attrs::Meta,
+    attrs::{AttrId, AttrInput, RawAttrs, collect_attrs},
     builtin::pseudo_derive_attr_expansion,
-    cfg_process::attr_macro_input_to_token_tree,
+    cfg_process,
     declarative::DeclarativeMacroExpander,
     fixup::{self, SyntaxFixupUndoInfo},
     hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt},
@@ -175,7 +177,7 @@ pub fn expand_speculative(
     let span_map = SpanMapRef::RealSpanMap(&span_map);
 
     // Build the subtree and token mapping for the speculative args
-    let (mut tt, undo_info) = match &loc.kind {
+    let (mut tt, undo_info) = match loc.kind {
         MacroCallKind::FnLike { .. } => (
             syntax_bridge::syntax_node_to_token_tree(
                 speculative_args,
@@ -198,35 +200,48 @@ pub fn expand_speculative(
             ),
             SyntaxFixupUndoInfo::NONE,
         ),
-        MacroCallKind::Derive { derive_macro_id, .. } => {
-            let MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } =
-                &derive_macro_id.loc(db).kind
-            else {
-                unreachable!("`derive_macro_id` should be `MacroCallKind::Attr`");
+        MacroCallKind::Derive { derive_attr_index: index, .. }
+        | MacroCallKind::Attr { invoc_attr_index: index, .. } => {
+            let censor = if let MacroCallKind::Derive { .. } = loc.kind {
+                censor_derive_input(index, &ast::Adt::cast(speculative_args.clone())?)
+            } else {
+                attr_source(index, &ast::Item::cast(speculative_args.clone())?)
+                    .into_iter()
+                    .map(|it| it.syntax().clone().into())
+                    .collect()
             };
-            attr_macro_input_to_token_tree(
-                db,
-                speculative_args,
+
+            let censor_cfg =
+                cfg_process::process_cfg_attrs(db, speculative_args, &loc).unwrap_or_default();
+            let mut fixups = fixup::fixup_syntax(
                 span_map,
+                speculative_args,
                 span,
-                true,
-                attr_ids,
-                loc.krate,
+                DocCommentDesugarMode::ProcMacro,
+            );
+            fixups.append.retain(|it, _| match it {
+                syntax::NodeOrToken::Token(_) => true,
+                it => !censor.contains(it) && !censor_cfg.contains(it),
+            });
+            fixups.remove.extend(censor);
+            fixups.remove.extend(censor_cfg);
+
+            (
+                syntax_bridge::syntax_node_to_token_tree_modified(
+                    speculative_args,
+                    span_map,
+                    fixups.append,
+                    fixups.remove,
+                    span,
+                    DocCommentDesugarMode::ProcMacro,
+                ),
+                fixups.undo_info,
             )
         }
-        MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => attr_macro_input_to_token_tree(
-            db,
-            speculative_args,
-            span_map,
-            span,
-            false,
-            attr_ids,
-            loc.krate,
-        ),
     };
 
-    let attr_arg = match &loc.kind {
-        MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => {
+    let attr_arg = match loc.kind {
+        MacroCallKind::Attr { invoc_attr_index, .. } => {
             if loc.def.is_attribute_derive() {
                 // for pseudo-derive expansion we actually pass the attribute itself only
                 ast::Attr::cast(speculative_args.clone()).and_then(|attr| attr.token_tree()).map(
@@ -245,21 +260,18 @@ pub fn expand_speculative(
                 // Attributes may have an input token tree, build the subtree and map for this as well
                 // then try finding a token id for our token if it is inside this input subtree.
                 let item = ast::Item::cast(speculative_args.clone())?;
-                let (_, _, _, meta) =
-                    attr_ids.invoc_attr().find_attr_range_with_source(db, loc.krate, &item);
-                match meta {
-                    Meta::TokenTree { tt, .. } => {
-                        let mut attr_arg = syntax_bridge::syntax_node_to_token_tree(
-                            tt.syntax(),
-                            span_map,
-                            span,
-                            DocCommentDesugarMode::ProcMacro,
-                        );
-                        attr_arg.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible;
-                        Some(attr_arg)
+                let attrs = RawAttrs::new_expanded(db, &item, span_map, loc.krate.cfg_options(db));
+                attrs.iter().find(|attr| attr.id == invoc_attr_index).and_then(|attr| {
+                    match attr.input.as_deref()? {
+                        AttrInput::TokenTree(tt) => {
+                            let mut attr_arg = tt.clone();
+                            attr_arg.top_subtree_delimiter_mut().kind =
+                                tt::DelimiterKind::Invisible;
+                            Some(attr_arg)
+                        }
+                        AttrInput::Literal(_) => None,
                     }
-                    _ => None,
-                }
+                })
             }
         }
         _ => None,
@@ -421,7 +433,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
     let (parse, map) = parse_with_map(db, loc.kind.file_id());
     let root = parse.syntax_node();
 
-    let (is_derive, censor_item_tree_attr_ids, item_node, span) = match &loc.kind {
+    let (censor, item_node, span) = match loc.kind {
         MacroCallKind::FnLike { ast_id, .. } => {
             let node = &ast_id.to_ptr(db).to_node(&root);
             let path_range = node
@@ -489,29 +501,53 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
         MacroCallKind::Derive { .. } => {
             unreachable!("`ExpandDatabase::macro_arg` called with `MacroCallKind::Derive`")
         }
-        MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
+        MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
             let node = ast_id.to_ptr(db).to_node(&root);
-            let range = attr_ids
-                .invoc_attr()
-                .find_attr_range_with_source(db, loc.krate, &node)
-                .3
-                .path_range();
-            let span = map.span_for_range(range);
+            let attr_source = attr_source(invoc_attr_index, &node);
 
-            let is_derive = matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive());
-            (is_derive, &**attr_ids, node, span)
+            let span = map.span_for_range(
+                attr_source
+                    .as_ref()
+                    .and_then(|it| it.path())
+                    .map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()),
+            );
+            // If derive attribute we need to censor the derive input
+            if matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive())
+                && ast::Adt::can_cast(node.syntax().kind())
+            {
+                let adt = ast::Adt::cast(node.syntax().clone()).unwrap();
+                let censor_derive_input = censor_derive_input(invoc_attr_index, &adt);
+                (censor_derive_input, node, span)
+            } else {
+                (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span)
+            }
         }
     };
 
-    let (mut tt, undo_info) = attr_macro_input_to_token_tree(
-        db,
-        item_node.syntax(),
-        map.as_ref(),
-        span,
-        is_derive,
-        censor_item_tree_attr_ids,
-        loc.krate,
-    );
+    let (mut tt, undo_info) = {
+        let syntax = item_node.syntax();
+        let censor_cfg = cfg_process::process_cfg_attrs(db, syntax, &loc).unwrap_or_default();
+        let mut fixups =
+            fixup::fixup_syntax(map.as_ref(), syntax, span, DocCommentDesugarMode::ProcMacro);
+        fixups.append.retain(|it, _| match it {
+            syntax::NodeOrToken::Token(_) => true,
+            it => !censor.contains(it) && !censor_cfg.contains(it),
+        });
+        fixups.remove.extend(censor);
+        fixups.remove.extend(censor_cfg);
+
+        (
+            syntax_bridge::syntax_node_to_token_tree_modified(
+                syntax,
+                map,
+                fixups.append,
+                fixups.remove,
+                span,
+                DocCommentDesugarMode::ProcMacro,
+            ),
+            fixups.undo_info,
+        )
+    };
 
     if loc.def.is_proc_macro() {
         // proc macros expect their inputs without parentheses, MBEs expect it with them included
@@ -521,6 +557,31 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
     (Arc::new(tt), undo_info, span)
 }
 
+// FIXME: Censoring info should be calculated by the caller! Namely by name resolution
+/// Derives expect all `#[derive(..)]` invocations up to (and including) the currently invoked one to be stripped
+fn censor_derive_input(derive_attr_index: AttrId, node: &ast::Adt) -> FxHashSet {
+    // FIXME: handle `cfg_attr`
+    cov_mark::hit!(derive_censoring);
+    collect_attrs(node)
+        .take(derive_attr_index.ast_index() + 1)
+        .filter_map(|(_, attr)| Either::left(attr))
+        // FIXME, this resolution should not be done syntactically
+        // derive is a proper macro now, no longer builtin
+        // But we do not have resolution at this stage, this means
+        // we need to know about all macro calls for the given ast item here
+        // so we require some kind of mapping...
+        .filter(|attr| attr.simple_name().as_deref() == Some("derive"))
+        .map(|it| it.syntax().clone().into())
+        .collect()
+}
+
+/// Attributes expect the invoking attribute to be stripped
+fn attr_source(invoc_attr_index: AttrId, node: &ast::Item) -> Option {
+    // FIXME: handle `cfg_attr`
+    cov_mark::hit!(attribute_macro_attr_censoring);
+    collect_attrs(node).nth(invoc_attr_index.ast_index()).and_then(|(_, attr)| Either::left(attr))
+}
+
 impl TokenExpander {
     fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander {
         match id.kind {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs
index 3fb9aca9649e..0d100c1364ab 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs
@@ -1,20 +1,16 @@
 //! Compiled declarative macro expanders (`macro_rules!` and `macro`)
 
-use std::{cell::OnceCell, ops::ControlFlow};
-
 use base_db::Crate;
+use intern::sym;
 use span::{Edition, Span, SyntaxContext};
 use stdx::TupleExt;
-use syntax::{
-    AstNode, AstToken,
-    ast::{self, HasAttrs},
-};
+use syntax::{AstNode, ast};
 use syntax_bridge::DocCommentDesugarMode;
 use triomphe::Arc;
 
 use crate::{
     AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, Lookup, MacroCallId,
-    attrs::{Meta, expand_cfg_attr},
+    attrs::RawAttrs,
     db::ExpandDatabase,
     hygiene::{Transparency, apply_mark},
     tt,
@@ -84,28 +80,29 @@ impl DeclarativeMacroExpander {
         let (root, map) = crate::db::parse_with_map(db, id.file_id);
         let root = root.syntax_node();
 
-        let transparency = |node: ast::AnyHasAttrs| {
-            let cfg_options = OnceCell::new();
-            expand_cfg_attr(
-                node.attrs(),
-                || cfg_options.get_or_init(|| def_crate.cfg_options(db)),
-                |attr, _, _, _| {
-                    if let Meta::NamedKeyValue { name: Some(name), value, .. } = attr
-                        && name.text() == "rustc_macro_transparency"
-                        && let Some(value) = value.and_then(ast::String::cast)
-                        && let Ok(value) = value.value()
-                    {
-                        match &*value {
-                            "transparent" => ControlFlow::Break(Transparency::Transparent),
-                            "semitransparent" => ControlFlow::Break(Transparency::SemiTransparent),
-                            "opaque" => ControlFlow::Break(Transparency::Opaque),
-                            _ => ControlFlow::Continue(()),
-                        }
-                    } else {
-                        ControlFlow::Continue(())
-                    }
+        let transparency = |node| {
+            // ... would be nice to have the item tree here
+            let attrs = RawAttrs::new_expanded(db, node, map.as_ref(), def_crate.cfg_options(db));
+            match attrs
+                .iter()
+                .find(|it| {
+                    it.path
+                        .as_ident()
+                        .map(|it| *it == sym::rustc_macro_transparency)
+                        .unwrap_or(false)
+                })?
+                .token_tree_value()?
+                .token_trees()
+                .flat_tokens()
+            {
+                [tt::TokenTree::Leaf(tt::Leaf::Ident(i)), ..] => match &i.sym {
+                    s if *s == sym::transparent => Some(Transparency::Transparent),
+                    s if *s == sym::semitransparent => Some(Transparency::SemiTransparent),
+                    s if *s == sym::opaque => Some(Transparency::Opaque),
+                    _ => None,
                 },
-            )
+                _ => None,
+            }
         };
         let ctx_edition = |ctx: SyntaxContext| {
             if ctx.is_root() {
@@ -136,8 +133,7 @@ impl DeclarativeMacroExpander {
                         "expected a token tree".into(),
                     )),
                 },
-                transparency(ast::AnyHasAttrs::from(macro_rules))
-                    .unwrap_or(Transparency::SemiTransparent),
+                transparency(¯o_rules).unwrap_or(Transparency::SemiTransparent),
             ),
             ast::Macro::MacroDef(macro_def) => (
                 match macro_def.body() {
@@ -165,7 +161,7 @@ impl DeclarativeMacroExpander {
                         "expected a token tree".into(),
                     )),
                 },
-                transparency(macro_def.into()).unwrap_or(Transparency::Opaque),
+                transparency(¯o_def).unwrap_or(Transparency::Opaque),
             ),
         };
         let edition = ctx_edition(match id.file_id {
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 fe557d68023d..a7f3e27a4553 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs
@@ -55,6 +55,30 @@ impl From for HirFilePosition {
     }
 }
 
+impl FilePositionWrapper {
+    pub fn with_edition(self, db: &dyn ExpandDatabase, edition: span::Edition) -> FilePosition {
+        FilePositionWrapper {
+            file_id: EditionedFileId::new(db, self.file_id, edition),
+            offset: self.offset,
+        }
+    }
+}
+
+impl FileRangeWrapper {
+    pub fn with_edition(self, db: &dyn ExpandDatabase, edition: span::Edition) -> FileRange {
+        FileRangeWrapper {
+            file_id: EditionedFileId::new(db, self.file_id, edition),
+            range: self.range,
+        }
+    }
+}
+
+impl InFileWrapper {
+    pub fn with_edition(self, db: &dyn ExpandDatabase, edition: span::Edition) -> InRealFile {
+        InRealFile { file_id: EditionedFileId::new(db, self.file_id, edition), value: self.value }
+    }
+}
+
 impl HirFileRange {
     pub fn file_range(self) -> Option {
         Some(FileRange { file_id: self.file_id.file_id()?, range: self.range })
@@ -383,7 +407,7 @@ impl InFile {
 
                 // Fall back to whole macro call.
                 let loc = db.lookup_intern_macro_call(mac_file);
-                loc.kind.original_call_range(db, loc.krate)
+                loc.kind.original_call_range(db)
             }
         }
     }
@@ -429,10 +453,7 @@ impl InFile {
                     Some(it) => it,
                     None => {
                         let loc = db.lookup_intern_macro_call(mac_file);
-                        (
-                            loc.kind.original_call_range(db, loc.krate),
-                            SyntaxContext::root(loc.def.edition),
-                        )
+                        (loc.kind.original_call_range(db), SyntaxContext::root(loc.def.edition))
                     }
                 }
             }
@@ -447,7 +468,7 @@ impl InFile {
                     Some(it) => it,
                     _ => {
                         let loc = db.lookup_intern_macro_call(mac_file);
-                        loc.kind.original_call_range(db, loc.krate)
+                        loc.kind.original_call_range(db)
                     }
                 }
             }
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 cba1c7c1d4b0..fe77e1565987 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
@@ -523,7 +523,6 @@ mod tests {
             fixups.remove,
             span_map.span_for_range(TextRange::empty(0.into())),
             DocCommentDesugarMode::Mbe,
-            |_, _| (true, Vec::new()),
         );
 
         let actual = format!("{tt}\n");
@@ -699,7 +698,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
-fn foo () {a .__ra_fixup ;}
+fn foo () {a . __ra_fixup ;}
 "#]],
         )
     }
@@ -714,7 +713,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
-fn foo () {a .__ra_fixup ; bar () ;}
+fn foo () {a . __ra_fixup ; bar () ;}
 "#]],
         )
     }
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 e1103ef43e0f..472ec83ffef5 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -25,17 +25,18 @@ mod cfg_process;
 mod fixup;
 mod prettify_macro_expansion_;
 
+use attrs::collect_attrs;
+use rustc_hash::FxHashMap;
 use salsa::plumbing::{AsId, FromId};
 use stdx::TupleExt;
-use thin_vec::ThinVec;
 use triomphe::Arc;
 
 use core::fmt;
-use std::{hash::Hash, ops};
+use std::hash::Hash;
 
 use base_db::Crate;
 use either::Either;
-use span::{Edition, ErasedFileAstId, FileAstId, Span, SyntaxContext};
+use span::{Edition, ErasedFileAstId, FileAstId, Span, SpanAnchor, SyntaxContext};
 use syntax::{
     SyntaxNode, SyntaxToken, TextRange, TextSize,
     ast::{self, AstNode},
@@ -316,6 +317,9 @@ pub enum MacroCallKind {
     Derive {
         ast_id: AstId,
         /// Syntactical index of the invoking `#[derive]` attribute.
+        ///
+        /// Outer attributes are counted first, then inner attributes. This does not support
+        /// out-of-line modules, which may have attributes spread across 2 files!
         derive_attr_index: AttrId,
         /// Index of the derive macro in the derive attribute
         derive_index: u32,
@@ -325,68 +329,17 @@ pub enum MacroCallKind {
     },
     Attr {
         ast_id: AstId,
-        // FIXME: This shouldn't be here, we can derive this from `invoc_attr_index`.
+        // FIXME: This shouldn't be here, we can derive this from `invoc_attr_index`
+        // but we need to fix the `cfg_attr` handling first.
         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.
+        /// Syntactical index of the invoking `#[attribute]`.
         ///
-        /// The macro should not see the attributes here.
-        ///
-        /// [`invoc_attr()`]: AttrMacroAttrIds::invoc_attr
-        censored_attr_ids: AttrMacroAttrIds,
+        /// Outer attributes are counted first, then inner attributes. This does not support
+        /// out-of-line modules, which may have attributes spread across 2 files!
+        invoc_attr_index: AttrId,
     },
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct AttrMacroAttrIds(AttrMacroAttrIdsRepr);
-
-impl AttrMacroAttrIds {
-    #[inline]
-    pub fn from_one(id: AttrId) -> Self {
-        Self(AttrMacroAttrIdsRepr::One(id))
-    }
-
-    #[inline]
-    pub fn from_many(ids: &[AttrId]) -> Self {
-        if let &[id] = ids {
-            Self(AttrMacroAttrIdsRepr::One(id))
-        } else {
-            Self(AttrMacroAttrIdsRepr::ManyDerives(ids.iter().copied().collect()))
-        }
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-enum AttrMacroAttrIdsRepr {
-    One(AttrId),
-    ManyDerives(ThinVec),
-}
-
-impl ops::Deref for AttrMacroAttrIds {
-    type Target = [AttrId];
-
-    #[inline]
-    fn deref(&self) -> &Self::Target {
-        match &self.0 {
-            AttrMacroAttrIdsRepr::One(one) => std::slice::from_ref(one),
-            AttrMacroAttrIdsRepr::ManyDerives(many) => many,
-        }
-    }
-}
-
-impl AttrMacroAttrIds {
-    #[inline]
-    pub fn invoc_attr(&self) -> AttrId {
-        match &self.0 {
-            AttrMacroAttrIdsRepr::One(it) => *it,
-            AttrMacroAttrIdsRepr::ManyDerives(it) => {
-                *it.last().expect("should always have at least one `AttrId`")
-            }
-        }
-    }
-}
-
 impl HirFileId {
     pub fn edition(self, db: &dyn ExpandDatabase) -> Edition {
         match self {
@@ -630,20 +583,34 @@ impl MacroDefId {
 
 impl MacroCallLoc {
     pub fn to_node(&self, db: &dyn ExpandDatabase) -> InFile {
-        match &self.kind {
+        match self.kind {
             MacroCallKind::FnLike { ast_id, .. } => {
                 ast_id.with_value(ast_id.to_node(db).syntax().clone())
             }
             MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
                 // FIXME: handle `cfg_attr`
-                let (attr, _, _, _) = derive_attr_index.find_attr_range(db, self.krate, *ast_id);
-                ast_id.with_value(attr.syntax().clone())
+                ast_id.with_value(ast_id.to_node(db)).map(|it| {
+                    collect_attrs(&it)
+                        .nth(derive_attr_index.ast_index())
+                        .and_then(|it| match it.1 {
+                            Either::Left(attr) => Some(attr.syntax().clone()),
+                            Either::Right(_) => None,
+                        })
+                        .unwrap_or_else(|| it.syntax().clone())
+                })
             }
-            MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
+            MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
                 if self.def.is_attribute_derive() {
-                    let (attr, _, _, _) =
-                        attr_ids.invoc_attr().find_attr_range(db, self.krate, *ast_id);
-                    ast_id.with_value(attr.syntax().clone())
+                    // FIXME: handle `cfg_attr`
+                    ast_id.with_value(ast_id.to_node(db)).map(|it| {
+                        collect_attrs(&it)
+                            .nth(invoc_attr_index.ast_index())
+                            .and_then(|it| match it.1 {
+                                Either::Left(attr) => Some(attr.syntax().clone()),
+                                Either::Right(_) => None,
+                            })
+                            .unwrap_or_else(|| it.syntax().clone())
+                    })
                 } else {
                     ast_id.with_value(ast_id.to_node(db).syntax().clone())
                 }
@@ -748,7 +715,7 @@ impl MacroCallKind {
     /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros
     /// get the macro path (rustc shows the whole `ast::MacroCall`), attribute macros get the
     /// attribute's range, and derives get only the specific derive that is being referred to.
-    pub fn original_call_range(self, db: &dyn ExpandDatabase, krate: Crate) -> FileRange {
+    pub fn original_call_range(self, db: &dyn ExpandDatabase) -> FileRange {
         let mut kind = self;
         let file_id = loop {
             match kind.file_id() {
@@ -770,11 +737,24 @@ impl MacroCallKind {
             }
             MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
                 // FIXME: should be the range of the macro name, not the whole derive
-                derive_attr_index.find_attr_range(db, krate, ast_id).2
+                // FIXME: handle `cfg_attr`
+                collect_attrs(&ast_id.to_node(db))
+                    .nth(derive_attr_index.ast_index())
+                    .expect("missing derive")
+                    .1
+                    .expect_left("derive is a doc comment?")
+                    .syntax()
+                    .text_range()
             }
             // FIXME: handle `cfg_attr`
-            MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
-                attr_ids.invoc_attr().find_attr_range(db, krate, ast_id).2
+            MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
+                collect_attrs(&ast_id.to_node(db))
+                    .nth(invoc_attr_index.ast_index())
+                    .expect("missing attribute")
+                    .1
+                    .expect_left("attribute macro is a doc comment?")
+                    .syntax()
+                    .text_range()
             }
         };
 
@@ -893,8 +873,7 @@ impl ExpansionInfo {
         let span = self.exp_map.span_at(token.start());
         match &self.arg_map {
             SpanMap::RealSpanMap(_) => {
-                let file_id =
-                    EditionedFileId::from_span_guess_origin(db, span.anchor.file_id).into();
+                let file_id = EditionedFileId::from_span(db, span.anchor.file_id).into();
                 let anchor_offset =
                     db.ast_id_map(file_id).get_erased(span.anchor.ast_id).text_range().start();
                 InFile { file_id, value: smallvec::smallvec![span.range + anchor_offset] }
@@ -950,7 +929,7 @@ pub fn map_node_range_up_rooted(
         start = start.min(span.range.start());
         end = end.max(span.range.end());
     }
-    let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id);
+    let file_id = EditionedFileId::from_span(db, anchor.file_id);
     let anchor_offset =
         db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start();
     Some(FileRange { file_id, range: TextRange::new(start, end) + anchor_offset })
@@ -976,12 +955,36 @@ pub fn map_node_range_up(
         start = start.min(span.range.start());
         end = end.max(span.range.end());
     }
-    let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id);
+    let file_id = EditionedFileId::from_span(db, anchor.file_id);
     let anchor_offset =
         db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start();
     Some((FileRange { file_id, range: TextRange::new(start, end) + anchor_offset }, ctx))
 }
 
+/// Maps up the text range out of the expansion hierarchy back into the original file its from.
+/// This version will aggregate the ranges of all spans with the same anchor and syntax context.
+pub fn map_node_range_up_aggregated(
+    db: &dyn ExpandDatabase,
+    exp_map: &ExpansionSpanMap,
+    range: TextRange,
+) -> FxHashMap<(SpanAnchor, SyntaxContext), TextRange> {
+    let mut map = FxHashMap::default();
+    for span in exp_map.spans_for_range(range) {
+        let range = map.entry((span.anchor, span.ctx)).or_insert_with(|| span.range);
+        *range = TextRange::new(
+            range.start().min(span.range.start()),
+            range.end().max(span.range.end()),
+        );
+    }
+    for ((anchor, _), range) in &mut map {
+        let file_id = EditionedFileId::from_span(db, anchor.file_id);
+        let anchor_offset =
+            db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start();
+        *range += anchor_offset;
+    }
+    map
+}
+
 /// Looks up the span at the given offset.
 pub fn span_for_offset(
     db: &dyn ExpandDatabase,
@@ -989,7 +992,7 @@ pub fn span_for_offset(
     offset: TextSize,
 ) -> (FileRange, SyntaxContext) {
     let span = exp_map.span_at(offset);
-    let file_id = EditionedFileId::from_span_guess_origin(db, span.anchor.file_id);
+    let file_id = EditionedFileId::from_span(db, span.anchor.file_id);
     let anchor_offset =
         db.ast_id_map(file_id.into()).get_erased(span.anchor.ast_id).text_range().start();
     (FileRange { file_id, range: span.range + anchor_offset }, span.ctx)
@@ -1059,7 +1062,7 @@ impl ExpandTo {
     }
 }
 
-intern::impl_internable!(ModPath);
+intern::impl_internable!(ModPath, attrs::AttrInput);
 
 #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)]
 #[doc(alias = "MacroFileId")]
@@ -1122,14 +1125,6 @@ impl HirFileId {
             HirFileId::MacroFile(_) => None,
         }
     }
-
-    #[inline]
-    pub fn krate(self, db: &dyn ExpandDatabase) -> Crate {
-        match self {
-            HirFileId::FileId(it) => it.krate(db),
-            HirFileId::MacroFile(it) => it.loc(db).krate,
-        }
-    }
 }
 
 impl PartialEq for HirFileId {
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 e9805e3f86b8..d84d978cdb7e 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
@@ -2,7 +2,7 @@
 
 use std::{
     fmt::{self, Display as _},
-    iter::{self, Peekable},
+    iter,
 };
 
 use crate::{
@@ -12,11 +12,10 @@ use crate::{
     tt,
 };
 use base_db::Crate;
-use intern::{Symbol, sym};
-use parser::T;
+use intern::sym;
 use smallvec::SmallVec;
 use span::{Edition, SyntaxContext};
-use syntax::{AstNode, SyntaxToken, ast};
+use syntax::{AstNode, ast};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct ModPath {
@@ -65,58 +64,6 @@ impl ModPath {
         ModPath { kind, segments: SmallVec::new_const() }
     }
 
-    pub fn from_tokens(
-        db: &dyn ExpandDatabase,
-        span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext,
-        is_abs: bool,
-        segments: impl Iterator,
-    ) -> Option {
-        let mut segments = segments.peekable();
-        let mut result = SmallVec::new_const();
-        let path_kind = if is_abs {
-            PathKind::Abs
-        } else {
-            let first = segments.next()?;
-            match first.kind() {
-                T![crate] => PathKind::Crate,
-                T![self] => PathKind::Super(handle_super(&mut segments)),
-                T![super] => PathKind::Super(1 + handle_super(&mut segments)),
-                T![ident] => {
-                    let first_text = first.text();
-                    if first_text == "$crate" {
-                        let ctxt = span_for_range(first.text_range());
-                        resolve_crate_root(db, ctxt)
-                            .map(PathKind::DollarCrate)
-                            .unwrap_or(PathKind::Crate)
-                    } else {
-                        result.push(Name::new_symbol_root(Symbol::intern(first_text)));
-                        PathKind::Plain
-                    }
-                }
-                _ => return None,
-            }
-        };
-        for segment in segments {
-            if segment.kind() != T![ident] {
-                return None;
-            }
-            result.push(Name::new_symbol_root(Symbol::intern(segment.text())));
-        }
-        if result.is_empty() {
-            return None;
-        }
-        result.shrink_to_fit();
-        return Some(ModPath { kind: path_kind, segments: result });
-
-        fn handle_super(segments: &mut Peekable>) -> u8 {
-            let mut result = 0;
-            while segments.next_if(|it| it.kind() == T![super]).is_some() {
-                result += 1;
-            }
-            result
-        }
-    }
-
     pub fn segments(&self) -> &[Name] {
         &self.segments
     }
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 8b0c0d72cd49..e5a778a95c7c 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs
@@ -1,12 +1,13 @@
 //! Span maps for real files and macro expansions.
 
 use span::{Span, SyntaxContext};
+use stdx::TupleExt;
 use syntax::{AstNode, TextRange, ast};
 use triomphe::Arc;
 
 pub use span::RealSpanMap;
 
-use crate::{HirFileId, MacroCallId, db::ExpandDatabase};
+use crate::{HirFileId, MacroCallId, attrs::collect_attrs, db::ExpandDatabase};
 
 pub type ExpansionSpanMap = span::SpanMap;
 
@@ -109,24 +110,26 @@ pub(crate) fn real_span_map(
     // them anchors too, but only if they have no attributes attached, as those might be proc-macros
     // and using different anchors inside of them will prevent spans from being joinable.
     tree.items().for_each(|item| match &item {
-        ast::Item::ExternBlock(it) if ast::attrs_including_inner(it).next().is_none() => {
+        ast::Item::ExternBlock(it)
+            if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) =>
+        {
             if let Some(extern_item_list) = it.extern_item_list() {
                 pairs.extend(
                     extern_item_list.extern_items().map(ast::Item::from).map(item_to_entry),
                 );
             }
         }
-        ast::Item::Impl(it) if ast::attrs_including_inner(it).next().is_none() => {
+        ast::Item::Impl(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => {
             if let Some(assoc_item_list) = it.assoc_item_list() {
                 pairs.extend(assoc_item_list.assoc_items().map(ast::Item::from).map(item_to_entry));
             }
         }
-        ast::Item::Module(it) if ast::attrs_including_inner(it).next().is_none() => {
+        ast::Item::Module(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => {
             if let Some(item_list) = it.item_list() {
                 pairs.extend(item_list.items().map(item_to_entry));
             }
         }
-        ast::Item::Trait(it) if ast::attrs_including_inner(it).next().is_none() => {
+        ast::Item::Trait(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => {
             if let Some(assoc_item_list) = it.assoc_item_list() {
                 pairs.extend(assoc_item_list.assoc_items().map(ast::Item::from).map(item_to_entry));
             }
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 0a6458562e15..18ebe7d7a539 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -6,7 +6,6 @@ mod tests;
 use base_db::Crate;
 use hir_def::{
     EnumVariantId, GeneralConstId, HasModule, StaticId,
-    attrs::AttrFlags,
     expr_store::Body,
     hir::{Expr, ExprId},
     type_ref::LiteralConstRef,
@@ -199,7 +198,7 @@ pub(crate) fn const_eval_discriminant_variant<'db>(
         return Ok(value);
     }
 
-    let repr = AttrFlags::repr(db, loc.parent.into());
+    let repr = db.enum_signature(loc.parent).repr;
     let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed());
 
     let mir_body = db.monomorphized_mir_body(
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
index c0e223380bca..0815e62f87ee 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -17,8 +17,8 @@ use std::fmt;
 
 use hir_def::{
     AdtId, ConstId, EnumId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup,
-    ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, attrs::AttrFlags,
-    db::DefDatabase, hir::Pat, item_tree::FieldsShape, signatures::StaticFlags, src::HasSource,
+    ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, db::DefDatabase, hir::Pat,
+    item_tree::FieldsShape, signatures::StaticFlags, src::HasSource,
 };
 use hir_expand::{
     HirFileId,
@@ -201,7 +201,7 @@ impl<'a> DeclValidator<'a> {
 
             // Don't run the lint on extern "[not Rust]" fn items with the
             // #[no_mangle] attribute.
-            let no_mangle = AttrFlags::query(self.db, func.into()).contains(AttrFlags::NO_MANGLE);
+            let no_mangle = self.db.attrs(func.into()).by_key(sym::no_mangle).exists();
             if no_mangle && data.abi.as_ref().is_some_and(|abi| *abi != sym::Rust) {
                 cov_mark::hit!(extern_func_no_mangle_ignored);
             } else {
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 c70c6b611944..fb942e336e65 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
@@ -2,9 +2,7 @@
 
 use std::{cell::LazyCell, fmt};
 
-use hir_def::{
-    EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId, attrs::AttrFlags,
-};
+use hir_def::{EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId};
 use intern::sym;
 use rustc_pattern_analysis::{
     IndexVec, PatCx, PrivateUninhabitedField,
@@ -120,7 +118,7 @@ impl<'a, 'db> MatchCheckCtx<'a, 'db> {
     /// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`.
     fn is_foreign_non_exhaustive(&self, adt: hir_def::AdtId) -> bool {
         let is_local = adt.krate(self.db) == self.module.krate();
-        !is_local && AttrFlags::query(self.db, adt.into()).contains(AttrFlags::NON_EXHAUSTIVE)
+        !is_local && self.db.attrs(adt.into()).by_key(sym::non_exhaustive).exists()
     }
 
     fn variant_id_for_adt(
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 8ac7ab19cd3b..53524d66a33c 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
@@ -144,7 +144,7 @@ struct UnsafeVisitor<'db> {
     inside_assignment: bool,
     inside_union_destructure: bool,
     callback: &'db mut dyn FnMut(UnsafeDiagnostic),
-    def_target_features: TargetFeatures<'db>,
+    def_target_features: TargetFeatures,
     // FIXME: This needs to be the edition of the span of each call.
     edition: Edition,
     /// On some targets (WASM), calling safe functions with `#[target_feature]` is always safe, even when
@@ -162,7 +162,7 @@ impl<'db> UnsafeVisitor<'db> {
     ) -> Self {
         let resolver = def.resolver(db);
         let def_target_features = match def {
-            DefWithBodyId::FunctionId(func) => TargetFeatures::from_fn(db, func),
+            DefWithBodyId::FunctionId(func) => TargetFeatures::from_attrs(&db.attrs(func.into())),
             _ => TargetFeatures::default(),
         };
         let krate = resolver.module().krate();
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 03ae970acaa7..9891f3f248bd 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -38,7 +38,7 @@ use hir_def::{
     lang_item::{LangItem, LangItemTarget, lang_item},
     layout::Integer,
     resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
-    signatures::{ConstSignature, EnumSignature, StaticSignature},
+    signatures::{ConstSignature, StaticSignature},
     type_ref::{ConstRef, LifetimeRefId, TypeRefId},
 };
 use hir_expand::{mod_path::ModPath, name::Name};
@@ -104,7 +104,7 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc ctx.collect_const(c, &db.const_signature(c)),
         DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)),
         DefWithBodyId::VariantId(v) => {
-            ctx.return_ty = match EnumSignature::variant_body_type(db, v.lookup(db).parent) {
+            ctx.return_ty = match db.enum_signature(v.lookup(db).parent).variant_body_type() {
                 hir_def::layout::IntegerType::Pointer(signed) => match signed {
                     true => ctx.types.isize,
                     false => ctx.types.usize,
@@ -759,7 +759,7 @@ pub(crate) struct InferenceContext<'body, 'db> {
     /// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext
     /// and resolve the path via its methods. This will ensure proper error reporting.
     pub(crate) resolver: Resolver<'db>,
-    target_features: OnceCell<(TargetFeatures<'db>, TargetFeatureIsSafeInTarget)>,
+    target_features: OnceCell<(TargetFeatures, TargetFeatureIsSafeInTarget)>,
     pub(crate) generic_def: GenericDefId,
     table: unify::InferenceTable<'db>,
     /// The traits in scope, disregarding block modules. This is used for caching purposes.
@@ -903,14 +903,14 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
     }
 
     fn target_features<'a>(
-        db: &'db dyn HirDatabase,
-        target_features: &'a OnceCell<(TargetFeatures<'db>, TargetFeatureIsSafeInTarget)>,
+        db: &dyn HirDatabase,
+        target_features: &'a OnceCell<(TargetFeatures, TargetFeatureIsSafeInTarget)>,
         owner: DefWithBodyId,
         krate: Crate,
-    ) -> (&'a TargetFeatures<'db>, TargetFeatureIsSafeInTarget) {
+    ) -> (&'a TargetFeatures, TargetFeatureIsSafeInTarget) {
         let (target_features, target_feature_is_safe) = target_features.get_or_init(|| {
             let target_features = match owner {
-                DefWithBodyId::FunctionId(id) => TargetFeatures::from_fn(db, id),
+                DefWithBodyId::FunctionId(id) => TargetFeatures::from_attrs(&db.attrs(id.into())),
                 _ => TargetFeatures::default(),
             };
             let target_feature_is_safe = match &krate.workspace_data(db).target {
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 9b95eef0e0d6..78889ccb89a2 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
@@ -37,11 +37,11 @@
 
 use hir_def::{
     CallableDefId,
-    attrs::AttrFlags,
     hir::{ExprId, ExprOrPatId},
     lang_item::LangItem,
     signatures::FunctionSignature,
 };
+use intern::sym;
 use rustc_ast_ir::Mutability;
 use rustc_type_ir::{
     BoundVar, TypeAndMut,
@@ -76,7 +76,7 @@ use crate::{
 struct Coerce<'a, 'b, 'db> {
     table: &'a mut InferenceTable<'db>,
     has_errors: &'a mut bool,
-    target_features: &'a mut dyn FnMut() -> (&'b TargetFeatures<'db>, TargetFeatureIsSafeInTarget),
+    target_features: &'a mut dyn FnMut() -> (&'b TargetFeatures, TargetFeatureIsSafeInTarget),
     use_lub: bool,
     /// Determines whether or not allow_two_phase_borrow is set on any
     /// autoref adjustments we create while coercing. We don't want to
@@ -864,14 +864,14 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
                             return Err(TypeError::IntrinsicCast);
                         }
 
-                        let attrs = AttrFlags::query(self.table.db, def_id.into());
-                        if attrs.contains(AttrFlags::RUSTC_FORCE_INLINE) {
+                        let attrs = self.table.db.attrs(def_id.into());
+                        if attrs.by_key(sym::rustc_force_inline).exists() {
                             return Err(TypeError::ForceInlineCast);
                         }
 
-                        if b_hdr.safety.is_safe() && attrs.contains(AttrFlags::HAS_TARGET_FEATURE) {
+                        if b_hdr.safety.is_safe() && attrs.by_key(sym::target_feature).exists() {
                             let fn_target_features =
-                                TargetFeatures::from_fn_no_implications(self.table.db, def_id);
+                                TargetFeatures::from_attrs_no_implications(&attrs);
                             // Allow the coercion if the current function has all the features that would be
                             // needed to call the coercee safely.
                             let (target_features, target_feature_is_safe) =
@@ -1056,7 +1056,7 @@ impl<'db> InferenceContext<'_, 'db> {
 
         let is_force_inline = |ty: Ty<'db>| {
             if let TyKind::FnDef(CallableIdWrapper(CallableDefId::FunctionId(did)), _) = ty.kind() {
-                AttrFlags::query(self.db, did.into()).contains(AttrFlags::RUSTC_FORCE_INLINE)
+                self.db.attrs(did.into()).by_key(sym::rustc_force_inline).exists()
             } else {
                 false
             }
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 a1d99a45287d..efb7244ff637 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
@@ -2365,11 +2365,9 @@ impl<'db> InferenceContext<'_, 'db> {
         };
 
         let data = self.db.function_signature(func);
-        let Some(legacy_const_generics_indices) = data.legacy_const_generics_indices(self.db, func)
-        else {
+        let Some(legacy_const_generics_indices) = &data.legacy_const_generics_indices else {
             return Default::default();
         };
-        let mut legacy_const_generics_indices = Box::<[u32]>::from(legacy_const_generics_indices);
 
         // only use legacy const generics if the param count matches with them
         if data.params.len() + legacy_const_generics_indices.len() != args.len() {
@@ -2378,8 +2376,9 @@ impl<'db> InferenceContext<'_, 'db> {
             } else {
                 // there are more parameters than there should be without legacy
                 // const params; use them
-                legacy_const_generics_indices.sort_unstable();
-                return legacy_const_generics_indices;
+                let mut indices = legacy_const_generics_indices.as_ref().clone();
+                indices.sort();
+                return indices;
             }
         }
 
@@ -2392,8 +2391,9 @@ impl<'db> InferenceContext<'_, 'db> {
             self.infer_expr(args[arg_idx as usize], &expected, ExprIsRead::Yes);
             // FIXME: evaluate and unify with the const
         }
-        legacy_const_generics_indices.sort_unstable();
-        legacy_const_generics_indices
+        let mut indices = legacy_const_generics_indices.as_ref().clone();
+        indices.sort();
+        indices
     }
 
     /// Dereferences a single level of immutable referencing.
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 b650f5c1a16a..fc0b9d30b333 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
@@ -4,7 +4,6 @@ use std::fmt;
 
 use hir_def::{
     AdtId, LocalFieldId, StructId,
-    attrs::AttrFlags,
     layout::{LayoutCalculatorError, LayoutData},
 };
 use la_arena::{Idx, RawIdx};
@@ -175,7 +174,8 @@ pub fn layout_of_ty_query<'db>(
         TyKind::Adt(def, args) => {
             match def.inner().id {
                 hir_def::AdtId::StructId(s) => {
-                    let repr = AttrFlags::repr(db, s.into()).unwrap_or_default();
+                    let data = db.struct_signature(s);
+                    let repr = data.repr.unwrap_or_default();
                     if repr.simd() {
                         return layout_of_simd_ty(db, s, repr.packed(), &args, trait_env, &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 ecebf7935d06..a8f04bf8c132 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
@@ -4,9 +4,9 @@ use std::{cmp, ops::Bound};
 
 use hir_def::{
     AdtId, VariantId,
-    attrs::AttrFlags,
     signatures::{StructFlags, VariantFields},
 };
+use intern::sym;
 use rustc_abi::{Integer, ReprOptions, TargetDataLayout};
 use rustc_index::IndexVec;
 use smallvec::SmallVec;
@@ -44,15 +44,15 @@ pub fn layout_of_adt_query<'db>(
             r.push(handle_variant(s.into(), s.fields(db))?);
             (
                 r,
-                AttrFlags::repr(db, s.into()).unwrap_or_default(),
+                sig.repr.unwrap_or_default(),
                 sig.flags.intersects(StructFlags::IS_UNSAFE_CELL | StructFlags::IS_UNSAFE_PINNED),
             )
         }
         AdtId::UnionId(id) => {
-            let repr = AttrFlags::repr(db, id.into());
+            let data = db.union_signature(id);
             let mut r = SmallVec::new();
             r.push(handle_variant(id.into(), id.fields(db))?);
-            (r, repr.unwrap_or_default(), false)
+            (r, data.repr.unwrap_or_default(), false)
         }
         AdtId::EnumId(e) => {
             let variants = e.enum_variants(db);
@@ -61,7 +61,7 @@ pub fn layout_of_adt_query<'db>(
                 .iter()
                 .map(|&(v, _, _)| handle_variant(v.into(), v.fields(db)))
                 .collect::, _>>()?;
-            (r, AttrFlags::repr(db, e.into()).unwrap_or_default(), false)
+            (r, db.enum_signature(e).repr.unwrap_or_default(), false)
         }
     };
     let variants = variants
@@ -105,12 +105,27 @@ pub(crate) fn layout_of_adt_cycle_result<'db>(
 }
 
 fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound, Bound) {
-    let range = AttrFlags::rustc_layout_scalar_valid_range(db, def);
-    let get = |value| match value {
-        Some(it) => Bound::Included(it),
-        None => Bound::Unbounded,
+    let attrs = db.attrs(def.into());
+    let get = |name| {
+        let attr = attrs.by_key(name).tt_values();
+        for tree in attr {
+            if let Some(it) = tree.iter().next_as_view() {
+                let text = it.to_string().replace('_', "");
+                let (text, base) = match text.as_bytes() {
+                    [b'0', b'x', ..] => (&text[2..], 16),
+                    [b'0', b'o', ..] => (&text[2..], 8),
+                    [b'0', b'b', ..] => (&text[2..], 2),
+                    _ => (&*text, 10),
+                };
+
+                if let Ok(it) = u128::from_str_radix(text, base) {
+                    return Bound::Included(it);
+                }
+            }
+        }
+        Bound::Unbounded
     };
-    (get(range.start), get(range.end))
+    (get(sym::rustc_layout_scalar_valid_range_start), get(sym::rustc_layout_scalar_valid_range_end))
 }
 
 /// Finds the appropriate Integer type and signedness for the given
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 1b5f4595ca3c..cec63566338f 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
@@ -8,11 +8,11 @@ use base_db::Crate;
 use hir_def::{
     AdtId, AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup,
     ModuleId, TraitId, TypeAliasId,
-    attrs::AttrFlags,
     nameres::{DefMap, block_def_map, crate_def_map},
     signatures::{ConstFlags, EnumFlags, FnFlags, StructFlags, TraitFlags, TypeAliasFlags},
 };
 use hir_expand::name::Name;
+use intern::sym;
 use rustc_ast_ir::Mutability;
 use rustc_hash::{FxHashMap, FxHashSet};
 use rustc_type_ir::{
@@ -230,8 +230,7 @@ impl TraitImpls {
                 // FIXME: Reservation impls should be considered during coherence checks. If we are
                 // (ever) to implement coherence checks, this filtering should be done by the trait
                 // solver.
-                if AttrFlags::query(db, impl_id.into()).contains(AttrFlags::RUSTC_RESERVATION_IMPL)
-                {
+                if db.attrs(impl_id.into()).by_key(sym::rustc_reservation_impl).exists() {
                     continue;
                 }
                 let target_trait = match db.impl_trait(impl_id) {
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 d4aab2d09496..4b1adecf8c87 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
@@ -3,11 +3,9 @@
 //!
 use std::cmp::{self, Ordering};
 
-use hir_def::{
-    CrateRootModuleId, attrs::AttrFlags, resolver::HasResolver, signatures::FunctionSignature,
-};
+use hir_def::{CrateRootModuleId, resolver::HasResolver, signatures::FunctionSignature};
 use hir_expand::name::Name;
-use intern::sym;
+use intern::{Symbol, sym};
 use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike, Ty as _};
 use stdx::never;
 
@@ -55,7 +53,7 @@ impl<'db> Evaluator<'db> {
         }
 
         let function_data = self.db.function_signature(def);
-        let attrs = AttrFlags::query(self.db, def.into());
+        let attrs = self.db.attrs(def.into());
         let is_intrinsic = FunctionSignature::is_intrinsic(self.db, def);
 
         if is_intrinsic {
@@ -67,7 +65,7 @@ impl<'db> Evaluator<'db> {
                 locals,
                 span,
                 !function_data.has_body()
-                    || attrs.contains(AttrFlags::RUSTC_INTRINSIC_MUST_BE_OVERRIDDEN),
+                    || attrs.by_key(sym::rustc_intrinsic_must_be_overridden).exists(),
             );
         }
         let is_extern_c = match def.lookup(self.db).container {
@@ -87,13 +85,18 @@ impl<'db> Evaluator<'db> {
                 .map(|()| true);
         }
 
-        if attrs.intersects(
-            AttrFlags::RUSTC_ALLOCATOR
-                | AttrFlags::RUSTC_DEALLOCATOR
-                | AttrFlags::RUSTC_REALLOCATOR
-                | AttrFlags::RUSTC_ALLOCATOR_ZEROED,
-        ) {
-            self.exec_alloc_fn(attrs, args, destination)?;
+        let alloc_fn =
+            attrs.iter().filter_map(|it| it.path().as_ident()).map(|it| it.symbol()).find(|it| {
+                [
+                    &sym::rustc_allocator,
+                    &sym::rustc_deallocator,
+                    &sym::rustc_reallocator,
+                    &sym::rustc_allocator_zeroed,
+                ]
+                .contains(it)
+            });
+        if let Some(alloc_fn) = alloc_fn {
+            self.exec_alloc_fn(alloc_fn, args, destination)?;
             return Ok(true);
         }
         if let Some(it) = self.detect_lang_function(def) {
@@ -242,14 +245,12 @@ impl<'db> Evaluator<'db> {
 
     fn exec_alloc_fn(
         &mut self,
-        alloc_fn: AttrFlags,
+        alloc_fn: &Symbol,
         args: &[IntervalAndTy<'db>],
         destination: Interval,
     ) -> Result<'db, ()> {
         match alloc_fn {
-            _ if alloc_fn
-                .intersects(AttrFlags::RUSTC_ALLOCATOR_ZEROED | AttrFlags::RUSTC_ALLOCATOR) =>
-            {
+            _ if *alloc_fn == sym::rustc_allocator_zeroed || *alloc_fn == sym::rustc_allocator => {
                 let [size, align] = args else {
                     return Err(MirEvalError::InternalError(
                         "rustc_allocator args are not provided".into(),
@@ -260,8 +261,8 @@ impl<'db> Evaluator<'db> {
                 let result = self.heap_allocate(size, align)?;
                 destination.write_from_bytes(self, &result.to_bytes())?;
             }
-            _ if alloc_fn.contains(AttrFlags::RUSTC_DEALLOCATOR) => { /* no-op for now */ }
-            _ if alloc_fn.contains(AttrFlags::RUSTC_REALLOCATOR) => {
+            _ if *alloc_fn == sym::rustc_deallocator => { /* no-op for now */ }
+            _ if *alloc_fn == sym::rustc_reallocator => {
                 let [ptr, old_size, align, new_size] = args else {
                     return Err(MirEvalError::InternalError(
                         "rustc_allocator args are not provided".into(),
@@ -287,14 +288,14 @@ impl<'db> Evaluator<'db> {
 
     fn detect_lang_function(&self, def: FunctionId) -> Option {
         use LangItem::*;
-        let attrs = AttrFlags::query(self.db, def.into());
+        let attrs = self.db.attrs(def.into());
 
-        if attrs.contains(AttrFlags::RUSTC_CONST_PANIC_STR) {
+        if attrs.by_key(sym::rustc_const_panic_str).exists() {
             // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE.
             return Some(LangItem::BeginPanic);
         }
 
-        let candidate = attrs.lang_item_with_attrs(self.db, def.into())?;
+        let candidate = attrs.lang_item()?;
         // We want to execute these functions with special logic
         // `PanicFmt` is not detected here as it's redirected later.
         if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
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 7f7d596be9fb..ce8b76837a3c 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
@@ -9,7 +9,6 @@ use base_db::Crate;
 use hir_def::{
     AdtId, AttrDefId, BlockId, CallableDefId, EnumVariantId, ItemContainerId, StructId, UnionId,
     VariantId,
-    attrs::AttrFlags,
     lang_item::LangItem,
     signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags},
 };
@@ -468,28 +467,28 @@ impl AdtDef {
 
                 let variants = vec![(VariantIdx(0), VariantDef::Struct(struct_id))];
 
-                let data_repr = data.repr(db, struct_id);
+                let mut repr = ReprOptions::default();
+                repr.align = data.repr.and_then(|r| r.align);
+                repr.pack = data.repr.and_then(|r| r.pack);
+                repr.int = data.repr.and_then(|r| r.int);
+
                 let mut repr_flags = ReprFlags::empty();
                 if flags.is_box {
                     repr_flags.insert(ReprFlags::IS_LINEAR);
                 }
-                if data_repr.is_some_and(|r| r.c()) {
+                if data.repr.is_some_and(|r| r.c()) {
                     repr_flags.insert(ReprFlags::IS_C);
                 }
-                if data_repr.is_some_and(|r| r.simd()) {
+                if data.repr.is_some_and(|r| r.simd()) {
                     repr_flags.insert(ReprFlags::IS_SIMD);
                 }
-                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()
-                };
+                repr.flags = repr_flags;
 
                 (flags, variants, repr)
             }
             AdtId::UnionId(union_id) => {
+                let data = db.union_signature(union_id);
+
                 let flags = AdtFlags {
                     is_enum: false,
                     is_union: true,
@@ -502,24 +501,22 @@ impl AdtDef {
 
                 let variants = vec![(VariantIdx(0), VariantDef::Union(union_id))];
 
-                let data_repr = AttrFlags::repr(db, union_id.into());
+                let mut repr = ReprOptions::default();
+                repr.align = data.repr.and_then(|r| r.align);
+                repr.pack = data.repr.and_then(|r| r.pack);
+                repr.int = data.repr.and_then(|r| r.int);
+
                 let mut repr_flags = ReprFlags::empty();
                 if flags.is_box {
                     repr_flags.insert(ReprFlags::IS_LINEAR);
                 }
-                if data_repr.is_some_and(|r| r.c()) {
+                if data.repr.is_some_and(|r| r.c()) {
                     repr_flags.insert(ReprFlags::IS_C);
                 }
-                if data_repr.is_some_and(|r| r.simd()) {
+                if data.repr.is_some_and(|r| r.simd()) {
                     repr_flags.insert(ReprFlags::IS_SIMD);
                 }
-                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()
-                };
+                repr.flags = repr_flags;
 
                 (flags, variants, repr)
             }
@@ -543,26 +540,24 @@ impl AdtDef {
                     .map(|(idx, v)| (idx, VariantDef::Enum(v.0)))
                     .collect();
 
-                let data_repr = AttrFlags::repr(db, enum_id.into());
+                let data = db.enum_signature(enum_id);
+
+                let mut repr = ReprOptions::default();
+                repr.align = data.repr.and_then(|r| r.align);
+                repr.pack = data.repr.and_then(|r| r.pack);
+                repr.int = data.repr.and_then(|r| r.int);
 
                 let mut repr_flags = ReprFlags::empty();
                 if flags.is_box {
                     repr_flags.insert(ReprFlags::IS_LINEAR);
                 }
-                if data_repr.is_some_and(|r| r.c()) {
+                if data.repr.is_some_and(|r| r.c()) {
                     repr_flags.insert(ReprFlags::IS_C);
                 }
-                if data_repr.is_some_and(|r| r.simd()) {
+                if data.repr.is_some_and(|r| r.simd()) {
                     repr_flags.insert(ReprFlags::IS_SIMD);
                 }
-
-                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()
-                };
+                repr.flags = repr_flags;
 
                 (flags, variants, repr)
             }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs
index 2bd675ba124e..0a8ed2cf0cab 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs
@@ -1,35 +1,31 @@
 //! Stuff for handling `#[target_feature]` (needed for unsafe check).
 
-use std::borrow::Cow;
 use std::sync::LazyLock;
 
-use hir_def::FunctionId;
-use hir_def::attrs::AttrFlags;
-use intern::Symbol;
+use hir_def::attr::Attrs;
+use hir_def::tt;
+use intern::{Symbol, sym};
 use rustc_hash::{FxHashMap, FxHashSet};
 
-use crate::db::HirDatabase;
-
 #[derive(Debug, Default, Clone)]
-pub struct TargetFeatures<'db> {
-    pub(crate) enabled: Cow<'db, FxHashSet>,
+pub struct TargetFeatures {
+    pub(crate) enabled: FxHashSet,
 }
 
-impl<'db> TargetFeatures<'db> {
-    pub fn from_fn(db: &'db dyn HirDatabase, owner: FunctionId) -> Self {
-        let mut result = TargetFeatures::from_fn_no_implications(db, owner);
+impl TargetFeatures {
+    pub fn from_attrs(attrs: &Attrs) -> Self {
+        let mut result = TargetFeatures::from_attrs_no_implications(attrs);
         result.expand_implications();
         result
     }
 
     fn expand_implications(&mut self) {
         let all_implications = LazyLock::force(&TARGET_FEATURE_IMPLICATIONS);
-        let enabled = self.enabled.to_mut();
-        let mut queue = enabled.iter().cloned().collect::>();
+        let mut queue = self.enabled.iter().cloned().collect::>();
         while let Some(feature) = queue.pop() {
             if let Some(implications) = all_implications.get(&feature) {
                 for implication in implications {
-                    if enabled.insert(implication.clone()) {
+                    if self.enabled.insert(implication.clone()) {
                         queue.push(implication.clone());
                     }
                 }
@@ -38,9 +34,25 @@ impl<'db> TargetFeatures<'db> {
     }
 
     /// Retrieves the target features from the attributes, and does not expand the target features implied by them.
-    pub(crate) fn from_fn_no_implications(db: &'db dyn HirDatabase, owner: FunctionId) -> Self {
-        let enabled = AttrFlags::target_features(db, owner);
-        Self { enabled: Cow::Borrowed(enabled) }
+    pub(crate) fn from_attrs_no_implications(attrs: &Attrs) -> Self {
+        let enabled = attrs
+            .by_key(sym::target_feature)
+            .tt_values()
+            .filter_map(|tt| match tt.token_trees().flat_tokens() {
+                [
+                    tt::TokenTree::Leaf(tt::Leaf::Ident(enable_ident)),
+                    tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. })),
+                    tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
+                        kind: tt::LitKind::Str,
+                        symbol: features,
+                        ..
+                    })),
+                ] if enable_ident.sym == sym::enable => Some(features),
+                _ => None,
+            })
+            .flat_map(|features| features.as_str().split(',').map(Symbol::intern))
+            .collect();
+        Self { enabled }
     }
 }
 
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 50625c1c26d5..bc4701970c76 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
@@ -31,6 +31,7 @@ fn foo() -> i32 {
         &[("infer_shim", 1)],
         expect_test::expect![[r#"
             [
+                "source_root_crates_shim",
                 "crate_local_def_map",
                 "file_item_tree_query",
                 "ast_id_map_shim",
@@ -39,7 +40,7 @@ fn foo() -> i32 {
                 "infer_shim",
                 "function_signature_shim",
                 "function_signature_with_source_map_shim",
-                "AttrFlags::query_",
+                "attrs_shim",
                 "body_shim",
                 "body_with_source_map_shim",
                 "trait_environment_shim",
@@ -78,7 +79,7 @@ fn foo() -> i32 {
                 "ast_id_map_shim",
                 "file_item_tree_query",
                 "real_span_map_shim",
-                "AttrFlags::query_",
+                "attrs_shim",
                 "function_signature_with_source_map_shim",
                 "function_signature_shim",
                 "body_with_source_map_shim",
@@ -117,6 +118,7 @@ fn baz() -> i32 {
         &[("infer_shim", 3)],
         expect_test::expect![[r#"
             [
+                "source_root_crates_shim",
                 "crate_local_def_map",
                 "file_item_tree_query",
                 "ast_id_map_shim",
@@ -125,7 +127,7 @@ fn baz() -> i32 {
                 "infer_shim",
                 "function_signature_shim",
                 "function_signature_with_source_map_shim",
-                "AttrFlags::query_",
+                "attrs_shim",
                 "body_shim",
                 "body_with_source_map_shim",
                 "trait_environment_shim",
@@ -133,8 +135,8 @@ fn baz() -> i32 {
                 "expr_scopes_shim",
                 "lang_item",
                 "crate_lang_items",
-                "AttrFlags::query_",
-                "AttrFlags::query_",
+                "attrs_shim",
+                "attrs_shim",
                 "infer_shim",
                 "function_signature_shim",
                 "function_signature_with_source_map_shim",
@@ -187,13 +189,13 @@ fn baz() -> i32 {
                 "ast_id_map_shim",
                 "file_item_tree_query",
                 "real_span_map_shim",
-                "AttrFlags::query_",
+                "attrs_shim",
                 "function_signature_with_source_map_shim",
                 "function_signature_shim",
                 "body_with_source_map_shim",
                 "body_shim",
-                "AttrFlags::query_",
-                "AttrFlags::query_",
+                "attrs_shim",
+                "attrs_shim",
                 "function_signature_with_source_map_shim",
                 "function_signature_shim",
                 "body_with_source_map_shim",
@@ -233,6 +235,7 @@ $0",
         &[("trait_impls_in_crate_shim", 1)],
         expect_test::expect![[r#"
             [
+                "source_root_crates_shim",
                 "crate_local_def_map",
                 "file_item_tree_query",
                 "ast_id_map_shim",
@@ -304,6 +307,7 @@ $0",
         &[("trait_impls_in_crate_shim", 1)],
         expect_test::expect![[r#"
             [
+                "source_root_crates_shim",
                 "crate_local_def_map",
                 "file_item_tree_query",
                 "ast_id_map_shim",
@@ -376,6 +380,7 @@ $0",
         &[("trait_impls_in_crate_shim", 1)],
         expect_test::expect![[r#"
             [
+                "source_root_crates_shim",
                 "crate_local_def_map",
                 "file_item_tree_query",
                 "ast_id_map_shim",
@@ -449,6 +454,7 @@ $0",
         &[("trait_impls_in_crate_shim", 1)],
         expect_test::expect![[r#"
             [
+                "source_root_crates_shim",
                 "crate_local_def_map",
                 "file_item_tree_query",
                 "ast_id_map_shim",
@@ -497,14 +503,14 @@ impl SomeStruct {
                 "real_span_map_shim",
                 "crate_local_def_map",
                 "trait_impls_in_crate_shim",
-                "AttrFlags::query_",
+                "attrs_shim",
                 "impl_trait_with_diagnostics_shim",
                 "impl_signature_shim",
                 "impl_signature_with_source_map_shim",
                 "impl_self_ty_with_diagnostics_shim",
                 "struct_signature_shim",
                 "struct_signature_with_source_map_shim",
-                "AttrFlags::query_",
+                "attrs_shim",
             ]
         "#]],
     );
@@ -554,6 +560,7 @@ fn main() {
         &[("trait_solve_shim", 0)],
         expect_test::expect![[r#"
             [
+                "source_root_crates_shim",
                 "crate_local_def_map",
                 "file_item_tree_query",
                 "ast_id_map_shim",
@@ -562,22 +569,22 @@ fn main() {
                 "TraitItems::query_with_diagnostics_",
                 "body_shim",
                 "body_with_source_map_shim",
-                "AttrFlags::query_",
+                "attrs_shim",
                 "ImplItems::of_",
                 "infer_shim",
                 "trait_signature_shim",
                 "trait_signature_with_source_map_shim",
-                "AttrFlags::query_",
+                "attrs_shim",
                 "function_signature_shim",
                 "function_signature_with_source_map_shim",
-                "AttrFlags::query_",
+                "attrs_shim",
                 "body_shim",
                 "body_with_source_map_shim",
                 "trait_environment_shim",
                 "lang_item",
                 "crate_lang_items",
-                "AttrFlags::query_",
-                "AttrFlags::query_",
+                "attrs_shim",
+                "attrs_shim",
                 "generic_predicates_shim",
                 "return_type_impl_traits_shim",
                 "infer_shim",
@@ -659,22 +666,22 @@ fn main() {
                 "crate_local_def_map",
                 "TraitItems::query_with_diagnostics_",
                 "body_with_source_map_shim",
-                "AttrFlags::query_",
+                "attrs_shim",
                 "body_shim",
                 "ImplItems::of_",
                 "infer_shim",
-                "AttrFlags::query_",
+                "attrs_shim",
                 "trait_signature_with_source_map_shim",
-                "AttrFlags::query_",
+                "attrs_shim",
                 "function_signature_with_source_map_shim",
                 "function_signature_shim",
                 "body_with_source_map_shim",
                 "body_shim",
                 "trait_environment_shim",
                 "crate_lang_items",
-                "AttrFlags::query_",
-                "AttrFlags::query_",
-                "AttrFlags::query_",
+                "attrs_shim",
+                "attrs_shim",
+                "attrs_shim",
                 "generic_predicates_shim",
                 "return_type_impl_traits_shim",
                 "infer_shim",
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
index 41dc4dc53375..ca5e33fe6ad0 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
@@ -9,7 +9,6 @@ use base_db::{
 };
 use hir_def::{
     EnumId, EnumVariantId, FunctionId, Lookup, TraitId,
-    attrs::AttrFlags,
     db::DefDatabase,
     hir::generics::WherePredicate,
     lang_item::LangItem,
@@ -120,7 +119,7 @@ pub fn target_feature_is_safe_in_target(target: &TargetData) -> TargetFeatureIsS
 pub fn is_fn_unsafe_to_call(
     db: &dyn HirDatabase,
     func: FunctionId,
-    caller_target_features: &TargetFeatures<'_>,
+    caller_target_features: &TargetFeatures,
     call_edition: Edition,
     target_feature_is_safe: TargetFeatureIsSafeInTarget,
 ) -> Unsafety {
@@ -131,7 +130,8 @@ pub fn is_fn_unsafe_to_call(
 
     if data.has_target_feature() && target_feature_is_safe == TargetFeatureIsSafeInTarget::No {
         // RFC 2396 .
-        let callee_target_features = TargetFeatures::from_fn_no_implications(db, func);
+        let callee_target_features =
+            TargetFeatures::from_attrs_no_implications(&db.attrs(func.into()));
         if !caller_target_features.enabled.is_superset(&callee_target_features.enabled) {
             return Unsafety::Unsafe;
         }
@@ -152,7 +152,7 @@ pub fn is_fn_unsafe_to_call(
             if is_intrinsic_block {
                 // legacy intrinsics
                 // extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute
-                if AttrFlags::query(db, func.into()).contains(AttrFlags::RUSTC_SAFE_INTRINSIC) {
+                if db.attrs(func.into()).by_key(sym::rustc_safe_intrinsic).exists() {
                     Unsafety::Safe
                 } else {
                     Unsafety::Unsafe
diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
index 3376c51fe5c9..147f1b8653be 100644
--- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
@@ -2,12 +2,9 @@
 
 use std::ops::ControlFlow;
 
-use cfg::CfgExpr;
-use either::Either;
 use hir_def::{
-    AssocItemId, AttrDefId, FieldId, InternedModuleId, LifetimeParamId, ModuleDefId,
-    TypeOrConstParamId,
-    attrs::{AttrFlags, Docs, IsInnerDoc},
+    AssocItemId, AttrDefId, ModuleDefId,
+    attr::AttrsWithOwner,
     expr_store::path::Path,
     item_scope::ItemInNs,
     per_ns::Namespace,
@@ -18,7 +15,6 @@ use hir_expand::{
     name::Name,
 };
 use hir_ty::{db::HirDatabase, method_resolution};
-use intern::Symbol;
 
 use crate::{
     Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl,
@@ -26,161 +22,28 @@ use crate::{
     Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
 };
 
-#[derive(Debug, Clone, Copy)]
-pub enum AttrsOwner {
-    AttrDef(AttrDefId),
-    Field(FieldId),
-    LifetimeParam(LifetimeParamId),
-    TypeOrConstParam(TypeOrConstParamId),
-}
-
-impl AttrsOwner {
-    #[inline]
-    fn attr_def(&self) -> Option {
-        match self {
-            AttrsOwner::AttrDef(it) => Some(*it),
-            _ => None,
-        }
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct AttrsWithOwner {
-    pub(crate) attrs: AttrFlags,
-    owner: AttrsOwner,
-}
-
-impl AttrsWithOwner {
-    fn new(db: &dyn HirDatabase, owner: AttrDefId) -> Self {
-        Self { attrs: AttrFlags::query(db, owner), owner: AttrsOwner::AttrDef(owner) }
-    }
-
-    fn new_field(db: &dyn HirDatabase, owner: FieldId) -> Self {
-        Self { attrs: AttrFlags::query_field(db, owner), owner: AttrsOwner::Field(owner) }
-    }
-
-    fn new_lifetime_param(db: &dyn HirDatabase, owner: LifetimeParamId) -> Self {
-        Self {
-            attrs: AttrFlags::query_lifetime_param(db, owner),
-            owner: AttrsOwner::LifetimeParam(owner),
-        }
-    }
-    fn new_type_or_const_param(db: &dyn HirDatabase, owner: TypeOrConstParamId) -> Self {
-        Self {
-            attrs: AttrFlags::query_type_or_const_param(db, owner),
-            owner: AttrsOwner::TypeOrConstParam(owner),
-        }
-    }
-
-    #[inline]
-    pub fn is_unstable(&self) -> bool {
-        self.attrs.contains(AttrFlags::IS_UNSTABLE)
-    }
-
-    #[inline]
-    pub fn is_macro_export(&self) -> bool {
-        self.attrs.contains(AttrFlags::IS_MACRO_EXPORT)
-    }
-
-    #[inline]
-    pub fn is_doc_notable_trait(&self) -> bool {
-        self.attrs.contains(AttrFlags::IS_DOC_NOTABLE_TRAIT)
-    }
-
-    #[inline]
-    pub fn is_doc_hidden(&self) -> bool {
-        self.attrs.contains(AttrFlags::IS_DOC_HIDDEN)
-    }
-
-    #[inline]
-    pub fn is_deprecated(&self) -> bool {
-        self.attrs.contains(AttrFlags::IS_DEPRECATED)
-    }
-
-    #[inline]
-    pub fn is_non_exhaustive(&self) -> bool {
-        self.attrs.contains(AttrFlags::NON_EXHAUSTIVE)
-    }
-
-    #[inline]
-    pub fn is_test(&self) -> bool {
-        self.attrs.contains(AttrFlags::IS_TEST)
-    }
-
-    #[inline]
-    pub fn lang(&self, db: &dyn HirDatabase) -> Option<&'static str> {
-        self.owner
-            .attr_def()
-            .and_then(|owner| self.attrs.lang_item_with_attrs(db, owner))
-            .map(|lang| lang.name())
-    }
-
-    #[inline]
-    pub fn doc_aliases<'db>(&self, db: &'db dyn HirDatabase) -> &'db [Symbol] {
-        let owner = match self.owner {
-            AttrsOwner::AttrDef(it) => Either::Left(it),
-            AttrsOwner::Field(it) => Either::Right(it),
-            AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return &[],
-        };
-        self.attrs.doc_aliases(db, owner)
-    }
-
-    #[inline]
-    pub fn cfgs<'db>(&self, db: &'db dyn HirDatabase) -> Option<&'db CfgExpr> {
-        let owner = match self.owner {
-            AttrsOwner::AttrDef(it) => Either::Left(it),
-            AttrsOwner::Field(it) => Either::Right(it),
-            AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return None,
-        };
-        self.attrs.cfgs(db, owner)
-    }
-
-    #[inline]
-    pub fn hir_docs<'db>(&self, db: &'db dyn HirDatabase) -> Option<&'db Docs> {
-        match self.owner {
-            AttrsOwner::AttrDef(it) => AttrFlags::docs(db, it).as_deref(),
-            AttrsOwner::Field(it) => AttrFlags::field_docs(db, it),
-            AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => None,
-        }
-    }
-}
-
-pub trait HasAttrs: Sized {
-    #[inline]
-    fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
-        match self.attr_id(db) {
-            AttrsOwner::AttrDef(it) => AttrsWithOwner::new(db, it),
-            AttrsOwner::Field(it) => AttrsWithOwner::new_field(db, it),
-            AttrsOwner::LifetimeParam(it) => AttrsWithOwner::new_lifetime_param(db, it),
-            AttrsOwner::TypeOrConstParam(it) => AttrsWithOwner::new_type_or_const_param(db, it),
-        }
-    }
-
+pub trait HasAttrs {
+    fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner;
     #[doc(hidden)]
-    fn attr_id(self, db: &dyn HirDatabase) -> AttrsOwner;
-
-    #[inline]
-    fn hir_docs(self, db: &dyn HirDatabase) -> Option<&Docs> {
-        match self.attr_id(db) {
-            AttrsOwner::AttrDef(it) => AttrFlags::docs(db, it).as_deref(),
-            AttrsOwner::Field(it) => AttrFlags::field_docs(db, it),
-            AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => None,
-        }
-    }
+    fn attr_id(self) -> AttrDefId;
 }
 
 macro_rules! impl_has_attrs {
     ($(($def:ident, $def_id:ident),)*) => {$(
         impl HasAttrs for $def {
-            #[inline]
-            fn attr_id(self, _db: &dyn HirDatabase) -> AttrsOwner {
-                AttrsOwner::AttrDef(AttrDefId::$def_id(self.into()))
+            fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
+                let def = AttrDefId::$def_id(self.into());
+                AttrsWithOwner::new(db, def)
+            }
+            fn attr_id(self) -> AttrDefId {
+                AttrDefId::$def_id(self.into())
             }
         }
     )*};
 }
 
 impl_has_attrs![
+    (Field, FieldId),
     (Variant, EnumVariantId),
     (Static, StaticId),
     (Const, ConstId),
@@ -189,6 +52,8 @@ impl_has_attrs![
     (Macro, MacroId),
     (Function, FunctionId),
     (Adt, AdtId),
+    (Module, ModuleId),
+    (GenericParam, GenericParamId),
     (Impl, ImplId),
     (ExternCrateDecl, ExternCrateId),
 ];
@@ -196,9 +61,11 @@ impl_has_attrs![
 macro_rules! impl_has_attrs_enum {
     ($($variant:ident),* for $enum:ident) => {$(
         impl HasAttrs for $variant {
-            #[inline]
-            fn attr_id(self, db: &dyn HirDatabase) -> AttrsOwner {
-                $enum::$variant(self).attr_id(db)
+            fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
+                $enum::$variant(self).attrs(db)
+            }
+            fn attr_id(self) -> AttrDefId {
+                $enum::$variant(self).attr_id()
             }
         }
     )*};
@@ -207,46 +74,30 @@ macro_rules! impl_has_attrs_enum {
 impl_has_attrs_enum![Struct, Union, Enum for Adt];
 impl_has_attrs_enum![TypeParam, ConstParam, LifetimeParam for GenericParam];
 
-impl HasAttrs for Module {
-    #[inline]
-    fn attr_id(self, db: &dyn HirDatabase) -> AttrsOwner {
-        AttrsOwner::AttrDef(AttrDefId::ModuleId(InternedModuleId::new(db, self.id)))
-    }
-}
-
-impl HasAttrs for GenericParam {
-    #[inline]
-    fn attr_id(self, _db: &dyn HirDatabase) -> AttrsOwner {
+impl HasAttrs for AssocItem {
+    fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
         match self {
-            GenericParam::TypeParam(it) => AttrsOwner::TypeOrConstParam(it.merge().into()),
-            GenericParam::ConstParam(it) => AttrsOwner::TypeOrConstParam(it.merge().into()),
-            GenericParam::LifetimeParam(it) => AttrsOwner::LifetimeParam(it.into()),
+            AssocItem::Function(it) => it.attrs(db),
+            AssocItem::Const(it) => it.attrs(db),
+            AssocItem::TypeAlias(it) => it.attrs(db),
         }
     }
-}
-
-impl HasAttrs for AssocItem {
-    #[inline]
-    fn attr_id(self, db: &dyn HirDatabase) -> AttrsOwner {
+    fn attr_id(self) -> AttrDefId {
         match self {
-            AssocItem::Function(it) => it.attr_id(db),
-            AssocItem::Const(it) => it.attr_id(db),
-            AssocItem::TypeAlias(it) => it.attr_id(db),
+            AssocItem::Function(it) => it.attr_id(),
+            AssocItem::Const(it) => it.attr_id(),
+            AssocItem::TypeAlias(it) => it.attr_id(),
         }
     }
 }
 
 impl HasAttrs for crate::Crate {
-    #[inline]
-    fn attr_id(self, db: &dyn HirDatabase) -> AttrsOwner {
-        self.root_module().attr_id(db)
+    fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
+        let def = AttrDefId::ModuleId(self.root_module().id);
+        AttrsWithOwner::new(db, def)
     }
-}
-
-impl HasAttrs for Field {
-    #[inline]
-    fn attr_id(self, _db: &dyn HirDatabase) -> AttrsOwner {
-        AttrsOwner::Field(self.into())
+    fn attr_id(self) -> AttrDefId {
+        AttrDefId::ModuleId(self.root_module().id)
     }
 }
 
@@ -256,22 +107,21 @@ pub fn resolve_doc_path_on(
     def: impl HasAttrs + Copy,
     link: &str,
     ns: Option,
-    is_inner_doc: IsInnerDoc,
+    is_inner_doc: bool,
 ) -> Option {
-    resolve_doc_path_on_(db, link, def.attr_id(db), ns, is_inner_doc)
+    resolve_doc_path_on_(db, link, def.attr_id(), ns, is_inner_doc)
 }
 
 fn resolve_doc_path_on_(
     db: &dyn HirDatabase,
     link: &str,
-    attr_id: AttrsOwner,
+    attr_id: AttrDefId,
     ns: Option,
-    is_inner_doc: IsInnerDoc,
+    is_inner_doc: bool,
 ) -> Option {
     let resolver = match attr_id {
-        AttrsOwner::AttrDef(AttrDefId::ModuleId(it)) => {
-            let it = it.loc(db);
-            if is_inner_doc.yes() {
+        AttrDefId::ModuleId(it) => {
+            if is_inner_doc {
                 it.resolver(db)
             } else if let Some(parent) = Module::from(it).parent(db) {
                 parent.id.resolver(db)
@@ -279,20 +129,20 @@ fn resolve_doc_path_on_(
                 it.resolver(db)
             }
         }
-        AttrsOwner::AttrDef(AttrDefId::AdtId(it)) => it.resolver(db),
-        AttrsOwner::AttrDef(AttrDefId::FunctionId(it)) => it.resolver(db),
-        AttrsOwner::AttrDef(AttrDefId::EnumVariantId(it)) => it.resolver(db),
-        AttrsOwner::AttrDef(AttrDefId::StaticId(it)) => it.resolver(db),
-        AttrsOwner::AttrDef(AttrDefId::ConstId(it)) => it.resolver(db),
-        AttrsOwner::AttrDef(AttrDefId::TraitId(it)) => it.resolver(db),
-        AttrsOwner::AttrDef(AttrDefId::TypeAliasId(it)) => it.resolver(db),
-        AttrsOwner::AttrDef(AttrDefId::ImplId(it)) => it.resolver(db),
-        AttrsOwner::AttrDef(AttrDefId::ExternBlockId(it)) => it.resolver(db),
-        AttrsOwner::AttrDef(AttrDefId::UseId(it)) => it.resolver(db),
-        AttrsOwner::AttrDef(AttrDefId::MacroId(it)) => it.resolver(db),
-        AttrsOwner::AttrDef(AttrDefId::ExternCrateId(it)) => it.resolver(db),
-        AttrsOwner::Field(it) => it.parent.resolver(db),
-        AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return None,
+        AttrDefId::FieldId(it) => it.parent.resolver(db),
+        AttrDefId::AdtId(it) => it.resolver(db),
+        AttrDefId::FunctionId(it) => it.resolver(db),
+        AttrDefId::EnumVariantId(it) => it.resolver(db),
+        AttrDefId::StaticId(it) => it.resolver(db),
+        AttrDefId::ConstId(it) => it.resolver(db),
+        AttrDefId::TraitId(it) => it.resolver(db),
+        AttrDefId::TypeAliasId(it) => it.resolver(db),
+        AttrDefId::ImplId(it) => it.resolver(db),
+        AttrDefId::ExternBlockId(it) => it.resolver(db),
+        AttrDefId::UseId(it) => it.resolver(db),
+        AttrDefId::MacroId(it) => it.resolver(db),
+        AttrDefId::ExternCrateId(it) => it.resolver(db),
+        AttrDefId::GenericParamId(_) => return None,
     };
 
     let mut modpath = doc_modpath_from_str(link)?;
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index 6ef6ea272e58..a6d67e8fb4fb 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -153,7 +153,8 @@ pub struct UnresolvedImport {
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct UnresolvedMacroCall {
-    pub range: InFile,
+    pub macro_call: InFile,
+    pub precise_location: Option,
     pub path: ModPath,
     pub is_bang: bool,
 }
@@ -184,7 +185,8 @@ pub struct InactiveCode {
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct MacroError {
-    pub range: InFile,
+    pub node: InFile,
+    pub precise_location: Option,
     pub message: String,
     pub error: bool,
     pub kind: &'static str,
@@ -192,7 +194,8 @@ pub struct MacroError {
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct MacroExpansionParseError {
-    pub range: InFile,
+    pub node: InFile,
+    pub precise_location: Option,
     pub errors: Arc<[SyntaxError]>,
 }
 
@@ -210,12 +213,12 @@ pub struct UnimplementedBuiltinMacro {
 
 #[derive(Debug)]
 pub struct InvalidDeriveTarget {
-    pub range: InFile,
+    pub node: InFile,
 }
 
 #[derive(Debug)]
 pub struct MalformedDerive {
-    pub range: InFile,
+    pub node: InFile,
 }
 
 #[derive(Debug)]
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 1b24aad103b4..941890312317 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -45,12 +45,11 @@ use arrayvec::ArrayVec;
 use base_db::{CrateDisplayName, CrateOrigin, LangCrateOrigin};
 use either::Either;
 use hir_def::{
-    AdtId, AssocItemId, AssocItemLoc, CallableDefId, ConstId, ConstParamId, CrateRootModuleId,
-    DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId,
-    GenericParamId, HasModule, ImplId, InternedModuleId, ItemContainerId, LifetimeParamId,
+    AdtId, AssocItemId, AssocItemLoc, AttrDefId, CallableDefId, ConstId, ConstParamId,
+    CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId,
+    FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
     LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, SyntheticSyntax,
     TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
-    attrs::AttrFlags,
     expr_store::{ExpressionStoreDiagnostics, ExpressionStoreSourceMap},
     hir::{
         BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat,
@@ -64,12 +63,13 @@ use hir_def::{
     },
     per_ns::PerNs,
     resolver::{HasResolver, Resolver},
-    signatures::{EnumSignature, ImplFlags, StaticFlags, StructFlags, TraitFlags, VariantFields},
+    signatures::{ImplFlags, StaticFlags, StructFlags, TraitFlags, VariantFields},
     src::HasSource as _,
     visibility::visibility_from_ast,
 };
 use hir_expand::{
-    AstId, MacroCallKind, RenderedExpandError, ValueResult, proc_macro::ProcMacroKind,
+    AstId, MacroCallKind, RenderedExpandError, ValueResult, attrs::collect_attrs,
+    proc_macro::ProcMacroKind,
 };
 use hir_ty::{
     TraitEnvironment, TyDefId, TyLoweringDiagnostic, ValueTyDefId, all_super_traits, autoderef,
@@ -98,8 +98,8 @@ use smallvec::SmallVec;
 use span::{AstIdNode, Edition, FileId};
 use stdx::{format_to, impl_from, never};
 use syntax::{
-    AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, ToSmolStr,
-    ast::{self, HasName, HasVisibility as _},
+    AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, T, TextRange, ToSmolStr,
+    ast::{self, HasAttrs as _, HasName, HasVisibility as _},
     format_smolstr,
 };
 use triomphe::{Arc, ThinArc};
@@ -107,7 +107,7 @@ use triomphe::{Arc, ThinArc};
 use crate::db::{DefDatabase, HirDatabase};
 
 pub use crate::{
-    attrs::{AttrsWithOwner, HasAttrs, resolve_doc_path_on},
+    attrs::{HasAttrs, resolve_doc_path_on},
     diagnostics::*,
     has_source::HasSource,
     semantics::{
@@ -130,7 +130,7 @@ pub use {
     hir_def::{
         Complete,
         FindPathConfig,
-        attrs::{Docs, IsInnerDoc},
+        attr::{AttrSourceMap, Attrs, AttrsWithOwner},
         find_path::PrefixKind,
         import_map,
         lang_item::LangItem,
@@ -144,6 +144,7 @@ pub use {
     },
     hir_expand::{
         EditionedFileId, ExpandResult, HirFileId, MacroCallId, MacroKind,
+        attrs::{Attr, AttrId},
         change::ChangeWithProcMacros,
         files::{
             FilePosition, FilePositionWrapper, FileRange, FileRangeWrapper, HirFilePosition,
@@ -290,10 +291,11 @@ impl Crate {
     }
 
     /// Try to get the root URL of the documentation of a crate.
-    pub fn get_html_root_url(self, db: &dyn HirDatabase) -> Option {
+    pub fn get_html_root_url(self: &Crate, db: &dyn HirDatabase) -> Option {
         // Look for #![doc(html_root_url = "...")]
-        let doc_url = AttrFlags::doc_html_root_url(db, self.id);
-        doc_url.as_ref().map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/")
+        let attrs = db.attrs(AttrDefId::ModuleId(self.root_module().into()));
+        let doc_url = attrs.by_key(sym::doc).find_string_value_in_tt(sym::html_root_url);
+        doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/")
     }
 
     pub fn cfg<'db>(&self, db: &'db dyn HirDatabase) -> &'db CfgOptions {
@@ -638,7 +640,7 @@ impl Module {
                 // FIXME: This is accidentally quadratic.
                 continue;
             }
-            emit_def_diagnostic(db, acc, diag, edition, def_map.krate());
+            emit_def_diagnostic(db, acc, diag, edition);
         }
 
         if !self.id.is_block_module() {
@@ -657,9 +659,8 @@ impl Module {
                     acc.extend(def.diagnostics(db, style_lints))
                 }
                 ModuleDef::Trait(t) => {
-                    let krate = t.krate(db);
                     for diag in TraitItems::query_with_diagnostics(db, t.id).1.iter() {
-                        emit_def_diagnostic(db, acc, diag, edition, krate.id);
+                        emit_def_diagnostic(db, acc, diag, edition);
                     }
 
                     for item in t.items(db) {
@@ -777,7 +778,7 @@ impl Module {
             let ast_id_map = db.ast_id_map(file_id);
 
             for diag in impl_def.id.impl_items_with_diagnostics(db).1.iter() {
-                emit_def_diagnostic(db, acc, diag, edition, loc.container.krate());
+                emit_def_diagnostic(db, acc, diag, edition);
             }
 
             if inherent_impls.invalid_impls().contains(&impl_def.id) {
@@ -808,10 +809,21 @@ impl Module {
                     return None;
                 }
                 let parent = impl_def.id.into();
-                let (lifetimes_attrs, type_and_consts_attrs) =
-                    AttrFlags::query_generic_params(db, parent);
-                let res = lifetimes_attrs.values().any(|it| it.contains(AttrFlags::MAY_DANGLE))
-                    || type_and_consts_attrs.values().any(|it| it.contains(AttrFlags::MAY_DANGLE));
+                let generic_params = db.generic_params(parent);
+                let lifetime_params = generic_params.iter_lt().map(|(local_id, _)| {
+                    GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id })
+                });
+                let type_params = generic_params
+                    .iter_type_or_consts()
+                    .filter(|(_, it)| it.type_param().is_some())
+                    .map(|(local_id, _)| {
+                        GenericParamId::TypeParamId(TypeParamId::from_unchecked(
+                            TypeOrConstParamId { parent, local_id },
+                        ))
+                    });
+                let res = type_params.chain(lifetime_params).any(|p| {
+                    db.attrs(AttrDefId::GenericParamId(p)).by_key(sym::may_dangle).exists()
+                });
                 Some(res)
             })()
             .unwrap_or(false);
@@ -972,17 +984,6 @@ impl Module {
     ) -> Option {
         hir_def::find_path::find_path(db, item.into().into(), self.into(), prefix_kind, true, cfg)
     }
-
-    #[inline]
-    pub fn doc_keyword(self, db: &dyn HirDatabase) -> Option {
-        AttrFlags::doc_keyword(db, InternedModuleId::new(db, self.id))
-    }
-
-    /// Whether it has `#[path = "..."]` attribute.
-    #[inline]
-    pub fn has_path(&self, db: &dyn HirDatabase) -> bool {
-        self.attrs(db).attrs.contains(AttrFlags::HAS_PATH)
-    }
 }
 
 fn macro_call_diagnostics<'db>(
@@ -997,19 +998,31 @@ fn macro_call_diagnostics<'db>(
     if let Some(err) = err {
         let loc = db.lookup_intern_macro_call(macro_call_id);
         let file_id = loc.kind.file_id();
-        let mut range = precise_macro_call_location(&loc.kind, db, loc.krate);
+        let node =
+            InFile::new(file_id, db.ast_id_map(file_id).get_erased(loc.kind.erased_ast_id()));
         let RenderedExpandError { message, error, kind } = err.render_to_string(db);
-        if Some(err.span().anchor.file_id) == file_id.file_id().map(|it| it.editioned_file_id(db)) {
-            range.value = err.span().range
-                + db.ast_id_map(file_id).get_erased(err.span().anchor.ast_id).text_range().start();
-        }
-        acc.push(MacroError { range, message, error, kind }.into());
+        let editioned_file_id = EditionedFileId::from_span(db, err.span().anchor.file_id);
+        let precise_location = if editioned_file_id == file_id {
+            Some(
+                err.span().range
+                    + db.ast_id_map(editioned_file_id.into())
+                        .get_erased(err.span().anchor.ast_id)
+                        .text_range()
+                        .start(),
+            )
+        } else {
+            None
+        };
+        acc.push(MacroError { node, precise_location, message, error, kind }.into());
     }
 
     if !parse_errors.is_empty() {
         let loc = db.lookup_intern_macro_call(macro_call_id);
-        let range = precise_macro_call_location(&loc.kind, db, loc.krate);
-        acc.push(MacroExpansionParseError { range, errors: parse_errors.clone() }.into())
+        let (node, precise_location) = precise_macro_call_location(&loc.kind, db);
+        acc.push(
+            MacroExpansionParseError { node, precise_location, errors: parse_errors.clone() }
+                .into(),
+        )
     }
 }
 
@@ -1033,7 +1046,6 @@ fn emit_macro_def_diagnostics<'db>(
             acc,
             &DefDiagnosticKind::MacroDefError { ast, message: e.to_string() },
             edition,
-            m.krate(db).id,
         );
     }
 }
@@ -1043,9 +1055,8 @@ fn emit_def_diagnostic<'db>(
     acc: &mut Vec>,
     diag: &DefDiagnostic,
     edition: Edition,
-    krate: base_db::Crate,
 ) {
-    emit_def_diagnostic_(db, acc, &diag.kind, edition, krate)
+    emit_def_diagnostic_(db, acc, &diag.kind, edition)
 }
 
 fn emit_def_diagnostic_<'db>(
@@ -1053,7 +1064,6 @@ fn emit_def_diagnostic_<'db>(
     acc: &mut Vec>,
     diag: &DefDiagnosticKind,
     edition: Edition,
-    krate: base_db::Crate,
 ) {
     match diag {
         DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => {
@@ -1076,7 +1086,8 @@ fn emit_def_diagnostic_<'db>(
             let RenderedExpandError { message, error, kind } = err.render_to_string(db);
             acc.push(
                 MacroError {
-                    range: InFile::new(ast.file_id, item.text_range()),
+                    node: InFile::new(ast.file_id, item.syntax_node_ptr()),
+                    precise_location: None,
                     message: format!("{}: {message}", path.display(db, edition)),
                     error,
                     kind,
@@ -1106,10 +1117,11 @@ fn emit_def_diagnostic_<'db>(
             );
         }
         DefDiagnosticKind::UnresolvedMacroCall { ast, path } => {
-            let location = precise_macro_call_location(ast, db, krate);
+            let (node, precise_location) = precise_macro_call_location(ast, db);
             acc.push(
                 UnresolvedMacroCall {
-                    range: location,
+                    macro_call: node,
+                    precise_location,
                     path: path.clone(),
                     is_bang: matches!(ast, MacroCallKind::FnLike { .. }),
                 }
@@ -1128,12 +1140,34 @@ fn emit_def_diagnostic_<'db>(
             );
         }
         DefDiagnosticKind::InvalidDeriveTarget { ast, id } => {
-            let derive = id.find_attr_range(db, krate, *ast).3.path_range();
-            acc.push(InvalidDeriveTarget { range: ast.with_value(derive) }.into());
+            let node = ast.to_node(db);
+            let derive = node.attrs().nth(*id);
+            match derive {
+                Some(derive) => {
+                    acc.push(
+                        InvalidDeriveTarget {
+                            node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&derive))),
+                        }
+                        .into(),
+                    );
+                }
+                None => stdx::never!("derive diagnostic on item without derive attribute"),
+            }
         }
         DefDiagnosticKind::MalformedDerive { ast, id } => {
-            let derive = id.find_attr_range(db, krate, *ast).2;
-            acc.push(MalformedDerive { range: ast.with_value(derive) }.into());
+            let node = ast.to_node(db);
+            let derive = node.attrs().nth(*id);
+            match derive {
+                Some(derive) => {
+                    acc.push(
+                        MalformedDerive {
+                            node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&derive))),
+                        }
+                        .into(),
+                    );
+                }
+                None => stdx::never!("derive diagnostic on item without derive attribute"),
+            }
         }
         DefDiagnosticKind::MacroDefError { ast, message } => {
             let node = ast.to_node(db);
@@ -1152,28 +1186,61 @@ fn emit_def_diagnostic_<'db>(
 fn precise_macro_call_location(
     ast: &MacroCallKind,
     db: &dyn HirDatabase,
-    krate: base_db::Crate,
-) -> InFile {
+) -> (InFile, Option) {
     // FIXME: maybe we actually want slightly different ranges for the different macro diagnostics
     // - e.g. the full attribute for macro errors, but only the name for name resolution
     match ast {
         MacroCallKind::FnLike { ast_id, .. } => {
             let node = ast_id.to_node(db);
-            let range = node
-                .path()
-                .and_then(|it| it.segment())
-                .and_then(|it| it.name_ref())
-                .map(|it| it.syntax().text_range());
-            let range = range.unwrap_or_else(|| node.syntax().text_range());
-            ast_id.with_value(range)
+            (
+                ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))),
+                node.path()
+                    .and_then(|it| it.segment())
+                    .and_then(|it| it.name_ref())
+                    .map(|it| it.syntax().text_range()),
+            )
         }
         MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => {
-            let range = derive_attr_index.find_derive_range(db, krate, *ast_id, *derive_index);
-            ast_id.with_value(range)
+            let node = ast_id.to_node(db);
+            // Compute the precise location of the macro name's token in the derive
+            // list.
+            let token = (|| {
+                let derive_attr = collect_attrs(&node)
+                    .nth(derive_attr_index.ast_index())
+                    .and_then(|x| Either::left(x.1))?;
+                let token_tree = derive_attr.meta()?.token_tree()?;
+                let chunk_by = token_tree
+                    .syntax()
+                    .children_with_tokens()
+                    .filter_map(|elem| match elem {
+                        syntax::NodeOrToken::Token(tok) => Some(tok),
+                        _ => None,
+                    })
+                    .chunk_by(|t| t.kind() == T![,]);
+                let (_, mut group) = chunk_by
+                    .into_iter()
+                    .filter(|&(comma, _)| !comma)
+                    .nth(*derive_index as usize)?;
+                group.find(|t| t.kind() == T![ident])
+            })();
+            (
+                ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))),
+                token.as_ref().map(|tok| tok.text_range()),
+            )
         }
-        MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
-            let attr_range = attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).2;
-            ast_id.with_value(attr_range)
+        MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
+            let node = ast_id.to_node(db);
+            let attr = collect_attrs(&node)
+                .nth(invoc_attr_index.ast_index())
+                .and_then(|x| Either::left(x.1))
+                .unwrap_or_else(|| {
+                    panic!("cannot find attribute #{}", invoc_attr_index.ast_index())
+                });
+
+            (
+                ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&attr))),
+                Some(attr.syntax().text_range()),
+            )
         }
     }
 }
@@ -1371,7 +1438,7 @@ impl Struct {
     }
 
     pub fn repr(self, db: &dyn HirDatabase) -> Option {
-        AttrFlags::repr(db, self.id.into())
+        db.struct_signature(self.id).repr
     }
 
     pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
@@ -1387,7 +1454,7 @@ impl Struct {
     }
 
     pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
-        AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE)
+        db.attrs(self.id.into()).is_unstable()
     }
 
     pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedStruct<'db> {
@@ -1476,7 +1543,7 @@ impl Union {
             .collect()
     }
     pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
-        AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE)
+        db.attrs(self.id.into()).is_unstable()
     }
 }
 
@@ -1511,7 +1578,7 @@ impl Enum {
     }
 
     pub fn repr(self, db: &dyn HirDatabase) -> Option {
-        AttrFlags::repr(db, self.id.into())
+        db.enum_signature(self.id).repr
     }
 
     pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> {
@@ -1527,7 +1594,7 @@ impl Enum {
         let interner = DbInterner::new_with(db, None, None);
         Type::new_for_crate(
             self.id.lookup(db).container.krate(),
-            match EnumSignature::variant_body_type(db, self.id) {
+            match db.enum_signature(self.id).variant_body_type() {
                 layout::IntegerType::Pointer(sign) => match sign {
                     true => Ty::new_int(interner, rustc_type_ir::IntTy::Isize),
                     false => Ty::new_uint(interner, rustc_type_ir::UintTy::Usize),
@@ -1568,7 +1635,7 @@ impl Enum {
     }
 
     pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
-        AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE)
+        db.attrs(self.id.into()).is_unstable()
     }
 }
 
@@ -1669,7 +1736,7 @@ impl Variant {
     }
 
     pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
-        AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE)
+        db.attrs(self.id.into()).is_unstable()
     }
 
     pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedVariant<'db> {
@@ -2154,7 +2221,8 @@ fn expr_store_diagnostics<'db>(
                 InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into()
             }
             ExpressionStoreDiagnostics::UnresolvedMacroCall { node, path } => UnresolvedMacroCall {
-                range: node.map(|ptr| ptr.text_range()),
+                macro_call: (*node).map(|ast_ptr| ast_ptr.into()),
+                precise_location: None,
                 path: path.clone(),
                 is_bang: true,
             }
@@ -2379,33 +2447,33 @@ impl Function {
 
     /// Does this function have `#[test]` attribute?
     pub fn is_test(self, db: &dyn HirDatabase) -> bool {
-        self.attrs(db).is_test()
+        db.attrs(self.id.into()).is_test()
     }
 
     /// is this a `fn main` or a function with an `export_name` of `main`?
     pub fn is_main(self, db: &dyn HirDatabase) -> bool {
-        self.exported_main(db)
+        db.attrs(self.id.into()).export_name() == Some(&sym::main)
             || self.module(db).is_crate_root() && db.function_signature(self.id).name == sym::main
     }
 
     /// Is this a function with an `export_name` of `main`?
     pub fn exported_main(self, db: &dyn HirDatabase) -> bool {
-        AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_EXPORT_NAME_MAIN)
+        db.attrs(self.id.into()).export_name() == Some(&sym::main)
     }
 
     /// Does this function have the ignore attribute?
     pub fn is_ignore(self, db: &dyn HirDatabase) -> bool {
-        AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_IGNORE)
+        db.attrs(self.id.into()).is_ignore()
     }
 
     /// Does this function have `#[bench]` attribute?
     pub fn is_bench(self, db: &dyn HirDatabase) -> bool {
-        AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_BENCH)
+        db.attrs(self.id.into()).is_bench()
     }
 
     /// Is this function marked as unstable with `#[feature]` attribute?
     pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
-        AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE)
+        db.attrs(self.id.into()).is_unstable()
     }
 
     pub fn is_unsafe_to_call(
@@ -2416,7 +2484,8 @@ impl Function {
     ) -> bool {
         let (target_features, target_feature_is_safe_in_target) = caller
             .map(|caller| {
-                let target_features = hir_ty::TargetFeatures::from_fn(db, caller.id);
+                let target_features =
+                    hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into()));
                 let target_feature_is_safe_in_target =
                     match &caller.krate(db).id.workspace_data(db).target {
                         Ok(target) => hir_ty::target_feature_is_safe_in_target(target),
@@ -2447,6 +2516,14 @@ impl Function {
     }
 
     pub fn as_proc_macro(self, db: &dyn HirDatabase) -> Option {
+        let attrs = db.attrs(self.id.into());
+        // FIXME: Store this in FunctionData flags?
+        if !(attrs.is_proc_macro()
+            || attrs.is_proc_macro_attribute()
+            || attrs.is_proc_macro_derive())
+        {
+            return None;
+        }
         let def_map = crate_def_map(db, HasModule::krate(&self.id, db));
         def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() })
     }
@@ -2899,7 +2976,7 @@ impl Trait {
 
     /// `#[rust_analyzer::completions(...)]` mode.
     pub fn complete(self, db: &dyn HirDatabase) -> Complete {
-        Complete::extract(true, self.attrs(db).attrs)
+        Complete::extract(true, &self.attrs(db))
     }
 }
 
@@ -3070,10 +3147,10 @@ impl Macro {
                 let loc = id.lookup(db);
                 let source = loc.source(db);
                 match loc.kind {
-                    ProcMacroKind::CustomDerive => AttrFlags::derive_info(db, self.id).map_or_else(
-                        || as_name_opt(source.value.name()),
-                        |info| Name::new_symbol_root(info.trait_name.clone()),
-                    ),
+                    ProcMacroKind::CustomDerive => db
+                        .attrs(id.into())
+                        .parse_proc_macro_derive()
+                        .map_or_else(|| as_name_opt(source.value.name()), |(it, _)| it),
                     ProcMacroKind::Bang | ProcMacroKind::Attr => as_name_opt(source.value.name()),
                 }
             }
@@ -3081,7 +3158,7 @@ impl Macro {
     }
 
     pub fn is_macro_export(self, db: &dyn HirDatabase) -> bool {
-        matches!(self.id, MacroId::MacroRulesId(_) if AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_MACRO_EXPORT))
+        matches!(self.id, MacroId::MacroRulesId(_) if db.attrs(self.id.into()).by_key(sym::macro_export).exists())
     }
 
     pub fn is_proc_macro(self) -> bool {
@@ -3905,10 +3982,18 @@ impl DeriveHelper {
     }
 
     pub fn name(&self, db: &dyn HirDatabase) -> Name {
-        AttrFlags::derive_info(db, self.derive)
-            .and_then(|it| it.helpers.get(self.idx as usize))
-            .map(|helper| Name::new_symbol_root(helper.clone()))
-            .unwrap_or_else(Name::missing)
+        match self.derive {
+            makro @ MacroId::Macro2Id(_) => db
+                .attrs(makro.into())
+                .parse_rustc_builtin_macro()
+                .and_then(|(_, helpers)| helpers.get(self.idx as usize).cloned()),
+            MacroId::MacroRulesId(_) => None,
+            makro @ MacroId::ProcMacroId(_) => db
+                .attrs(makro.into())
+                .parse_proc_macro_derive()
+                .and_then(|(_, helpers)| helpers.get(self.idx as usize).cloned()),
+        }
+        .unwrap_or_else(Name::missing)
     }
 }
 
@@ -4129,7 +4214,7 @@ impl TypeParam {
     }
 
     pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
-        self.attrs(db).is_unstable()
+        db.attrs(GenericParamId::from(self.id).into()).is_unstable()
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 8eb1c9725cd2..62ce3daab75d 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -21,6 +21,7 @@ use hir_def::{
 };
 use hir_expand::{
     EditionedFileId, ExpandResult, FileRange, HirFileId, InMacroFile, MacroCallId,
+    attrs::collect_attrs,
     builtin::{BuiltinFnLikeExpander, EagerExpander},
     db::ExpandDatabase,
     files::{FileRangeWrapper, HirFileRange, InRealFile},
@@ -35,7 +36,7 @@ use intern::{Interned, Symbol, sym};
 use itertools::Itertools;
 use rustc_hash::{FxHashMap, FxHashSet};
 use smallvec::{SmallVec, smallvec};
-use span::{FileId, SyntaxContext};
+use span::{Edition, FileId, SyntaxContext};
 use stdx::{TupleExt, always};
 use syntax::{
     AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange,
@@ -385,14 +386,17 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     pub fn attach_first_edition(&self, file: FileId) -> Option {
-        let krate = self.file_to_module_defs(file).next()?.krate();
-        Some(EditionedFileId::new(self.db, file, krate.edition(self.db), krate.id))
+        Some(EditionedFileId::new(
+            self.db,
+            file,
+            self.file_to_module_defs(file).next()?.krate().edition(self.db),
+        ))
     }
 
     pub fn parse_guess_edition(&self, file_id: FileId) -> ast::SourceFile {
         let file_id = self
             .attach_first_edition(file_id)
-            .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(self.db, file_id));
+            .unwrap_or_else(|| EditionedFileId::new(self.db, file_id, Edition::CURRENT));
 
         let tree = self.db.parse(file_id).tree();
         self.cache(tree.syntax().clone(), file_id.into());
@@ -1193,34 +1197,33 @@ impl<'db> SemanticsImpl<'db> {
                                     .zip(Some(item))
                             })
                             .map(|(call_id, item)| {
-                                let item_range = item.syntax().text_range();
-                                let loc = db.lookup_intern_macro_call(call_id);
-                                let text_range = match loc.kind {
+                                let attr_id = match db.lookup_intern_macro_call(call_id).kind {
                                     hir_expand::MacroCallKind::Attr {
-                                        censored_attr_ids: attr_ids,
-                                        ..
-                                    } => {
-                                        // FIXME: here, the attribute's text range is used to strip away all
-                                        // entries from the start of the attribute "list" up the invoking
-                                        // attribute. But in
-                                        // ```
-                                        // mod foo {
-                                        //     #![inner]
-                                        // }
-                                        // ```
-                                        // we don't wanna strip away stuff in the `mod foo {` range, that is
-                                        // here if the id corresponds to an inner attribute we got strip all
-                                        // text ranges of the outer ones, and then all of the inner ones up
-                                        // to the invoking attribute so that the inbetween is ignored.
-                                        // FIXME: Should cfg_attr be handled differently?
-                                        let (attr, _, _, _) = attr_ids
-                                            .invoc_attr()
-                                            .find_attr_range_with_source(db, loc.krate, &item);
-                                        let start = attr.syntax().text_range().start();
-                                        TextRange::new(start, item_range.end())
-                                    }
-                                    _ => item_range,
+                                        invoc_attr_index, ..
+                                    } => invoc_attr_index.ast_index(),
+                                    _ => 0,
                                 };
+                                // FIXME: here, the attribute's text range is used to strip away all
+                                // entries from the start of the attribute "list" up the invoking
+                                // attribute. But in
+                                // ```
+                                // mod foo {
+                                //     #![inner]
+                                // }
+                                // ```
+                                // we don't wanna strip away stuff in the `mod foo {` range, that is
+                                // here if the id corresponds to an inner attribute we got strip all
+                                // text ranges of the outer ones, and then all of the inner ones up
+                                // to the invoking attribute so that the inbetween is ignored.
+                                let text_range = item.syntax().text_range();
+                                let start = collect_attrs(&item)
+                                    .nth(attr_id)
+                                    .map(|attr| match attr.1 {
+                                        Either::Left(it) => it.syntax().text_range().start(),
+                                        Either::Right(it) => it.syntax().text_range().start(),
+                                    })
+                                    .unwrap_or_else(|| text_range.start());
+                                let text_range = TextRange::new(start, text_range.end());
                                 filter_duplicates(tokens, text_range);
                                 process_expansion_for_token(ctx, &mut stack, call_id)
                             })
@@ -1470,14 +1473,6 @@ impl<'db> SemanticsImpl<'db> {
         FileRangeWrapper { file_id: file_id.file_id(self.db), range }
     }
 
-    pub fn diagnostics_display_range_for_range(
-        &self,
-        src: InFile,
-    ) -> FileRangeWrapper {
-        let FileRange { file_id, range } = src.original_node_file_range_rooted(self.db);
-        FileRangeWrapper { file_id: file_id.file_id(self.db), range }
-    }
-
     fn token_ancestors_with_macros(
         &self,
         token: SyntaxToken,
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs
index 165ac7e4a08d..5019a5987e51 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs
@@ -5,7 +5,7 @@
 //! node for a *child*, and get its hir.
 
 use either::Either;
-use hir_expand::HirFileId;
+use hir_expand::{HirFileId, attrs::collect_attrs};
 use span::AstIdNode;
 use syntax::{AstPtr, ast};
 
@@ -94,7 +94,6 @@ impl ChildBySource for ModuleId {
 
 impl ChildBySource for ItemScope {
     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
-        let krate = file_id.krate(db);
         self.declarations().for_each(|item| add_module_def(db, res, file_id, item));
         self.impls().for_each(|imp| insert_item_loc(db, res, file_id, imp, keys::IMPL));
         self.extern_blocks().for_each(|extern_block| {
@@ -124,10 +123,12 @@ impl ChildBySource for ItemScope {
             |(ast_id, calls)| {
                 let adt = ast_id.to_node(db);
                 calls.for_each(|(attr_id, call_id, calls)| {
-                    // FIXME: Fix cfg_attr handling.
-                    let (attr, _, _, _) = attr_id.find_attr_range_with_source(db, krate, &adt);
-                    res[keys::DERIVE_MACRO_CALL]
-                        .insert(AstPtr::new(&attr), (attr_id, call_id, calls.into()));
+                    if let Some((_, Either::Left(attr))) =
+                        collect_attrs(&adt).nth(attr_id.ast_index())
+                    {
+                        res[keys::DERIVE_MACRO_CALL]
+                            .insert(AstPtr::new(&attr), (attr_id, call_id, calls.into()));
+                    }
                 });
             },
         );
diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index 9059c88ad66a..d8c624e5c689 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -392,12 +392,12 @@ impl<'a> SymbolCollector<'a> {
         let mut do_not_complete = Complete::Yes;
 
         if let Some(attrs) = def.attrs(self.db) {
-            do_not_complete = Complete::extract(matches!(def, ModuleDef::Trait(_)), attrs.attrs);
+            do_not_complete = Complete::extract(matches!(def, ModuleDef::Trait(_)), &attrs);
             if let Some(trait_do_not_complete) = trait_do_not_complete {
                 do_not_complete = Complete::for_trait_item(trait_do_not_complete, do_not_complete);
             }
 
-            for alias in attrs.doc_aliases(self.db) {
+            for alias in attrs.doc_aliases() {
                 self.symbols.insert(FileSymbol {
                     name: alias.clone(),
                     def,
@@ -441,9 +441,9 @@ impl<'a> SymbolCollector<'a> {
 
         let mut do_not_complete = Complete::Yes;
         if let Some(attrs) = def.attrs(self.db) {
-            do_not_complete = Complete::extract(matches!(def, ModuleDef::Trait(_)), attrs.attrs);
+            do_not_complete = Complete::extract(matches!(def, ModuleDef::Trait(_)), &attrs);
 
-            for alias in attrs.doc_aliases(self.db) {
+            for alias in attrs.doc_aliases() {
                 self.symbols.insert(FileSymbol {
                     name: alias.clone(),
                     def,
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 e06c534e3c51..7843ab9e8f25 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
@@ -1,7 +1,7 @@
 use std::iter::{self, Peekable};
 
 use either::Either;
-use hir::{Adt, AsAssocItem, Crate, FindPathConfig, HasAttrs, ModuleDef, Semantics};
+use hir::{Adt, AsAssocItem, Crate, FindPathConfig, HasAttrs, ModuleDef, Semantics, sym};
 use ide_db::RootDatabase;
 use ide_db::assists::ExprFillDefaultMode;
 use ide_db::syntax_helpers::suggest_name;
@@ -401,7 +401,7 @@ impl ExtendedVariant {
     fn should_be_hidden(self, db: &RootDatabase, krate: Crate) -> bool {
         match self {
             ExtendedVariant::Variant { variant: var, .. } => {
-                var.attrs(db).is_doc_hidden() && var.module(db).krate() != krate
+                var.attrs(db).has_doc_hidden() && var.module(db).krate() != krate
             }
             _ => false,
         }
@@ -424,7 +424,7 @@ impl ExtendedEnum {
     fn is_non_exhaustive(&self, db: &RootDatabase, krate: Crate) -> bool {
         match self {
             ExtendedEnum::Enum { enum_: e, .. } => {
-                e.attrs(db).is_non_exhaustive() && e.module(db).krate() != krate
+                e.attrs(db).by_key(sym::non_exhaustive).exists() && e.module(db).krate() != krate
             }
             _ => false,
         }
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 46f210804da3..8b24d33bf996 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
@@ -1,4 +1,4 @@
-use hir::HasVisibility;
+use hir::{HasVisibility, sym};
 use ide_db::{
     FxHashMap, FxHashSet,
     assists::AssistId,
@@ -93,7 +93,7 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option) ->
                 let mut buf = String::from("./");
                 let db = ctx.db();
                 match parent_module.name(db) {
-                    Some(name) if !parent_module.is_mod_rs(db) && !parent_module.has_path(db) => {
+                    Some(name)
+                        if !parent_module.is_mod_rs(db)
+                            && parent_module
+                                .attrs(db)
+                                .by_key(sym::path)
+                                .string_value_unescape()
+                                .is_none() =>
+                    {
                         format_to!(buf, "{}/", name.as_str())
                     }
                     _ => (),
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 eb7553222a68..2977f8b8c2e7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -68,7 +68,7 @@ pub mod utils;
 
 use hir::Semantics;
 use ide_db::{EditionedFileId, RootDatabase};
-use syntax::TextRange;
+use syntax::{Edition, TextRange};
 
 pub(crate) use crate::assist_context::{AssistContext, Assists};
 
@@ -90,7 +90,7 @@ pub fn assists(
     let sema = Semantics::new(db);
     let file_id = sema
         .attach_first_edition(range.file_id)
-        .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, range.file_id));
+        .unwrap_or_else(|| EditionedFileId::new(db, range.file_id, Edition::CURRENT));
     let ctx = AssistContext::new(sema, config, hir::FileRange { file_id, range: range.range });
     let mut acc = Assists::new(&ctx, resolve);
     handlers::all().iter().for_each(|handler| {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
index 2e220b129fe1..ade60691b57b 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
@@ -321,13 +321,11 @@ fn check_with_config(
     let _tracing = setup_tracing();
     let (mut db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
     db.enable_proc_attr_macros();
-    let sema = Semantics::new(&db);
-    let file_with_caret_id =
-        sema.attach_first_edition(file_with_caret_id.file_id(&db)).unwrap_or(file_with_caret_id);
     let text_without_caret = db.file_text(file_with_caret_id.file_id(&db)).text(&db).to_string();
 
     let frange = hir::FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
 
+    let sema = Semantics::new(&db);
     let ctx = AssistContext::new(sema, &config, frange);
     let resolve = match expected {
         ExpectedResult::Unresolved => AssistResolveStrategy::None,
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 7a86339c1c9c..5a3c5a39dac7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -93,7 +93,16 @@ pub fn test_related_attribute_syn(fn_def: &ast::Fn) -> Option {
 }
 
 pub fn has_test_related_attribute(attrs: &hir::AttrsWithOwner) -> bool {
-    attrs.is_test()
+    attrs.iter().any(|attr| {
+        let path = attr.path();
+        (|| {
+            Some(
+                path.segments().first()?.as_str().starts_with("test")
+                    || path.segments().last()?.as_str().ends_with("test"),
+            )
+        })()
+        .unwrap_or_default()
+    })
 }
 
 #[derive(Clone, Copy, PartialEq)]
@@ -119,7 +128,7 @@ pub fn filter_assoc_items(
         .copied()
         .filter(|assoc_item| {
             if ignore_items == IgnoreAssocItems::DocHiddenAttrPresent
-                && assoc_item.attrs(sema.db).is_doc_hidden()
+                && assoc_item.attrs(sema.db).has_doc_hidden()
             {
                 if let hir::AssocItem::Function(f) = assoc_item
                     && !f.has_body(sema.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 df577b8ed02e..c87c46d98127 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
@@ -56,7 +56,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(description.to_owned()));
         item.add_to(acc, ctx.db)
     }
 }
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 20d01485a45a..d1e05a4359f1 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
@@ -266,7 +266,7 @@ fn import_on_the_fly(
             let original_item = &import.original_item;
             !ctx.is_item_hidden(&import.item_to_import)
                 && !ctx.is_item_hidden(original_item)
-                && ctx.check_stability(original_item.attrs(ctx.db).as_ref())
+                && ctx.check_stability(original_item.attrs(ctx.db).as_deref())
         })
         .filter(|import| filter_excluded_flyimport(ctx, import))
         .sorted_by(|a, b| {
@@ -313,7 +313,7 @@ fn import_on_the_fly_pat_(
             let original_item = &import.original_item;
             !ctx.is_item_hidden(&import.item_to_import)
                 && !ctx.is_item_hidden(original_item)
-                && ctx.check_stability(original_item.attrs(ctx.db).as_ref())
+                && ctx.check_stability(original_item.attrs(ctx.db).as_deref())
         })
         .sorted_by(|a, b| {
             let key = |import_path| {
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 4474d6181c20..73cbe3f0aaab 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
@@ -440,7 +440,7 @@ fn add_custom_postfix_completions(
             let body = snippet.postfix_snippet(receiver_text);
             let mut builder =
                 postfix_snippet(trigger, snippet.description.as_deref().unwrap_or_default(), &body);
-            builder.documentation(Documentation::new_owned(format!("```rust\n{body}\n```")));
+            builder.documentation(Documentation::new(format!("```rust\n{body}\n```")));
             for import in imports.into_iter() {
                 builder.add_import(import);
             }
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 04450aea75bf..ead9852eff53 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
@@ -139,7 +139,7 @@ fn add_custom_completions(
             };
             let body = snip.snippet();
             let mut builder = snippet(ctx, cap, trigger, &body);
-            builder.documentation(Documentation::new_owned(format!("```rust\n{body}\n```")));
+            builder.documentation(Documentation::new(format!("```rust\n{body}\n```")));
             for import in imports.into_iter() {
                 builder.add_import(import);
             }
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 c95b83ef8a02..fc2cc3b796ec 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -558,7 +558,7 @@ impl CompletionContext<'_> {
         I: hir::HasAttrs + Copy,
     {
         let attrs = item.attrs(self.db);
-        attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect()
+        attrs.doc_aliases().map(|it| it.as_str().into()).collect()
     }
 
     /// Check if an item is `#[doc(hidden)]`.
@@ -572,7 +572,7 @@ impl CompletionContext<'_> {
     }
 
     /// Checks whether this item should be listed in regards to stability. Returns `true` if we should.
-    pub(crate) fn check_stability(&self, attrs: Option<&hir::AttrsWithOwner>) -> bool {
+    pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool {
         let Some(attrs) = attrs else {
             return true;
         };
@@ -590,15 +590,15 @@ impl CompletionContext<'_> {
 
     /// Whether the given trait is an operator trait or not.
     pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
-        match trait_.attrs(self.db).lang(self.db) {
-            Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang),
+        match trait_.attrs(self.db).lang() {
+            Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()),
             None => false,
         }
     }
 
     /// Whether the given trait has `#[doc(notable_trait)]`
     pub(crate) fn is_doc_notable_trait(&self, trait_: hir::Trait) -> bool {
-        trait_.attrs(self.db).is_doc_notable_trait()
+        trait_.attrs(self.db).has_doc_notable_trait()
     }
 
     /// Returns the traits in scope, with the [`Drop`] trait removed.
@@ -662,7 +662,7 @@ impl CompletionContext<'_> {
     fn is_visible_impl(
         &self,
         vis: &hir::Visibility,
-        attrs: &hir::AttrsWithOwner,
+        attrs: &hir::Attrs,
         defining_crate: hir::Crate,
     ) -> Visible {
         if !self.check_stability(Some(attrs)) {
@@ -684,18 +684,14 @@ impl CompletionContext<'_> {
         if self.is_doc_hidden(attrs, defining_crate) { Visible::No } else { Visible::Yes }
     }
 
-    pub(crate) fn is_doc_hidden(
-        &self,
-        attrs: &hir::AttrsWithOwner,
-        defining_crate: hir::Crate,
-    ) -> bool {
+    pub(crate) fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool {
         // `doc(hidden)` items are only completed within the defining crate.
-        self.krate != defining_crate && attrs.is_doc_hidden()
+        self.krate != defining_crate && attrs.has_doc_hidden()
     }
 
     pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec {
         if let Some(attrs) = scope_def.attrs(self.db) {
-            attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect()
+            attrs.doc_aliases().map(|it| it.as_str().into()).collect()
         } else {
             vec![]
         }
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 c526c7f070bf..303c71230d60 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
@@ -57,8 +57,7 @@ pub struct CompletionItem {
 
     /// Additional info to show in the UI pop up.
     pub detail: Option,
-    // FIXME: Make this with `'db` lifetime.
-    pub documentation: Option>,
+    pub documentation: Option,
 
     /// Whether this item is marked as deprecated
     pub deprecated: bool,
@@ -489,8 +488,7 @@ pub(crate) struct Builder {
     insert_text: Option,
     is_snippet: bool,
     detail: Option,
-    // FIXME: Make this with `'db` lifetime.
-    documentation: Option>,
+    documentation: Option,
     lookup: Option,
     kind: CompletionItemKind,
     text_edit: Option,
@@ -646,11 +644,11 @@ impl Builder {
         self
     }
     #[allow(unused)]
-    pub(crate) fn documentation(&mut self, docs: Documentation<'_>) -> &mut Builder {
+    pub(crate) fn documentation(&mut self, docs: Documentation) -> &mut Builder {
         self.set_documentation(Some(docs))
     }
-    pub(crate) fn set_documentation(&mut self, docs: Option>) -> &mut Builder {
-        self.documentation = docs.map(Documentation::into_owned);
+    pub(crate) fn set_documentation(&mut self, docs: Option) -> &mut Builder {
+        self.documentation = docs;
         self
     }
     pub(crate) fn set_deprecated(&mut self, deprecated: bool) -> &mut Builder {
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 77a2a3a3a9a0..094e679501fc 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -10,7 +10,7 @@ pub(crate) mod type_alias;
 pub(crate) mod union_literal;
 pub(crate) mod variant;
 
-use hir::{AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type};
+use hir::{AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type, sym};
 use ide_db::text_edit::TextEdit;
 use ide_db::{
     RootDatabase, SnippetCap, SymbolKind,
@@ -91,7 +91,8 @@ impl<'a> RenderContext<'a> {
     }
 
     fn is_deprecated(&self, def: impl HasAttrs) -> bool {
-        def.attrs(self.db()).is_deprecated()
+        let attrs = def.attrs(self.db());
+        attrs.by_key(sym::deprecated).exists()
     }
 
     fn is_deprecated_assoc_item(&self, as_assoc_item: impl AsAssocItem) -> bool {
@@ -114,7 +115,7 @@ impl<'a> RenderContext<'a> {
     }
 
     // FIXME: remove this
-    fn docs(&self, def: impl HasDocs) -> Option> {
+    fn docs(&self, def: impl HasDocs) -> Option {
         def.docs(self.db())
     }
 }
@@ -320,9 +321,7 @@ pub(crate) fn render_expr(
     );
     let edit = TextEdit::replace(source_range, snippet);
     item.snippet_edit(ctx.config.snippet_cap?, edit);
-    item.documentation(Documentation::new_owned(String::from(
-        "Autogenerated expression by term search",
-    )));
+    item.documentation(Documentation::new(String::from("Autogenerated expression by term search")));
     item.set_relevance(crate::CompletionRelevance {
         type_match: compute_type_match(ctx, &expr.ty(ctx.db)),
         ..Default::default()
@@ -555,7 +554,7 @@ fn res_to_kind(resolution: ScopeDef) -> CompletionItemKind {
     }
 }
 
-fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option> {
+fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option {
     use hir::ModuleDef::*;
     match resolution {
         ScopeDef::ModuleDef(Module(it)) => it.docs(db),
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 8b14f05b72b2..6c89e49f94e8 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
@@ -180,7 +180,7 @@ impl Variant {
         }
     }
 
-    fn docs(self, db: &dyn HirDatabase) -> Option> {
+    fn docs(self, db: &dyn HirDatabase) -> Option {
         match self {
             Variant::Struct(it) => it.docs(db),
             Variant::EnumVariant(it) => it.docs(db),
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 60474a31b4d3..312d3bd426f9 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
@@ -108,7 +108,7 @@ fn build_completion(
     label: SmolStr,
     lookup: SmolStr,
     pat: String,
-    def: impl HasDocs,
+    def: impl HasDocs + Copy,
     adt_ty: hir::Type<'_>,
     // Missing in context of match statement completions
     is_variant_missing: bool,
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 cfd6340f1eea..37d0fa18c497 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
@@ -1,7 +1,7 @@
 //! Code common to structs, unions, and enum variants.
 
 use crate::context::CompletionContext;
-use hir::{HasAttrs, HasCrate, HasVisibility, HirDisplay, StructKind};
+use hir::{HasAttrs, HasCrate, HasVisibility, HirDisplay, StructKind, sym};
 use ide_db::SnippetCap;
 use itertools::Itertools;
 use syntax::SmolStr;
@@ -105,8 +105,8 @@ pub(crate) fn visible_fields(
         .copied()
         .collect::>();
     let has_invisible_field = n_fields - fields.len() > 0;
-    let is_foreign_non_exhaustive =
-        item.attrs(ctx.db).is_non_exhaustive() && item.krate(ctx.db) != module.krate();
+    let is_foreign_non_exhaustive = item.attrs(ctx.db).by_key(sym::non_exhaustive).exists()
+        && item.krate(ctx.db) != module.krate();
     let fields_omitted = has_invisible_field || is_foreign_non_exhaustive;
     Some((fields, fields_omitted))
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
index 36d739455030..b32a89545726 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
@@ -160,12 +160,12 @@ pub(crate) fn position(
     #[rust_analyzer::rust_fixture] ra_fixture: &str,
 ) -> (RootDatabase, FilePosition) {
     let mut database = RootDatabase::default();
-    let change_fixture = ChangeFixture::parse(ra_fixture);
+    let change_fixture = ChangeFixture::parse(&database, ra_fixture);
     database.enable_proc_attr_macros();
     database.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
     let offset = range_or_offset.expect_offset();
-    let position = FilePosition { file_id: file_id.file_id(), offset };
+    let position = FilePosition { file_id: file_id.file_id(&database), offset };
     (database, position)
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
index 9ce85b2bf330..c051fd863de6 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -5,10 +5,8 @@
 
 // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
 
-use std::borrow::Cow;
-
 use crate::RootDatabase;
-use crate::documentation::{Documentation, HasDocs};
+use crate::documentation::{DocsRangeMap, Documentation, HasDocs};
 use crate::famous_defs::FamousDefs;
 use arrayvec::ArrayVec;
 use either::Either;
@@ -23,7 +21,7 @@ use hir::{
 use span::Edition;
 use stdx::{format_to, impl_from};
 use syntax::{
-    SyntaxKind, SyntaxNode, SyntaxToken,
+    SyntaxKind, SyntaxNode, SyntaxToken, TextSize,
     ast::{self, AstNode},
     match_ast,
 };
@@ -201,25 +199,21 @@ impl Definition {
         Some(name)
     }
 
-    pub fn docs<'db>(
+    pub fn docs(
         &self,
-        db: &'db RootDatabase,
+        db: &RootDatabase,
         famous_defs: Option<&FamousDefs<'_, '_>>,
         display_target: DisplayTarget,
-    ) -> Option> {
-        self.docs_with_rangemap(db, famous_defs, display_target).map(|docs| match docs {
-            Either::Left(Cow::Borrowed(docs)) => Documentation::new_borrowed(docs.docs()),
-            Either::Left(Cow::Owned(docs)) => Documentation::new_owned(docs.into_docs()),
-            Either::Right(docs) => docs,
-        })
+    ) -> Option {
+        self.docs_with_rangemap(db, famous_defs, display_target).map(|(docs, _)| docs)
     }
 
-    pub fn docs_with_rangemap<'db>(
+    pub fn docs_with_rangemap(
         &self,
-        db: &'db RootDatabase,
+        db: &RootDatabase,
         famous_defs: Option<&FamousDefs<'_, '_>>,
         display_target: DisplayTarget,
-    ) -> Option, Documentation<'db>>> {
+    ) -> Option<(Documentation, Option)> {
         let docs = match self {
             Definition::Macro(it) => it.docs_with_rangemap(db),
             Definition::Field(it) => it.docs_with_rangemap(db),
@@ -235,13 +229,15 @@ impl Definition {
                 it.docs_with_rangemap(db).or_else(|| {
                     // docs are missing, try to fall back to the docs of the aliased item.
                     let adt = it.ty(db).as_adt()?;
-                    let mut docs = adt.docs_with_rangemap(db)?.into_owned();
+                    let (docs, range_map) = adt.docs_with_rangemap(db)?;
                     let header_docs = format!(
                         "*This is the documentation for* `{}`\n\n",
                         adt.display(db, display_target)
                     );
-                    docs.prepend_str(&header_docs);
-                    Some(Cow::Owned(docs))
+                    let offset = TextSize::new(header_docs.len() as u32);
+                    let range_map = range_map.shift_docstring_line_range(offset);
+                    let docs = header_docs + docs.as_str();
+                    Some((Documentation::new(docs), range_map))
                 })
             }
             Definition::BuiltinType(it) => {
@@ -250,7 +246,7 @@ impl Definition {
                     let primitive_mod =
                         format!("prim_{}", it.name().display(fd.0.db, display_target.edition));
                     let doc_owner = find_std_module(fd, &primitive_mod, display_target.edition)?;
-                    doc_owner.docs_with_rangemap(db)
+                    doc_owner.docs_with_rangemap(fd.0.db)
                 })
             }
             Definition::BuiltinLifetime(StaticLifetime) => None,
@@ -286,7 +282,7 @@ impl Definition {
                     );
                 }
 
-                return Some(Either::Right(Documentation::new_owned(docs.replace('*', "\\*"))));
+                return Some((Documentation::new(docs.replace('*', "\\*")), None));
             }
             Definition::ToolModule(_) => None,
             Definition::DeriveHelper(_) => None,
@@ -303,7 +299,7 @@ impl Definition {
             let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?;
             item.docs_with_rangemap(db)
         })
-        .map(Either::Left)
+        .map(|(docs, range_map)| (docs, Some(range_map)))
     }
 
     pub fn label(&self, db: &RootDatabase, display_target: DisplayTarget) -> String {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
index 4c4691cca2ca..cab19aadfd01 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
@@ -1,100 +1,337 @@
 //! Documentation attribute related utilities.
-use std::borrow::Cow;
-
-use hir::{HasAttrs, db::HirDatabase, resolve_doc_path_on};
+use either::Either;
+use hir::{
+    AttrId, AttrSourceMap, AttrsWithOwner, HasAttrs, InFile,
+    db::{DefDatabase, HirDatabase},
+    resolve_doc_path_on, sym,
+};
+use itertools::Itertools;
+use span::{TextRange, TextSize};
+use syntax::{
+    AstToken,
+    ast::{self, IsString},
+};
 
 /// Holds documentation
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Documentation<'db>(Cow<'db, str>);
+pub struct Documentation(String);
 
-impl<'db> Documentation<'db> {
-    #[inline]
-    pub fn new_owned(s: String) -> Self {
-        Documentation(Cow::Owned(s))
+impl Documentation {
+    pub fn new(s: String) -> Self {
+        Documentation(s)
     }
 
-    #[inline]
-    pub fn new_borrowed(s: &'db str) -> Self {
-        Documentation(Cow::Borrowed(s))
-    }
-
-    #[inline]
-    pub fn into_owned(self) -> Documentation<'static> {
-        Documentation::new_owned(self.0.into_owned())
-    }
-
-    #[inline]
     pub fn as_str(&self) -> &str {
         &self.0
     }
 }
 
-pub trait HasDocs: HasAttrs + Copy {
-    fn docs(self, db: &dyn HirDatabase) -> Option> {
-        let docs = match self.docs_with_rangemap(db)? {
-            Cow::Borrowed(docs) => Documentation::new_borrowed(docs.docs()),
-            Cow::Owned(docs) => Documentation::new_owned(docs.into_docs()),
-        };
-        Some(docs)
+impl From for String {
+    fn from(Documentation(string): Documentation) -> Self {
+        string
     }
-    fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option> {
-        self.hir_docs(db).map(Cow::Borrowed)
+}
+
+pub trait HasDocs: HasAttrs {
+    fn docs(self, db: &dyn HirDatabase) -> Option;
+    fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)>;
+    fn resolve_doc_path(
+        self,
+        db: &dyn HirDatabase,
+        link: &str,
+        ns: Option,
+        is_inner_doc: bool,
+    ) -> Option;
+}
+/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
+#[derive(Debug)]
+pub struct DocsRangeMap {
+    source_map: AttrSourceMap,
+    // (docstring-line-range, attr_index, attr-string-range)
+    // a mapping from the text range of a line of the [`Documentation`] to the attribute index and
+    // the original (untrimmed) syntax doc line
+    mapping: Vec<(TextRange, AttrId, TextRange)>,
+}
+
+impl DocsRangeMap {
+    /// Maps a [`TextRange`] relative to the documentation string back to its AST range
+    pub fn map(&self, range: TextRange) -> Option<(InFile, AttrId)> {
+        let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?;
+        let (line_docs_range, idx, original_line_src_range) = self.mapping[found];
+        if !line_docs_range.contains_range(range) {
+            return None;
+        }
+
+        let relative_range = range - line_docs_range.start();
+
+        let InFile { file_id, value: source } = self.source_map.source_of_id(idx);
+        match source {
+            Either::Left(attr) => {
+                let string = get_doc_string_in_attr(attr)?;
+                let text_range = string.open_quote_text_range()?;
+                let range = TextRange::at(
+                    text_range.end() + original_line_src_range.start() + relative_range.start(),
+                    string.syntax().text_range().len().min(range.len()),
+                );
+                Some((InFile { file_id, value: range }, idx))
+            }
+            Either::Right(comment) => {
+                let text_range = comment.syntax().text_range();
+                let range = TextRange::at(
+                    text_range.start()
+                        + TextSize::try_from(comment.prefix().len()).ok()?
+                        + original_line_src_range.start()
+                        + relative_range.start(),
+                    text_range.len().min(range.len()),
+                );
+                Some((InFile { file_id, value: range }, idx))
+            }
+        }
+    }
+
+    pub fn shift_docstring_line_range(self, offset: TextSize) -> DocsRangeMap {
+        let mapping = self
+            .mapping
+            .into_iter()
+            .map(|(buf_offset, id, base_offset)| {
+                let buf_offset = buf_offset.checked_add(offset).unwrap();
+                (buf_offset, id, base_offset)
+            })
+            .collect_vec();
+        DocsRangeMap { source_map: self.source_map, mapping }
+    }
+}
+
+pub fn docs_with_rangemap(
+    db: &dyn DefDatabase,
+    attrs: &AttrsWithOwner,
+) -> Option<(Documentation, DocsRangeMap)> {
+    let docs = attrs
+        .by_key(sym::doc)
+        .attrs()
+        .filter_map(|attr| attr.string_value_unescape().map(|s| (s, attr.id)));
+    let indent = doc_indent(attrs);
+    let mut buf = String::new();
+    let mut mapping = Vec::new();
+    for (doc, idx) in docs {
+        if !doc.is_empty() {
+            let mut base_offset = 0;
+            for raw_line in doc.split('\n') {
+                let line = raw_line.trim_end();
+                let line_len = line.len();
+                let (offset, line) = match line.char_indices().nth(indent) {
+                    Some((offset, _)) => (offset, &line[offset..]),
+                    None => (0, line),
+                };
+                let buf_offset = buf.len();
+                buf.push_str(line);
+                mapping.push((
+                    TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?),
+                    idx,
+                    TextRange::at(
+                        (base_offset + offset).try_into().ok()?,
+                        line_len.try_into().ok()?,
+                    ),
+                ));
+                buf.push('\n');
+                base_offset += raw_line.len() + 1;
+            }
+        } else {
+            buf.push('\n');
+        }
+    }
+    buf.pop();
+    if buf.is_empty() {
+        None
+    } else {
+        Some((Documentation(buf), DocsRangeMap { mapping, source_map: attrs.source_map(db) }))
+    }
+}
+
+pub fn docs_from_attrs(attrs: &hir::Attrs) -> Option {
+    let docs = attrs.by_key(sym::doc).attrs().filter_map(|attr| attr.string_value_unescape());
+    let indent = doc_indent(attrs);
+    let mut buf = String::new();
+    for doc in docs {
+        // str::lines doesn't yield anything for the empty string
+        if !doc.is_empty() {
+            // We don't trim trailing whitespace from doc comments as multiple trailing spaces
+            // indicates a hard line break in Markdown.
+            let lines = doc.lines().map(|line| {
+                line.char_indices().nth(indent).map_or(line, |(offset, _)| &line[offset..])
+            });
+
+            buf.extend(Itertools::intersperse(lines, "\n"));
+        }
+        buf.push('\n');
+    }
+    buf.pop();
+    if buf.is_empty() { None } else { Some(buf) }
+}
+
+macro_rules! impl_has_docs {
+    ($($def:ident,)*) => {$(
+        impl HasDocs for hir::$def {
+            fn docs(self, db: &dyn HirDatabase) -> Option {
+                docs_from_attrs(&self.attrs(db)).map(Documentation)
+            }
+            fn docs_with_rangemap(
+                self,
+                db: &dyn HirDatabase,
+            ) -> Option<(Documentation, DocsRangeMap)> {
+                docs_with_rangemap(db, &self.attrs(db))
+            }
+            fn resolve_doc_path(
+                self,
+                db: &dyn HirDatabase,
+                link: &str,
+                ns: Option,
+                is_inner_doc: bool,
+            ) -> Option {
+                resolve_doc_path_on(db, self, link, ns, is_inner_doc)
+            }
+        }
+    )*};
+}
+
+impl_has_docs![
+    Variant, Field, Static, Const, Trait, TypeAlias, Macro, Function, Adt, Module, Impl, Crate,
+];
+
+macro_rules! impl_has_docs_enum {
+    ($($variant:ident),* for $enum:ident) => {$(
+        impl HasDocs for hir::$variant {
+            fn docs(self, db: &dyn HirDatabase) -> Option {
+                hir::$enum::$variant(self).docs(db)
+            }
+
+            fn docs_with_rangemap(
+                self,
+                db: &dyn HirDatabase,
+            ) -> Option<(Documentation, DocsRangeMap)> {
+                hir::$enum::$variant(self).docs_with_rangemap(db)
+            }
+            fn resolve_doc_path(
+                self,
+                db: &dyn HirDatabase,
+                link: &str,
+                ns: Option,
+                is_inner_doc: bool,
+            ) -> Option {
+                hir::$enum::$variant(self).resolve_doc_path(db, link, ns, is_inner_doc)
+            }
+        }
+    )*};
+}
+
+impl_has_docs_enum![Struct, Union, Enum for Adt];
+
+impl HasDocs for hir::AssocItem {
+    fn docs(self, db: &dyn HirDatabase) -> Option {
+        match self {
+            hir::AssocItem::Function(it) => it.docs(db),
+            hir::AssocItem::Const(it) => it.docs(db),
+            hir::AssocItem::TypeAlias(it) => it.docs(db),
+        }
+    }
+
+    fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)> {
+        match self {
+            hir::AssocItem::Function(it) => it.docs_with_rangemap(db),
+            hir::AssocItem::Const(it) => it.docs_with_rangemap(db),
+            hir::AssocItem::TypeAlias(it) => it.docs_with_rangemap(db),
+        }
+    }
+
+    fn resolve_doc_path(
+        self,
+        db: &dyn HirDatabase,
+        link: &str,
+        ns: Option,
+        is_inner_doc: bool,
+    ) -> Option {
+        match self {
+            hir::AssocItem::Function(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+            hir::AssocItem::Const(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+            hir::AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+        }
+    }
+}
+
+impl HasDocs for hir::ExternCrateDecl {
+    fn docs(self, db: &dyn HirDatabase) -> Option {
+        let crate_docs = docs_from_attrs(&self.resolved_crate(db)?.root_module().attrs(db));
+        let decl_docs = docs_from_attrs(&self.attrs(db));
+        match (decl_docs, crate_docs) {
+            (None, None) => None,
+            (Some(decl_docs), None) => Some(decl_docs),
+            (None, Some(crate_docs)) => Some(crate_docs),
+            (Some(mut decl_docs), Some(crate_docs)) => {
+                decl_docs.push('\n');
+                decl_docs.push('\n');
+                decl_docs += &crate_docs;
+                Some(decl_docs)
+            }
+        }
+        .map(Documentation::new)
+    }
+
+    fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)> {
+        let crate_docs = docs_with_rangemap(db, &self.resolved_crate(db)?.root_module().attrs(db));
+        let decl_docs = docs_with_rangemap(db, &self.attrs(db));
+        match (decl_docs, crate_docs) {
+            (None, None) => None,
+            (Some(decl_docs), None) => Some(decl_docs),
+            (None, Some(crate_docs)) => Some(crate_docs),
+            (
+                Some((Documentation(mut decl_docs), mut decl_range_map)),
+                Some((Documentation(crate_docs), crate_range_map)),
+            ) => {
+                decl_docs.push('\n');
+                decl_docs.push('\n');
+                let offset = TextSize::new(decl_docs.len() as u32);
+                decl_docs += &crate_docs;
+                let crate_range_map = crate_range_map.shift_docstring_line_range(offset);
+                decl_range_map.mapping.extend(crate_range_map.mapping);
+                Some((Documentation(decl_docs), decl_range_map))
+            }
+        }
     }
     fn resolve_doc_path(
         self,
         db: &dyn HirDatabase,
         link: &str,
         ns: Option,
-        is_inner_doc: hir::IsInnerDoc,
+        is_inner_doc: bool,
     ) -> Option {
         resolve_doc_path_on(db, self, link, ns, is_inner_doc)
     }
 }
 
-macro_rules! impl_has_docs {
-    ($($def:ident,)*) => {$(
-        impl HasDocs for hir::$def {}
-    )*};
-}
-
-impl_has_docs![
-    Variant, Field, Static, Const, Trait, TypeAlias, Macro, Function, Adt, Module, Impl, Crate,
-    AssocItem, Struct, Union, Enum,
-];
-
-impl HasDocs for hir::ExternCrateDecl {
-    fn docs(self, db: &dyn HirDatabase) -> Option> {
-        let crate_docs = self.resolved_crate(db)?.hir_docs(db);
-        let decl_docs = self.hir_docs(db);
-        match (decl_docs, crate_docs) {
-            (None, None) => None,
-            (Some(docs), None) | (None, Some(docs)) => {
-                Some(Documentation::new_borrowed(docs.docs()))
-            }
-            (Some(decl_docs), Some(crate_docs)) => {
-                let mut docs = String::with_capacity(
-                    decl_docs.docs().len() + "\n\n".len() + crate_docs.docs().len(),
-                );
-                docs.push_str(decl_docs.docs());
-                docs.push_str("\n\n");
-                docs.push_str(crate_docs.docs());
-                Some(Documentation::new_owned(docs))
-            }
-        }
-    }
-
-    fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option> {
-        let crate_docs = self.resolved_crate(db)?.hir_docs(db);
-        let decl_docs = self.hir_docs(db);
-        match (decl_docs, crate_docs) {
-            (None, None) => None,
-            (Some(docs), None) | (None, Some(docs)) => Some(Cow::Borrowed(docs)),
-            (Some(decl_docs), Some(crate_docs)) => {
-                let mut docs = decl_docs.clone();
-                docs.append_str("\n\n");
-                docs.append(crate_docs);
-                Some(Cow::Owned(docs))
-            }
+fn get_doc_string_in_attr(it: &ast::Attr) -> Option {
+    match it.expr() {
+        // #[doc = lit]
+        Some(ast::Expr::Literal(lit)) => match lit.kind() {
+            ast::LiteralKind::String(it) => Some(it),
+            _ => None,
+        },
+        // #[cfg_attr(..., doc = "", ...)]
+        None => {
+            // FIXME: See highlight injection for what to do here
+            None
         }
+        _ => None,
     }
 }
+
+fn doc_indent(attrs: &hir::Attrs) -> usize {
+    let mut min = !0;
+    for val in attrs.by_key(sym::doc).attrs().filter_map(|attr| attr.string_value_unescape()) {
+        if let Some(m) =
+            val.lines().filter_map(|line| line.chars().position(|c| !c.is_whitespace())).min()
+        {
+            min = min.min(m);
+        }
+    }
+    min
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs b/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs
index cd86e7765196..1f056a835bc6 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs
@@ -25,14 +25,18 @@ impl RootDatabase {
         // We don't want a mistake in the fixture to crash r-a, so we wrap this in `catch_unwind()`.
         std::panic::catch_unwind(|| {
             let mut db = RootDatabase::default();
-            let fixture =
-                test_fixture::ChangeFixture::parse_with_proc_macros(text, minicore.0, Vec::new());
+            let fixture = test_fixture::ChangeFixture::parse_with_proc_macros(
+                &db,
+                text,
+                minicore.0,
+                Vec::new(),
+            );
             db.apply_change(fixture.change);
             let files = fixture
                 .files
                 .into_iter()
                 .zip(fixture.file_lines)
-                .map(|(file_id, range)| (file_id.file_id(), range))
+                .map(|(file_id, range)| (file_id.file_id(&db), range))
                 .collect();
             (db, files, fixture.sysroot_files)
         })
@@ -521,7 +525,7 @@ impl_empty_upmap_from_ra_fixture!(
     &str,
     String,
     SmolStr,
-    Documentation<'_>,
+    Documentation,
     SymbolKind,
     CfgExpr,
     ReferenceCategory,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs b/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs
index 36a6938af6b8..eacd9b9b4d2f 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs
@@ -33,7 +33,7 @@ pub fn is_rust_fence(s: &str) -> bool {
 
 const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
 
-pub fn format_docs(src: &Documentation<'_>) -> String {
+pub fn format_docs(src: &Documentation) -> String {
     format_docs_(src.as_str())
 }
 
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 8b53cea7e6d3..f1d076e874d5 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -12,7 +12,7 @@ use either::Either;
 use hir::{
     Adt, AsAssocItem, DefWithBody, EditionedFileId, FileRange, FileRangeWrapper, HasAttrs,
     HasContainer, HasSource, InFile, InFileWrapper, InRealFile, InlineAsmOperand, ItemContainer,
-    ModuleSource, PathResolution, Semantics, Visibility,
+    ModuleSource, PathResolution, Semantics, Visibility, sym,
 };
 use memchr::memmem::Finder;
 use parser::SyntaxKind;
@@ -169,7 +169,7 @@ impl SearchScope {
             entries.extend(
                 source_root
                     .iter()
-                    .map(|id| (EditionedFileId::new(db, id, crate_data.edition, krate), None)),
+                    .map(|id| (EditionedFileId::new(db, id, crate_data.edition), None)),
             );
         }
         SearchScope { entries }
@@ -183,9 +183,11 @@ impl SearchScope {
 
             let source_root = db.file_source_root(root_file).source_root_id(db);
             let source_root = db.source_root(source_root).source_root(db);
-            entries.extend(source_root.iter().map(|id| {
-                (EditionedFileId::new(db, id, rev_dep.edition(db), rev_dep.into()), None)
-            }));
+            entries.extend(
+                source_root
+                    .iter()
+                    .map(|id| (EditionedFileId::new(db, id, rev_dep.edition(db)), None)),
+            );
         }
         SearchScope { entries }
     }
@@ -199,7 +201,7 @@ impl SearchScope {
         SearchScope {
             entries: source_root
                 .iter()
-                .map(|id| (EditionedFileId::new(db, id, of.edition(db), of.into()), None))
+                .map(|id| (EditionedFileId::new(db, id, of.edition(db)), None))
                 .collect(),
         }
     }
@@ -366,7 +368,7 @@ impl Definition {
         if let Definition::Macro(macro_def) = self {
             return match macro_def.kind(db) {
                 hir::MacroKind::Declarative => {
-                    if macro_def.attrs(db).is_macro_export() {
+                    if macro_def.attrs(db).by_key(sym::macro_export).exists() {
                         SearchScope::reverse_dependencies(db, module.krate())
                     } else {
                         SearchScope::krate(db, module.krate())
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
index 427a51055948..30d1df4f8e55 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -3,7 +3,7 @@
         Module {
             id: ModuleId {
                 krate: Crate(
-                    Id(2c00),
+                    Id(3000),
                 ),
                 block: None,
                 local_id: Idx::(0),
@@ -16,7 +16,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                3801,
+                                3401,
                             ),
                         },
                     ),
@@ -24,7 +24,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -50,7 +50,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                3800,
+                                3400,
                             ),
                         },
                     ),
@@ -58,7 +58,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -84,7 +84,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                3800,
+                                3400,
                             ),
                         },
                     ),
@@ -92,7 +92,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -118,7 +118,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                3800,
+                                3400,
                             ),
                         },
                     ),
@@ -126,7 +126,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -152,7 +152,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                3800,
+                                3400,
                             ),
                         },
                     ),
@@ -160,7 +160,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -186,7 +186,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                3801,
+                                3401,
                             ),
                         },
                     ),
@@ -194,7 +194,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -220,7 +220,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                3800,
+                                3400,
                             ),
                         },
                     ),
@@ -228,7 +228,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index ce93fa59e258..973256c470f3 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -3,7 +3,7 @@
         Module {
             id: ModuleId {
                 krate: Crate(
-                    Id(2c00),
+                    Id(3000),
                 ),
                 block: None,
                 local_id: Idx::(0),
@@ -22,7 +22,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -49,14 +49,14 @@
                 def: TypeAlias(
                     TypeAlias {
                         id: TypeAliasId(
-                            6c00,
+                            6800,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -88,7 +88,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -115,14 +115,14 @@
                 def: Const(
                     Const {
                         id: ConstId(
-                            6400,
+                            6000,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -147,14 +147,14 @@
                 def: Const(
                     Const {
                         id: ConstId(
-                            6402,
+                            6002,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -180,7 +180,7 @@
                     Enum(
                         Enum {
                             id: EnumId(
-                                5000,
+                                4c00,
                             ),
                         },
                     ),
@@ -188,7 +188,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -214,7 +214,7 @@
                     Macro {
                         id: Macro2Id(
                             Macro2Id(
-                                4c00,
+                                4800,
                             ),
                         ),
                     },
@@ -222,7 +222,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -248,7 +248,7 @@
                     Macro {
                         id: Macro2Id(
                             Macro2Id(
-                                4c00,
+                                4800,
                             ),
                         ),
                     },
@@ -256,7 +256,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -281,14 +281,14 @@
                 def: Static(
                     Static {
                         id: StaticId(
-                            6800,
+                            6400,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -314,7 +314,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4801,
+                                4401,
                             ),
                         },
                     ),
@@ -322,7 +322,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -348,7 +348,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4800,
+                                4400,
                             ),
                         },
                     ),
@@ -356,7 +356,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: MacroFile(
                         MacroCallId(
-                            Id(3c00),
+                            Id(3800),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -382,7 +382,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4805,
+                                4405,
                             ),
                         },
                     ),
@@ -390,7 +390,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -418,7 +418,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4806,
+                                4406,
                             ),
                         },
                     ),
@@ -426,7 +426,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -454,7 +454,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4807,
+                                4407,
                             ),
                         },
                     ),
@@ -462,7 +462,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -488,7 +488,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4802,
+                                4402,
                             ),
                         },
                     ),
@@ -496,7 +496,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -521,14 +521,14 @@
                 def: Trait(
                     Trait {
                         id: TraitId(
-                            5c00,
+                            5800,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -554,7 +554,7 @@
                     Macro {
                         id: Macro2Id(
                             Macro2Id(
-                                4c00,
+                                4800,
                             ),
                         ),
                     },
@@ -562,7 +562,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -588,7 +588,7 @@
                     Union(
                         Union {
                             id: UnionId(
-                                5400,
+                                5000,
                             ),
                         },
                     ),
@@ -596,7 +596,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -622,7 +622,7 @@
                     Module {
                         id: ModuleId {
                             krate: Crate(
-                                Id(2c00),
+                                Id(3000),
                             ),
                             block: None,
                             local_id: Idx::(1),
@@ -632,7 +632,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -658,7 +658,7 @@
                     Module {
                         id: ModuleId {
                             krate: Crate(
-                                Id(2c00),
+                                Id(3000),
                             ),
                             block: None,
                             local_id: Idx::(2),
@@ -668,7 +668,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -694,7 +694,7 @@
                     Macro {
                         id: MacroRulesId(
                             MacroRulesId(
-                                3801,
+                                3401,
                             ),
                         ),
                     },
@@ -702,7 +702,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -727,14 +727,14 @@
                 def: Function(
                     Function {
                         id: FunctionId(
-                            6002,
+                            5c02,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -761,14 +761,14 @@
                 def: Function(
                     Function {
                         id: FunctionId(
-                            6001,
+                            5c01,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -796,7 +796,7 @@
                     Macro {
                         id: MacroRulesId(
                             MacroRulesId(
-                                3800,
+                                3400,
                             ),
                         ),
                     },
@@ -804,7 +804,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -829,14 +829,14 @@
                 def: Function(
                     Function {
                         id: FunctionId(
-                            6000,
+                            5c00,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -862,7 +862,7 @@
                     Macro {
                         id: MacroRulesId(
                             MacroRulesId(
-                                3801,
+                                3401,
                             ),
                         ),
                     },
@@ -870,7 +870,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -895,14 +895,14 @@
                 def: Function(
                     Function {
                         id: FunctionId(
-                            6003,
+                            5c03,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -930,7 +930,7 @@
         Module {
             id: ModuleId {
                 krate: Crate(
-                    Id(2c00),
+                    Id(3000),
                 ),
                 block: None,
                 local_id: Idx::(1),
@@ -943,7 +943,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4803,
+                                4403,
                             ),
                         },
                     ),
@@ -951,7 +951,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3000),
+                            Id(2000),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -977,7 +977,7 @@
         Module {
             id: ModuleId {
                 krate: Crate(
-                    Id(2c00),
+                    Id(3000),
                 ),
                 block: None,
                 local_id: Idx::(2),
@@ -989,14 +989,14 @@
                 def: Trait(
                     Trait {
                         id: TraitId(
-                            5c00,
+                            5800,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3001),
+                            Id(2001),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -1022,7 +1022,7 @@
                     Macro {
                         id: Macro2Id(
                             Macro2Id(
-                                4c00,
+                                4800,
                             ),
                         ),
                     },
@@ -1030,7 +1030,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3001),
+                            Id(2001),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -1056,7 +1056,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4804,
+                                4404,
                             ),
                         },
                     ),
@@ -1064,7 +1064,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3001),
+                            Id(2001),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -1090,7 +1090,7 @@
                     Macro {
                         id: Macro2Id(
                             Macro2Id(
-                                4c00,
+                                4800,
                             ),
                         ),
                     },
@@ -1098,7 +1098,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3001),
+                            Id(2001),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -1124,7 +1124,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4804,
+                                4404,
                             ),
                         },
                     ),
@@ -1132,7 +1132,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(3001),
+                            Id(2001),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
index 3ab837aa613f..22872b577f71 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
@@ -13,7 +13,7 @@
         loc: DeclarationLocation {
             hir_file_id: FileId(
                 EditionedFileId(
-                    Id(3001),
+                    Id(2001),
                 ),
             ),
             ptr: SyntaxNodePtr {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt
index a6a808d616a7..9f98bf87e2e8 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt
@@ -13,7 +13,7 @@
         loc: DeclarationLocation {
             hir_file_id: FileId(
                 EditionedFileId(
-                    Id(3001),
+                    Id(2001),
                 ),
             ),
             ptr: SyntaxNodePtr {
@@ -47,7 +47,7 @@
         loc: DeclarationLocation {
             hir_file_id: FileId(
                 EditionedFileId(
-                    Id(3000),
+                    Id(2000),
                 ),
             ),
             ptr: SyntaxNodePtr {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
index 7b9fdb1e1cf3..61e28386d072 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
@@ -114,7 +114,8 @@ fn assoc_item_of_trait(
 #[cfg(test)]
 mod tests {
     use expect_test::{Expect, expect};
-    use hir::{EditionedFileId, FilePosition, Semantics};
+    use hir::FilePosition;
+    use hir::Semantics;
     use span::Edition;
     use syntax::ast::{self, AstNode};
     use test_fixture::ChangeFixture;
@@ -126,11 +127,10 @@ mod tests {
         #[rust_analyzer::rust_fixture] ra_fixture: &str,
     ) -> (RootDatabase, FilePosition) {
         let mut database = RootDatabase::default();
-        let change_fixture = ChangeFixture::parse(ra_fixture);
+        let change_fixture = ChangeFixture::parse(&database, ra_fixture);
         database.apply_change(change_fixture.change);
         let (file_id, range_or_offset) =
             change_fixture.file_position.expect("expected a marker ($0)");
-        let file_id = EditionedFileId::from_span_guess_origin(&database, file_id);
         let offset = range_or_offset.expect_offset();
         (database, FilePosition { file_id, offset })
     }
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 dfa9639f6eb9..8611ef653b02 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
@@ -95,7 +95,7 @@ fn f() {
   //^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled
 
     #[cfg(no)] #[cfg(no2)] mod m;
-  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled
+  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no and no2 are disabled
 
     #[cfg(all(not(a), b))] enum E {}
   //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: b is disabled
@@ -130,6 +130,7 @@ trait Bar {
     /// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
     #[test]
     fn inactive_via_cfg_attr() {
+        cov_mark::check!(cfg_attr_active);
         check(
             r#"
     #[cfg_attr(not(never), cfg(no))] fn f() {}
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 9aa7aed16964..8b708f229d00 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
@@ -8,7 +8,7 @@ pub(crate) fn invalid_derive_target(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::InvalidDeriveTarget,
 ) -> Diagnostic {
-    let display_range = ctx.sema.diagnostics_display_range_for_range(d.range);
+    let display_range = ctx.sema.diagnostics_display_range(d.node);
 
     Diagnostic::new(
         DiagnosticCode::RustcHardError("E0774"),
@@ -29,7 +29,7 @@ mod tests {
 //- minicore:derive
 mod __ {
     #[derive()]
-   // ^^^^^^ error: `derive` may only be applied to `struct`s, `enum`s and `union`s
+  //^^^^^^^^^^^ error: `derive` may only be applied to `struct`s, `enum`s and `union`s
     fn main() {}
 }
             "#,
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 a44b043f433c..6a1ecae65150 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
@@ -13,7 +13,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
 // 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 {
     // Use more accurate position if available.
-    let display_range = ctx.sema.diagnostics_display_range_for_range(d.range);
+    let display_range = ctx.resolve_precise_location(&d.node, d.precise_location);
     Diagnostic::new(
         DiagnosticCode::Ra(d.kind, if d.error { Severity::Error } else { Severity::WeakWarning }),
         d.message.clone(),
@@ -27,10 +27,8 @@ pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) ->
 // This diagnostic is shown for macro expansion errors.
 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)),
-        None => ctx.sema.diagnostics_display_range(d.node.map(|it| it.syntax_node_ptr())),
-    };
+    let display_range =
+        ctx.resolve_precise_location(&d.node.map(|it| it.syntax_node_ptr()), d.name);
     Diagnostic::new(
         DiagnosticCode::Ra("macro-def-error", Severity::Error),
         d.message.clone(),
@@ -137,12 +135,10 @@ macro_rules! env { () => {} }
 #[rustc_builtin_macro]
 macro_rules! concat { () => {} }
 
-  include!(concat!(
-        // ^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run
-    env!(
-  //^^^ error: `OUT_DIR` not set, build scripts may have failed to run
-        "OUT_DIR"), "/out.rs"));
-      //^^^^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run
+  include!(concat!(env!("OUT_DIR"), "/out.rs"));
+                      //^^^^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run
+                 //^^^^^^^^^^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run
+         //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run
 "#,
         );
     }
@@ -186,7 +182,7 @@ fn main() {
            //^^^^^^^^^^^^^^^^ error: failed to load file `does not exist`
 
     include!(concat!("does ", "not ", "exist"));
-                  // ^^^^^^^^^^^^^^^^^^^^^^^^ error: failed to load file `does not exist`
+                  //^^^^^^^^^^^^^^^^^^^^^^^^^^ error: failed to load file `does not exist`
 
     env!(invalid);
        //^^^^^^^ error: expected string literal
@@ -293,7 +289,7 @@ include!("include-me.rs");
 //- /include-me.rs
 /// long doc that pushes the diagnostic range beyond the first file's text length
   #[err]
- // ^^^ error: unresolved macro `err`
+//^^^^^^error: unresolved macro `err`
 mod prim_never {}
 "#,
         );
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 7d0c71f4fa7c..701b30b9b593 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
@@ -7,7 +7,7 @@ pub(crate) fn malformed_derive(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::MalformedDerive,
 ) -> Diagnostic {
-    let display_range = ctx.sema.diagnostics_display_range_for_range(d.range);
+    let display_range = ctx.sema.diagnostics_display_range(d.node);
 
     Diagnostic::new(
         DiagnosticCode::RustcHardError("E0777"),
@@ -28,7 +28,7 @@ mod tests {
 //- minicore:derive
 mod __ {
     #[derive = "aaaa"]
-   // ^^^^^^^^^^^^^^^ error: malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]`
+  //^^^^^^^^^^^^^^^^^^ error: malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]`
     struct Foo;
 }
             "#,
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 030c82ca0ba7..a87b8c42ac1d 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
@@ -8,7 +8,8 @@ pub(crate) fn unresolved_macro_call(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::UnresolvedMacroCall,
 ) -> Diagnostic {
-    let display_range = ctx.sema.diagnostics_display_range_for_range(d.range);
+    // Use more accurate position if available.
+    let display_range = ctx.resolve_precise_location(&d.macro_call, d.precise_location);
     let bang = if d.is_bang { "!" } else { "" };
     Diagnostic::new(
         DiagnosticCode::RustcHardError("unresolved-macro-call"),
@@ -75,7 +76,7 @@ self::m!(); self::m2!();
             r#"
     mod _test_inner {
         #![empty_attr]
-        // ^^^^^^^^^^ error: unresolved macro `empty_attr`
+      //^^^^^^^^^^^^^^ error: unresolved macro `empty_attr`
     }
 "#,
         );
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 5c8f030de4de..1530e6465246 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -102,7 +102,7 @@ use ide_db::{
 use itertools::Itertools;
 use syntax::{
     AstPtr, Edition, NodeOrToken, SmolStr, SyntaxKind, SyntaxNode, SyntaxNodePtr, T, TextRange,
-    ast::{self, AstNode},
+    ast::{self, AstNode, HasAttrs},
 };
 
 // FIXME: Make this an enum
@@ -277,6 +277,31 @@ struct DiagnosticsContext<'a> {
     is_nightly: bool,
 }
 
+impl DiagnosticsContext<'_> {
+    fn resolve_precise_location(
+        &self,
+        node: &InFile,
+        precise_location: Option,
+    ) -> FileRange {
+        let sema = &self.sema;
+        (|| {
+            let precise_location = precise_location?;
+            let root = sema.parse_or_expand(node.file_id);
+            match root.covering_element(precise_location) {
+                syntax::NodeOrToken::Node(it) => Some(sema.original_range(&it)),
+                syntax::NodeOrToken::Token(it) => {
+                    node.with_value(it).original_file_range_opt(sema.db)
+                }
+            }
+        })()
+        .map(|frange| ide_db::FileRange {
+            file_id: frange.file_id.file_id(self.sema.db),
+            range: frange.range,
+        })
+        .unwrap_or_else(|| sema.diagnostics_display_range(*node))
+    }
+}
+
 /// Request parser level diagnostics for the given [`FileId`].
 pub fn syntax_diagnostics(
     db: &RootDatabase,
@@ -292,7 +317,7 @@ pub fn syntax_diagnostics(
     let sema = Semantics::new(db);
     let editioned_file_id = sema
         .attach_first_edition(file_id)
-        .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, file_id));
+        .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id));
 
     let (file_id, _) = editioned_file_id.unpack(db);
 
@@ -323,7 +348,7 @@ pub fn semantic_diagnostics(
     let sema = Semantics::new(db);
     let editioned_file_id = sema
         .attach_first_edition(file_id)
-        .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, file_id));
+        .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id));
 
     let (file_id, edition) = editioned_file_id.unpack(db);
     let mut res = Vec::new();
@@ -401,7 +426,7 @@ pub fn semantic_diagnostics(
                         Diagnostic::new(
                             DiagnosticCode::SyntaxError,
                             format!("Syntax Error in Expansion: {err}"),
-                            ctx.sema.diagnostics_display_range_for_range(d.range),
+                            ctx.resolve_precise_location(&d.node.clone(), d.precise_location),
                         )
                 }));
                 continue;
@@ -652,7 +677,7 @@ fn find_outline_mod_lint_severity(
     let lint_groups = lint_groups(&diag.code, edition);
     lint_attrs(
         sema,
-        &ast::AnyHasAttrs::cast(module_source_file.value).expect("SourceFile always has attrs"),
+        ast::AnyHasAttrs::cast(module_source_file.value).expect("SourceFile always has attrs"),
         edition,
     )
     .for_each(|(lint, severity)| {
@@ -673,7 +698,7 @@ fn lint_severity_at(
         .ancestors()
         .filter_map(ast::AnyHasAttrs::cast)
         .find_map(|ancestor| {
-            lint_attrs(sema, &ancestor, edition)
+            lint_attrs(sema, ancestor, edition)
                 .find_map(|(lint, severity)| lint_groups.contains(&lint).then_some(severity))
         })
         .or_else(|| {
@@ -681,13 +706,13 @@ fn lint_severity_at(
         })
 }
 
-// FIXME: Switch this to analysis' `expand_cfg_attr`.
 fn lint_attrs<'a>(
     sema: &'a Semantics<'a, RootDatabase>,
-    ancestor: &'a ast::AnyHasAttrs,
+    ancestor: ast::AnyHasAttrs,
     edition: Edition,
 ) -> impl Iterator + 'a {
-    ast::attrs_including_inner(ancestor)
+    ancestor
+        .attrs_including_inner()
         .filter_map(|attr| {
             attr.as_simple_call().and_then(|(name, value)| match &*name {
                 "allow" | "expect" => Some(Either::Left(iter::once((Severity::Allow, value)))),
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs
index de26879c2959..181cc74a51d4 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs
@@ -17,7 +17,7 @@ pub fn ssr_from_comment(
     frange: FileRange,
 ) -> Option<(MatchFinder<'_>, TextRange)> {
     let comment = {
-        let file_id = EditionedFileId::current_edition_guess_origin(db, frange.file_id);
+        let file_id = EditionedFileId::current_edition(db, frange.file_id);
 
         let file = db.parse(file_id);
         file.tree().syntax().token_at_offset(frange.range.start()).find_map(ast::Comment::cast)
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
index 7b2142a9f348..43ad12c1f699 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
@@ -125,9 +125,9 @@ impl<'db> MatchFinder<'db> {
     ) -> Result, SsrError> {
         restrict_ranges.retain(|range| !range.range.is_empty());
         let sema = Semantics::new(db);
-        let file_id = sema.attach_first_edition(lookup_context.file_id).unwrap_or_else(|| {
-            EditionedFileId::current_edition_guess_origin(db, lookup_context.file_id)
-        });
+        let file_id = sema
+            .attach_first_edition(lookup_context.file_id)
+            .unwrap_or_else(|| EditionedFileId::current_edition(db, lookup_context.file_id));
         let resolution_scope = resolving::ResolutionScope::new(
             &sema,
             hir::FilePosition { file_id, offset: lookup_context.offset },
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs
index d23d22b4e898..72f857ceda90 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs
@@ -135,9 +135,11 @@ impl<'db> MatchFinder<'db> {
         // seems to get put into a single source root.
         let mut files = Vec::new();
         self.search_files_do(|file_id| {
-            files.push(self.sema.attach_first_edition(file_id).unwrap_or_else(|| {
-                EditionedFileId::current_edition_guess_origin(self.sema.db, file_id)
-            }));
+            files.push(
+                self.sema
+                    .attach_first_edition(file_id)
+                    .unwrap_or_else(|| EditionedFileId::current_edition(self.sema.db, file_id)),
+            );
         });
         SearchScope::files(&files)
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index 0ed91cf7f588..c197d559aa89 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -13,13 +13,13 @@ use stdx::format_to;
 use url::Url;
 
 use hir::{
-    Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrsWithOwner, HasAttrs, db::HirDatabase,
+    Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrsWithOwner, HasAttrs, db::HirDatabase, sym,
 };
 use ide_db::{
     RootDatabase,
     base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, RootQueryDb},
     defs::{Definition, NameClass, NameRefClass},
-    documentation::{Documentation, HasDocs},
+    documentation::{DocsRangeMap, Documentation, HasDocs, docs_with_rangemap},
     helpers::pick_best_token,
 };
 use syntax::{
@@ -54,7 +54,7 @@ pub(crate) fn rewrite_links(
     db: &RootDatabase,
     markdown: &str,
     definition: Definition,
-    range_map: Option<&hir::Docs>,
+    range_map: Option,
 ) -> String {
     let mut cb = broken_link_clone_cb;
     let doc = Parser::new_with_broken_link_callback(markdown, MARKDOWN_OPTIONS, Some(&mut cb))
@@ -74,9 +74,9 @@ pub(crate) fn rewrite_links(
                 TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap());
             let is_inner_doc = range_map
                 .as_ref()
-                .and_then(|range_map| range_map.find_ast_range(text_range))
-                .map(|(_, is_inner)| is_inner)
-                .unwrap_or(hir::IsInnerDoc::No);
+                .and_then(|range_map| range_map.map(text_range))
+                .map(|(_, attr_id)| attr_id.is_inner_attr())
+                .unwrap_or(false);
             if let Some((target, title)) =
                 rewrite_intra_doc_link(db, definition, target, title, is_inner_doc, link_type)
             {
@@ -187,7 +187,7 @@ pub(crate) fn external_docs(
 /// Extracts all links from a given markdown text returning the definition text range, link-text
 /// and the namespace if known.
 pub(crate) fn extract_definitions_from_docs(
-    docs: &Documentation<'_>,
+    docs: &Documentation,
 ) -> Vec<(TextRange, String, Option)> {
     Parser::new_with_broken_link_callback(
         docs.as_str(),
@@ -214,7 +214,7 @@ pub(crate) fn resolve_doc_path_for_def(
     def: Definition,
     link: &str,
     ns: Option,
-    is_inner_doc: hir::IsInnerDoc,
+    is_inner_doc: bool,
 ) -> Option {
     match def {
         Definition::Module(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
@@ -324,11 +324,11 @@ impl DocCommentToken {
             let token_start = t.text_range().start();
             let abs_in_expansion_offset = token_start + relative_comment_offset + descended_prefix_len;
             let (attributes, def) = Self::doc_attributes(sema, &node, is_inner)?;
-            let doc_mapping = attributes.hir_docs(sema.db)?;
+            let (docs, doc_mapping) = docs_with_rangemap(sema.db, &attributes)?;
             let (in_expansion_range, link, ns, is_inner) =
-                extract_definitions_from_docs(&Documentation::new_borrowed(doc_mapping.docs())).into_iter().find_map(|(range, link, ns)| {
-                    let (mapped, is_inner) = doc_mapping.find_ast_range(range)?;
-                    (mapped.value.contains(abs_in_expansion_offset)).then_some((mapped.value, link, ns, is_inner))
+                extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| {
+                    let (mapped, idx) = doc_mapping.map(range)?;
+                    (mapped.value.contains(abs_in_expansion_offset)).then_some((mapped.value, link, ns, idx.is_inner_attr()))
                 })?;
             // get the relative range to the doc/attribute in the expansion
             let in_expansion_relative_range = in_expansion_range - descended_prefix_len - token_start;
@@ -416,7 +416,7 @@ fn rewrite_intra_doc_link(
     def: Definition,
     target: &str,
     title: &str,
-    is_inner_doc: hir::IsInnerDoc,
+    is_inner_doc: bool,
     link_type: LinkType,
 ) -> Option<(String, String)> {
     let (link, ns) = parse_intra_doc_link(target);
@@ -659,12 +659,14 @@ fn filename_and_frag_for_def(
         Definition::Crate(_) => String::from("index.html"),
         Definition::Module(m) => match m.name(db) {
             // `#[doc(keyword = "...")]` is internal used only by rust compiler
-            Some(name) => match m.doc_keyword(db) {
-                Some(kw) => {
-                    format!("keyword.{kw}.html")
+            Some(name) => {
+                match m.attrs(db).by_key(sym::doc).find_string_value_in_tt(sym::keyword) {
+                    Some(kw) => {
+                        format!("keyword.{kw}.html")
+                    }
+                    None => format!("{}/index.html", name.as_str()),
                 }
-                None => format!("{}/index.html", name.as_str()),
-            },
+            }
             None => String::from("index.html"),
         },
         Definition::Trait(t) => {
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
index 34ffc11c4b5f..3fd885535a23 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
@@ -1,11 +1,11 @@
-use std::{borrow::Cow, iter};
+use std::iter;
 
 use expect_test::{Expect, expect};
 use hir::Semantics;
 use ide_db::{
     FilePosition, FileRange, RootDatabase,
     defs::Definition,
-    documentation::{Documentation, HasDocs},
+    documentation::{DocsRangeMap, Documentation, HasDocs},
 };
 use itertools::Itertools;
 use syntax::{AstNode, SyntaxNode, ast, match_ast};
@@ -45,9 +45,9 @@ fn check_external_docs(
 fn check_rewrite(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
     let sema = &Semantics::new(&analysis.db);
-    let (cursor_def, docs) = def_under_cursor(sema, &position);
+    let (cursor_def, docs, range) = def_under_cursor(sema, &position);
     let res =
-        hir::attach_db(sema.db, || rewrite_links(sema.db, docs.docs(), cursor_def, Some(&docs)));
+        hir::attach_db(sema.db, || rewrite_links(sema.db, docs.as_str(), cursor_def, Some(range)));
     expect.assert_eq(&res)
 }
 
@@ -57,36 +57,33 @@ fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
     let (analysis, position, mut expected) = fixture::annotations(ra_fixture);
     expected.sort_by_key(key_fn);
     let sema = &Semantics::new(&analysis.db);
-    hir::attach_db(sema.db, || {
-        let (cursor_def, docs) = def_under_cursor(sema, &position);
-        let defs = extract_definitions_from_docs(&Documentation::new_borrowed(docs.docs()));
-        let actual: Vec<_> = defs
-            .into_iter()
-            .flat_map(|(text_range, link, ns)| {
-                let attr = docs.find_ast_range(text_range);
-                let is_inner_attr =
-                    attr.map(|(_file, is_inner)| is_inner).unwrap_or(hir::IsInnerDoc::No);
-                let def = resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr)
-                    .unwrap_or_else(|| panic!("Failed to resolve {link}"));
-                def.try_to_nav(sema).unwrap().into_iter().zip(iter::repeat(link))
-            })
-            .map(|(nav_target, link)| {
-                let range = FileRange {
-                    file_id: nav_target.file_id,
-                    range: nav_target.focus_or_full_range(),
-                };
-                (range, link)
-            })
-            .sorted_by_key(key_fn)
-            .collect();
-        assert_eq!(expected, actual);
-    });
+    let (cursor_def, docs, range) = def_under_cursor(sema, &position);
+    let defs = extract_definitions_from_docs(&docs);
+    let actual: Vec<_> = defs
+        .into_iter()
+        .flat_map(|(text_range, link, ns)| {
+            let attr = range.map(text_range);
+            let is_inner_attr = attr.map(|(_file, attr)| attr.is_inner_attr()).unwrap_or(false);
+            let def = hir::attach_db(sema.db, || {
+                resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr)
+                    .unwrap_or_else(|| panic!("Failed to resolve {link}"))
+            });
+            def.try_to_nav(sema).unwrap().into_iter().zip(iter::repeat(link))
+        })
+        .map(|(nav_target, link)| {
+            let range =
+                FileRange { file_id: nav_target.file_id, range: nav_target.focus_or_full_range() };
+            (range, link)
+        })
+        .sorted_by_key(key_fn)
+        .collect();
+    assert_eq!(expected, actual);
 }
 
-fn def_under_cursor<'db>(
-    sema: &Semantics<'db, RootDatabase>,
+fn def_under_cursor(
+    sema: &Semantics<'_, RootDatabase>,
     position: &FilePosition,
-) -> (Definition, Cow<'db, hir::Docs>) {
+) -> (Definition, Documentation, DocsRangeMap) {
     let (docs, def) = sema
         .parse_guess_edition(position.file_id)
         .syntax()
@@ -97,14 +94,14 @@ fn def_under_cursor<'db>(
         .find_map(|it| node_to_def(sema, &it))
         .expect("no def found")
         .unwrap();
-    let docs = docs.expect("no docs found for cursor def");
-    (def, docs)
+    let (docs, range) = docs.expect("no docs found for cursor def");
+    (def, docs, range)
 }
 
-fn node_to_def<'db>(
-    sema: &Semantics<'db, RootDatabase>,
+fn node_to_def(
+    sema: &Semantics<'_, RootDatabase>,
     node: &SyntaxNode,
-) -> Option>, Definition)>> {
+) -> Option, Definition)>> {
     Some(match_ast! {
         match node {
             ast::SourceFile(it)  => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Module(def))),
diff --git a/src/tools/rust-analyzer/crates/ide/src/fixture.rs b/src/tools/rust-analyzer/crates/ide/src/fixture.rs
index 1a8591d25dca..fbf89042fae1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/fixture.rs
@@ -7,10 +7,10 @@ use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
 /// Creates analysis for a single file.
 pub(crate) fn file(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Analysis, FileId) {
     let mut host = AnalysisHost::default();
-    let change_fixture = ChangeFixture::parse(ra_fixture);
+    let change_fixture = ChangeFixture::parse(&host.db, ra_fixture);
     host.db.enable_proc_attr_macros();
     host.db.apply_change(change_fixture.change);
-    (host.analysis(), change_fixture.files[0].file_id())
+    (host.analysis(), change_fixture.files[0].file_id(&host.db))
 }
 
 /// Creates analysis from a multi-file fixture, returns positions marked with $0.
@@ -18,23 +18,23 @@ pub(crate) fn position(
     #[rust_analyzer::rust_fixture] ra_fixture: &str,
 ) -> (Analysis, FilePosition) {
     let mut host = AnalysisHost::default();
-    let change_fixture = ChangeFixture::parse(ra_fixture);
+    let change_fixture = ChangeFixture::parse(&host.db, ra_fixture);
     host.db.enable_proc_attr_macros();
     host.db.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
     let offset = range_or_offset.expect_offset();
-    (host.analysis(), FilePosition { file_id: file_id.file_id(), offset })
+    (host.analysis(), FilePosition { file_id: file_id.file_id(&host.db), offset })
 }
 
 /// Creates analysis for a single file, returns range marked with a pair of $0.
 pub(crate) fn range(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Analysis, FileRange) {
     let mut host = AnalysisHost::default();
-    let change_fixture = ChangeFixture::parse(ra_fixture);
+    let change_fixture = ChangeFixture::parse(&host.db, ra_fixture);
     host.db.enable_proc_attr_macros();
     host.db.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
     let range = range_or_offset.expect_range();
-    (host.analysis(), FileRange { file_id: file_id.file_id(), range })
+    (host.analysis(), FileRange { file_id: file_id.file_id(&host.db), range })
 }
 
 /// Creates analysis for a single file, returns range marked with a pair of $0 or a position marked with $0.
@@ -42,11 +42,11 @@ pub(crate) fn range_or_position(
     #[rust_analyzer::rust_fixture] ra_fixture: &str,
 ) -> (Analysis, FileId, RangeOrOffset) {
     let mut host = AnalysisHost::default();
-    let change_fixture = ChangeFixture::parse(ra_fixture);
+    let change_fixture = ChangeFixture::parse(&host.db, ra_fixture);
     host.db.enable_proc_attr_macros();
     host.db.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
-    (host.analysis(), file_id.file_id(), range_or_offset)
+    (host.analysis(), file_id.file_id(&host.db), range_or_offset)
 }
 
 /// Creates analysis from a multi-file fixture, returns positions marked with $0.
@@ -54,24 +54,25 @@ pub(crate) fn annotations(
     #[rust_analyzer::rust_fixture] ra_fixture: &str,
 ) -> (Analysis, FilePosition, Vec<(FileRange, String)>) {
     let mut host = AnalysisHost::default();
-    let change_fixture = ChangeFixture::parse(ra_fixture);
+    let change_fixture = ChangeFixture::parse(&host.db, ra_fixture);
     host.db.enable_proc_attr_macros();
     host.db.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
     let offset = range_or_offset.expect_offset();
 
+    let db = &host.db;
     let annotations = change_fixture
         .files
         .iter()
         .flat_map(|&file_id| {
-            let file_text = host.analysis().file_text(file_id.file_id()).unwrap();
+            let file_text = host.analysis().file_text(file_id.file_id(&host.db)).unwrap();
             let annotations = extract_annotations(&file_text);
             annotations
                 .into_iter()
-                .map(move |(range, data)| (FileRange { file_id: file_id.file_id(), range }, data))
+                .map(move |(range, data)| (FileRange { file_id: file_id.file_id(db), range }, data))
         })
         .collect();
-    (host.analysis(), FilePosition { file_id: file_id.file_id(), offset }, annotations)
+    (host.analysis(), FilePosition { file_id: file_id.file_id(&host.db), offset }, annotations)
 }
 
 /// Creates analysis from a multi-file fixture with annotations without $0
@@ -79,19 +80,20 @@ pub(crate) fn annotations_without_marker(
     #[rust_analyzer::rust_fixture] ra_fixture: &str,
 ) -> (Analysis, Vec<(FileRange, String)>) {
     let mut host = AnalysisHost::default();
-    let change_fixture = ChangeFixture::parse(ra_fixture);
+    let change_fixture = ChangeFixture::parse(&host.db, ra_fixture);
     host.db.enable_proc_attr_macros();
     host.db.apply_change(change_fixture.change);
 
+    let db = &host.db;
     let annotations = change_fixture
         .files
         .iter()
         .flat_map(|&file_id| {
-            let file_text = host.analysis().file_text(file_id.file_id()).unwrap();
+            let file_text = host.analysis().file_text(file_id.file_id(db)).unwrap();
             let annotations = extract_annotations(&file_text);
             annotations
                 .into_iter()
-                .map(move |(range, data)| (FileRange { file_id: file_id.file_id(), range }, data))
+                .map(move |(range, data)| (FileRange { file_id: file_id.file_id(db), range }, data))
         })
         .collect();
     (host.analysis(), annotations)
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
index cc333d66caf3..875403c4e32a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
@@ -355,7 +355,7 @@ trait Bar {}
 
 fn test() {
     #[derive(Copy)]
-   // ^^^^^^^^^^^^
+  //^^^^^^^^^^^^^^^
     struct Foo$0;
 
     impl Foo {}
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 f7870032ea28..04ce5a7567f3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
@@ -62,7 +62,7 @@ pub(crate) fn highlight_related(
     let _p = tracing::info_span!("highlight_related").entered();
     let file_id = sema
         .attach_first_edition(file_id)
-        .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(sema.db, file_id));
+        .unwrap_or_else(|| EditionedFileId::current_edition(sema.db, file_id));
     let syntax = sema.parse(file_id).syntax().clone();
 
     let token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind {
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 5bdfb5735658..a1eff3aaee78 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -1,5 +1,5 @@
 //! Logic for rendering the different hover messages
-use std::{borrow::Cow, env, mem, ops::Not};
+use std::{env, mem, ops::Not};
 
 use either::Either;
 use hir::{
@@ -11,7 +11,7 @@ use hir::{
 use ide_db::{
     RootDatabase,
     defs::{Definition, find_std_module},
-    documentation::{Documentation, HasDocs},
+    documentation::{DocsRangeMap, HasDocs},
     famous_defs::FamousDefs,
     generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
     syntax_helpers::prettify_macro_expansion,
@@ -278,9 +278,9 @@ pub(super) fn keyword(
         keyword_hints(sema, token, parent, edition, display_target);
 
     let doc_owner = find_std_module(&famous_defs, &keyword_mod, edition)?;
-    let docs = doc_owner.docs_with_rangemap(sema.db)?;
+    let (docs, range_map) = doc_owner.docs_with_rangemap(sema.db)?;
     let (markup, range_map) =
-        markup(Some(Either::Left(docs)), description, None, None, String::new());
+        markup(Some(docs.into()), Some(range_map), description, None, None, String::new());
     let markup = process_markup(sema.db, Definition::Module(doc_owner), &markup, range_map, config);
     Some(HoverResult { markup, actions })
 }
@@ -370,12 +370,12 @@ pub(super) fn process_markup(
     db: &RootDatabase,
     def: Definition,
     markup: &Markup,
-    markup_range_map: Option,
+    markup_range_map: Option,
     config: &HoverConfig<'_>,
 ) -> Markup {
     let markup = markup.as_str();
     let markup = if config.links_in_hover {
-        rewrite_links(db, markup, def, markup_range_map.as_ref())
+        rewrite_links(db, markup, def, markup_range_map)
     } else {
         remove_links(markup)
     };
@@ -484,7 +484,7 @@ pub(super) fn definition(
     config: &HoverConfig<'_>,
     edition: Edition,
     display_target: DisplayTarget,
-) -> (Markup, Option) {
+) -> (Markup, Option) {
     let mod_path = definition_path(db, &def, edition);
     let label = match def {
         Definition::Trait(trait_) => trait_
@@ -520,7 +520,12 @@ pub(super) fn definition(
         }
         _ => def.label(db, display_target),
     };
-    let docs = def.docs_with_rangemap(db, famous_defs, display_target);
+    let (docs, range_map) =
+        if let Some((docs, doc_range)) = def.docs_with_rangemap(db, famous_defs, display_target) {
+            (Some(docs), doc_range)
+        } else {
+            (None, None)
+        };
     let value = || match def {
         Definition::Variant(it) => {
             if !it.parent_enum(db).is_data_carrying(db) {
@@ -837,7 +842,14 @@ pub(super) fn definition(
         }
     };
 
-    markup(docs, desc, extra.is_empty().not().then_some(extra), mod_path, subst_types)
+    markup(
+        docs.map(Into::into),
+        range_map,
+        desc,
+        extra.is_empty().not().then_some(extra),
+        mod_path,
+        subst_types,
+    )
 }
 
 #[derive(Debug)]
@@ -1112,12 +1124,13 @@ fn definition_path(db: &RootDatabase, &def: &Definition, edition: Edition) -> Op
 }
 
 fn markup(
-    docs: Option, Documentation<'_>>>,
+    docs: Option,
+    range_map: Option,
     rust: String,
     extra: Option,
     mod_path: Option,
     subst_types: String,
-) -> (Markup, Option) {
+) -> (Markup, Option) {
     let mut buf = String::new();
 
     if let Some(mod_path) = mod_path
@@ -1138,21 +1151,10 @@ fn markup(
     if let Some(doc) = docs {
         format_to!(buf, "\n___\n\n");
         let offset = TextSize::new(buf.len() as u32);
-        let docs_str = match &doc {
-            Either::Left(docs) => docs.docs(),
-            Either::Right(docs) => docs.as_str(),
-        };
-        format_to!(buf, "{}", docs_str);
-        let range_map = match doc {
-            Either::Left(range_map) => {
-                let mut range_map = range_map.into_owned();
-                range_map.shift_by(offset);
-                Some(range_map)
-            }
-            Either::Right(_) => None,
-        };
+        let buf_range_map = range_map.map(|range_map| range_map.shift_docstring_line_range(offset));
+        format_to!(buf, "{}", doc);
 
-        (buf.into(), range_map)
+        (buf.into(), buf_range_map)
     } else {
         (buf.into(), None)
     }
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 d474e50d3c2c..21550d5e6665 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -90,7 +90,7 @@ pub(crate) fn inlay_hints(
     let sema = Semantics::new(db);
     let file_id = sema
         .attach_first_edition(file_id)
-        .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, file_id));
+        .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id));
     let file = sema.parse(file_id);
     let file = file.syntax();
 
@@ -143,7 +143,7 @@ pub(crate) fn inlay_hints_resolve(
     let sema = Semantics::new(db);
     let file_id = sema
         .attach_first_edition(file_id)
-        .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, file_id));
+        .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id));
     let file = sema.parse(file_id);
     let file = file.syntax();
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index a633877adb4e..857252832ffe 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -331,8 +331,7 @@ impl Analysis {
     pub fn parse(&self, file_id: FileId) -> Cancellable {
         // FIXME edition
         self.with_db(|db| {
-            let editioned_file_id_wrapper =
-                EditionedFileId::current_edition_guess_origin(&self.db, file_id);
+            let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id);
 
             db.parse(editioned_file_id_wrapper).tree()
         })
@@ -361,7 +360,7 @@ impl Analysis {
     /// supported).
     pub fn matching_brace(&self, position: FilePosition) -> Cancellable> {
         self.with_db(|db| {
-            let file_id = EditionedFileId::current_edition_guess_origin(&self.db, position.file_id);
+            let file_id = EditionedFileId::current_edition(&self.db, position.file_id);
             let parse = db.parse(file_id);
             let file = parse.tree();
             matching_brace::matching_brace(&file, position.offset)
@@ -422,7 +421,7 @@ impl Analysis {
     pub fn join_lines(&self, config: &JoinLinesConfig, frange: FileRange) -> Cancellable {
         self.with_db(|db| {
             let editioned_file_id_wrapper =
-                EditionedFileId::current_edition_guess_origin(&self.db, frange.file_id);
+                EditionedFileId::current_edition(&self.db, frange.file_id);
             let parse = db.parse(editioned_file_id_wrapper);
             join_lines::join_lines(config, &parse.tree(), frange.range)
         })
@@ -463,8 +462,7 @@ impl Analysis {
     ) -> Cancellable> {
         // FIXME: Edition
         self.with_db(|db| {
-            let editioned_file_id_wrapper =
-                EditionedFileId::current_edition_guess_origin(&self.db, file_id);
+            let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id);
             let source_file = db.parse(editioned_file_id_wrapper).tree();
             file_structure::file_structure(&source_file, config)
         })
@@ -495,8 +493,7 @@ impl Analysis {
     /// Returns the set of folding ranges.
     pub fn folding_ranges(&self, file_id: FileId) -> Cancellable> {
         self.with_db(|db| {
-            let editioned_file_id_wrapper =
-                EditionedFileId::current_edition_guess_origin(&self.db, file_id);
+            let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id);
 
             folding_ranges::folding_ranges(&db.parse(editioned_file_id_wrapper).tree())
         })
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 8e73ddf8bfc3..b222ff3eec0b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
@@ -54,8 +54,7 @@ pub struct NavigationTarget {
     // FIXME: Symbol
     pub container_name: Option,
     pub description: Option,
-    // FIXME: Use the database lifetime here.
-    pub docs: Option>,
+    pub docs: Option,
     /// In addition to a `name` field, a `NavigationTarget` may also be aliased
     /// In such cases we want a `NavigationTarget` to be accessible by its alias
     // FIXME: Symbol
@@ -164,7 +163,7 @@ impl NavigationTarget {
                             full_range,
                             SymbolKind::Module,
                         );
-                        res.docs = module.docs(db).map(Documentation::into_owned);
+                        res.docs = module.docs(db);
                         res.description = Some(
                             module.display(db, module.krate().to_display_target(db)).to_string(),
                         );
@@ -438,7 +437,7 @@ where
                 D::KIND,
             )
             .map(|mut res| {
-                res.docs = self.docs(db).map(Documentation::into_owned);
+                res.docs = self.docs(db);
                 res.description = hir::attach_db(db, || {
                     Some(self.display(db, self.krate(db).to_display_target(db)).to_string())
                 });
@@ -537,7 +536,7 @@ impl TryToNav for hir::ExternCrateDecl {
                     SymbolKind::Module,
                 );
 
-                res.docs = self.docs(db).map(Documentation::into_owned);
+                res.docs = self.docs(db);
                 res.description = Some(self.display(db, krate.to_display_target(db)).to_string());
                 res.container_name = container_name(db, *self, edition);
                 res
@@ -559,9 +558,10 @@ impl TryToNav for hir::Field {
             FieldSource::Named(it) => {
                 NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field).map(
                     |mut res| {
-                        res.docs = self.docs(db).map(Documentation::into_owned);
-                        res.description =
-                            Some(self.display(db, krate.to_display_target(db)).to_string());
+                        res.docs = self.docs(db);
+                        res.description = hir::attach_db(db, || {
+                            Some(self.display(db, krate.to_display_target(db)).to_string())
+                        });
                         res
                     },
                 )
@@ -600,7 +600,7 @@ impl TryToNav for hir::Macro {
                 self.kind(db).into(),
             )
             .map(|mut res| {
-                res.docs = self.docs(db).map(Documentation::into_owned);
+                res.docs = self.docs(db);
                 res
             }),
         )
@@ -939,7 +939,7 @@ pub(crate) fn orig_range_with_focus_r(
 ) -> UpmappingResult<(FileRange, Option)> {
     let Some(name) = focus_range else { return orig_range_r(db, hir_file, value) };
 
-    let call = || db.lookup_intern_macro_call(hir_file.macro_file().unwrap());
+    let call_kind = || db.lookup_intern_macro_call(hir_file.macro_file().unwrap()).kind;
 
     let def_range =
         || db.lookup_intern_macro_call(hir_file.macro_file().unwrap()).def.definition_range(db);
@@ -965,8 +965,7 @@ pub(crate) fn orig_range_with_focus_r(
                             // name lies outside the node, so instead point to the macro call which
                             // *should* contain the name
                             _ => {
-                                let call = call();
-                                let kind = call.kind;
+                                let kind = call_kind();
                                 let range = kind.clone().original_call_range_with_input(db);
                                 //If the focus range is in the attribute/derive body, we
                                 // need to point the call site to the entire body, if not, fall back
@@ -978,7 +977,7 @@ pub(crate) fn orig_range_with_focus_r(
                                 {
                                     range
                                 } else {
-                                    kind.original_call_range(db, call.krate)
+                                    kind.original_call_range(db)
                                 }
                             }
                         },
@@ -1007,14 +1006,11 @@ pub(crate) fn orig_range_with_focus_r(
                         },
                     ),
                     // node is in macro def, just show the focus
-                    _ => {
-                        let call = call();
-                        (
-                            // show the macro call
-                            (call.kind.original_call_range(db, call.krate), None),
-                            Some((focus_range, Some(focus_range))),
-                        )
-                    }
+                    _ => (
+                        // show the macro call
+                        (call_kind().original_call_range(db), None),
+                        Some((focus_range, Some(focus_range))),
+                    ),
                 }
             }
             // lost name? can't happen for single tokens
diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs
index c4dcd588d693..a53a19299727 100644
--- a/src/tools/rust-analyzer/crates/ide/src/references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/references.rs
@@ -1124,10 +1124,7 @@ pub(super) struct Foo$0 {
         check_with_scope(
             code,
             Some(&mut |db| {
-                SearchScope::single_file(EditionedFileId::current_edition_guess_origin(
-                    db,
-                    FileId::from_raw(2),
-                ))
+                SearchScope::single_file(EditionedFileId::current_edition(db, FileId::from_raw(2)))
             }),
             expect![[r#"
                 quux Function FileId(0) 19..35 26..30
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index 4b475dac87b5..494701d97def 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -3,13 +3,17 @@ use std::{fmt, sync::OnceLock};
 use arrayvec::ArrayVec;
 use ast::HasName;
 use cfg::{CfgAtom, CfgExpr};
-use hir::{AsAssocItem, HasAttrs, HasCrate, HasSource, Semantics, Symbol, db::HirDatabase, sym};
+use hir::{
+    AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, Semantics, Symbol, db::HirDatabase,
+    sym,
+};
 use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
 use ide_db::impl_empty_upmap_from_ra_fixture;
 use ide_db::{
     FilePosition, FxHashMap, FxIndexMap, FxIndexSet, RootDatabase, SymbolKind,
     base_db::RootQueryDb,
     defs::Definition,
+    documentation::docs_from_attrs,
     helpers::visit_file_defs,
     search::{FileReferenceNode, SearchScope},
 };
@@ -319,7 +323,7 @@ pub(crate) fn runnable_fn(
     def: hir::Function,
 ) -> Option {
     let edition = def.krate(sema.db).edition(sema.db);
-    let under_cfg_test = has_cfg_test(def.module(sema.db).attrs(sema.db).cfgs(sema.db));
+    let under_cfg_test = has_cfg_test(def.module(sema.db).attrs(sema.db));
     let kind = if !under_cfg_test && def.is_main(sema.db) {
         RunnableKind::Bin
     } else {
@@ -354,7 +358,7 @@ pub(crate) fn runnable_fn(
     let file_range = fn_source.syntax().original_file_range_with_macro_call_input(sema.db);
     let update_test = UpdateTest::find_snapshot_macro(sema, file_range);
 
-    let cfg = def.attrs(sema.db).cfgs(sema.db).cloned();
+    let cfg = def.attrs(sema.db).cfg();
     Some(Runnable { use_name_in_title: false, nav, kind, cfg, update_test })
 }
 
@@ -362,8 +366,8 @@ pub(crate) fn runnable_mod(
     sema: &Semantics<'_, RootDatabase>,
     def: hir::Module,
 ) -> Option {
-    let cfg = def.attrs(sema.db).cfgs(sema.db);
-    if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(cfg)) {
+    if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(def.attrs(sema.db)))
+    {
         return None;
     }
     let path = def
@@ -377,7 +381,8 @@ pub(crate) fn runnable_mod(
         })
         .join("::");
 
-    let cfg = cfg.cloned();
+    let attrs = def.attrs(sema.db);
+    let cfg = attrs.cfg();
     let nav = NavigationTarget::from_module_to_decl(sema.db, def).call_site();
 
     let module_source = sema.module_definition_node(def);
@@ -404,10 +409,10 @@ pub(crate) fn runnable_impl(
     let display_target = def.module(sema.db).krate().to_display_target(sema.db);
     let edition = display_target.edition;
     let attrs = def.attrs(sema.db);
-    if !has_runnable_doc_test(sema.db, &attrs) {
+    if !has_runnable_doc_test(&attrs) {
         return None;
     }
-    let cfg = attrs.cfgs(sema.db).cloned();
+    let cfg = attrs.cfg();
     let nav = def.try_to_nav(sema)?.call_site();
     let ty = def.self_ty(sema.db);
     let adt_name = ty.as_adt()?.name(sema.db);
@@ -437,16 +442,8 @@ pub(crate) fn runnable_impl(
     })
 }
 
-fn has_cfg_test(cfg: Option<&CfgExpr>) -> bool {
-    return cfg.is_some_and(has_cfg_test_impl);
-
-    fn has_cfg_test_impl(cfg: &CfgExpr) -> bool {
-        match cfg {
-            CfgExpr::Atom(CfgAtom::Flag(s)) => *s == sym::test,
-            CfgExpr::Any(cfgs) | CfgExpr::All(cfgs) => cfgs.iter().any(has_cfg_test_impl),
-            _ => false,
-        }
-    }
+fn has_cfg_test(attrs: AttrsWithOwner) -> bool {
+    attrs.cfgs().any(|cfg| matches!(&cfg, CfgExpr::Atom(CfgAtom::Flag(s)) if *s == sym::test))
 }
 
 /// Creates a test mod runnable for outline modules at the top of their definition.
@@ -456,8 +453,8 @@ fn runnable_mod_outline_definition(
 ) -> Option {
     def.as_source_file_id(sema.db)?;
 
-    let cfg = def.attrs(sema.db).cfgs(sema.db);
-    if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(cfg)) {
+    if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(def.attrs(sema.db)))
+    {
         return None;
     }
     let path = def
@@ -471,7 +468,8 @@ fn runnable_mod_outline_definition(
         })
         .join("::");
 
-    let cfg = cfg.cloned();
+    let attrs = def.attrs(sema.db);
+    let cfg = attrs.cfg();
 
     let mod_source = sema.module_definition_node(def);
     let mod_syntax = mod_source.file_syntax(sema.db);
@@ -510,7 +508,7 @@ fn module_def_doctest(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Op
     let display_target = krate
         .unwrap_or_else(|| (*db.all_crates().last().expect("no crate graph present")).into())
         .to_display_target(db);
-    if !has_runnable_doc_test(db, &attrs) {
+    if !has_runnable_doc_test(&attrs) {
         return None;
     }
     let def_name = def.name(db)?;
@@ -556,7 +554,7 @@ fn module_def_doctest(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Op
         use_name_in_title: false,
         nav,
         kind: RunnableKind::DocTest { test_id },
-        cfg: attrs.cfgs(db).cloned(),
+        cfg: attrs.cfg(),
         update_test: UpdateTest::default(),
     };
     Some(res)
@@ -573,15 +571,15 @@ impl TestAttr {
     }
 }
 
-fn has_runnable_doc_test(db: &RootDatabase, attrs: &hir::AttrsWithOwner) -> bool {
+fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
     const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
     const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
         &["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
 
-    attrs.hir_docs(db).is_some_and(|doc| {
+    docs_from_attrs(attrs).is_some_and(|doc| {
         let mut in_code_block = false;
 
-        for line in doc.docs().lines() {
+        for line in doc.lines() {
             if let Some(header) =
                 RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence))
             {
diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
index a8fc57a431b4..5f7e12cf53f8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -31,7 +31,7 @@ use crate::RootDatabase;
 /// edited.
 #[derive(Debug)]
 pub struct SignatureHelp {
-    pub doc: Option>,
+    pub doc: Option,
     pub signature: String,
     pub active_parameter: Option,
     parameters: Vec,
@@ -174,7 +174,7 @@ fn signature_help_for_call(
     let mut fn_params = None;
     match callable.kind() {
         hir::CallableKind::Function(func) => {
-            res.doc = func.docs(db).map(Documentation::into_owned);
+            res.doc = func.docs(db);
             format_to!(res.signature, "fn {}", func.name(db).display(db, edition));
 
             let generic_params = GenericDef::Function(func)
@@ -196,7 +196,7 @@ fn signature_help_for_call(
             });
         }
         hir::CallableKind::TupleStruct(strukt) => {
-            res.doc = strukt.docs(db).map(Documentation::into_owned);
+            res.doc = strukt.docs(db);
             format_to!(res.signature, "struct {}", strukt.name(db).display(db, edition));
 
             let generic_params = GenericDef::Adt(strukt.into())
@@ -209,7 +209,7 @@ fn signature_help_for_call(
             }
         }
         hir::CallableKind::TupleEnumVariant(variant) => {
-            res.doc = variant.docs(db).map(Documentation::into_owned);
+            res.doc = variant.docs(db);
             format_to!(
                 res.signature,
                 "enum {}",
@@ -314,33 +314,33 @@ fn signature_help_for_generics(
     let db = sema.db;
     match generics_def {
         hir::GenericDef::Function(it) => {
-            res.doc = it.docs(db).map(Documentation::into_owned);
+            res.doc = it.docs(db);
             format_to!(res.signature, "fn {}", it.name(db).display(db, edition));
         }
         hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
-            res.doc = it.docs(db).map(Documentation::into_owned);
+            res.doc = it.docs(db);
             format_to!(res.signature, "enum {}", it.name(db).display(db, edition));
             if let Some(variant) = variant {
                 // In paths, generics of an enum can be specified *after* one of its variants.
                 // eg. `None::`
                 // We'll use the signature of the enum, but include the docs of the variant.
-                res.doc = variant.docs(db).map(Documentation::into_owned);
+                res.doc = variant.docs(db);
             }
         }
         hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
-            res.doc = it.docs(db).map(Documentation::into_owned);
+            res.doc = it.docs(db);
             format_to!(res.signature, "struct {}", it.name(db).display(db, edition));
         }
         hir::GenericDef::Adt(hir::Adt::Union(it)) => {
-            res.doc = it.docs(db).map(Documentation::into_owned);
+            res.doc = it.docs(db);
             format_to!(res.signature, "union {}", it.name(db).display(db, edition));
         }
         hir::GenericDef::Trait(it) => {
-            res.doc = it.docs(db).map(Documentation::into_owned);
+            res.doc = it.docs(db);
             format_to!(res.signature, "trait {}", it.name(db).display(db, edition));
         }
         hir::GenericDef::TypeAlias(it) => {
-            res.doc = it.docs(db).map(Documentation::into_owned);
+            res.doc = it.docs(db);
             format_to!(res.signature, "type {}", it.name(db).display(db, edition));
         }
         // These don't have generic args that can be specified
@@ -495,7 +495,7 @@ fn signature_help_for_tuple_struct_pat(
     let fields: Vec<_> = if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res {
         let en = variant.parent_enum(db);
 
-        res.doc = en.docs(db).map(Documentation::into_owned);
+        res.doc = en.docs(db);
         format_to!(
             res.signature,
             "enum {}::{} (",
@@ -512,7 +512,7 @@ fn signature_help_for_tuple_struct_pat(
 
         match adt {
             hir::Adt::Struct(it) => {
-                res.doc = it.docs(db).map(Documentation::into_owned);
+                res.doc = it.docs(db);
                 format_to!(res.signature, "struct {} (", it.name(db).display(db, edition));
                 it.fields(db)
             }
@@ -622,7 +622,7 @@ fn signature_help_for_record_<'db>(
         fields = variant.fields(db);
         let en = variant.parent_enum(db);
 
-        res.doc = en.docs(db).map(Documentation::into_owned);
+        res.doc = en.docs(db);
         format_to!(
             res.signature,
             "enum {}::{} {{ ",
@@ -639,12 +639,12 @@ fn signature_help_for_record_<'db>(
         match adt {
             hir::Adt::Struct(it) => {
                 fields = it.fields(db);
-                res.doc = it.docs(db).map(Documentation::into_owned);
+                res.doc = it.docs(db);
                 format_to!(res.signature, "struct {} {{ ", it.name(db).display(db, edition));
             }
             hir::Adt::Union(it) => {
                 fields = it.fields(db);
-                res.doc = it.docs(db).map(Documentation::into_owned);
+                res.doc = it.docs(db);
                 format_to!(res.signature, "union {} {{ ", it.name(db).display(db, edition));
             }
             _ => return None,
@@ -740,12 +740,12 @@ mod tests {
         #[rust_analyzer::rust_fixture] ra_fixture: &str,
     ) -> (RootDatabase, FilePosition) {
         let mut database = RootDatabase::default();
-        let change_fixture = ChangeFixture::parse(ra_fixture);
+        let change_fixture = ChangeFixture::parse(&database, ra_fixture);
         database.apply_change(change_fixture.change);
         let (file_id, range_or_offset) =
             change_fixture.file_position.expect("expected a marker ($0)");
         let offset = range_or_offset.expect_offset();
-        let position = FilePosition { file_id: file_id.file_id(), offset };
+        let position = FilePosition { file_id: file_id.file_id(&database), offset };
         (database, position)
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
index ec8292968dbf..e261928c413f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -42,8 +42,7 @@ pub struct ReferenceData {
 
 #[derive(Debug)]
 pub struct TokenStaticData {
-    // FIXME: Make this have the lifetime of the database.
-    pub documentation: Option>,
+    pub documentation: Option,
     pub hover: Option,
     pub definition: Option,
     pub references: Vec,
@@ -110,7 +109,7 @@ fn documentation_for_definition(
     sema: &Semantics<'_, RootDatabase>,
     def: Definition,
     scope_node: &SyntaxNode,
-) -> Option> {
+) -> Option {
     let famous_defs = match &def {
         Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(scope_node)?.krate())),
         _ => None,
@@ -125,7 +124,6 @@ fn documentation_for_definition(
             })
             .to_display_target(sema.db),
     )
-    .map(Documentation::into_owned)
 }
 
 // FIXME: This is a weird function
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
index 782a73d20ca3..66895cb0b053 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
@@ -199,7 +199,7 @@ pub(crate) fn highlight(
     let sema = Semantics::new(db);
     let file_id = sema
         .attach_first_edition(file_id)
-        .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, file_id));
+        .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id));
 
     // Determine the root based on the given range.
     let (root, range_to_highlight) = {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs
index 597550b482cd..75e46b8ebfde 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs
@@ -20,7 +20,7 @@ pub(crate) fn highlight_as_html_with_config(
     let sema = Semantics::new(db);
     let file_id = sema
         .attach_first_edition(file_id)
-        .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, file_id));
+        .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id));
     let file = sema.parse(file_id);
     let file = file.syntax();
     fn rainbowify(seed: u64) -> String {
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 26d2bb5e0288..7955f5ac0de9 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
@@ -1,13 +1,16 @@
 //! "Recursive" Syntax highlighting for code in doctests and fixtures.
 
-use hir::{EditionedFileId, HirFileId, InFile, Semantics};
+use std::mem;
+
+use either::Either;
+use hir::{EditionedFileId, HirFileId, InFile, Semantics, sym};
+use ide_db::range_mapper::RangeMapper;
 use ide_db::{
-    SymbolKind, defs::Definition, documentation::Documentation, range_mapper::RangeMapper,
-    rust_doc::is_rust_fence,
+    SymbolKind, defs::Definition, documentation::docs_with_rangemap, rust_doc::is_rust_fence,
 };
 use syntax::{
-    SyntaxNode, TextRange, TextSize,
-    ast::{self, IsString},
+    AstToken, NodeOrToken, SyntaxNode, TextRange, TextSize,
+    ast::{self, AstNode, IsString, QuoteOffsets},
 };
 
 use crate::{
@@ -93,79 +96,118 @@ pub(super) fn doc_comment(
         None => return,
     };
     let src_file_id: HirFileId = src_file_id.into();
-    let Some(docs) = attributes.hir_docs(sema.db) else { return };
 
     // Extract intra-doc links and emit highlights for them.
-    extract_definitions_from_docs(&Documentation::new_borrowed(docs.docs()))
-        .into_iter()
-        .filter_map(|(range, link, ns)| {
-            docs.find_ast_range(range)
-                .filter(|(mapping, _)| mapping.file_id == src_file_id)
-                .and_then(|(InFile { value: mapped_range, .. }, is_inner)| {
-                    Some(mapped_range)
-                        .zip(resolve_doc_path_for_def(sema.db, def, &link, ns, is_inner))
-                })
-        })
-        .for_each(|(range, def)| {
-            hl.add(HlRange {
-                range,
-                highlight: module_def_to_hl_tag(def)
-                    | HlMod::Documentation
-                    | HlMod::Injected
-                    | HlMod::IntraDocLink,
-                binding_hash: None,
+    if let Some((docs, doc_mapping)) = docs_with_rangemap(sema.db, &attributes) {
+        extract_definitions_from_docs(&docs)
+            .into_iter()
+            .filter_map(|(range, link, ns)| {
+                doc_mapping
+                    .map(range)
+                    .filter(|(mapping, _)| mapping.file_id == src_file_id)
+                    .and_then(|(InFile { value: mapped_range, .. }, attr_id)| {
+                        Some(mapped_range).zip(resolve_doc_path_for_def(
+                            sema.db,
+                            def,
+                            &link,
+                            ns,
+                            attr_id.is_inner_attr(),
+                        ))
+                    })
             })
-        });
+            .for_each(|(range, def)| {
+                hl.add(HlRange {
+                    range,
+                    highlight: module_def_to_hl_tag(def)
+                        | HlMod::Documentation
+                        | HlMod::Injected
+                        | HlMod::IntraDocLink,
+                    binding_hash: None,
+                })
+            })
+    }
 
     // Extract doc-test sources from the docs and calculate highlighting for them.
 
     let mut inj = RangeMapper::default();
     inj.add_unmapped("fn doctest() {\n");
 
+    let attrs_source_map = attributes.source_map(sema.db);
+
     let mut is_codeblock = false;
     let mut is_doctest = false;
 
-    let mut has_doctests = false;
+    let mut new_comments = Vec::new();
+    let mut string;
 
-    let mut docs_offset = TextSize::new(0);
-    for mut line in docs.docs().split('\n') {
-        let mut line_docs_offset = docs_offset;
-        docs_offset += TextSize::of(line) + TextSize::of("\n");
-
-        match RUSTDOC_FENCES.into_iter().find_map(|fence| line.find(fence)) {
-            Some(idx) => {
-                is_codeblock = !is_codeblock;
-                // Check whether code is rust by inspecting fence guards
-                let guards = &line[idx + RUSTDOC_FENCE_LENGTH..];
-                let is_rust = is_rust_fence(guards);
-                is_doctest = is_codeblock && is_rust;
-                continue;
-            }
-            None if !is_doctest => continue,
-            None => (),
-        }
-
-        // lines marked with `#` should be ignored in output, we skip the `#` char
-        if line.starts_with('#') {
-            line_docs_offset += TextSize::of("#");
-            line = &line["#".len()..];
-        }
-
-        let Some((InFile { file_id, value: mapped_range }, _)) =
-            docs.find_ast_range(TextRange::at(line_docs_offset, TextSize::of(line)))
-        else {
-            continue;
-        };
+    for attr in attributes.by_key(sym::doc).attrs() {
+        let InFile { file_id, value: src } = attrs_source_map.source_of(attr);
         if file_id != src_file_id {
             continue;
         }
+        let (line, range) = match &src {
+            Either::Left(it) => {
+                string = match find_doc_string_in_attr(attr, it) {
+                    Some(it) => it,
+                    None => continue,
+                };
+                let text = string.text();
+                let text_range = string.syntax().text_range();
+                match string.quote_offsets() {
+                    Some(QuoteOffsets { contents, .. }) => {
+                        (&text[contents - text_range.start()], contents)
+                    }
+                    None => (text, text_range),
+                }
+            }
+            Either::Right(comment) => {
+                let value = comment.prefix().len();
+                let range = comment.syntax().text_range();
+                (
+                    &comment.text()[value..],
+                    TextRange::new(range.start() + TextSize::try_from(value).unwrap(), range.end()),
+                )
+            }
+        };
 
-        has_doctests = true;
-        inj.add(line, mapped_range);
-        inj.add_unmapped("\n");
+        let mut range_start = range.start();
+        for line in line.split('\n') {
+            let line_len = TextSize::from(line.len() as u32);
+            let prev_range_start = {
+                let next_range_start = range_start + line_len + TextSize::from(1);
+                mem::replace(&mut range_start, next_range_start)
+            };
+            let mut pos = TextSize::from(0);
+
+            match RUSTDOC_FENCES.into_iter().find_map(|fence| line.find(fence)) {
+                Some(idx) => {
+                    is_codeblock = !is_codeblock;
+                    // Check whether code is rust by inspecting fence guards
+                    let guards = &line[idx + RUSTDOC_FENCE_LENGTH..];
+                    let is_rust = is_rust_fence(guards);
+                    is_doctest = is_codeblock && is_rust;
+                    continue;
+                }
+                None if !is_doctest => continue,
+                None => (),
+            }
+
+            // whitespace after comment is ignored
+            if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) {
+                pos += TextSize::of(ws);
+            }
+            // lines marked with `#` should be ignored in output, we skip the `#` char
+            if line[pos.into()..].starts_with('#') {
+                pos += TextSize::of('#');
+            }
+
+            new_comments.push(TextRange::at(prev_range_start, pos));
+            inj.add(&line[pos.into()..], TextRange::new(pos, line_len) + prev_range_start);
+            inj.add_unmapped("\n");
+        }
     }
 
-    if !has_doctests {
+    if new_comments.is_empty() {
         return; // no need to run an analysis on an empty file
     }
 
@@ -198,6 +240,37 @@ pub(super) fn doc_comment(
             }
         }
     }
+
+    for range in new_comments {
+        hl.add(HlRange {
+            range,
+            highlight: HlTag::Comment | HlMod::Documentation,
+            binding_hash: None,
+        });
+    }
+}
+
+fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option {
+    match it.expr() {
+        // #[doc = lit]
+        Some(ast::Expr::Literal(lit)) => match lit.kind() {
+            ast::LiteralKind::String(it) => Some(it),
+            _ => None,
+        },
+        // #[cfg_attr(..., doc = "", ...)]
+        None => {
+            // We gotta hunt the string token manually here
+            let text = attr.string_value()?.as_str();
+            // FIXME: We just pick the first string literal that has the same text as the doc attribute
+            // This means technically we might highlight the wrong one
+            it.syntax()
+                .descendants_with_tokens()
+                .filter_map(NodeOrToken::into_token)
+                .filter_map(ast::String::cast)
+                .find(|string| string.text().get(1..string.text().len() - 1) == Some(text))
+        }
+        _ => None,
+    }
 }
 
 fn module_def_to_hl_tag(def: Definition) -> HlTag {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 53750ae0bac0..d00f279c8299 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -42,21 +42,21 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 
//! This is a module to test doc injection.
 //! ```
-//! fn test() {}
+//! fn test() {}
 //! ```
 
 //! Syntactic name ref highlighting testing
 //! ```rust
-//! extern crate self;
-//! extern crate other as otter;
-//! extern crate core;
-//! trait T { type Assoc; }
-//! fn f<Arg>() -> use<Arg> where (): T<Assoc = ()> {}
+//! extern crate self;
+//! extern crate other as otter;
+//! extern crate core;
+//! trait T { type Assoc; }
+//! fn f<Arg>() -> use<Arg> where (): T<Assoc = ()> {}
 //! ```
 mod outline_module;
 
 /// ```
-/// let _ = "early doctests should not go boom";
+/// let _ = "early doctests should not go boom";
 /// ```
 struct Foo {
     bar: bool,
@@ -65,15 +65,15 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 /// This is an impl of [`Foo`] with a code block.
 ///
 /// ```
-/// fn foo() {
+/// fn foo() {
 ///
-/// }
+/// }
 /// ```
 impl Foo {
     /// ```
-    /// let _ = "Call me
+    /// let _ = "Call me
     //    KILLER WHALE
-    ///     Ishmael.";
+    ///     Ishmael.";
     /// ```
     pub const bar: bool = true;
 
@@ -82,8 +82,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     /// # Examples
     ///
     /// ```
-    /// # #![allow(unused_mut)]
-    /// let mut foo: Foo = Foo::new();
+    /// # #![allow(unused_mut)]
+    /// let mut foo: Foo = Foo::new();
     /// ```
     pub const fn new() -> Foo {
         Foo { bar: true }
@@ -94,38 +94,38 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     /// # Examples
     ///
     /// ```
-    /// use x::y;
+    /// use x::y;
     ///
-    /// let foo = Foo::new();
+    /// let foo = Foo::new();
     ///
-    /// // calls bar on foo
-    /// assert!(foo.bar());
+    /// // calls bar on foo
+    /// assert!(foo.bar());
     ///
-    /// let bar = foo.bar || Foo::bar;
+    /// let bar = foo.bar || Foo::bar;
     ///
-    /// /* multi-line
-    ///        comment */
+    /// /* multi-line
+    ///        comment */
     ///
-    /// let multi_line_string = "Foo
-    ///   bar\n
-    ///          ";
+    /// let multi_line_string = "Foo
+    ///   bar\n
+    ///          ";
     ///
     /// ```
     ///
     /// ```rust,no_run
-    /// let foobar = Foo::new().bar();
+    /// let foobar = Foo::new().bar();
     /// ```
     ///
     /// ~~~rust,no_run
-    /// // code block with tilde.
-    /// let foobar = Foo::new().bar();
+    /// // code block with tilde.
+    /// let foobar = Foo::new().bar();
     /// ~~~
     ///
     /// ```
-    /// // functions
-    /// fn foo<T, const X: usize>(arg: i32) {
-    ///     let x: T = X;
-    /// }
+    /// // functions
+    /// fn foo<T, const X: usize>(arg: i32) {
+    ///     let x: T = X;
+    /// }
     /// ```
     ///
     /// ```sh
@@ -150,8 +150,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 /// ```
-/// macro_rules! noop { ($expr:expr) => { $expr }}
-/// noop!(1);
+/// macro_rules! noop { ($expr:expr) => { $expr }}
+/// noop!(1);
 /// ```
 macro_rules! noop {
     ($expr:expr) => {
@@ -160,18 +160,18 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 /// ```rust
-/// let _ = example(&[1, 2, 3]);
+/// let _ = example(&[1, 2, 3]);
 /// ```
 ///
 /// ```
-/// loop {}
+/// loop {}
 #[cfg_attr(not(feature = "false"), doc = "loop {}")]
 #[doc = "loop {}"]
 /// ```
 ///
 #[cfg_attr(feature = "alloc", doc = "```rust")]
 #[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
-/// let _ = example(&alloc::vec![1, 2, 3]);
+/// let _ = example(&alloc::vec![1, 2, 3]);
 /// ```
 pub fn mix_and_match() {}
 
@@ -187,7 +187,7 @@ It is beyond me why you'd use these when you got ///
 /**
     Really, I don't get it
     ```rust
-    let _ = example(&[1, 2, 3]);
+    let _ = example(&[1, 2, 3]);
     ```
     [`block_comments`] tests these without indentation
 */
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs
index 0381865fed45..ed55ac5bf04b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs
@@ -75,10 +75,7 @@ pub(crate) fn on_char_typed(
     // FIXME: We are hitting the database here, if we are unlucky this call might block momentarily
     // causing the editor to feel sluggish!
     let edition = Edition::CURRENT_FIXME;
-    let editioned_file_id_wrapper = EditionedFileId::from_span_guess_origin(
-        db,
-        span::EditionedFileId::new(position.file_id, edition),
-    );
+    let editioned_file_id_wrapper = EditionedFileId::new(db, position.file_id, edition);
     let file = &db.parse(editioned_file_id_wrapper);
     let char_matches_position =
         file.tree().syntax().text().char_at(position.offset) == Some(char_typed);
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
index 76a2802d294c..fdc583a15cc7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
@@ -51,7 +51,7 @@ use ide_db::text_edit::TextEdit;
 // ![On Enter](https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif)
 pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option {
     let editioned_file_id_wrapper =
-        ide_db::base_db::EditionedFileId::current_edition_guess_origin(db, position.file_id);
+        ide_db::base_db::EditionedFileId::current_edition(db, position.file_id);
     let parse = db.parse(editioned_file_id_wrapper);
     let file = parse.tree();
     let token = file.syntax().token_at_offset(position.offset).left_biased()?;
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
index c9a2f31696f4..2cd751463bdb 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
@@ -12,6 +12,6 @@ pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String {
     let sema = Semantics::new(db);
     let file_id = sema
         .attach_first_edition(file_id)
-        .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(db, file_id));
+        .unwrap_or_else(|| EditionedFileId::current_edition(db, file_id));
     db.file_item_tree(file_id.into()).pretty_print(db, file_id.edition(db))
 }
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 54ad9603ba03..de24bc09ff0f 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
@@ -145,9 +145,7 @@ impl flags::AnalysisStats {
                     if !source_root.is_library || self.with_deps {
                         let length = db.file_text(file_id).text(db).lines().count();
                         let item_stats = db
-                            .file_item_tree(
-                                EditionedFileId::current_edition_guess_origin(db, file_id).into(),
-                            )
+                            .file_item_tree(EditionedFileId::current_edition(db, file_id).into())
                             .item_tree_stats()
                             .into();
 
@@ -157,9 +155,7 @@ impl flags::AnalysisStats {
                     } else {
                         let length = db.file_text(file_id).text(db).lines().count();
                         let item_stats = db
-                            .file_item_tree(
-                                EditionedFileId::current_edition_guess_origin(db, file_id).into(),
-                            )
+                            .file_item_tree(EditionedFileId::current_edition(db, file_id).into())
                             .item_tree_stats()
                             .into();
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index 92bb2c1ce4fa..37f83f6dee67 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -514,12 +514,12 @@ mod test {
 
     fn position(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (AnalysisHost, FilePosition) {
         let mut host = AnalysisHost::default();
-        let change_fixture = ChangeFixture::parse(ra_fixture);
+        let change_fixture = ChangeFixture::parse(host.raw_database(), ra_fixture);
         host.raw_database_mut().apply_change(change_fixture.change);
         let (file_id, range_or_offset) =
             change_fixture.file_position.expect("expected a marker ()");
         let offset = range_or_offset.expect_offset();
-        let position = FilePosition { file_id: file_id.file_id(), offset };
+        let position = FilePosition { file_id: file_id.file_id(host.raw_database()), offset };
         (host, position)
     }
 
@@ -870,7 +870,7 @@ pub mod example_mod {
         let s = "/// foo\nfn bar() {}";
 
         let mut host = AnalysisHost::default();
-        let change_fixture = ChangeFixture::parse(s);
+        let change_fixture = ChangeFixture::parse(host.raw_database(), s);
         host.raw_database_mut().apply_change(change_fixture.change);
 
         let analysis = host.analysis();
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
index cc2ab0f07ca0..e3e3a143de03 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
@@ -73,7 +73,7 @@ impl flags::Search {
                 let sr = db.source_root(root).source_root(db);
                 for file_id in sr.iter() {
                     for debug_info in match_finder.debug_where_text_equal(
-                        EditionedFileId::current_edition_guess_origin(db, file_id),
+                        EditionedFileId::current_edition(db, file_id),
                         debug_snippet,
                     ) {
                         println!("{debug_info:#?}");
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
index 2cb0fe9eefad..0362e13b88b7 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
@@ -141,7 +141,7 @@ fn all_unresolved_references(
 ) -> Vec {
     let file_id = sema
         .attach_first_edition(file_id)
-        .unwrap_or_else(|| EditionedFileId::current_edition_guess_origin(sema.db, file_id));
+        .unwrap_or_else(|| EditionedFileId::current_edition(sema.db, file_id));
     let file = sema.parse(file_id);
     let root = file.syntax();
 
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 5a42cbd933f9..04b20033062e 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
@@ -119,7 +119,7 @@ pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSe
     }
 }
 
-pub(crate) fn documentation(documentation: Documentation<'_>) -> lsp_types::Documentation {
+pub(crate) fn documentation(documentation: Documentation) -> lsp_types::Documentation {
     let value = format_docs(&documentation);
     let markup_content = lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value };
     lsp_types::Documentation::MarkupContent(markup_content)
@@ -1970,7 +1970,7 @@ pub(crate) fn markup_content(
         ide::HoverDocFormat::Markdown => lsp_types::MarkupKind::Markdown,
         ide::HoverDocFormat::PlainText => lsp_types::MarkupKind::PlainText,
     };
-    let value = format_docs(&Documentation::new_owned(markup.into()));
+    let value = format_docs(&Documentation::new(markup.into()));
     lsp_types::MarkupContent { kind, value }
 }
 
diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
index 2d1955d1f651..4e525be3fe3c 100644
--- a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
@@ -1,6 +1,6 @@
 //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`].
 
-use std::{collections::VecDeque, fmt, hash::Hash};
+use std::{fmt, hash::Hash};
 
 use intern::Symbol;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -102,34 +102,26 @@ where
     SpanData: Copy + fmt::Debug,
     SpanMap: SpanMapper>,
 {
-    let mut c =
-        Converter::new(node, map, Default::default(), Default::default(), span, mode, |_, _| {
-            (true, Vec::new())
-        });
+    let mut c = Converter::new(node, map, Default::default(), Default::default(), span, mode);
     convert_tokens(&mut c)
 }
 
 /// Converts a syntax tree to a [`tt::Subtree`] using the provided span map to populate the
 /// subtree's spans. Additionally using the append and remove parameters, the additional tokens can
 /// be injected or hidden from the output.
-pub fn syntax_node_to_token_tree_modified(
+pub fn syntax_node_to_token_tree_modified(
     node: &SyntaxNode,
     map: SpanMap,
     append: FxHashMap>>>,
     remove: FxHashSet,
     call_site: SpanData,
     mode: DocCommentDesugarMode,
-    on_enter: OnEvent,
 ) -> tt::TopSubtree>
 where
     SpanMap: SpanMapper>,
     SpanData: Copy + fmt::Debug,
-    OnEvent: FnMut(
-        &mut PreorderWithTokens,
-        &WalkEvent,
-    ) -> (bool, Vec>>),
 {
-    let mut c = Converter::new(node, map, append, remove, call_site, mode, on_enter);
+    let mut c = Converter::new(node, map, append, remove, call_site, mode);
     convert_tokens(&mut c)
 }
 
@@ -632,9 +624,9 @@ where
     }
 }
 
-struct Converter {
+struct Converter {
     current: Option,
-    current_leaves: VecDeque>,
+    current_leaves: Vec>,
     preorder: PreorderWithTokens,
     range: TextRange,
     punct_offset: Option<(SyntaxToken, TextSize)>,
@@ -644,13 +636,9 @@ struct Converter {
     remove: FxHashSet,
     call_site: S,
     mode: DocCommentDesugarMode,
-    on_event: OnEvent,
 }
 
-impl Converter
-where
-    OnEvent: FnMut(&mut PreorderWithTokens, &WalkEvent) -> (bool, Vec>),
-{
+impl Converter {
     fn new(
         node: &SyntaxNode,
         map: SpanMap,
@@ -658,9 +646,8 @@ where
         remove: FxHashSet,
         call_site: S,
         mode: DocCommentDesugarMode,
-        on_enter: OnEvent,
     ) -> Self {
-        let mut converter = Converter {
+        let mut this = Converter {
             current: None,
             preorder: node.preorder_with_tokens(),
             range: node.text_range(),
@@ -669,21 +656,16 @@ where
             append,
             remove,
             call_site,
-            current_leaves: VecDeque::new(),
+            current_leaves: vec![],
             mode,
-            on_event: on_enter,
         };
-        converter.current = converter.next_token();
-        converter
+        let first = this.next_token();
+        this.current = first;
+        this
     }
 
     fn next_token(&mut self) -> Option {
         while let Some(ev) = self.preorder.next() {
-            let (keep_event, insert_leaves) = (self.on_event)(&mut self.preorder, &ev);
-            self.current_leaves.extend(insert_leaves);
-            if !keep_event {
-                continue;
-            }
             match ev {
                 WalkEvent::Enter(token) => {
                     if self.remove.contains(&token) {
@@ -693,9 +675,10 @@ where
                             }
                             node => {
                                 self.preorder.skip_subtree();
-                                if let Some(v) = self.append.remove(&node) {
+                                if let Some(mut v) = self.append.remove(&node) {
+                                    v.reverse();
                                     self.current_leaves.extend(v);
-                                    continue;
+                                    return None;
                                 }
                             }
                         }
@@ -704,9 +687,10 @@ where
                     }
                 }
                 WalkEvent::Leave(ele) => {
-                    if let Some(v) = self.append.remove(&ele) {
+                    if let Some(mut v) = self.append.remove(&ele) {
+                        v.reverse();
                         self.current_leaves.extend(v);
-                        continue;
+                        return None;
                     }
                 }
             }
@@ -731,8 +715,8 @@ impl SynToken {
     }
 }
 
-impl SrcToken, S> for SynToken {
-    fn kind(&self, _ctx: &Converter) -> SyntaxKind {
+impl SrcToken, S> for SynToken {
+    fn kind(&self, _ctx: &Converter) -> SyntaxKind {
         match self {
             SynToken::Ordinary(token) => token.kind(),
             SynToken::Punct { token, offset: i } => {
@@ -744,14 +728,14 @@ impl SrcToken, S> for SynTok
             }
         }
     }
-    fn to_char(&self, _ctx: &Converter) -> Option {
+    fn to_char(&self, _ctx: &Converter) -> Option {
         match self {
             SynToken::Ordinary(_) => None,
             SynToken::Punct { token: it, offset: i } => it.text().chars().nth(*i),
             SynToken::Leaf(_) => None,
         }
     }
-    fn to_text(&self, _ctx: &Converter) -> SmolStr {
+    fn to_text(&self, _ctx: &Converter) -> SmolStr {
         match self {
             SynToken::Ordinary(token) | SynToken::Punct { token, offset: _ } => token.text().into(),
             SynToken::Leaf(_) => {
@@ -768,11 +752,10 @@ impl SrcToken, S> for SynTok
     }
 }
 
-impl TokenConverter for Converter
+impl TokenConverter for Converter
 where
     S: Copy,
     SpanMap: SpanMapper,
-    OnEvent: FnMut(&mut PreorderWithTokens, &WalkEvent) -> (bool, Vec>),
 {
     type Token = SynToken;
     fn convert_doc_comment(
@@ -798,7 +781,10 @@ where
             ));
         }
 
-        if let Some(leaf) = self.current_leaves.pop_front() {
+        if let Some(leaf) = self.current_leaves.pop() {
+            if self.current_leaves.is_empty() {
+                self.current = self.next_token();
+            }
             return Some((SynToken::Leaf(leaf), TextRange::empty(TextSize::new(0))));
         }
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs
index 5d67fd449175..aea99a4389b9 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs
@@ -26,8 +26,7 @@ pub use self::{
     generated::{nodes::*, tokens::*},
     node_ext::{
         AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind,
-        SlicePatComponents, StructKind, TokenTreeChildren, TypeBoundKind, TypeOrConstParam,
-        VisibilityKind,
+        SlicePatComponents, StructKind, TypeBoundKind, TypeOrConstParam, VisibilityKind,
     },
     operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
     token_ext::{
@@ -36,7 +35,6 @@ pub use self::{
     traits::{
         AttrDocCommentIter, DocCommentIter, HasArgList, HasAttrs, HasDocComments, HasGenericArgs,
         HasGenericParams, HasLoopBody, HasModuleItem, HasName, HasTypeBounds, HasVisibility,
-        attrs_including_inner,
     },
 };
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
index 901d17bb1491..af741d100f68 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
@@ -10,7 +10,7 @@ use parser::SyntaxKind;
 use rowan::{GreenNodeData, GreenTokenData};
 
 use crate::{
-    NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, TokenText,
+    NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, T, TokenText,
     ast::{
         self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName,
         HasTypeBounds, SyntaxNode, support,
@@ -1114,39 +1114,3 @@ impl ast::OrPat {
             .filter(|it| it.kind() == T![|])
     }
 }
-
-/// An iterator over the elements in an [`ast::TokenTree`].
-///
-/// Does not yield trivia or the delimiters.
-#[derive(Clone)]
-pub struct TokenTreeChildren {
-    iter: SyntaxElementChildren,
-}
-
-impl TokenTreeChildren {
-    #[inline]
-    pub fn new(tt: &ast::TokenTree) -> Self {
-        let mut iter = tt.syntax.children_with_tokens();
-        iter.next(); // Bump the opening delimiter.
-        Self { iter }
-    }
-}
-
-impl Iterator for TokenTreeChildren {
-    type Item = NodeOrToken;
-
-    #[inline]
-    fn next(&mut self) -> Option {
-        self.iter.find_map(|item| match item {
-            NodeOrToken::Node(node) => ast::TokenTree::cast(node).map(NodeOrToken::Node),
-            NodeOrToken::Token(token) => {
-                let kind = token.kind();
-                (!matches!(
-                    kind,
-                    SyntaxKind::WHITESPACE | SyntaxKind::COMMENT | T![')'] | T![']'] | T!['}']
-                ))
-                .then_some(NodeOrToken::Token(token))
-            }
-        })
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
index 83ab87c1c687..e1a9f3ac0341 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
@@ -40,8 +40,8 @@ impl ast::Comment {
     }
 
     /// Returns the textual content of a doc comment node as a single string with prefix and suffix
-    /// removed, plus the offset of the returned string from the beginning of the comment.
-    pub fn doc_comment(&self) -> Option<(&str, TextSize)> {
+    /// removed.
+    pub fn doc_comment(&self) -> Option<&str> {
         let kind = self.kind();
         match kind {
             CommentKind { shape, doc: Some(_) } => {
@@ -52,7 +52,7 @@ impl ast::Comment {
                 } else {
                     text
                 };
-                Some((text, TextSize::of(prefix)))
+                Some(text)
             }
             _ => None,
         }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
index 2f4109a2c976..5290f32dd27d 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
@@ -4,9 +4,8 @@
 use either::Either;
 
 use crate::{
-    SyntaxElement, SyntaxNode, SyntaxToken, T,
+    SyntaxElement, SyntaxToken, T,
     ast::{self, AstChildren, AstNode, AstToken, support},
-    match_ast,
     syntax_node::SyntaxElementChildren,
 };
 
@@ -77,42 +76,32 @@ pub trait HasAttrs: AstNode {
         self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom)
     }
 
-    /// This may return the same node as called with (with `SourceFile`). The caller has the responsibility
-    /// to avoid duplicate attributes.
-    fn inner_attributes_node(&self) -> Option {
-        let syntax = self.syntax();
-        Some(match_ast! {
-            match syntax {
-                // A `SourceFile` contains the inner attributes of itself.
-                ast::SourceFile(_) => syntax.clone(),
-                ast::ExternBlock(it) => it.extern_item_list()?.syntax().clone(),
-                ast::Fn(it) => it.body()?.stmt_list()?.syntax().clone(),
-                ast::MatchExpr(it) => it.match_arm_list()?.syntax().clone(),
-                ast::Impl(it) => it.assoc_item_list()?.syntax().clone(),
-                ast::Trait(it) => it.assoc_item_list()?.syntax().clone(),
-                ast::Module(it) => it.item_list()?.syntax().clone(),
-                ast::BlockExpr(it) => {
-                    if !it.may_carry_attributes() {
-                        return None;
-                    }
-                    syntax.clone()
-                },
-                _ => return None,
-            }
-        })
-    }
-}
+    /// Returns all attributes of this node, including inner attributes that may not be directly under this node
+    /// but under a child.
+    fn attrs_including_inner(self) -> impl Iterator
+    where
+        Self: Sized,
+    {
+        let inner_attrs_node = if let Some(it) =
+            support::child::(self.syntax()).and_then(|it| it.stmt_list())
+        {
+            Some(it.syntax)
+        } else if let Some(it) = support::child::(self.syntax()) {
+            Some(it.syntax)
+        } else if let Some(it) = support::child::(self.syntax()) {
+            Some(it.syntax)
+        } else if let Some(it) = support::child::(self.syntax()) {
+            Some(it.syntax)
+        } else if let Some(it) = support::child::(self.syntax()) {
+            Some(it.syntax)
+        } else if let Some(it) = support::child::(self.syntax()) {
+            Some(it.syntax)
+        } else {
+            None
+        };
 
-/// Returns all attributes of this node, including inner attributes that may not be directly under this node
-/// but under a child.
-pub fn attrs_including_inner(owner: &dyn HasAttrs) -> impl Iterator + Clone {
-    owner.attrs().filter(|attr| attr.kind().is_outer()).chain(
-        owner
-            .inner_attributes_node()
-            .into_iter()
-            .flat_map(|node| support::children::(&node))
-            .filter(|attr| attr.kind().is_inner()),
-    )
+        self.attrs().chain(inner_attrs_node.into_iter().flat_map(|it| support::children(&it)))
+    }
 }
 
 pub trait HasDocComments: HasAttrs {
@@ -129,7 +118,7 @@ impl DocCommentIter {
     #[cfg(test)]
     pub fn doc_comment_text(self) -> Option {
         let docs = itertools::Itertools::join(
-            &mut self.filter_map(|comment| comment.doc_comment().map(|it| it.0.to_owned())),
+            &mut self.filter_map(|comment| comment.doc_comment().map(ToOwned::to_owned)),
             "\n",
         );
         if docs.is_empty() { None } else { Some(docs) }
@@ -162,7 +151,7 @@ impl AttrDocCommentIter {
 impl Iterator for AttrDocCommentIter {
     type Item = Either;
     fn next(&mut self) -> Option {
-        self.iter.find_map(|el| match el {
+        self.iter.by_ref().find_map(|el| match el {
             SyntaxElement::Node(node) => ast::Attr::cast(node).map(Either::Left),
             SyntaxElement::Token(tok) => {
                 ast::Comment::cast(tok).filter(ast::Comment::is_doc).map(Either::Right)
diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
index 2b05add55216..aefe81f83e29 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
@@ -5,7 +5,7 @@ use base_db::target::TargetData;
 use base_db::{
     Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData,
     DependencyBuilder, Env, FileChange, FileSet, FxIndexMap, LangCrateOrigin, SourceDatabase,
-    SourceRoot, Version, VfsPath,
+    SourceRoot, Version, VfsPath, salsa,
 };
 use cfg::CfgOptions;
 use hir_expand::{
@@ -37,11 +37,10 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static {
         #[rust_analyzer::rust_fixture] ra_fixture: &str,
     ) -> (Self, EditionedFileId) {
         let mut db = Self::default();
-        let fixture = ChangeFixture::parse(ra_fixture);
+        let fixture = ChangeFixture::parse(&db, ra_fixture);
         fixture.change.apply(&mut db);
         assert_eq!(fixture.files.len(), 1, "Multiple file found in the fixture");
-        let file = EditionedFileId::from_span_guess_origin(&db, fixture.files[0]);
-        (db, file)
+        (db, fixture.files[0])
     }
 
     #[track_caller]
@@ -49,21 +48,16 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static {
         #[rust_analyzer::rust_fixture] ra_fixture: &str,
     ) -> (Self, Vec) {
         let mut db = Self::default();
-        let fixture = ChangeFixture::parse(ra_fixture);
+        let fixture = ChangeFixture::parse(&db, ra_fixture);
         fixture.change.apply(&mut db);
         assert!(fixture.file_position.is_none());
-        let files = fixture
-            .files
-            .into_iter()
-            .map(|file| EditionedFileId::from_span_guess_origin(&db, file))
-            .collect();
-        (db, files)
+        (db, fixture.files)
     }
 
     #[track_caller]
     fn with_files(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Self {
         let mut db = Self::default();
-        let fixture = ChangeFixture::parse(ra_fixture);
+        let fixture = ChangeFixture::parse(&db, ra_fixture);
         fixture.change.apply(&mut db);
         assert!(fixture.file_position.is_none());
         db
@@ -75,8 +69,12 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static {
         proc_macros: Vec<(String, ProcMacro)>,
     ) -> Self {
         let mut db = Self::default();
-        let fixture =
-            ChangeFixture::parse_with_proc_macros(ra_fixture, MiniCore::RAW_SOURCE, proc_macros);
+        let fixture = ChangeFixture::parse_with_proc_macros(
+            &db,
+            ra_fixture,
+            MiniCore::RAW_SOURCE,
+            proc_macros,
+        );
         fixture.change.apply(&mut db);
         assert!(fixture.file_position.is_none());
         db
@@ -101,13 +99,12 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static {
         #[rust_analyzer::rust_fixture] ra_fixture: &str,
     ) -> (Self, EditionedFileId, RangeOrOffset) {
         let mut db = Self::default();
-        let fixture = ChangeFixture::parse(ra_fixture);
+        let fixture = ChangeFixture::parse(&db, ra_fixture);
         fixture.change.apply(&mut db);
 
         let (file_id, range_or_offset) = fixture
             .file_position
             .expect("Could not find file position in fixture. Did you forget to add an `$0`?");
-        let file_id = EditionedFileId::from_span_guess_origin(&db, file_id);
         (db, file_id, range_or_offset)
     }
 
@@ -119,9 +116,9 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static {
 impl WithFixture for DB {}
 
 pub struct ChangeFixture {
-    pub file_position: Option<(span::EditionedFileId, RangeOrOffset)>,
+    pub file_position: Option<(EditionedFileId, RangeOrOffset)>,
     pub file_lines: Vec,
-    pub files: Vec,
+    pub files: Vec,
     pub change: ChangeWithProcMacros,
     pub sysroot_files: Vec,
 }
@@ -129,11 +126,15 @@ pub struct ChangeFixture {
 const SOURCE_ROOT_PREFIX: &str = "/";
 
 impl ChangeFixture {
-    pub fn parse(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> ChangeFixture {
-        Self::parse_with_proc_macros(ra_fixture, MiniCore::RAW_SOURCE, Vec::new())
+    pub fn parse(
+        db: &dyn salsa::Database,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    ) -> ChangeFixture {
+        Self::parse_with_proc_macros(db, ra_fixture, MiniCore::RAW_SOURCE, Vec::new())
     }
 
     pub fn parse_with_proc_macros(
+        db: &dyn salsa::Database,
         #[rust_analyzer::rust_fixture] ra_fixture: &str,
         minicore_raw: &str,
         mut proc_macro_defs: Vec<(String, ProcMacro)>,
@@ -201,7 +202,7 @@ impl ChangeFixture {
             let meta = FileMeta::from_fixture(entry, current_source_root_kind);
             if let Some(range_or_offset) = range_or_offset {
                 file_position =
-                    Some((span::EditionedFileId::new(file_id, meta.edition), range_or_offset));
+                    Some((EditionedFileId::new(db, file_id, meta.edition), range_or_offset));
             }
 
             assert!(meta.path.starts_with(SOURCE_ROOT_PREFIX));
@@ -258,7 +259,7 @@ impl ChangeFixture {
             source_change.change_file(file_id, Some(text));
             let path = VfsPath::new_virtual_path(meta.path);
             file_set.insert(file_id, path);
-            files.push(span::EditionedFileId::new(file_id, meta.edition));
+            files.push(EditionedFileId::new(db, file_id, meta.edition));
             file_id = FileId::from_raw(file_id.index() + 1);
         }
 

From 9735524533967ae13d5d58f9feafd601037375fa Mon Sep 17 00:00:00 2001
From: Jonathan Brouwer 
Date: Wed, 22 Oct 2025 21:43:50 +0200
Subject: [PATCH 067/170] Add `AcceptContext::suggestions`

Signed-off-by: Jonathan Brouwer 
---
 .../rustc_attr_parsing/src/attributes/cfg.rs  |  5 ++--
 .../src/attributes/inline.rs                  |  3 +-
 .../src/attributes/link_attrs.rs              |  3 +-
 .../src/attributes/macro_attrs.rs             | 19 ++++--------
 .../src/attributes/must_use.rs                |  3 +-
 .../src/attributes/test_attrs.rs              |  6 ++--
 compiler/rustc_attr_parsing/src/context.rs    | 30 +++++++++++--------
 .../src/session_diagnostics.rs                |  9 +++---
 compiler/rustc_feature/src/builtin_attrs.rs   | 21 ++++++++-----
 ...invalid_macro_export_argument.allow.stderr |  8 ++---
 .../invalid_macro_export_argument.deny.stderr | 16 +++++-----
 tests/ui/attributes/malformed-attrs.stderr    |  2 +-
 12 files changed, 61 insertions(+), 64 deletions(-)

diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs
index af94e8acaf68..9f0c98668924 100644
--- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs
@@ -326,7 +326,8 @@ pub fn parse_cfg_attr(
             }) {
                 Ok(r) => return Some(r),
                 Err(e) => {
-                    let suggestions = CFG_ATTR_TEMPLATE.suggestions(cfg_attr.style, sym::cfg_attr);
+                    let suggestions =
+                        CFG_ATTR_TEMPLATE.suggestions(Some(cfg_attr.style), sym::cfg_attr);
                     e.with_span_suggestions(
                         cfg_attr.span,
                         "must be of the form",
@@ -356,7 +357,7 @@ pub fn parse_cfg_attr(
                 template: CFG_ATTR_TEMPLATE,
                 attribute: AttrPath::from_ast(&cfg_attr.get_normal_item().path),
                 reason,
-                attr_style: cfg_attr.style,
+                suggestions: CFG_ATTR_TEMPLATE.suggestions(Some(cfg_attr.style), sym::cfg_attr),
             });
         }
     }
diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs
index a10ad27fcc68..eda272fb7f2b 100644
--- a/compiler/rustc_attr_parsing/src/attributes/inline.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs
@@ -56,8 +56,7 @@ impl SingleAttributeParser for InlineParser {
                 }
             }
             ArgParser::NameValue(_) => {
-                let suggestions = >::TEMPLATE
-                    .suggestions(cx.attr_style, "inline");
+                let suggestions = cx.suggestions();
                 let span = cx.attr_span;
                 cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
                 return None;
diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
index 40ecd91e5cfc..797d04b914dd 100644
--- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
@@ -71,8 +71,7 @@ impl CombineAttributeParser for LinkParser {
             // Specifically `#[link = "dl"]` is accepted with a FCW
             // For more information, see https://github.com/rust-lang/rust/pull/143193
             ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => {
-                let suggestions = >::TEMPLATE
-                    .suggestions(cx.attr_style, "link");
+                let suggestions = cx.suggestions();
                 let span = cx.attr_span;
                 cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
                 return None;
diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
index 849141c34f7d..787003519e78 100644
--- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
@@ -1,4 +1,3 @@
-use rustc_ast::AttrStyle;
 use rustc_errors::DiagArgValue;
 use rustc_hir::attrs::MacroUseArgs;
 
@@ -102,7 +101,7 @@ impl AttributeParser for MacroUseParser {
                     }
                 }
                 ArgParser::NameValue(_) => {
-                    let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use);
+                    let suggestions = cx.suggestions();
                     cx.emit_err(IllFormedAttributeInputLint {
                         num_suggestions: suggestions.len(),
                         suggestions: DiagArgValue::StrListSepByAnd(
@@ -149,19 +148,14 @@ impl SingleAttributeParser for MacroExportParser {
     ]);
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option {
-        let suggestions = || {
-            >::TEMPLATE
-                .suggestions(AttrStyle::Inner, "macro_export")
-        };
         let local_inner_macros = match args {
             ArgParser::NoArgs => false,
             ArgParser::List(list) => {
                 let Some(l) = list.single() else {
                     let span = cx.attr_span;
+                    let suggestions = cx.suggestions();
                     cx.emit_lint(
-                        AttributeLintKind::InvalidMacroExportArguments {
-                            suggestions: suggestions(),
-                        },
+                        AttributeLintKind::InvalidMacroExportArguments { suggestions },
                         span,
                     );
                     return None;
@@ -170,10 +164,9 @@ impl SingleAttributeParser for MacroExportParser {
                     Some(sym::local_inner_macros) => true,
                     _ => {
                         let span = cx.attr_span;
+                        let suggestions = cx.suggestions();
                         cx.emit_lint(
-                            AttributeLintKind::InvalidMacroExportArguments {
-                                suggestions: suggestions(),
-                            },
+                            AttributeLintKind::InvalidMacroExportArguments { suggestions },
                             span,
                         );
                         return None;
@@ -182,7 +175,7 @@ impl SingleAttributeParser for MacroExportParser {
             }
             ArgParser::NameValue(_) => {
                 let span = cx.attr_span;
-                let suggestions = suggestions();
+                let suggestions = cx.suggestions();
                 cx.emit_err(IllFormedAttributeInputLint {
                     num_suggestions: suggestions.len(),
                     suggestions: DiagArgValue::StrListSepByAnd(
diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs
index e6a5141d7830..51b43e96adf9 100644
--- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs
@@ -45,8 +45,7 @@ impl SingleAttributeParser for MustUseParser {
                     Some(value_str)
                 }
                 ArgParser::List(_) => {
-                    let suggestions = >::TEMPLATE
-                        .suggestions(cx.attr_style, "must_use");
+                    let suggestions = cx.suggestions();
                     cx.emit_err(IllFormedAttributeInputLint {
                         num_suggestions: suggestions.len(),
                         suggestions: DiagArgValue::StrListSepByAnd(
diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
index 510ff1ded490..23ecc0bf7d29 100644
--- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
@@ -20,8 +20,7 @@ impl SingleAttributeParser for IgnoreParser {
                 ArgParser::NoArgs => None,
                 ArgParser::NameValue(name_value) => {
                     let Some(str_value) = name_value.value_as_str() else {
-                        let suggestions = >::TEMPLATE
-                            .suggestions(cx.attr_style, "ignore");
+                        let suggestions = cx.suggestions();
                         let span = cx.attr_span;
                         cx.emit_lint(
                             AttributeLintKind::IllFormedAttributeInput { suggestions },
@@ -32,8 +31,7 @@ impl SingleAttributeParser for IgnoreParser {
                     Some(str_value)
                 }
                 ArgParser::List(_) => {
-                    let suggestions = >::TEMPLATE
-                        .suggestions(cx.attr_style, "ignore");
+                    let suggestions = cx.suggestions();
                     let span = cx.attr_span;
                     cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
                     return None;
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 6749bdfb1a67..7b14e24a218b 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -427,7 +427,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
                     i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
                 }),
             },
-            attr_style: self.attr_style,
+            suggestions: self.suggestions(),
         })
     }
 
@@ -438,7 +438,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
             template: self.template.clone(),
             attribute: self.attr_path.clone(),
             reason: AttributeParseErrorReason::ExpectedIntegerLiteral,
-            attr_style: self.attr_style,
+            suggestions: self.suggestions(),
         })
     }
 
@@ -449,7 +449,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
             template: self.template.clone(),
             attribute: self.attr_path.clone(),
             reason: AttributeParseErrorReason::ExpectedList,
-            attr_style: self.attr_style,
+            suggestions: self.suggestions(),
         })
     }
 
@@ -460,7 +460,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
             template: self.template.clone(),
             attribute: self.attr_path.clone(),
             reason: AttributeParseErrorReason::ExpectedNoArgs,
-            attr_style: self.attr_style,
+            suggestions: self.suggestions(),
         })
     }
 
@@ -472,7 +472,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
             template: self.template.clone(),
             attribute: self.attr_path.clone(),
             reason: AttributeParseErrorReason::ExpectedIdentifier,
-            attr_style: self.attr_style,
+            suggestions: self.suggestions(),
         })
     }
 
@@ -485,7 +485,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
             template: self.template.clone(),
             attribute: self.attr_path.clone(),
             reason: AttributeParseErrorReason::ExpectedNameValue(name),
-            attr_style: self.attr_style,
+            suggestions: self.suggestions(),
         })
     }
 
@@ -497,7 +497,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
             template: self.template.clone(),
             attribute: self.attr_path.clone(),
             reason: AttributeParseErrorReason::DuplicateKey(key),
-            attr_style: self.attr_style,
+            suggestions: self.suggestions(),
         })
     }
 
@@ -510,7 +510,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
             template: self.template.clone(),
             attribute: self.attr_path.clone(),
             reason: AttributeParseErrorReason::UnexpectedLiteral,
-            attr_style: self.attr_style,
+            suggestions: self.suggestions(),
         })
     }
 
@@ -521,7 +521,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
             template: self.template.clone(),
             attribute: self.attr_path.clone(),
             reason: AttributeParseErrorReason::ExpectedSingleArgument,
-            attr_style: self.attr_style,
+            suggestions: self.suggestions(),
         })
     }
 
@@ -532,7 +532,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
             template: self.template.clone(),
             attribute: self.attr_path.clone(),
             reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument,
-            attr_style: self.attr_style,
+            suggestions: self.suggestions(),
         })
     }
 
@@ -552,7 +552,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
                 strings: false,
                 list: false,
             },
-            attr_style: self.attr_style,
+            suggestions: self.suggestions(),
         })
     }
 
@@ -573,7 +573,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
                 strings: false,
                 list: true,
             },
-            attr_style: self.attr_style,
+            suggestions: self.suggestions(),
         })
     }
 
@@ -593,7 +593,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
                 strings: true,
                 list: false,
             },
-            attr_style: self.attr_style,
+            suggestions: self.suggestions(),
         })
     }
 
@@ -605,6 +605,10 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
             span,
         );
     }
+
+    pub(crate) fn suggestions(&self) -> Vec {
+        self.template.suggestions(Some(self.attr_style), &self.attr_path)
+    }
 }
 
 impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 7b82f3baec09..82bd29218313 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -1,6 +1,6 @@
 use std::num::IntErrorKind;
 
-use rustc_ast::{self as ast, AttrStyle, Path};
+use rustc_ast::{self as ast, Path};
 use rustc_errors::codes::*;
 use rustc_errors::{
     Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
@@ -613,10 +613,10 @@ pub(crate) enum AttributeParseErrorReason<'a> {
 pub(crate) struct AttributeParseError<'a> {
     pub(crate) span: Span,
     pub(crate) attr_span: Span,
-    pub(crate) attr_style: AttrStyle,
     pub(crate) template: AttributeTemplate,
     pub(crate) attribute: AttrPath,
     pub(crate) reason: AttributeParseErrorReason<'a>,
+    pub(crate) suggestions: Vec,
 }
 
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
@@ -752,16 +752,15 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
         if let Some(link) = self.template.docs {
             diag.note(format!("for more information, visit <{link}>"));
         }
-        let suggestions = self.template.suggestions(self.attr_style, &name);
 
         diag.span_suggestions(
             self.attr_span,
-            if suggestions.len() == 1 {
+            if self.suggestions.len() == 1 {
                 "must be of the form"
             } else {
                 "try changing it to one of the following valid forms of the attribute"
             },
-            suggestions,
+            self.suggestions,
             Applicability::HasPlaceholders,
         );
 
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 220388ffe435..a2acac8b3045 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -133,24 +133,29 @@ pub struct AttributeTemplate {
 }
 
 impl AttributeTemplate {
-    pub fn suggestions(&self, style: AttrStyle, name: impl std::fmt::Display) -> Vec {
+    pub fn suggestions(
+        &self,
+        style: Option,
+        name: impl std::fmt::Display,
+    ) -> Vec {
         let mut suggestions = vec![];
-        let inner = match style {
-            AttrStyle::Outer => "",
-            AttrStyle::Inner => "!",
+        let (start, end) = match style {
+            Some(AttrStyle::Outer) => ("#[", "]"),
+            Some(AttrStyle::Inner) => ("#![", "]"),
+            None => ("", ""),
         };
         if self.word {
-            suggestions.push(format!("#{inner}[{name}]"));
+            suggestions.push(format!("{start}{name}{end}"));
         }
         if let Some(descr) = self.list {
             for descr in descr {
-                suggestions.push(format!("#{inner}[{name}({descr})]"));
+                suggestions.push(format!("{start}{name}({descr}){end}"));
             }
         }
-        suggestions.extend(self.one_of.iter().map(|&word| format!("#{inner}[{name}({word})]")));
+        suggestions.extend(self.one_of.iter().map(|&word| format!("{start}{name}({word}){end}")));
         if let Some(descr) = self.name_value_str {
             for descr in descr {
-                suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
+                suggestions.push(format!("{start}{name} = \"{descr}\"{end}"));
             }
         }
         suggestions.sort();
diff --git a/tests/ui/attributes/invalid_macro_export_argument.allow.stderr b/tests/ui/attributes/invalid_macro_export_argument.allow.stderr
index 162b315b072a..2db60a689728 100644
--- a/tests/ui/attributes/invalid_macro_export_argument.allow.stderr
+++ b/tests/ui/attributes/invalid_macro_export_argument.allow.stderr
@@ -1,5 +1,5 @@
 Future incompatibility report: Future breakage diagnostic:
-warning: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]`
+warning: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]`
   --> $DIR/invalid_macro_export_argument.rs:7:1
    |
 LL | #[macro_export(hello, world)]
@@ -9,7 +9,7 @@ LL | #[macro_export(hello, world)]
    = note: for more information, see issue #57571 
 
 Future breakage diagnostic:
-warning: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]`
+warning: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]`
   --> $DIR/invalid_macro_export_argument.rs:14:1
    |
 LL | #[macro_export(not_local_inner_macros)]
@@ -19,7 +19,7 @@ LL | #[macro_export(not_local_inner_macros)]
    = note: for more information, see issue #57571 
 
 Future breakage diagnostic:
-warning: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]`
+warning: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]`
   --> $DIR/invalid_macro_export_argument.rs:31:1
    |
 LL | #[macro_export()]
@@ -29,7 +29,7 @@ LL | #[macro_export()]
    = note: for more information, see issue #57571 
 
 Future breakage diagnostic:
-warning: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]`
+warning: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]`
   --> $DIR/invalid_macro_export_argument.rs:38:1
    |
 LL | #[macro_export("blah")]
diff --git a/tests/ui/attributes/invalid_macro_export_argument.deny.stderr b/tests/ui/attributes/invalid_macro_export_argument.deny.stderr
index ad225ae187b1..fd50b824d0a2 100644
--- a/tests/ui/attributes/invalid_macro_export_argument.deny.stderr
+++ b/tests/ui/attributes/invalid_macro_export_argument.deny.stderr
@@ -1,4 +1,4 @@
-error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]`
+error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]`
   --> $DIR/invalid_macro_export_argument.rs:7:1
    |
 LL | #[macro_export(hello, world)]
@@ -12,7 +12,7 @@ note: the lint level is defined here
 LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))]
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]`
+error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]`
   --> $DIR/invalid_macro_export_argument.rs:14:1
    |
 LL | #[macro_export(not_local_inner_macros)]
@@ -21,7 +21,7 @@ LL | #[macro_export(not_local_inner_macros)]
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 
 
-error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]`
+error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]`
   --> $DIR/invalid_macro_export_argument.rs:31:1
    |
 LL | #[macro_export()]
@@ -30,7 +30,7 @@ LL | #[macro_export()]
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 
 
-error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]`
+error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]`
   --> $DIR/invalid_macro_export_argument.rs:38:1
    |
 LL | #[macro_export("blah")]
@@ -42,7 +42,7 @@ LL | #[macro_export("blah")]
 error: aborting due to 4 previous errors
 
 Future incompatibility report: Future breakage diagnostic:
-error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]`
+error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]`
   --> $DIR/invalid_macro_export_argument.rs:7:1
    |
 LL | #[macro_export(hello, world)]
@@ -57,7 +57,7 @@ LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))]
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Future breakage diagnostic:
-error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]`
+error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]`
   --> $DIR/invalid_macro_export_argument.rs:14:1
    |
 LL | #[macro_export(not_local_inner_macros)]
@@ -72,7 +72,7 @@ LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))]
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Future breakage diagnostic:
-error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]`
+error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]`
   --> $DIR/invalid_macro_export_argument.rs:31:1
    |
 LL | #[macro_export()]
@@ -87,7 +87,7 @@ LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))]
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Future breakage diagnostic:
-error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]`
+error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]`
   --> $DIR/invalid_macro_export_argument.rs:38:1
    |
 LL | #[macro_export("blah")]
diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr
index c29bd0245bf0..5627cb452a81 100644
--- a/tests/ui/attributes/malformed-attrs.stderr
+++ b/tests/ui/attributes/malformed-attrs.stderr
@@ -701,7 +701,7 @@ error: valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `
 LL | #[macro_use = 1]
    | ^^^^^^^^^^^^^^^^
 
-error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]`
+error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]`
   --> $DIR/malformed-attrs.rs:221:1
    |
 LL | #[macro_export = 18]

From 24502dd2d2e6e02289308411e12c6cf034cae666 Mon Sep 17 00:00:00 2001
From: Jonathan Brouwer 
Date: Wed, 22 Oct 2025 21:57:13 +0200
Subject: [PATCH 068/170] Improve suggestions for `cfg_attr`

Signed-off-by: Jonathan Brouwer 
---
 compiler/rustc_attr_parsing/src/attributes/cfg.rs |  1 +
 compiler/rustc_attr_parsing/src/context.rs        | 15 +++++++++++++--
 compiler/rustc_attr_parsing/src/interface.rs      |  4 ++++
 .../cfg_attr-attr-syntax-validation.stderr        | 14 ++++++--------
 4 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs
index 9f0c98668924..dab9e7666c23 100644
--- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs
@@ -389,6 +389,7 @@ fn parse_cfg_attr_internal<'a>(
     let cfg_predicate = AttributeParser::parse_single_args(
         sess,
         attribute.span,
+        attribute.get_normal_item().span(),
         attribute.style,
         AttrPath {
             segments: attribute
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 7b14e24a218b..6694dac8bb25 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -337,8 +337,16 @@ pub struct Late;
 /// Gives [`AttributeParser`]s enough information to create errors, for example.
 pub struct AcceptContext<'f, 'sess, S: Stage> {
     pub(crate) shared: SharedContext<'f, 'sess, S>,
-    /// The span of the attribute currently being parsed
+
+    /// The outer span of the attribute currently being parsed
+    /// #[attribute(...)]
+    /// ^^^^^^^^^^^^^^^^^ outer span
+    /// For attributes in `cfg_attr`, the outer span and inner spans are equal.
     pub(crate) attr_span: Span,
+    /// The inner span of the attribute currently being parsed
+    /// #[attribute(...)]
+    ///   ^^^^^^^^^^^^^^  inner span
+    pub(crate) inner_span: Span,
 
     /// Whether it is an inner or outer attribute
     pub(crate) attr_style: AttrStyle,
@@ -607,7 +615,10 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
     }
 
     pub(crate) fn suggestions(&self) -> Vec {
-        self.template.suggestions(Some(self.attr_style), &self.attr_path)
+        // If the outer and inner spans are equal, we are parsing an attribute from `cfg_attr`,
+        // So don't display an attribute style in the suggestions
+        let style = (self.attr_span != self.inner_span).then_some(self.attr_style);
+        self.template.suggestions(style, &self.attr_path)
     }
 }
 
diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs
index b8ef11c26d80..953b0ebfaf04 100644
--- a/compiler/rustc_attr_parsing/src/interface.rs
+++ b/compiler/rustc_attr_parsing/src/interface.rs
@@ -142,6 +142,7 @@ impl<'sess> AttributeParser<'sess, Early> {
         Self::parse_single_args(
             sess,
             attr.span,
+            normal_attr.item.span(),
             attr.style,
             path.get_attribute_path(),
             target_span,
@@ -159,6 +160,7 @@ impl<'sess> AttributeParser<'sess, Early> {
     pub fn parse_single_args(
         sess: &'sess Session,
         attr_span: Span,
+        inner_span: Span,
         attr_style: AttrStyle,
         attr_path: AttrPath,
         target_span: Span,
@@ -186,6 +188,7 @@ impl<'sess> AttributeParser<'sess, Early> {
                 },
             },
             attr_span,
+            inner_span,
             attr_style,
             template,
             attr_path,
@@ -305,6 +308,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
                                     emit_lint: &mut emit_lint,
                                 },
                                 attr_span: lower_span(attr.span),
+                                inner_span: lower_span(attr.get_normal_item().span()),
                                 attr_style: attr.style,
                                 template: &accept.template,
                                 attr_path: path.get_attribute_path(),
diff --git a/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr b/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr
index 2b7a7da3e33d..fd03fa62864a 100644
--- a/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr
+++ b/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr
@@ -113,7 +113,7 @@ error[E0539]: malformed `link_section` attribute input
   --> $DIR/cfg_attr-attr-syntax-validation.rs:44:18
    |
 LL | #[cfg_attr(true, link_section)]
-   |                  ^^^^^^^^^^^^ help: must be of the form: `#[link_section = "name"]`
+   |                  ^^^^^^^^^^^^ help: must be of the form: `link_section = "name"`
    |
    = note: for more information, visit 
 
@@ -129,14 +129,12 @@ LL | #[cfg_attr(true, inline())]
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[cfg_attr(true, inline())]
-LL + #[cfg_attr(true, #[inline(always)])]
-   |
-LL - #[cfg_attr(true, inline())]
-LL + #[cfg_attr(true, #[inline(never)])]
-   |
-LL - #[cfg_attr(true, inline())]
-LL + #[cfg_attr(true, #[inline])]
+LL + #[cfg_attr(true, inline)]
    |
+LL | #[cfg_attr(true, inline(always))]
+   |                         ++++++
+LL | #[cfg_attr(true, inline(never))]
+   |                         +++++
 
 warning: `#[link_section]` attribute cannot be used on structs
   --> $DIR/cfg_attr-attr-syntax-validation.rs:44:18

From 40e36a0687b40d9e1790bc6fa20ed7320ad2daac Mon Sep 17 00:00:00 2001
From: Shoyu Vanilla 
Date: Wed, 22 Oct 2025 01:58:02 +0900
Subject: [PATCH 069/170] Bump rustc deps

---
 src/tools/rust-analyzer/Cargo.lock            |  44 +++----
 src/tools/rust-analyzer/Cargo.toml            |  16 +--
 .../crates/hir-ty/src/display.rs              |  17 ++-
 .../rust-analyzer/crates/hir-ty/src/lib.rs    |  12 +-
 .../rust-analyzer/crates/hir-ty/src/lower.rs  |  16 +--
 .../crates/hir-ty/src/next_solver/consts.rs   |  50 ++++----
 .../crates/hir-ty/src/next_solver/fold.rs     |  19 ++-
 .../infer/canonical/canonicalizer.rs          |  51 +++-----
 .../hir-ty/src/next_solver/infer/select.rs    |   8 +-
 .../crates/hir-ty/src/next_solver/interner.rs |  25 ++--
 .../crates/hir-ty/src/next_solver/opaques.rs  |  79 +------------
 .../crates/hir-ty/src/next_solver/region.rs   |  30 ++++-
 .../crates/hir-ty/src/next_solver/ty.rs       | 109 ++++++++----------
 13 files changed, 220 insertions(+), 256 deletions(-)

diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index ea8d1a781dcc..b557b10e5c77 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -1824,9 +1824,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_abi"
-version = "0.133.0"
+version = "0.137.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c063a7fef3c49d03837ee9a5d988aad83630c3460b03b32355c279d3fafa7d07"
+checksum = "a4ce5c9ea794353e02beae390c4674f74ffb23a2ad9de763469fdcef5c1026ef"
 dependencies = [
  "bitflags 2.9.4",
  "ra-ap-rustc_hashes",
@@ -1836,24 +1836,24 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_ast_ir"
-version = "0.133.0"
+version = "0.137.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a210dbd77e794b33ff17d2d15750dee44eeabd1330685d69a6bad272d515892a"
+checksum = "1696b77af9bbfe1fcc7a09c907561061c6ef4c8bd6d5f1675b927bc62d349103"
 
 [[package]]
 name = "ra-ap-rustc_hashes"
-version = "0.133.0"
+version = "0.137.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dea031ea45bb92cd346ed222b35c812e355f304183096ee91bb437b3813c6348"
+checksum = "c055d8b0d8a592d8cf9547495189f52c1ee5c691d28df1628253a816214e8521"
 dependencies = [
  "rustc-stable-hash",
 ]
 
 [[package]]
 name = "ra-ap-rustc_index"
-version = "0.133.0"
+version = "0.137.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db0114f842b20cba9beb2d9002ca31ae706b47f28ba2d6a49cbf9fd65fa72b9d"
+checksum = "a08a03e3d4a452144b68f48130eda3a2894d4d79e99ddb44bdb4e0ab8c384e10"
 dependencies = [
  "ra-ap-rustc_index_macros",
  "smallvec",
@@ -1861,9 +1861,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_index_macros"
-version = "0.133.0"
+version = "0.137.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc738a5bb06fb3893725fbeb3640ff1822bb2aae3f416c4a49f0a706ba89d1cc"
+checksum = "a1e0446b4d65a8ce19d8fd12826c4bf2365ffa4b8fe0ee94daf5968fe36e920c"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1872,9 +1872,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_lexer"
-version = "0.133.0"
+version = "0.137.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31c35b3d812cfc101d3f534640c13f24c0ec50ee2249685e4c20b2868609c9ee"
+checksum = "ac80365383a3c749f38af567fdcfaeff3fa6ea5df3846852abbce73e943921b9"
 dependencies = [
  "memchr",
  "unicode-properties",
@@ -1883,9 +1883,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_next_trait_solver"
-version = "0.133.0"
+version = "0.137.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7b0fa6fb8e0717ebd0836f8de4a6efc954fca1a8652980fd2584dbe448c7d95"
+checksum = "a39b419d2d6f7fdec7e0981b7fb7d5beb5dda7140064f1199704ec9dadbb6f73"
 dependencies = [
  "derive-where",
  "ra-ap-rustc_index",
@@ -1896,9 +1896,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_parse_format"
-version = "0.133.0"
+version = "0.137.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33d01bad23470cc749ef607476890aabcc8993ca3ef87d4241d0f6a08c6f9402"
+checksum = "b743b0c8f795842e41b1720bbc5af6e896129fb9acf04e9785774bfb0dc5947c"
 dependencies = [
  "ra-ap-rustc_lexer",
  "rustc-literal-escaper 0.0.5",
@@ -1906,9 +1906,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_pattern_analysis"
-version = "0.133.0"
+version = "0.137.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a181cf7943dc16e888046584d6172be95818811b25d695dbacbb4dd71973cc3"
+checksum = "cf944dce80137195528f89a576f70153c2060a6f8ca49c3fa9f55f9da14ab937"
 dependencies = [
  "ra-ap-rustc_index",
  "rustc-hash 2.1.1",
@@ -1919,9 +1919,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_type_ir"
-version = "0.133.0"
+version = "0.137.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87c99f33be18d9e50cefef5442822da1f0b416e9a17a483879a9704e08a6a6e6"
+checksum = "1bfe2722b20bc889a9d7711bd3a1f4f7b082940491241615aa643c17e0deffec"
 dependencies = [
  "arrayvec",
  "bitflags 2.9.4",
@@ -1939,9 +1939,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_type_ir_macros"
-version = "0.133.0"
+version = "0.137.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77b162d65e058abfc058e6b67ae68156cc282fbd78da148c1a7ec77b4230661e"
+checksum = "6fad1527df26aaa77367393fae86f42818b33e02b3737a19f3846d1c7671e7f9"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 8a108974681a..ecb2686a2277 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -86,14 +86,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
 vfs = { path = "./crates/vfs", version = "0.0.0" }
 edition = { path = "./crates/edition", version = "0.0.0" }
 
-ra-ap-rustc_lexer = { version = "0.133", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.133", default-features = false }
-ra-ap-rustc_index = { version = "0.133", default-features = false }
-ra-ap-rustc_abi = { version = "0.133", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.133", default-features = false }
-ra-ap-rustc_ast_ir = { version = "0.133", default-features = false }
-ra-ap-rustc_type_ir = { version = "0.133", default-features = false }
-ra-ap-rustc_next_trait_solver = { version = "0.133", default-features = false }
+ra-ap-rustc_lexer = { version = "0.137", default-features = false }
+ra-ap-rustc_parse_format = { version = "0.137", default-features = false }
+ra-ap-rustc_index = { version = "0.137", default-features = false }
+ra-ap-rustc_abi = { version = "0.137", default-features = false }
+ra-ap-rustc_pattern_analysis = { version = "0.137", default-features = false }
+ra-ap-rustc_ast_ir = { version = "0.137", default-features = false }
+ra-ap-rustc_type_ir = { version = "0.137", default-features = false }
+ra-ap-rustc_next_trait_solver = { version = "0.137", default-features = false }
 
 # local crates that aren't published to crates.io. These should not have versions.
 
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 2b92408f0f6b..e807ce62e8cf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -38,7 +38,7 @@ use rustc_apfloat::{
 use rustc_ast_ir::FloatTy;
 use rustc_hash::FxHashSet;
 use rustc_type_ir::{
-    AliasTyKind, CoroutineArgsParts, RegionKind, Upcast,
+    AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, RegionKind, Upcast,
     inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _, Tys as _},
 };
 use smallvec::SmallVec;
@@ -682,9 +682,12 @@ impl<'db> HirDisplay<'db> for Const<'db> {
     fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result<(), HirDisplayError> {
         match self.kind() {
             ConstKind::Placeholder(_) => write!(f, ""),
-            ConstKind::Bound(db, bound_const) => {
+            ConstKind::Bound(BoundVarIndexKind::Bound(db), bound_const) => {
                 write!(f, "?{}.{}", db.as_u32(), bound_const.var.as_u32())
             }
+            ConstKind::Bound(BoundVarIndexKind::Canonical, bound_const) => {
+                write!(f, "?c.{}", bound_const.var.as_u32())
+            }
             ConstKind::Infer(..) => write!(f, "#c#"),
             ConstKind::Param(param) => {
                 let generics = generics(f.db, param.id.parent());
@@ -1525,9 +1528,12 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
                     }
                 }
             }
-            TyKind::Bound(debruijn, ty) => {
+            TyKind::Bound(BoundVarIndexKind::Bound(debruijn), ty) => {
                 write!(f, "?{}.{}", debruijn.as_usize(), ty.var.as_usize())?
             }
+            TyKind::Bound(BoundVarIndexKind::Canonical, ty) => {
+                write!(f, "?c.{}", ty.var.as_usize())?
+            }
             TyKind::Dynamic(bounds, region) => {
                 // We want to put auto traits after principal traits, regardless of their written order.
                 let mut bounds_to_display = SmallVec::<[_; 4]>::new();
@@ -1955,9 +1961,12 @@ impl<'db> HirDisplay<'db> for Region<'db> {
                 write!(f, "{}", param_data.name.display(f.db, f.edition()))?;
                 Ok(())
             }
-            RegionKind::ReBound(db, idx) => {
+            RegionKind::ReBound(BoundVarIndexKind::Bound(db), idx) => {
                 write!(f, "?{}.{}", db.as_u32(), idx.var.as_u32())
             }
+            RegionKind::ReBound(BoundVarIndexKind::Canonical, idx) => {
+                write!(f, "?c.{}", idx.var.as_u32())
+            }
             RegionKind::ReVar(_) => write!(f, "_"),
             RegionKind::ReStatic => write!(f, "'static"),
             RegionKind::ReError(..) => {
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 536c81ab03b2..094a3e5326e9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -61,7 +61,7 @@ use la_arena::Idx;
 use mir::{MirEvalError, VTableMap};
 use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
 use rustc_type_ir::{
-    TypeSuperVisitable, TypeVisitableExt, UpcastFrom,
+    BoundVarIndexKind, TypeSuperVisitable, TypeVisitableExt, UpcastFrom,
     inherent::{IntoKind, SliceLike, Ty as _},
 };
 use syntax::ast::{ConstArg, make};
@@ -405,7 +405,7 @@ where
                     ))
                 }
                 TyKind::Infer(_) => error(),
-                TyKind::Bound(index, _) if index > self.binder => error(),
+                TyKind::Bound(BoundVarIndexKind::Bound(index), _) if index > self.binder => error(),
                 _ => t.try_super_fold_with(self),
             }
         }
@@ -432,7 +432,9 @@ where
                     Ok(Const::new_bound(self.interner, self.binder, BoundConst { var }))
                 }
                 ConstKind::Infer(_) => error(),
-                ConstKind::Bound(index, _) if index > self.binder => error(),
+                ConstKind::Bound(BoundVarIndexKind::Bound(index), _) if index > self.binder => {
+                    error()
+                }
                 _ => ct.try_super_fold_with(self),
             }
         }
@@ -454,7 +456,9 @@ where
                     ))
                 }
                 RegionKind::ReVar(_) => error(),
-                RegionKind::ReBound(index, _) if index > self.binder => error(),
+                RegionKind::ReBound(BoundVarIndexKind::Bound(index), _) if index > self.binder => {
+                    error()
+                }
                 _ => Ok(region),
             }
         }
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 42f7290962bd..6f7ca4829d52 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -42,8 +42,8 @@ use rustc_ast_ir::Mutability;
 use rustc_hash::FxHashSet;
 use rustc_pattern_analysis::Captures;
 use rustc_type_ir::{
-    AliasTyKind, ConstKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection,
-    ExistentialTraitRef, FnSig, OutlivesPredicate,
+    AliasTyKind, BoundVarIndexKind, ConstKind, DebruijnIndex, ExistentialPredicate,
+    ExistentialProjection, ExistentialTraitRef, FnSig, OutlivesPredicate,
     TyKind::{self},
     TypeVisitableExt,
     inherent::{GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, Ty as _},
@@ -858,11 +858,13 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
         if let Some(bounds) = bounds {
             let region = match lifetime {
                 Some(it) => match it.kind() {
-                    rustc_type_ir::RegionKind::ReBound(db, var) => Region::new_bound(
-                        self.interner,
-                        db.shifted_out_to_binder(DebruijnIndex::from_u32(2)),
-                        var,
-                    ),
+                    rustc_type_ir::RegionKind::ReBound(BoundVarIndexKind::Bound(db), var) => {
+                        Region::new_bound(
+                            self.interner,
+                            db.shifted_out_to_binder(DebruijnIndex::from_u32(2)),
+                            var,
+                        )
+                    }
                     _ => it,
                 },
                 None => Region::new_static(self.interner),
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 c28af948bfc8..926dbdc03d03 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
@@ -6,8 +6,9 @@ use hir_def::ConstParamId;
 use macros::{TypeFoldable, TypeVisitable};
 use rustc_ast_ir::visit::VisitorResult;
 use rustc_type_ir::{
-    BoundVar, DebruijnIndex, FlagComputation, Flags, TypeFoldable, TypeSuperFoldable,
-    TypeSuperVisitable, TypeVisitable, TypeVisitableExt, WithCachedTypeInfo,
+    BoundVar, BoundVarIndexKind, ConstVid, DebruijnIndex, FlagComputation, Flags, InferConst,
+    TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
+    WithCachedTypeInfo,
     inherent::{IntoKind, ParamEnv as _, PlaceholderLike, SliceLike},
     relate::Relate,
 };
@@ -49,11 +50,11 @@ impl<'db> Const<'db> {
     }
 
     pub fn error(interner: DbInterner<'db>) -> Self {
-        Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed))
+        Const::new(interner, ConstKind::Error(ErrorGuaranteed))
     }
 
     pub fn new_param(interner: DbInterner<'db>, param: ParamConst) -> Self {
-        Const::new(interner, rustc_type_ir::ConstKind::Param(param))
+        Const::new(interner, ConstKind::Param(param))
     }
 
     pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderConst) -> Self {
@@ -61,7 +62,7 @@ impl<'db> Const<'db> {
     }
 
     pub fn new_bound(interner: DbInterner<'db>, index: DebruijnIndex, bound: BoundConst) -> Self {
-        Const::new(interner, ConstKind::Bound(index, bound))
+        Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Bound(index), bound))
     }
 
     pub fn new_valtree(
@@ -340,28 +341,34 @@ impl<'db> Flags for Const<'db> {
 }
 
 impl<'db> rustc_type_ir::inherent::Const> for Const<'db> {
-    fn new_infer(interner: DbInterner<'db>, var: rustc_type_ir::InferConst) -> Self {
+    fn new_infer(interner: DbInterner<'db>, var: InferConst) -> Self {
         Const::new(interner, ConstKind::Infer(var))
     }
 
-    fn new_var(interner: DbInterner<'db>, var: rustc_type_ir::ConstVid) -> Self {
-        Const::new(interner, ConstKind::Infer(rustc_type_ir::InferConst::Var(var)))
+    fn new_var(interner: DbInterner<'db>, var: ConstVid) -> Self {
+        Const::new(interner, ConstKind::Infer(InferConst::Var(var)))
     }
 
-    fn new_bound(
-        interner: DbInterner<'db>,
-        debruijn: rustc_type_ir::DebruijnIndex,
-        var: BoundConst,
-    ) -> Self {
-        Const::new(interner, ConstKind::Bound(debruijn, var))
+    fn new_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundConst) -> Self {
+        Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), var))
     }
 
-    fn new_anon_bound(
+    fn new_anon_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundVar) -> Self {
+        Const::new(
+            interner,
+            ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), BoundConst { var }),
+        )
+    }
+
+    fn new_canonical_bound(interner: DbInterner<'db>, var: BoundVar) -> Self {
+        Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Canonical, BoundConst { var }))
+    }
+
+    fn new_placeholder(
         interner: DbInterner<'db>,
-        debruijn: rustc_type_ir::DebruijnIndex,
-        var: rustc_type_ir::BoundVar,
+        param:  as rustc_type_ir::Interner>::PlaceholderConst,
     ) -> Self {
-        Const::new(interner, ConstKind::Bound(debruijn, BoundConst { var }))
+        Const::new(interner, ConstKind::Placeholder(param))
     }
 
     fn new_unevaluated(
@@ -378,13 +385,6 @@ impl<'db> rustc_type_ir::inherent::Const> for Const<'db> {
     fn new_error(interner: DbInterner<'db>, guar: ErrorGuaranteed) -> Self {
         Const::new(interner, ConstKind::Error(guar))
     }
-
-    fn new_placeholder(
-        interner: DbInterner<'db>,
-        param:  as rustc_type_ir::Interner>::PlaceholderConst,
-    ) -> Self {
-        Const::new(interner, ConstKind::Placeholder(param))
-    }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs
index 588d42857493..f776b6ecfc43 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs
@@ -1,8 +1,8 @@
 //! Fold impls for the next-trait-solver.
 
 use rustc_type_ir::{
-    DebruijnIndex, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
-    inherent::IntoKind,
+    BoundVarIndexKind, DebruijnIndex, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable,
+    TypeVisitableExt, inherent::IntoKind,
 };
 
 use crate::next_solver::BoundConst;
@@ -79,7 +79,9 @@ where
 
     fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
         match t.kind() {
-            TyKind::Bound(debruijn, bound_ty) if debruijn == self.current_index => {
+            TyKind::Bound(BoundVarIndexKind::Bound(debruijn), bound_ty)
+                if debruijn == self.current_index =>
+            {
                 let ty = self.delegate.replace_ty(bound_ty);
                 debug_assert!(!ty.has_vars_bound_above(DebruijnIndex::ZERO));
                 rustc_type_ir::shift_vars(self.interner, ty, self.current_index.as_u32())
@@ -96,9 +98,12 @@ where
 
     fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
         match r.kind() {
-            RegionKind::ReBound(debruijn, br) if debruijn == self.current_index => {
+            RegionKind::ReBound(BoundVarIndexKind::Bound(debruijn), br)
+                if debruijn == self.current_index =>
+            {
                 let region = self.delegate.replace_region(br);
-                if let RegionKind::ReBound(debruijn1, br) = region.kind() {
+                if let RegionKind::ReBound(BoundVarIndexKind::Bound(debruijn1), br) = region.kind()
+                {
                     // If the callback returns a bound region,
                     // that region should always use the INNERMOST
                     // debruijn index. Then we adjust it to the
@@ -115,7 +120,9 @@ where
 
     fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
         match ct.kind() {
-            ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => {
+            ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), bound_const)
+                if debruijn == self.current_index =>
+            {
                 let ct = self.delegate.replace_const(bound_const);
                 debug_assert!(!ct.has_vars_bound_above(DebruijnIndex::ZERO));
                 rustc_type_ir::shift_vars(self.interner, ct, self.current_index.as_u32())
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs
index e6a818fdf3bc..7995545b0eed 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs
@@ -8,19 +8,18 @@
 use rustc_hash::FxHashMap;
 use rustc_index::Idx;
 use rustc_type_ir::InferTy::{self, FloatVar, IntVar, TyVar};
-use rustc_type_ir::inherent::{Const as _, IntoKind as _, SliceLike, Ty as _};
+use rustc_type_ir::inherent::{Const as _, IntoKind as _, Region as _, SliceLike, Ty as _};
 use rustc_type_ir::{
-    BoundVar, CanonicalQueryInput, DebruijnIndex, Flags, InferConst, RegionKind, TyVid, TypeFlags,
-    TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex,
+    BoundVar, BoundVarIndexKind, CanonicalQueryInput, DebruijnIndex, Flags, InferConst, RegionKind,
+    TyVid, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex,
 };
 use smallvec::SmallVec;
 use tracing::debug;
 
 use crate::next_solver::infer::InferCtxt;
 use crate::next_solver::{
-    Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, Canonical, CanonicalVarKind,
-    CanonicalVars, Const, ConstKind, DbInterner, GenericArg, ParamEnvAnd, Placeholder, Region, Ty,
-    TyKind,
+    Binder, Canonical, CanonicalVarKind, CanonicalVars, Const, ConstKind, DbInterner, GenericArg,
+    ParamEnvAnd, Placeholder, Region, Ty, TyKind,
 };
 
 /// When we canonicalize a value to form a query, we wind up replacing
@@ -345,12 +344,9 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> {
 
     fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
         match r.kind() {
-            RegionKind::ReBound(index, ..) => {
-                if index >= self.binder_index {
-                    panic!("escaping late-bound region during canonicalization");
-                } else {
-                    r
-                }
+            RegionKind::ReBound(BoundVarIndexKind::Bound(..), ..) => r,
+            RegionKind::ReBound(BoundVarIndexKind::Canonical, ..) => {
+                panic!("canonicalized bound var found during canonicalization");
             }
 
             RegionKind::ReStatic
@@ -427,12 +423,9 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> {
                 self.canonicalize_ty_var(CanonicalVarKind::PlaceholderTy(placeholder), t)
             }
 
-            TyKind::Bound(debruijn, _) => {
-                if debruijn >= self.binder_index {
-                    panic!("escaping bound type during canonicalization")
-                } else {
-                    t
-                }
+            TyKind::Bound(BoundVarIndexKind::Bound(..), _) => t,
+            TyKind::Bound(BoundVarIndexKind::Canonical, ..) => {
+                panic!("canonicalized bound var found during canonicalization");
             }
 
             TyKind::Closure(..)
@@ -503,12 +496,11 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> {
             ConstKind::Infer(InferConst::Fresh(_)) => {
                 panic!("encountered a fresh const during canonicalization")
             }
-            ConstKind::Bound(debruijn, _) => {
-                if debruijn >= self.binder_index {
-                    panic!("escaping bound const during canonicalization")
-                } else {
-                    return ct;
-                }
+            ConstKind::Bound(BoundVarIndexKind::Bound(..), _) => {
+                return ct;
+            }
+            ConstKind::Bound(BoundVarIndexKind::Canonical, ..) => {
+                panic!("canonicalized bound var found during canonicalization");
             }
             ConstKind::Placeholder(placeholder) => {
                 return self
@@ -758,8 +750,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> {
         r: Region<'db>,
     ) -> Region<'db> {
         let var = self.canonical_var(info, r.into());
-        let br = BoundRegion { var, kind: BoundRegionKind::Anon };
-        Region::new_bound(self.cx(), self.binder_index, br)
+        Region::new_canonical_bound(self.cx(), var)
     }
 
     /// Given a type variable `ty_var` of the given kind, first check
@@ -769,11 +760,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> {
     fn canonicalize_ty_var(&mut self, info: CanonicalVarKind<'db>, ty_var: Ty<'db>) -> Ty<'db> {
         debug_assert_eq!(ty_var, self.infcx.shallow_resolve(ty_var));
         let var = self.canonical_var(info, ty_var.into());
-        Ty::new_bound(
-            self.tcx,
-            self.binder_index,
-            BoundTy { kind: crate::next_solver::BoundTyKind::Anon, var },
-        )
+        Ty::new_canonical_bound(self.cx(), var)
     }
 
     /// Given a type variable `const_var` of the given kind, first check
@@ -787,6 +774,6 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> {
     ) -> Const<'db> {
         debug_assert_eq!(const_var, self.infcx.shallow_resolve_const(const_var));
         let var = self.canonical_var(info, const_var.into());
-        Const::new_bound(self.tcx, self.binder_index, BoundConst { var })
+        Const::new_canonical_bound(self.cx(), var)
     }
 }
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 d2f584b38cf4..52ad410df6be 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
@@ -353,7 +353,9 @@ fn candidate_should_be_dropped_in_favor_of<'db>(
         // Prefer dyn candidates over non-dyn candidates. This is necessary to
         // handle the unsoundness between `impl Any for T` and `dyn Any: Any`.
         (
-            CandidateSource::Impl(_) | CandidateSource::ParamEnv(_) | CandidateSource::AliasBound,
+            CandidateSource::Impl(_)
+            | CandidateSource::ParamEnv(_)
+            | CandidateSource::AliasBound(_),
             CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
         ) => true,
 
@@ -399,7 +401,9 @@ fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option>
                 })
             }
             CandidateSource::BuiltinImpl(builtin) => ImplSource::Builtin(builtin, nested),
-            CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => ImplSource::Param(nested),
+            CandidateSource::ParamEnv(_) | CandidateSource::AliasBound(_) => {
+                ImplSource::Param(nested)
+            }
             CandidateSource::CoherenceUnknowable => {
                 panic!("didn't expect to select an unknowable candidate")
             }
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 ce8b76837a3c..a509fd893d3c 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
@@ -18,8 +18,8 @@ use rustc_hash::FxHashSet;
 use rustc_index::bit_set::DenseBitSet;
 use rustc_type_ir::{
     AliasTermKind, AliasTyKind, BoundVar, CollectAndApply, DebruijnIndex, EarlyBinder,
-    FlagComputation, Flags, GenericArgKind, ImplPolarity, InferTy, TraitRef, TypeVisitableExt,
-    UniverseIndex, Upcast, Variance,
+    FlagComputation, Flags, GenericArgKind, ImplPolarity, InferTy, Interner, TraitRef,
+    TypeVisitableExt, UniverseIndex, Upcast, Variance,
     elaborate::elaborate,
     error::TypeError,
     inherent::{self, GenericsOf, IntoKind, SliceLike as _, Span as _, Ty as _},
@@ -33,8 +33,8 @@ use crate::{
     method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint},
     next_solver::{
         AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
-        CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, ImplIdWrapper, RegionAssumptions,
-        SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper,
+        CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, ImplIdWrapper, OpaqueTypeKey,
+        RegionAssumptions, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper,
         util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls},
     },
 };
@@ -850,7 +850,7 @@ macro_rules! as_lang_item {
     }};
 }
 
-impl<'db> rustc_type_ir::Interner for DbInterner<'db> {
+impl<'db> Interner for DbInterner<'db> {
     type DefId = SolverDefId;
     type LocalDefId = SolverDefId;
     type LocalDefIds = SolverDefIds<'db>;
@@ -877,9 +877,9 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> {
 
     fn mk_predefined_opaques_in_body(
         self,
-        data: rustc_type_ir::solve::PredefinedOpaquesData,
+        data: &[(OpaqueTypeKey<'db>, Self::Ty)],
     ) -> Self::PredefinedOpaques {
-        PredefinedOpaques::new(self, data)
+        PredefinedOpaques::new_from_iter(self, data.iter().cloned())
     }
 
     type CanonicalVarKinds = CanonicalVars<'db>;
@@ -997,8 +997,8 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> {
         from_entry(&f())
     }
 
-    fn evaluation_is_concurrent(&self) -> bool {
-        false
+    fn assert_evaluation_is_concurrent(&self) {
+        panic!("evaluation shouldn't be concurrent yet")
     }
 
     fn expand_abstract_consts>(self, _: T) -> T {
@@ -1953,6 +1953,13 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> {
             Self,
         >(self, canonical_goal)
     }
+
+    fn is_sizedness_trait(self, def_id: Self::TraitId) -> bool {
+        matches!(
+            self.as_trait_lang_item(def_id),
+            Some(SolverTraitLangItem::Sized | SolverTraitLangItem::MetaSized)
+        )
+    }
 }
 
 impl<'db> DbInterner<'db> {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs
index 8714c95f27d8..e8f5be2eb598 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs
@@ -1,85 +1,18 @@
 //! Things related to opaques in the next-trait-solver.
 
 use rustc_ast_ir::try_visit;
+use rustc_type_ir::inherent::SliceLike;
 
-use crate::next_solver::SolverDefId;
-
-use super::{DbInterner, interned_vec_nolifetime_salsa};
+use super::{DbInterner, SolverDefId, Ty, interned_vec_db, interned_vec_nolifetime_salsa};
 
 pub type OpaqueTypeKey<'db> = rustc_type_ir::OpaqueTypeKey>;
-pub type PredefinedOpaquesData<'db> = rustc_type_ir::solve::PredefinedOpaquesData>;
+
+type PredefinedOpaque<'db> = (OpaqueTypeKey<'db>, Ty<'db>);
+interned_vec_db!(PredefinedOpaques, PredefinedOpaque);
+
 pub type ExternalConstraintsData<'db> =
     rustc_type_ir::solve::ExternalConstraintsData>;
 
-#[salsa::interned(constructor = new_, debug)]
-pub struct PredefinedOpaques<'db> {
-    #[returns(ref)]
-    kind_: rustc_type_ir::solve::PredefinedOpaquesData>,
-}
-
-impl<'db> PredefinedOpaques<'db> {
-    pub fn new(interner: DbInterner<'db>, data: PredefinedOpaquesData<'db>) -> Self {
-        PredefinedOpaques::new_(interner.db(), data)
-    }
-
-    pub fn inner(&self) -> &PredefinedOpaquesData<'db> {
-        crate::with_attached_db(|db| {
-            let inner = self.kind_(db);
-            // SAFETY: ¯\_(ツ)_/¯
-            unsafe { std::mem::transmute(inner) }
-        })
-    }
-}
-
-impl<'db> rustc_type_ir::TypeVisitable> for PredefinedOpaques<'db> {
-    fn visit_with>>(
-        &self,
-        visitor: &mut V,
-    ) -> V::Result {
-        self.opaque_types.visit_with(visitor)
-    }
-}
-
-impl<'db> rustc_type_ir::TypeFoldable> for PredefinedOpaques<'db> {
-    fn try_fold_with>>(
-        self,
-        folder: &mut F,
-    ) -> Result {
-        Ok(PredefinedOpaques::new(
-            folder.cx(),
-            PredefinedOpaquesData {
-                opaque_types: self
-                    .opaque_types
-                    .iter()
-                    .cloned()
-                    .map(|opaque| opaque.try_fold_with(folder))
-                    .collect::>()?,
-            },
-        ))
-    }
-    fn fold_with>>(self, folder: &mut F) -> Self {
-        PredefinedOpaques::new(
-            folder.cx(),
-            PredefinedOpaquesData {
-                opaque_types: self
-                    .opaque_types
-                    .iter()
-                    .cloned()
-                    .map(|opaque| opaque.fold_with(folder))
-                    .collect(),
-            },
-        )
-    }
-}
-
-impl<'db> std::ops::Deref for PredefinedOpaques<'db> {
-    type Target = PredefinedOpaquesData<'db>;
-
-    fn deref(&self) -> &Self::Target {
-        self.inner()
-    }
-}
-
 interned_vec_nolifetime_salsa!(SolverDefIds, SolverDefId);
 
 #[salsa::interned(constructor = new_, debug)]
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 a3cfa65eb373..b5f0e6de2910 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
@@ -3,7 +3,8 @@
 use hir_def::LifetimeParamId;
 use intern::Symbol;
 use rustc_type_ir::{
-    BoundVar, DebruijnIndex, Flags, INNERMOST, RegionVid, TypeFlags, TypeFoldable, TypeVisitable,
+    BoundVar, BoundVarIndexKind, DebruijnIndex, Flags, INNERMOST, RegionVid, TypeFlags,
+    TypeFoldable, TypeVisitable,
     inherent::{IntoKind, PlaceholderLike, SliceLike},
     relate::Relate,
 };
@@ -67,7 +68,7 @@ impl<'db> Region<'db> {
         index: DebruijnIndex,
         bound: BoundRegion,
     ) -> Region<'db> {
-        Region::new(interner, RegionKind::ReBound(index, bound))
+        Region::new(interner, RegionKind::ReBound(BoundVarIndexKind::Bound(index), bound))
     }
 
     pub fn is_placeholder(&self) -> bool {
@@ -116,7 +117,11 @@ impl<'db> Region<'db> {
             RegionKind::ReStatic => {
                 flags |= TypeFlags::HAS_FREE_REGIONS;
             }
-            RegionKind::ReBound(..) => {
+            RegionKind::ReBound(BoundVarIndexKind::Canonical, ..) => {
+                flags |= TypeFlags::HAS_RE_BOUND;
+                flags |= TypeFlags::HAS_CANONICAL_BOUND;
+            }
+            RegionKind::ReBound(BoundVarIndexKind::Bound(..), ..) => {
                 flags |= TypeFlags::HAS_RE_BOUND;
             }
             RegionKind::ReErased => {
@@ -293,7 +298,7 @@ impl<'db> Flags for Region<'db> {
 
     fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex {
         match &self.inner() {
-            RegionKind::ReBound(debruijn, _) => debruijn.shifted_in(1),
+            RegionKind::ReBound(BoundVarIndexKind::Bound(debruijn), _) => debruijn.shifted_in(1),
             _ => INNERMOST,
         }
     }
@@ -305,7 +310,7 @@ impl<'db> rustc_type_ir::inherent::Region> for Region<'db> {
         debruijn: rustc_type_ir::DebruijnIndex,
         var: BoundRegion,
     ) -> Self {
-        Region::new(interner, RegionKind::ReBound(debruijn, var))
+        Region::new(interner, RegionKind::ReBound(BoundVarIndexKind::Bound(debruijn), var))
     }
 
     fn new_anon_bound(
@@ -315,7 +320,20 @@ impl<'db> rustc_type_ir::inherent::Region> for Region<'db> {
     ) -> Self {
         Region::new(
             interner,
-            RegionKind::ReBound(debruijn, BoundRegion { var, kind: BoundRegionKind::Anon }),
+            RegionKind::ReBound(
+                BoundVarIndexKind::Bound(debruijn),
+                BoundRegion { var, kind: BoundRegionKind::Anon },
+            ),
+        )
+    }
+
+    fn new_canonical_bound(interner: DbInterner<'db>, var: rustc_type_ir::BoundVar) -> Self {
+        Region::new(
+            interner,
+            RegionKind::ReBound(
+                BoundVarIndexKind::Canonical,
+                BoundRegion { var, kind: BoundRegionKind::Anon },
+            ),
         )
     }
 
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 7cf23b82f63d..3abbd2865746 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
@@ -11,9 +11,10 @@ use hir_def::{TraitId, type_ref::Rawness};
 use rustc_abi::{Float, Integer, Size};
 use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult};
 use rustc_type_ir::{
-    AliasTyKind, BoundVar, ClosureKind, FlagComputation, Flags, FloatTy, FloatVid, InferTy, IntTy,
-    IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable,
-    TypeVisitableExt, TypeVisitor, UintTy, Upcast, WithCachedTypeInfo,
+    AliasTyKind, BoundVar, BoundVarIndexKind, ClosureKind, DebruijnIndex, FlagComputation, Flags,
+    FloatTy, FloatVid, InferTy, IntTy, IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable,
+    TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast,
+    WithCachedTypeInfo,
     inherent::{
         AdtDef as _, BoundExistentialPredicates, BoundVarLike, Const as _, GenericArgs as _,
         IntoKind, ParamLike, PlaceholderLike, Safety as _, SliceLike, Ty as _,
@@ -27,7 +28,7 @@ use crate::{
     ImplTraitId,
     db::HirDatabase,
     next_solver::{
-        AdtDef, Binder, CallableIdWrapper, Clause, ClauseKind, ClosureIdWrapper, Const,
+        AdtDef, AliasTy, Binder, CallableIdWrapper, Clause, ClauseKind, ClosureIdWrapper, Const,
         CoroutineIdWrapper, FnSig, GenericArg, PolyFnSig, Region, TraitRef, TypeAliasIdWrapper,
         abi::Safety,
         interner::InternedWrapperNoDebug,
@@ -895,27 +896,28 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
         Ty::new(interner, TyKind::Placeholder(param))
     }
 
-    fn new_bound(
-        interner: DbInterner<'db>,
-        debruijn: rustc_type_ir::DebruijnIndex,
-        var: BoundTy,
-    ) -> Self {
-        Ty::new(interner, TyKind::Bound(debruijn, var))
+    fn new_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundTy) -> Self {
+        Ty::new(interner, TyKind::Bound(BoundVarIndexKind::Bound(debruijn), var))
     }
 
-    fn new_anon_bound(
-        interner: DbInterner<'db>,
-        debruijn: rustc_type_ir::DebruijnIndex,
-        var: BoundVar,
-    ) -> Self {
-        Ty::new(interner, TyKind::Bound(debruijn, BoundTy { var, kind: BoundTyKind::Anon }))
+    fn new_anon_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundVar) -> Self {
+        Ty::new(
+            interner,
+            TyKind::Bound(
+                BoundVarIndexKind::Bound(debruijn),
+                BoundTy { var, kind: BoundTyKind::Anon },
+            ),
+        )
     }
 
-    fn new_alias(
-        interner: DbInterner<'db>,
-        kind: rustc_type_ir::AliasTyKind,
-        alias_ty: rustc_type_ir::AliasTy>,
-    ) -> Self {
+    fn new_canonical_bound(interner: DbInterner<'db>, var: BoundVar) -> Self {
+        Ty::new(
+            interner,
+            TyKind::Bound(BoundVarIndexKind::Canonical, BoundTy { var, kind: BoundTyKind::Anon }),
+        )
+    }
+
+    fn new_alias(interner: DbInterner<'db>, kind: AliasTyKind, alias_ty: AliasTy<'db>) -> Self {
         Ty::new(interner, TyKind::Alias(kind, alias_ty))
     }
 
@@ -925,7 +927,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
 
     fn new_adt(
         interner: DbInterner<'db>,
-        adt_def:  as rustc_type_ir::Interner>::AdtDef,
+        adt_def:  as Interner>::AdtDef,
         args: GenericArgs<'db>,
     ) -> Self {
         Ty::new(interner, TyKind::Adt(adt_def, args))
@@ -937,8 +939,8 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
 
     fn new_dynamic(
         interner: DbInterner<'db>,
-        preds:  as rustc_type_ir::Interner>::BoundExistentialPredicates,
-        region:  as rustc_type_ir::Interner>::Region,
+        preds:  as Interner>::BoundExistentialPredicates,
+        region:  as Interner>::Region,
     ) -> Self {
         Ty::new(interner, TyKind::Dynamic(preds, region))
     }
@@ -946,7 +948,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
     fn new_coroutine(
         interner: DbInterner<'db>,
         def_id: CoroutineIdWrapper,
-        args:  as rustc_type_ir::Interner>::GenericArgs,
+        args:  as Interner>::GenericArgs,
     ) -> Self {
         Ty::new(interner, TyKind::Coroutine(def_id, args))
     }
@@ -954,7 +956,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
     fn new_coroutine_closure(
         interner: DbInterner<'db>,
         def_id: CoroutineIdWrapper,
-        args:  as rustc_type_ir::Interner>::GenericArgs,
+        args:  as Interner>::GenericArgs,
     ) -> Self {
         Ty::new(interner, TyKind::CoroutineClosure(def_id, args))
     }
@@ -962,7 +964,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
     fn new_closure(
         interner: DbInterner<'db>,
         def_id: ClosureIdWrapper,
-        args:  as rustc_type_ir::Interner>::GenericArgs,
+        args:  as Interner>::GenericArgs,
     ) -> Self {
         Ty::new(interner, TyKind::Closure(def_id, args))
     }
@@ -970,7 +972,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
     fn new_coroutine_witness(
         interner: DbInterner<'db>,
         def_id: CoroutineIdWrapper,
-        args:  as rustc_type_ir::Interner>::GenericArgs,
+        args:  as Interner>::GenericArgs,
     ) -> Self {
         Ty::new(interner, TyKind::CoroutineWitness(def_id, args))
     }
@@ -978,7 +980,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
     fn new_coroutine_witness_for_coroutine(
         interner: DbInterner<'db>,
         def_id: CoroutineIdWrapper,
-        coroutine_args:  as rustc_type_ir::Interner>::GenericArgs,
+        coroutine_args:  as Interner>::GenericArgs,
     ) -> Self {
         // HACK: Coroutine witness types are lifetime erased, so they
         // never reference any lifetime args from the coroutine. We erase
@@ -1006,7 +1008,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
 
     fn new_ref(
         interner: DbInterner<'db>,
-        region:  as rustc_type_ir::Interner>::Region,
+        region:  as Interner>::Region,
         ty: Self,
         mutbl: rustc_ast_ir::Mutability,
     ) -> Self {
@@ -1016,7 +1018,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
     fn new_array_with_const_len(
         interner: DbInterner<'db>,
         ty: Self,
-        len:  as rustc_type_ir::Interner>::Const,
+        len:  as Interner>::Const,
     ) -> Self {
         Ty::new(interner, TyKind::Array(ty, len))
     }
@@ -1025,10 +1027,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
         Ty::new(interner, TyKind::Slice(ty))
     }
 
-    fn new_tup(
-        interner: DbInterner<'db>,
-        tys: &[ as rustc_type_ir::Interner>::Ty],
-    ) -> Self {
+    fn new_tup(interner: DbInterner<'db>, tys: &[ as Interner>::Ty]) -> Self {
         Ty::new(interner, TyKind::Tuple(Tys::new_from_iter(interner, tys.iter().cloned())))
     }
 
@@ -1043,7 +1042,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
     fn new_fn_def(
         interner: DbInterner<'db>,
         def_id: CallableIdWrapper,
-        args:  as rustc_type_ir::Interner>::GenericArgs,
+        args:  as Interner>::GenericArgs,
     ) -> Self {
         Ty::new(interner, TyKind::FnDef(def_id, args))
     }
@@ -1059,12 +1058,19 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
     fn new_pat(
         interner: DbInterner<'db>,
         ty: Self,
-        pat:  as rustc_type_ir::Interner>::Pat,
+        pat:  as Interner>::Pat,
     ) -> Self {
         Ty::new(interner, TyKind::Pat(ty, pat))
     }
 
-    fn tuple_fields(self) ->  as rustc_type_ir::Interner>::Tys {
+    fn new_unsafe_binder(
+        interner: DbInterner<'db>,
+        ty: rustc_type_ir::Binder,  as Interner>::Ty>,
+    ) -> Self {
+        Ty::new(interner, TyKind::UnsafeBinder(ty.into()))
+    }
+
+    fn tuple_fields(self) ->  as Interner>::Tys {
         match self.kind() {
             TyKind::Tuple(args) => args,
             _ => panic!("tuple_fields called on non-tuple: {self:?}"),
@@ -1111,10 +1117,11 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
         }
     }
 
-    fn discriminant_ty(
-        self,
-        interner: DbInterner<'db>,
-    ) ->  as rustc_type_ir::Interner>::Ty {
+    fn has_unsafe_fields(self) -> bool {
+        false
+    }
+
+    fn discriminant_ty(self, interner: DbInterner<'db>) ->  as Interner>::Ty {
         match self.kind() {
             TyKind::Adt(adt, _) if adt.is_enum() => adt.repr().discr_type().to_ty(interner),
             TyKind::Coroutine(_, args) => args.as_coroutine().discr_ty(interner),
@@ -1168,20 +1175,6 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> {
             TyKind::UnsafeBinder(..) => unimplemented!(),
         }
     }
-
-    fn new_unsafe_binder(
-        interner: DbInterner<'db>,
-        ty: rustc_type_ir::Binder<
-            DbInterner<'db>,
-             as rustc_type_ir::Interner>::Ty,
-        >,
-    ) -> Self {
-        Ty::new(interner, TyKind::UnsafeBinder(ty.into()))
-    }
-
-    fn has_unsafe_fields(self) -> bool {
-        false
-    }
 }
 
 interned_vec_db!(Tys, Ty);
@@ -1193,14 +1186,14 @@ impl<'db> Tys<'db> {
 }
 
 impl<'db> rustc_type_ir::inherent::Tys> for Tys<'db> {
-    fn inputs(self) ->  as rustc_type_ir::Interner>::FnInputTys {
+    fn inputs(self) ->  as Interner>::FnInputTys {
         Tys::new_from_iter(
             DbInterner::conjure(),
             self.as_slice().split_last().unwrap().1.iter().copied(),
         )
     }
 
-    fn output(self) ->  as rustc_type_ir::Interner>::Ty {
+    fn output(self) ->  as Interner>::Ty {
         *self.as_slice().split_last().unwrap().0
     }
 }

From 9610f01c52f398de62fcfb14b626c63a4c54cad9 Mon Sep 17 00:00:00 2001
From: Shoyu Vanilla 
Date: Wed, 22 Oct 2025 02:22:55 +0900
Subject: [PATCH 070/170] Port `CanonicalInstantiator` from rustc

---
 .../infer/canonical/instantiate.rs            | 85 +++++++++++++++++--
 1 file changed, 80 insertions(+), 5 deletions(-)

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 64287fe47261..636029107154 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
@@ -6,13 +6,15 @@
 //!
 //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
 
-use crate::next_solver::BoundConst;
 use crate::next_solver::{
-    BoundRegion, BoundTy, Canonical, CanonicalVarValues, DbInterner, fold::FnMutDelegate,
+    BoundConst, BoundRegion, BoundTy, Canonical, CanonicalVarValues, Clauses, Const, ConstKind,
+    DbInterner, GenericArg, Predicate, Region, RegionKind, Ty, TyKind, fold::FnMutDelegate,
 };
+use rustc_hash::FxHashMap;
 use rustc_type_ir::{
-    GenericArgKind, TypeFoldable,
-    inherent::{IntoKind, SliceLike},
+    BoundVarIndexKind, GenericArgKind, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable,
+    TypeVisitableExt,
+    inherent::{GenericArg as _, IntoKind, SliceLike},
 };
 
 pub trait CanonicalExt<'db, V> {
@@ -91,6 +93,79 @@ where
             },
         };
 
-        tcx.replace_escaping_bound_vars_uncached(value, delegate)
+        let value = tcx.replace_escaping_bound_vars_uncached(value, delegate);
+        value.fold_with(&mut CanonicalInstantiator {
+            tcx,
+            var_values: var_values.var_values.as_slice(),
+            cache: Default::default(),
+        })
+    }
+}
+
+/// Replaces the bound vars in a canonical binder with var values.
+struct CanonicalInstantiator<'db, 'a> {
+    tcx: DbInterner<'db>,
+
+    // The values that the bound vars are being instantiated with.
+    var_values: &'a [GenericArg<'db>],
+
+    // Because we use `BoundVarIndexKind::Canonical`, we can cache
+    // based only on the entire ty, not worrying about a `DebruijnIndex`
+    cache: FxHashMap, Ty<'db>>,
+}
+
+impl<'db, 'a> TypeFolder> for CanonicalInstantiator<'db, 'a> {
+    fn cx(&self) -> DbInterner<'db> {
+        self.tcx
+    }
+
+    fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
+        match t.kind() {
+            TyKind::Bound(BoundVarIndexKind::Canonical, bound_ty) => {
+                self.var_values[bound_ty.var.as_usize()].expect_ty()
+            }
+            _ => {
+                if !t.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) {
+                    t
+                } else if let Some(&t) = self.cache.get(&t) {
+                    t
+                } else {
+                    let res = t.super_fold_with(self);
+                    assert!(self.cache.insert(t, res).is_none());
+                    res
+                }
+            }
+        }
+    }
+
+    fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
+        match r.kind() {
+            RegionKind::ReBound(BoundVarIndexKind::Canonical, br) => {
+                self.var_values[br.var.as_usize()].expect_region()
+            }
+            _ => r,
+        }
+    }
+
+    fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
+        match ct.kind() {
+            ConstKind::Bound(BoundVarIndexKind::Canonical, bound_const) => {
+                self.var_values[bound_const.var.as_usize()].expect_const()
+            }
+            _ => ct.super_fold_with(self),
+        }
+    }
+
+    fn fold_predicate(&mut self, p: Predicate<'db>) -> Predicate<'db> {
+        if p.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) { p.super_fold_with(self) } else { p }
+    }
+
+    fn fold_clauses(&mut self, c: Clauses<'db>) -> Clauses<'db> {
+        if !c.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) {
+            return c;
+        }
+
+        // FIXME: We might need cache here for perf like rustc
+        c.super_fold_with(self)
     }
 }

From c00dfa3a11409a7bc117f08cc39f6a6400067d47 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Thu, 18 Sep 2025 13:26:16 +0800
Subject: [PATCH 071/170] Heuristic sensing parenthesis completion of fields

We have conducted heuristic sensing on method parentheses, but it cannot complete fields

Example
---
```rust
struct Foo { far: i32 }
impl Foo {
    fn foo(&self) {}
}
fn foo() -> (i32, i32) {
    let foo = Foo { far: 4 };
    foo.f$0
    (2, 3)
}
```

**Before this PR**:

```text
me foo()  fn(&self)
...
```

**After this PR**:

```text
fd far          i32
me foo()  fn(&self)
...
```
---
 .../ide-completion/src/completions/dot.rs     |  85 +++++++---
 .../ide-completion/src/completions/postfix.rs |   2 +-
 .../crates/ide-completion/src/context.rs      |   4 +-
 .../ide-completion/src/context/analysis.rs    |  82 +++++----
 .../crates/ide-completion/src/render.rs       |   3 +-
 .../ide-completion/src/render/function.rs     |   4 +-
 .../ide-completion/src/tests/expression.rs    | 157 ++++++++++++++++++
 7 files changed, 274 insertions(+), 63 deletions(-)

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 72b245ccafd9..511b59385702 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
@@ -25,9 +25,7 @@ pub(crate) fn complete_dot(
         _ => return,
     };
 
-    let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });
-    let is_method_access_with_parens =
-        matches!(dot_access.kind, DotAccessKind::Method { has_parens: true });
+    let has_parens = matches!(dot_access.kind, DotAccessKind::Method);
     let traits_in_scope = ctx.traits_in_scope();
 
     // Suggest .await syntax for types that implement Future trait
@@ -48,7 +46,7 @@ pub(crate) fn complete_dot(
                 DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
                     DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
                 }
-                it @ DotAccessKind::Method { .. } => *it,
+                it @ DotAccessKind::Method => *it,
             };
             let dot_access = DotAccess {
                 receiver: dot_access.receiver.clone(),
@@ -67,8 +65,7 @@ pub(crate) fn complete_dot(
                     acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty)
                 },
                 |acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty),
-                is_field_access,
-                is_method_access_with_parens,
+                has_parens,
             );
             complete_methods(ctx, &future_output, &traits_in_scope, |func| {
                 acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None)
@@ -82,8 +79,7 @@ pub(crate) fn complete_dot(
         receiver_ty,
         |acc, field, ty| acc.add_field(ctx, dot_access, None, field, &ty),
         |acc, field, ty| acc.add_tuple_field(ctx, None, field, &ty),
-        is_field_access,
-        is_method_access_with_parens,
+        has_parens,
     );
     complete_methods(ctx, receiver_ty, &traits_in_scope, |func| {
         acc.add_method(ctx, dot_access, func, None, None)
@@ -112,7 +108,7 @@ pub(crate) fn complete_dot(
                 DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
                     DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
                 }
-                it @ DotAccessKind::Method { .. } => *it,
+                it @ DotAccessKind::Method => *it,
             };
             let dot_access = DotAccess {
                 receiver: dot_access.receiver.clone(),
@@ -173,7 +169,6 @@ pub(crate) fn complete_undotted_self(
             )
         },
         |acc, field, ty| acc.add_tuple_field(ctx, Some(SmolStr::new_static("self")), field, &ty),
-        true,
         false,
     );
     complete_methods(ctx, &ty, &ctx.traits_in_scope(), |func| {
@@ -182,7 +177,7 @@ pub(crate) fn complete_undotted_self(
             &DotAccess {
                 receiver: None,
                 receiver_ty: None,
-                kind: DotAccessKind::Method { has_parens: false },
+                kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal: false },
                 ctx: DotAccessExprCtx {
                     in_block_expr: expr_ctx.in_block_expr,
                     in_breakable: expr_ctx.in_breakable,
@@ -201,15 +196,13 @@ fn complete_fields(
     receiver: &hir::Type<'_>,
     mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type<'_>),
     mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type<'_>),
-    is_field_access: bool,
-    is_method_access_with_parens: bool,
+    has_parens: bool,
 ) {
     let mut seen_names = FxHashSet::default();
     for receiver in receiver.autoderef(ctx.db) {
         for (field, ty) in receiver.fields(ctx.db) {
             if seen_names.insert(field.name(ctx.db))
-                && (is_field_access
-                    || (is_method_access_with_parens && (ty.is_fn() || ty.is_closure())))
+                && (!has_parens || ty.is_fn() || ty.is_closure())
             {
                 named_field(acc, field, ty);
             }
@@ -218,8 +211,7 @@ fn complete_fields(
             // Tuples are always the last type in a deref chain, so just check if the name is
             // already seen without inserting into the hashset.
             if !seen_names.contains(&hir::Name::new_tuple_field(i))
-                && (is_field_access
-                    || (is_method_access_with_parens && (ty.is_fn() || ty.is_closure())))
+                && (!has_parens || ty.is_fn() || ty.is_closure())
             {
                 // Tuple fields are always public (tuple struct fields are handled above).
                 tuple_index(acc, i, ty);
@@ -1364,18 +1356,71 @@ fn foo() {
             r#"
 struct Foo { baz: fn() }
 impl Foo {
-    fn bar(self, t: T): T { t }
+    fn bar(self, t: T) -> T { t }
 }
 
 fn baz() {
     let foo = Foo{ baz: || {} };
-    foo.ba$0::<>;
+    foo.ba$0;
 }
 "#,
             expect![[r#"
-                me bar(…) fn(self, T)
+                fd baz                fn()
+                me bar(…) fn(self, T) -> T
             "#]],
         );
+
+        check_edit(
+            "baz",
+            r#"
+struct Foo { baz: fn() }
+impl Foo {
+    fn bar(self, t: T) -> T { t }
+}
+
+fn baz() {
+    let foo = Foo{ baz: || {} };
+    foo.ba$0;
+}
+"#,
+            r#"
+struct Foo { baz: fn() }
+impl Foo {
+    fn bar(self, t: T) -> T { t }
+}
+
+fn baz() {
+    let foo = Foo{ baz: || {} };
+    (foo.baz)();
+}
+"#,
+        );
+
+        check_edit(
+            "bar",
+            r#"
+struct Foo { baz: fn() }
+impl Foo {
+    fn bar(self, t: T) -> T { t }
+}
+
+fn baz() {
+    let foo = Foo{ baz: || {} };
+    foo.ba$0;
+}
+"#,
+            r#"
+struct Foo { baz: fn() }
+impl Foo {
+    fn bar(self, t: T) -> T { t }
+}
+
+fn baz() {
+    let foo = Foo{ baz: || {} };
+    foo.bar(${1:t})$0;
+}
+"#,
+        );
     }
 
     #[test]
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 4474d6181c20..70761534cd2c 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
@@ -43,7 +43,7 @@ pub(crate) fn complete_postfix(
                 DotAccessKind::Field { receiver_is_ambiguous_float_literal } => {
                     receiver_is_ambiguous_float_literal
                 }
-                DotAccessKind::Method { .. } => false,
+                DotAccessKind::Method => false,
             },
         ),
         _ => return,
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 c95b83ef8a02..b245c0d9831c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -405,9 +405,7 @@ pub(crate) enum DotAccessKind {
         /// like `0.$0`
         receiver_is_ambiguous_float_literal: bool,
     },
-    Method {
-        has_parens: bool,
-    },
+    Method,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
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 f0a03dedfe88..c01b544ff6ef 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
@@ -891,44 +891,53 @@ fn classify_name_ref<'db>(
         return Some(make_res(kind));
     }
 
+    let field_expr_handle = |recviver, node| {
+        let receiver = find_opt_node_in_file(original_file, recviver);
+        let receiver_is_ambiguous_float_literal = match &receiver {
+            Some(ast::Expr::Literal(l)) => matches! {
+                l.kind(),
+                ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().is_some_and(|it| it.text().ends_with('.'))
+            },
+            _ => false,
+        };
+
+        let receiver_is_part_of_indivisible_expression = match &receiver {
+            Some(ast::Expr::IfExpr(_)) => {
+                let next_token_kind =
+                    next_non_trivia_token(name_ref.syntax().clone()).map(|t| t.kind());
+                next_token_kind == Some(SyntaxKind::ELSE_KW)
+            }
+            _ => false,
+        };
+        if receiver_is_part_of_indivisible_expression {
+            return None;
+        }
+
+        let mut receiver_ty = receiver.as_ref().and_then(|it| sema.type_of_expr(it));
+        if receiver_is_ambiguous_float_literal {
+            // `123.|` is parsed as a float but should actually be an integer.
+            always!(receiver_ty.as_ref().is_none_or(|receiver_ty| receiver_ty.original.is_float()));
+            receiver_ty =
+                Some(TypeInfo { original: hir::BuiltinType::i32().ty(sema.db), adjusted: None });
+        }
+
+        let kind = NameRefKind::DotAccess(DotAccess {
+            receiver_ty,
+            kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal },
+            receiver,
+            ctx: DotAccessExprCtx {
+                in_block_expr: is_in_block(node),
+                in_breakable: is_in_breakable(node).unzip().0,
+            },
+        });
+        Some(make_res(kind))
+    };
+
     let segment = match_ast! {
         match parent {
             ast::PathSegment(segment) => segment,
             ast::FieldExpr(field) => {
-                let receiver = find_opt_node_in_file(original_file, field.expr());
-                let receiver_is_ambiguous_float_literal = match &receiver {
-                    Some(ast::Expr::Literal(l)) => matches! {
-                        l.kind(),
-                        ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().is_some_and(|it| it.text().ends_with('.'))
-                    },
-                    _ => false,
-                };
-
-                let receiver_is_part_of_indivisible_expression = match &receiver {
-                    Some(ast::Expr::IfExpr(_)) => {
-                        let next_token_kind = next_non_trivia_token(name_ref.syntax().clone()).map(|t| t.kind());
-                        next_token_kind == Some(SyntaxKind::ELSE_KW)
-                    },
-                    _ => false
-                };
-                if receiver_is_part_of_indivisible_expression {
-                    return None;
-                }
-
-                let mut receiver_ty = receiver.as_ref().and_then(|it| sema.type_of_expr(it));
-                if receiver_is_ambiguous_float_literal {
-                    // `123.|` is parsed as a float but should actually be an integer.
-                    always!(receiver_ty.as_ref().is_none_or(|receiver_ty| receiver_ty.original.is_float()));
-                    receiver_ty = Some(TypeInfo { original: hir::BuiltinType::i32().ty(sema.db), adjusted: None });
-                }
-
-                let kind = NameRefKind::DotAccess(DotAccess {
-                    receiver_ty,
-                    kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal },
-                    receiver,
-                    ctx: DotAccessExprCtx { in_block_expr: is_in_block(field.syntax()), in_breakable: is_in_breakable(field.syntax()).unzip().0 }
-                });
-                return Some(make_res(kind));
+                return field_expr_handle(field.expr(), field.syntax());
             },
             ast::ExternCrate(_) => {
                 let kind = NameRefKind::ExternCrate;
@@ -937,9 +946,12 @@ fn classify_name_ref<'db>(
             ast::MethodCallExpr(method) => {
                 let receiver = find_opt_node_in_file(original_file, method.receiver());
                 let has_parens = has_parens(&method);
+                if !has_parens && let Some(res) = field_expr_handle(method.receiver(), method.syntax()) {
+                    return Some(res)
+                }
                 let kind = NameRefKind::DotAccess(DotAccess {
                     receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
-                    kind: DotAccessKind::Method { has_parens },
+                    kind: DotAccessKind::Method,
                     receiver,
                     ctx: DotAccessExprCtx { in_block_expr: is_in_block(method.syntax()), in_breakable: is_in_breakable(method.syntax()).unzip().0 }
                 });
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 77a2a3a3a9a0..bc5589a64550 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -170,8 +170,7 @@ pub(crate) fn render_field(
             builder.insert(receiver.syntax().text_range().start(), "(".to_owned());
             builder.insert(ctx.source_range().end(), ")".to_owned());
 
-            let is_parens_needed =
-                !matches!(dot_access.kind, DotAccessKind::Method { has_parens: true });
+            let is_parens_needed = !matches!(dot_access.kind, DotAccessKind::Method);
 
             if is_parens_needed {
                 builder.insert(ctx.source_range().end(), "()".to_owned());
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 c466019f991f..3235323b3a59 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
@@ -93,8 +93,8 @@ fn render(
             has_call_parens,
             ..
         }) => (false, has_call_parens, ctx.completion.config.snippet_cap),
-        FuncKind::Method(&DotAccess { kind: DotAccessKind::Method { has_parens }, .. }, _) => {
-            (true, has_parens, ctx.completion.config.snippet_cap)
+        FuncKind::Method(&DotAccess { kind: DotAccessKind::Method, .. }, _) => {
+            (true, true, ctx.completion.config.snippet_cap)
         }
         FuncKind::Method(DotAccess { kind: DotAccessKind::Field { .. }, .. }, _) => {
             (true, false, ctx.completion.config.snippet_cap)
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 f75fa7943ba6..09af635f01ca 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
@@ -2936,6 +2936,43 @@ fn foo() {
     );
 }
 
+#[test]
+fn ambiguous_float_literal_in_ambiguous_method_call() {
+    check(
+        r#"
+#![rustc_coherence_is_core]
+
+impl i32 {
+    pub fn int_method(self) {}
+}
+impl f64 {
+    pub fn float_method(self) {}
+}
+
+fn foo() -> (i32, i32) {
+    1.$0
+    (2, 3)
+}
+    "#,
+        expect![[r#"
+            me int_method() fn(self)
+            sn box    Box::new(expr)
+            sn call   function(expr)
+            sn const        const {}
+            sn dbg        dbg!(expr)
+            sn dbgr      dbg!(&expr)
+            sn deref           *expr
+            sn let               let
+            sn letm          let mut
+            sn match   match expr {}
+            sn ref             &expr
+            sn refm        &mut expr
+            sn return    return expr
+            sn unsafe      unsafe {}
+        "#]],
+    );
+}
+
 #[test]
 fn let_in_condition() {
     check_edit("let", r#"fn f() { if $0 {} }"#, r#"fn f() { if let $1 = $0 {} }"#);
@@ -3113,6 +3150,126 @@ fn let_in_previous_line_of_ambiguous_expr() {
     );
 }
 
+#[test]
+fn field_in_previous_line_of_ambiguous_expr() {
+    check(
+        r#"
+        struct Foo { field: i32 }
+        impl Foo {
+            fn method(&self) {}
+        }
+        fn foo() -> (i32, i32) {
+            let foo = Foo { field: 4 };
+            foo.$0
+            (2, 3)
+        }"#,
+        expect![[r#"
+            fd field           i32
+            me method()  fn(&self)
+            sn box  Box::new(expr)
+            sn call function(expr)
+            sn const      const {}
+            sn dbg      dbg!(expr)
+            sn dbgr    dbg!(&expr)
+            sn deref         *expr
+            sn let             let
+            sn letm        let mut
+            sn match match expr {}
+            sn ref           &expr
+            sn refm      &mut expr
+            sn return  return expr
+            sn unsafe    unsafe {}
+        "#]],
+    );
+
+    check(
+        r#"
+        struct Foo { field: i32 }
+        impl Foo {
+            fn method(&self) {}
+        }
+        fn foo() -> (i32, i32) {
+            let foo = Foo { field: 4 };
+            foo.a$0
+            (2, 3)
+        }"#,
+        expect![[r#"
+            fd field           i32
+            me method()  fn(&self)
+            sn box  Box::new(expr)
+            sn call function(expr)
+            sn const      const {}
+            sn dbg      dbg!(expr)
+            sn dbgr    dbg!(&expr)
+            sn deref         *expr
+            sn let             let
+            sn letm        let mut
+            sn match match expr {}
+            sn ref           &expr
+            sn refm      &mut expr
+            sn return  return expr
+            sn unsafe    unsafe {}
+        "#]],
+    );
+}
+
+#[test]
+fn fn_field_in_previous_line_of_ambiguous_expr() {
+    check(
+        r#"
+        struct Foo { field: fn() }
+        impl Foo {
+            fn method(&self) {}
+        }
+        fn foo() -> (i32, i32) {
+            let foo = Foo { field: || () };
+            foo.$0
+            (2, 3)
+        }"#,
+        expect![[r#"
+            fd field          fn()
+            me method()  fn(&self)
+            sn box  Box::new(expr)
+            sn call function(expr)
+            sn const      const {}
+            sn dbg      dbg!(expr)
+            sn dbgr    dbg!(&expr)
+            sn deref         *expr
+            sn let             let
+            sn letm        let mut
+            sn match match expr {}
+            sn ref           &expr
+            sn refm      &mut expr
+            sn return  return expr
+            sn unsafe    unsafe {}
+        "#]],
+    );
+
+    check_edit(
+        "field",
+        r#"
+        struct Foo { field: fn() }
+        impl Foo {
+            fn method(&self) {}
+        }
+        fn foo() -> (i32, i32) {
+            let foo = Foo { field: || () };
+            foo.a$0
+            (2, 3)
+        }"#,
+        r#"
+        struct Foo { field: fn() }
+        impl Foo {
+            fn method(&self) {}
+        }
+        fn foo() -> (i32, i32) {
+            let foo = Foo { field: || () };
+            (foo.field)()
+            (2, 3)
+        }"#,
+    );
+}
+
 #[test]
 fn private_inherent_and_public_trait() {
     check(

From 68cb0a9b3fb7e632640b5337d3dd39d304ea727c Mon Sep 17 00:00:00 2001
From: daladim 
Date: Thu, 23 Oct 2025 16:35:49 +0200
Subject: [PATCH 072/170] Added the "negation" semantic token

---
 .../crates/ide/src/syntax_highlighting/highlight.rs          | 2 +-
 .../rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs | 5 ++++-
 .../crates/rust-analyzer/src/lsp/semantic_tokens.rs          | 1 +
 .../rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs   | 1 +
 4 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
index d73575fb9549..829d1279a839 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
@@ -137,7 +137,7 @@ fn punctuation(
         }
         (T![!], MACRO_RULES) => HlPunct::MacroBang.into(),
         (T![!], NEVER_TYPE) => HlTag::BuiltinType.into(),
-        (T![!], PREFIX_EXPR) => HlOperator::Logical.into(),
+        (T![!], PREFIX_EXPR) => HlOperator::Negation.into(),
         (T![*], PTR_TYPE) => HlTag::Keyword.into(),
         (T![*], PREFIX_EXPR) => {
             let h = HlTag::Operator(HlOperator::Other).into();
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
index 4b8762640c74..456a61298741 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
@@ -124,8 +124,10 @@ pub enum HlOperator {
     Bitwise,
     /// +, -, *, /, +=, -=, *=, /=
     Arithmetic,
-    /// &&, ||, !
+    /// &&, ||
     Logical,
+    /// !
+    Negation,
     /// >, <, ==, >=, <=, !=
     Comparison,
     /// Other operators
@@ -194,6 +196,7 @@ impl HlTag {
                 HlOperator::Arithmetic => "arithmetic",
                 HlOperator::Logical => "logical",
                 HlOperator::Comparison => "comparison",
+                HlOperator::Negation => "negation",
                 HlOperator::Other => "operator",
             },
             HlTag::StringLiteral => "string_literal",
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
index 3c21e1992525..828118a0866d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
@@ -91,6 +91,7 @@ define_semantic_token_types![
         (LIFETIME, "lifetime"),
         (LOGICAL, "logical") => OPERATOR,
         (MACRO_BANG, "macroBang") => MACRO,
+        (NEGATION, "negation") => OPERATOR,
         (PARENTHESIS, "parenthesis"),
         (PROC_MACRO, "procMacro") => MACRO,
         (PUNCTUATION, "punctuation"),
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 cd384ca713ec..2d2dacbe2e15 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
@@ -840,6 +840,7 @@ fn semantic_token_type_and_modifiers(
             HlOperator::Bitwise => types::BITWISE,
             HlOperator::Arithmetic => types::ARITHMETIC,
             HlOperator::Logical => types::LOGICAL,
+            HlOperator::Negation => types::NEGATION,
             HlOperator::Comparison => types::COMPARISON,
             HlOperator::Other => types::OPERATOR,
         },

From 1ef688810f938a5ea1f4cec78e5727d6abcedb3e Mon Sep 17 00:00:00 2001
From: daladim 
Date: Thu, 23 Oct 2025 16:40:40 +0200
Subject: [PATCH 073/170] Updated unit tests

---
 .../src/syntax_highlighting/test_data/highlight_general.html    | 2 +-
 .../src/syntax_highlighting/test_data/highlight_operators.html  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
index d99b29cfb8fa..d058191aef72 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
@@ -148,7 +148,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     let baz = (-42,);
     let baz = -baz.0;
 
-    let _ = !true;
+    let _ = !true;
 
     'foo: loop {
         break 'foo;
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
index 9c42401ed077..cceb159c9dd4 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
@@ -41,7 +41,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
fn main() {
-    1 + 1 - 1 * 1 / 1 % 1 | 1 & 1 ! 1 ^ 1 >> 1 << 1;
+    1 + 1 - 1 * 1 / 1 % 1 | 1 & 1 ! 1 ^ 1 >> 1 << 1;
     let mut a = 0;
     a += 1;
     a -= 1;

From a5346c609ec52353b0d931ca7ca0f84016f4d7b6 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Tue, 16 Sep 2025 04:13:27 +0200
Subject: [PATCH 074/170] Improve source code for `highlight.rs`

---
 src/librustdoc/html/highlight.rs | 706 ++++++++++++++++---------------
 src/librustdoc/lib.rs            |   1 +
 2 files changed, 359 insertions(+), 348 deletions(-)

diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index f2055608aa9d..a9cf10034432 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -5,7 +5,6 @@
 //!
 //! Use the `render_with_highlighting` to highlight some rust code.
 
-use std::borrow::Cow;
 use std::collections::VecDeque;
 use std::fmt::{self, Display, Write};
 use std::{cmp, iter};
@@ -134,151 +133,341 @@ fn can_merge(class1: Option, class2: Option, text: &str) -> bool {
     }
 }
 
+#[derive(Debug)]
+struct Content {
+    text: String,
+    /// If `Some` and the `span` is different from the parent, then it might generate a link so we
+    /// need to keep this information.
+    class: Option,
+    needs_escape: bool,
+}
+
+#[derive(Debug)]
+struct Element {
+    /// If `class` is `None`, then it's just plain text with no HTML tag.
+    class: Option,
+    /// Content for the current element.
+    content: Vec,
+}
+
+impl Element {
+    fn new(class: Option, text: String, needs_escape: bool) -> Self {
+        Self { class, content: vec![Content { text, class, needs_escape }] }
+    }
+
+    fn can_merge(&self, other: &Self) -> bool {
+        other.content.iter().all(|c| can_merge(self.class, other.class, &c.text))
+    }
+
+    fn write_elem_to(&self, out: &mut W, href_context: &Option>, parent_class: Option) {
+        let mut prev = parent_class;
+        let mut closing_tag = None;
+        for part in &self.content {
+            let text: &dyn Display = if part.needs_escape { &EscapeBodyText(&part.text) } else { &part.text };
+            if part.class.is_some() {
+                // We only try to generate links as the `` should have already be generated
+                // by the caller of `write_elem_to`.
+                if let Some(new_closing_tag) = string_without_closing_tag(
+                    out,
+                    text,
+                    part.class,
+                    href_context,
+                    prev != part.class,
+                ) {
+                    if new_closing_tag == "" {
+                        out.write_str(new_closing_tag).unwrap();
+                        closing_tag = None;
+                    } else {
+                        closing_tag = Some(new_closing_tag);
+                    }
+                }
+                prev = part.class;
+            } else {
+                write!(out, "{text}").unwrap();
+            }
+        }
+        if let Some(closing_tag) = closing_tag {
+            out.write_str(closing_tag).unwrap();
+        }
+    }
+}
+
+#[derive(Debug)]
+enum ElementOrStack {
+    Element(Element),
+    Stack(ElementStack),
+}
+
+#[derive(Debug)]
+struct ElementStack {
+    elements: Vec,
+    parent: Option>,
+    class: Option,
+}
+
+impl ElementStack {
+    fn new() -> Self {
+        Self::new_with_class(None)
+    }
+
+    fn new_with_class(class: Option) -> Self {
+        Self { elements: Vec::new(), parent: None, class }
+    }
+
+    fn push_element(&mut self, mut elem: Element) {
+        if let Some(ElementOrStack::Element(last)) = self.elements.last_mut()
+            && last.can_merge(&elem)
+        {
+            for part in elem.content.drain(..) {
+                last.content.push(part);
+            }
+        } else {
+            self.elements.push(ElementOrStack::Element(elem));
+        }
+    }
+
+    fn empty_stack_and_set_new_heads(&mut self, class: Class, element: Element) {
+        self.elements.clear();
+        if let Some(parent) = &mut self.parent {
+            parent.empty_stack_and_set_new_heads(class, element);
+        } else {
+            let mut new_stack = ElementStack::new_with_class(Some(class));
+            new_stack.elements.push(ElementOrStack::Element(element));
+            self.parent.replace(Box::new(new_stack));
+        }
+    }
+
+    fn enter_stack(&mut self, ElementStack { elements, parent, class }: ElementStack) {
+        assert!(parent.is_none(), "`enter_stack` used with a non empty parent");
+        let parent_elements = std::mem::take(&mut self.elements);
+        let parent_parent = std::mem::take(&mut self.parent);
+        self.parent = Some(Box::new(ElementStack {
+            elements: parent_elements,
+            parent: parent_parent,
+            class: self.class,
+        }));
+        self.class = class;
+        self.elements = elements;
+    }
+
+    fn enter_elem(&mut self, class: Class) {
+        let elements = std::mem::take(&mut self.elements);
+        let parent = std::mem::take(&mut self.parent);
+        self.parent = Some(Box::new(ElementStack { elements, parent, class: self.class }));
+        self.class = Some(class);
+    }
+
+    fn exit_elem(&mut self) {
+        let Some(element) = std::mem::take(&mut self.parent) else {
+            panic!("exiting an element where there is no parent");
+        };
+        let ElementStack { elements, parent, class } = Box::into_inner(element);
+
+        let old_elements = std::mem::take(&mut self.elements);
+        self.elements = elements;
+        self.elements.push(ElementOrStack::Stack(ElementStack {
+            elements: old_elements,
+            class: self.class,
+            parent: None,
+        }));
+        self.parent = parent;
+        self.class = class;
+    }
+
+    fn write_content(&self, out: &mut W, href_context: &Option>) {
+        let mut elem = self;
+
+        // We get the top most item.
+        while let Some(parent) = &elem.parent {
+            elem = parent;
+        }
+        // Now we can output the whole content.
+        elem.write_to(out, href_context, None);
+    }
+
+    fn write_to(
+        &self,
+        out: &mut W,
+        href_context: &Option>,
+        parent_class: Option,
+    ) {
+        // If it only contains stack, it means it has no content of its own so no need to generate
+        // a tag.
+        let closing_tag = if let Some(Class::Expansion) = self.class {
+            out.write_str("").unwrap();
+            ""
+        } else if let Some(class) = self.class
+            // `PreludeTy` can never include more than an ident so it should not generate
+            // a wrapping `span`.
+            && !matches!(class, Class::PreludeTy(_))
+        {
+            // Macro is the only `ElementStack` that can generate a link to definition to its
+            // whole content, so to prevent having ``,
+            // we generate the `` directly here.
+            //
+            // For other elements, the links will be generated in `write_elem_to`.
+            let href_context = if matches!(class, Class::Macro(_)) {
+                href_context
+            } else {
+                &None
+            };
+            string_without_closing_tag(out, "", Some(class), href_context, self.class != parent_class)
+                .expect(
+                    "internal error: enter_span was called with Some(class) but did not \
+                    return a closing HTML tag",
+                )
+        } else {
+            ""
+        };
+
+        for child_elem in self.elements.iter() {
+            let child_elem = match child_elem {
+                ElementOrStack::Element(elem) => elem,
+                ElementOrStack::Stack(stack) => {
+                    stack.write_to(out, href_context, parent_class);
+                    continue;
+                }
+            };
+            if child_elem.content.is_empty() {
+                continue;
+            }
+            child_elem.write_elem_to(out, href_context, parent_class);
+        }
+
+        out.write_str(closing_tag).unwrap();
+    }
+}
+
 /// This type is used as a conveniency to prevent having to pass all its fields as arguments into
 /// the various functions (which became its methods).
 struct TokenHandler<'a, 'tcx, F: Write> {
     out: &'a mut F,
-    /// It contains the closing tag and the associated `Class`.
-    closing_tags: Vec<(&'static str, Class)>,
-    /// This is used because we don't automatically generate the closing tag on `ExitSpan` in
-    /// case an `EnterSpan` event with the same class follows.
-    pending_exit_span: Option,
-    /// `current_class` and `pending_elems` are used to group HTML elements with same `class`
-    /// attributes to reduce the DOM size.
-    current_class: Option,
+    element_stack: ElementStack,
     /// We need to keep the `Class` for each element because it could contain a `Span` which is
     /// used to generate links.
-    pending_elems: Vec<(Cow<'a, str>, Option)>,
     href_context: Option>,
-    write_line_number: fn(&mut F, u32, &'static str),
+    write_line_number: fn(u32) -> String,
+    line: u32,
+    max_lines: u32,
 }
 
 impl std::fmt::Debug for TokenHandler<'_, '_, F> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("TokenHandler")
-            .field("closing_tags", &self.closing_tags)
-            .field("pending_exit_span", &self.pending_exit_span)
-            .field("current_class", &self.current_class)
-            .field("pending_elems", &self.pending_elems)
-            .finish()
+        f.debug_struct("TokenHandler").field("element_stack", &self.element_stack).finish()
     }
 }
 
 impl TokenHandler<'_, '_, F> {
-    fn handle_exit_span(&mut self) {
-        // We can't get the last `closing_tags` element using `pop()` because `closing_tags` is
-        // being used in `write_pending_elems`.
-        let class = self.closing_tags.last().expect("ExitSpan without EnterSpan").1;
-        // We flush everything just in case...
-        self.write_pending_elems(Some(class));
-
-        exit_span(self.out, self.closing_tags.pop().expect("ExitSpan without EnterSpan").0);
-        self.pending_exit_span = None;
+    fn handle_backline(&mut self) -> Option {
+        self.line += 1;
+        if self.line < self.max_lines {
+            return Some((self.write_line_number)(self.line));
+        }
+        None
     }
 
-    /// Write all the pending elements sharing a same (or at mergeable) `Class`.
-    ///
-    /// If there is a "parent" (if a `EnterSpan` event was encountered) and the parent can be merged
-    /// with the elements' class, then we simply write the elements since the `ExitSpan` event will
-    /// close the tag.
-    ///
-    /// Otherwise, if there is only one pending element, we let the `string` function handle both
-    /// opening and closing the tag, otherwise we do it into this function.
-    ///
-    /// It returns `true` if `current_class` must be set to `None` afterwards.
-    fn write_pending_elems(&mut self, current_class: Option) -> bool {
-        if self.pending_elems.is_empty() {
-            return false;
-        }
-        if let Some((_, parent_class)) = self.closing_tags.last()
-            && can_merge(current_class, Some(*parent_class), "")
+    fn push_element_without_backline_check(
+        &mut self,
+        class: Option,
+        text: String,
+        needs_escape: bool,
+    ) {
+        self.element_stack.push_element(Element::new(class, text, needs_escape))
+    }
+
+    fn push_element(&mut self, class: Option, mut text: String) {
+        let needs_escape = if text == "\n"
+            && let Some(backline) = self.handle_backline()
         {
-            for (text, class) in self.pending_elems.iter() {
-                string(
-                    self.out,
-                    EscapeBodyText(text),
-                    *class,
-                    &self.href_context,
-                    false,
-                    self.write_line_number,
-                );
-            }
+            text.push_str(&backline);
+            false
         } else {
-            // We only want to "open" the tag ourselves if we have more than one pending and if the
-            // current parent tag is not the same as our pending content.
-            let close_tag = if self.pending_elems.len() > 1
-                && let Some(current_class) = current_class
-                // `PreludeTy` can never include more than an ident so it should not generate
-                // a wrapping `span`.
-                && !matches!(current_class, Class::PreludeTy(_))
-            {
-                Some(enter_span(self.out, current_class, &self.href_context))
-            } else {
-                None
-            };
-            // To prevent opening a macro expansion span being closed right away because
-            // the currently open item is replaced by a new class.
-            let last_pending =
-                self.pending_elems.pop_if(|(_, class)| *class == Some(Class::Expansion));
-            for (text, class) in self.pending_elems.iter() {
-                string(
-                    self.out,
-                    EscapeBodyText(text),
-                    *class,
-                    &self.href_context,
-                    close_tag.is_none(),
-                    self.write_line_number,
-                );
-            }
-            if let Some(close_tag) = close_tag {
-                exit_span(self.out, close_tag);
-            }
-            if let Some((text, class)) = last_pending {
-                string(
-                    self.out,
-                    EscapeBodyText(&text),
-                    class,
-                    &self.href_context,
-                    close_tag.is_none(),
-                    self.write_line_number,
-                );
-            }
-        }
-        self.pending_elems.clear();
-        true
+            true
+        };
+
+        self.push_element_without_backline_check(class, text, needs_escape);
     }
 
-    #[inline]
-    fn write_line_number(&mut self, line: u32, extra: &'static str) {
-        (self.write_line_number)(self.out, line, extra);
+    fn start_expansion(&mut self) {
+        // We display everything.
+        self.element_stack.write_content(self.out, &self.href_context);
+
+        // We remove everything and recreate the stack with the expansion at its head.
+        self.element_stack.empty_stack_and_set_new_heads(
+            Class::Expansion,
+            Element {
+                class: None,
+                content: vec![Content {
+                    text: format!(
+                        "",
+                        self.line,
+                    ),
+                    class: None,
+                    needs_escape: false,
+                }],
+            },
+        );
+    }
+
+    fn add_expanded_code(&mut self, expanded_code: &ExpandedCode) {
+        self.element_stack.push_element(Element::new(
+            None,
+            format!("{}", expanded_code.code),
+            false,
+        ));
+        self.element_stack.enter_stack(ElementStack::new_with_class(Some(Class::Original)));
+    }
+
+    fn close_expansion(&mut self) {
+        let mut old_stack = Vec::new();
+
+        // We inline everything into the top-most element.
+        while self.element_stack.parent.is_some() {
+            self.element_stack.exit_elem();
+            if let Some(ElementOrStack::Stack(stack)) = self.element_stack.elements.last()
+                && let Some(class) = stack.class
+                && class != Class::Original
+            {
+                old_stack.push(class);
+            }
+        }
+        // We display everything.
+        self.element_stack.write_content(self.out, &self.href_context);
+
+        // We recreate the tree but without the expansion node.
+        self.element_stack.elements.clear();
+        self.element_stack.class = None;
+        for class in old_stack.iter().rev() {
+            self.element_stack.enter_stack(ElementStack::new_with_class(Some(*class)));
+        }
     }
 }
 
 impl Drop for TokenHandler<'_, '_, F> {
     /// When leaving, we need to flush all pending data to not have missing content.
     fn drop(&mut self) {
-        if self.pending_exit_span.is_some() {
-            self.handle_exit_span();
-        } else {
-            self.write_pending_elems(self.current_class);
-        }
+        self.element_stack.write_content(self.out, &self.href_context);
     }
 }
 
-fn write_scraped_line_number(out: &mut impl Write, line: u32, extra: &'static str) {
+fn scraped_line_number(line: u32) -> String {
     // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr
     // Do not show "1 2 3 4 5 ..." in web search results.
-    write!(out, "{extra}{line}",).unwrap();
+    format!("{line}")
 }
 
-fn write_line_number(out: &mut impl Write, line: u32, extra: &'static str) {
+fn line_number(line: u32) -> String {
     // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr
     // Do not show "1 2 3 4 5 ..." in web search results.
-    write!(out, "{extra}{line}",).unwrap();
+    format!("{line}")
 }
 
-fn empty_line_number(out: &mut impl Write, _: u32, extra: &'static str) {
-    out.write_str(extra).unwrap();
+fn empty_line_number(_: u32) -> String {
+    String::new()
 }
 
 fn get_next_expansion(
@@ -292,80 +481,24 @@ fn get_next_expansion(
 fn get_expansion<'a, W: Write>(
     token_handler: &mut TokenHandler<'_, '_, W>,
     expanded_codes: &'a [ExpandedCode],
-    line: u32,
     span: Span,
 ) -> Option<&'a ExpandedCode> {
-    if let Some(expanded_code) = get_next_expansion(expanded_codes, line, span) {
-        let (closing, reopening) = if let Some(current_class) = token_handler.current_class
-            && let class = current_class.as_html()
-            && !class.is_empty()
-        {
-            ("", format!(""))
-        } else {
-            ("", String::new())
-        };
-        let id = format!("expand-{line}");
-        token_handler.pending_elems.push((
-            Cow::Owned(format!(
-                "{closing}\
-\
-    {reopening}",
-            )),
-            Some(Class::Expansion),
-        ));
-        Some(expanded_code)
-    } else {
-        None
-    }
-}
-
-fn start_expansion(out: &mut Vec<(Cow<'_, str>, Option)>, expanded_code: &ExpandedCode) {
-    out.push((
-        Cow::Owned(format!(
-            "{}",
-            expanded_code.code,
-        )),
-        Some(Class::Expansion),
-    ));
+    let expanded_code = get_next_expansion(expanded_codes, token_handler.line, span)?;
+    token_handler.start_expansion();
+    Some(expanded_code)
 }
 
 fn end_expansion<'a, W: Write>(
     token_handler: &mut TokenHandler<'_, '_, W>,
     expanded_codes: &'a [ExpandedCode],
-    expansion_start_tags: &[(&'static str, Class)],
-    line: u32,
     span: Span,
 ) -> Option<&'a ExpandedCode> {
-    if let Some(expanded_code) = get_next_expansion(expanded_codes, line, span) {
-        // We close the current "original" content.
-        token_handler.pending_elems.push((Cow::Borrowed(""), Some(Class::Expansion)));
-        return Some(expanded_code);
+    token_handler.element_stack.exit_elem();
+    let expansion = get_next_expansion(expanded_codes, token_handler.line, span);
+    if expansion.is_none() {
+        token_handler.close_expansion();
     }
-
-    let skip = iter::zip(token_handler.closing_tags.as_slice(), expansion_start_tags)
-        .position(|(tag, start_tag)| tag != start_tag)
-        .unwrap_or_else(|| cmp::min(token_handler.closing_tags.len(), expansion_start_tags.len()));
-
-    let tags = iter::chain(
-        expansion_start_tags.iter().skip(skip),
-        token_handler.closing_tags.iter().skip(skip),
-    );
-
-    let mut elem = Cow::Borrowed("");
-
-    for (tag, _) in tags.clone() {
-        elem.to_mut().push_str(tag);
-    }
-    for (_, class) in tags {
-        write!(elem.to_mut(), "", class.as_html()).unwrap();
-    }
-
-    token_handler.pending_elems.push((elem, Some(Class::Expansion)));
-    None
+    expansion
 }
 
 #[derive(Clone, Copy)]
@@ -417,29 +550,29 @@ pub(super) fn write_code(
         if src.contains('\r') { src.replace("\r\n", "\n").into() } else { Cow::Borrowed(src) };
     let mut token_handler = TokenHandler {
         out,
-        closing_tags: Vec::new(),
-        pending_exit_span: None,
-        current_class: None,
-        pending_elems: Vec::with_capacity(20),
         href_context,
         write_line_number: match line_info {
             Some(line_info) => {
                 if line_info.is_scraped_example {
-                    write_scraped_line_number
+                    scraped_line_number
                 } else {
-                    write_line_number
+                    line_number
                 }
             }
             None => empty_line_number,
         },
+        line: 0,
+        max_lines: u32::MAX,
+        element_stack: ElementStack::new(),
     };
 
-    let (mut line, max_lines) = if let Some(line_info) = line_info {
-        token_handler.write_line_number(line_info.start_line, "");
-        (line_info.start_line, line_info.max_lines)
-    } else {
-        (0, u32::MAX)
-    };
+    if let Some(line_info) = line_info {
+        token_handler.line = line_info.start_line - 1;
+        token_handler.max_lines = line_info.max_lines;
+        if let Some(text) = token_handler.handle_backline() {
+            token_handler.push_element_without_backline_check(None, text, false);
+        }
+    }
 
     let (expanded_codes, file_span) = match token_handler.href_context.as_ref().and_then(|c| {
         let expanded_codes = c.context.shared.expanded_codes.get(&c.file_span.lo())?;
@@ -448,114 +581,44 @@ pub(super) fn write_code(
         Some((expanded_codes, file_span)) => (expanded_codes.as_slice(), file_span),
         None => (&[] as &[ExpandedCode], DUMMY_SP),
     };
-    let mut current_expansion = get_expansion(&mut token_handler, expanded_codes, line, file_span);
-    token_handler.write_pending_elems(None);
-    let mut expansion_start_tags = Vec::new();
+    let mut current_expansion = get_expansion(&mut token_handler, expanded_codes, file_span);
 
     Classifier::new(
         &src,
         token_handler.href_context.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP),
         decoration_info,
     )
-    .highlight(&mut |span, highlight| {
-        match highlight {
-            Highlight::Token { text, class } => {
-                // If we received a `ExitSpan` event and then have a non-compatible `Class`, we
-                // need to close the ``.
-                let need_current_class_update = if let Some(pending) =
-                    token_handler.pending_exit_span
-                    && !can_merge(Some(pending), class, text)
+    .highlight(&mut |span, highlight| match highlight {
+        Highlight::Token { text, class } => {
+            token_handler.push_element(class, text.to_string());
+
+            if text == "\n" {
+                if current_expansion.is_none() {
+                    current_expansion = get_expansion(&mut token_handler, expanded_codes, span);
+                }
+                if let Some(ref current_expansion) = current_expansion
+                    && current_expansion.span.lo() == span.hi()
                 {
-                    token_handler.handle_exit_span();
-                    true
-                // If the two `Class` are different, time to flush the current content and start
-                // a new one.
-                } else if !can_merge(token_handler.current_class, class, text) {
-                    token_handler.write_pending_elems(token_handler.current_class);
-                    true
-                } else {
-                    token_handler.current_class.is_none()
-                };
-
-                if need_current_class_update {
-                    token_handler.current_class = class.map(Class::dummy);
+                    token_handler.add_expanded_code(current_expansion);
                 }
-                if text == "\n" {
-                    line += 1;
-                    if line < max_lines {
-                        token_handler
-                            .pending_elems
-                            .push((Cow::Borrowed(text), Some(Class::Backline(line))));
-                    }
-                    if current_expansion.is_none() {
-                        current_expansion =
-                            get_expansion(&mut token_handler, expanded_codes, line, span);
-                        expansion_start_tags = token_handler.closing_tags.clone();
-                    }
-                    if let Some(ref current_expansion) = current_expansion
-                        && current_expansion.span.lo() == span.hi()
+            } else {
+                let mut need_end = false;
+                if let Some(ref current_expansion) = current_expansion {
+                    if current_expansion.span.lo() == span.hi() {
+                        token_handler.add_expanded_code(current_expansion);
+                    } else if current_expansion.end_line == token_handler.line
+                        && span.hi() >= current_expansion.span.hi()
                     {
-                        start_expansion(&mut token_handler.pending_elems, current_expansion);
-                    }
-                } else {
-                    token_handler.pending_elems.push((Cow::Borrowed(text), class));
-
-                    let mut need_end = false;
-                    if let Some(ref current_expansion) = current_expansion {
-                        if current_expansion.span.lo() == span.hi() {
-                            start_expansion(&mut token_handler.pending_elems, current_expansion);
-                        } else if current_expansion.end_line == line
-                            && span.hi() >= current_expansion.span.hi()
-                        {
-                            need_end = true;
-                        }
-                    }
-                    if need_end {
-                        current_expansion = end_expansion(
-                            &mut token_handler,
-                            expanded_codes,
-                            &expansion_start_tags,
-                            line,
-                            span,
-                        );
+                        need_end = true;
                     }
                 }
-            }
-            Highlight::EnterSpan { class } => {
-                let mut should_add = true;
-                if let Some(pending_exit_span) = token_handler.pending_exit_span {
-                    if class.is_equal_to(pending_exit_span) {
-                        should_add = false;
-                    } else {
-                        token_handler.handle_exit_span();
-                    }
-                } else {
-                    // We flush everything just in case...
-                    if token_handler.write_pending_elems(token_handler.current_class) {
-                        token_handler.current_class = None;
-                    }
+                if need_end {
+                    current_expansion = end_expansion(&mut token_handler, expanded_codes, span);
                 }
-                if should_add {
-                    let closing_tag =
-                        enter_span(token_handler.out, class, &token_handler.href_context);
-                    token_handler.closing_tags.push((closing_tag, class));
-                }
-
-                token_handler.current_class = None;
-                token_handler.pending_exit_span = None;
             }
-            Highlight::ExitSpan => {
-                token_handler.current_class = None;
-                token_handler.pending_exit_span = Some(
-                    token_handler
-                        .closing_tags
-                        .last()
-                        .as_ref()
-                        .expect("ExitSpan without EnterSpan")
-                        .1,
-                );
-            }
-        };
+        }
+        Highlight::EnterSpan { class } => token_handler.element_stack.enter_elem(class),
+        Highlight::ExitSpan => token_handler.element_stack.exit_elem(),
     });
 }
 
@@ -585,9 +648,10 @@ enum Class {
     PreludeVal(Span),
     QuestionMark,
     Decoration(&'static str),
-    Backline(u32),
     /// Macro expansion.
     Expansion,
+    /// "original" code without macro expansion.
+    Original,
 }
 
 impl Class {
@@ -605,17 +669,6 @@ impl Class {
         }
     }
 
-    /// If `self` contains a `Span`, it'll be replaced with `DUMMY_SP` to prevent creating links
-    /// on "empty content" (because of the attributes merge).
-    fn dummy(self) -> Self {
-        match self {
-            Self::Self_(_) => Self::Self_(DUMMY_SP),
-            Self::Macro(_) => Self::Macro(DUMMY_SP),
-            Self::Ident(_) => Self::Ident(DUMMY_SP),
-            s => s,
-        }
-    }
-
     /// Returns the css class expected by rustdoc for each `Class`.
     fn as_html(self) -> &'static str {
         match self {
@@ -636,8 +689,8 @@ impl Class {
             Class::PreludeVal(_) => "prelude-val",
             Class::QuestionMark => "question-mark",
             Class::Decoration(kind) => kind,
-            Class::Backline(_) => "",
-            Class::Expansion => "",
+            Class::Expansion => "expansion",
+            Class::Original => "original",
         }
     }
 
@@ -662,12 +715,23 @@ impl Class {
             | Self::Lifetime
             | Self::QuestionMark
             | Self::Decoration(_)
-            | Self::Backline(_)
+            // | Self::Backline(_)
+            | Self::Original
             | Self::Expansion => None,
         }
     }
 }
 
+impl fmt::Display for Class {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let html = self.as_html();
+        if html.is_empty() {
+            return Ok(());
+        }
+        write!(f, " class=\"{html}\"")
+    }
+}
+
 #[derive(Debug)]
 enum Highlight<'a> {
     Token { text: &'a str, class: Option },
@@ -1190,60 +1254,6 @@ impl<'src> Classifier<'src> {
     }
 }
 
-/// Called when we start processing a span of text that should be highlighted.
-/// The `Class` argument specifies how it should be highlighted.
-fn enter_span(
-    out: &mut impl Write,
-    klass: Class,
-    href_context: &Option>,
-) -> &'static str {
-    string_without_closing_tag(out, "", Some(klass), href_context, true).expect(
-        "internal error: enter_span was called with Some(klass) but did not return a \
-            closing HTML tag",
-    )
-}
-
-/// Called at the end of a span of highlighted text.
-fn exit_span(out: &mut impl Write, closing_tag: &str) {
-    out.write_str(closing_tag).unwrap();
-}
-
-/// Called for a span of text. If the text should be highlighted differently
-/// from the surrounding text, then the `Class` argument will be a value other
-/// than `None`.
-///
-/// The following sequences of callbacks are equivalent:
-/// ```plain
-///     enter_span(Foo), string("text", None), exit_span()
-///     string("text", Foo)
-/// ```
-///
-/// The latter can be thought of as a shorthand for the former, which is more
-/// flexible.
-///
-/// Note that if `context` is not `None` and that the given `klass` contains a `Span`, the function
-/// will then try to find this `span` in the `span_correspondence_map`. If found, it'll then
-/// generate a link for this element (which corresponds to where its definition is located).
-fn string(
-    out: &mut W,
-    text: EscapeBodyText<'_>,
-    klass: Option,
-    href_context: &Option>,
-    open_tag: bool,
-    write_line_number_callback: fn(&mut W, u32, &'static str),
-) {
-    if let Some(Class::Backline(line)) = klass {
-        write_line_number_callback(out, line, "\n");
-    } else if let Some(Class::Expansion) = klass {
-        // This has already been escaped so we get the text to write it directly.
-        out.write_str(text.0).unwrap();
-    } else if let Some(closing_tag) =
-        string_without_closing_tag(out, text, klass, href_context, open_tag)
-    {
-        out.write_str(closing_tag).unwrap();
-    }
-}
-
 fn generate_link_to_def(
     out: &mut impl Write,
     text_s: &str,
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 5f72064f0a8c..820b2392e07c 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -6,6 +6,7 @@
 #![feature(ascii_char)]
 #![feature(ascii_char_variants)]
 #![feature(assert_matches)]
+#![feature(box_into_inner)]
 #![feature(box_patterns)]
 #![feature(debug_closure_helpers)]
 #![feature(file_buffered)]

From c6e26f98810b306669d03f86156f88998d2b67c5 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Wed, 24 Sep 2025 18:31:38 +0200
Subject: [PATCH 075/170] Make compatible stack elements "glue" together to
 prevent creating more HTML tags than necessary

---
 src/librustdoc/html/highlight.rs | 95 ++++++++++++++++++++++++--------
 1 file changed, 71 insertions(+), 24 deletions(-)

diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index a9cf10034432..2fd5ef5608e8 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -159,11 +159,17 @@ impl Element {
         other.content.iter().all(|c| can_merge(self.class, other.class, &c.text))
     }
 
-    fn write_elem_to(&self, out: &mut W, href_context: &Option>, parent_class: Option) {
+    fn write_elem_to(
+        &self,
+        out: &mut W,
+        href_context: &Option>,
+        parent_class: Option,
+    ) {
         let mut prev = parent_class;
         let mut closing_tag = None;
         for part in &self.content {
-            let text: &dyn Display = if part.needs_escape { &EscapeBodyText(&part.text) } else { &part.text };
+            let text: &dyn Display =
+                if part.needs_escape { &EscapeBodyText(&part.text) } else { &part.text };
             if part.class.is_some() {
                 // We only try to generate links as the `` should have already be generated
                 // by the caller of `write_elem_to`.
@@ -198,11 +204,18 @@ enum ElementOrStack {
     Stack(ElementStack),
 }
 
+/// This represents the stack of HTML elements. For example a macro expansion
+/// will contain other elements which might themselves contain other elements
+/// (like macros).
+///
+/// This allows to easily handle HTML tags instead of having a more complicated
+/// state machine to keep track of which tags are open.
 #[derive(Debug)]
 struct ElementStack {
     elements: Vec,
     parent: Option>,
     class: Option,
+    pending_exit: bool,
 }
 
 impl ElementStack {
@@ -211,10 +224,15 @@ impl ElementStack {
     }
 
     fn new_with_class(class: Option) -> Self {
-        Self { elements: Vec::new(), parent: None, class }
+        Self { elements: Vec::new(), parent: None, class, pending_exit: false }
     }
 
     fn push_element(&mut self, mut elem: Element) {
+        if self.pending_exit
+            && !can_merge(self.class, elem.class, elem.content.first().map_or("", |c| &c.text))
+        {
+            self.exit_current_stack();
+        }
         if let Some(ElementOrStack::Element(last)) = self.elements.last_mut()
             && last.can_merge(&elem)
         {
@@ -237,7 +255,21 @@ impl ElementStack {
         }
     }
 
-    fn enter_stack(&mut self, ElementStack { elements, parent, class }: ElementStack) {
+    fn enter_stack(
+        &mut self,
+        ElementStack { elements, parent, class, pending_exit }: ElementStack,
+    ) {
+        if self.pending_exit {
+            if can_merge(self.class, class, "") {
+                self.pending_exit = false;
+                for elem in elements {
+                    self.elements.push(elem);
+                }
+                // Compatible stacks, nothing to be done here!
+                return;
+            }
+            self.exit_current_stack();
+        }
         assert!(parent.is_none(), "`enter_stack` used with a non empty parent");
         let parent_elements = std::mem::take(&mut self.elements);
         let parent_parent = std::mem::take(&mut self.parent);
@@ -245,23 +277,27 @@ impl ElementStack {
             elements: parent_elements,
             parent: parent_parent,
             class: self.class,
+            pending_exit: self.pending_exit,
         }));
         self.class = class;
         self.elements = elements;
+        self.pending_exit = pending_exit;
     }
 
-    fn enter_elem(&mut self, class: Class) {
-        let elements = std::mem::take(&mut self.elements);
-        let parent = std::mem::take(&mut self.parent);
-        self.parent = Some(Box::new(ElementStack { elements, parent, class: self.class }));
-        self.class = Some(class);
-    }
-
+    /// This sets the `pending_exit` field to `true`. Meaning that if we try to push another stack
+    /// which is not compatible with this one, it will exit the current one before adding the new
+    /// one.
     fn exit_elem(&mut self) {
+        self.pending_exit = true;
+    }
+
+    /// Unlike `exit_elem`, this method directly exits the current stack. It is called when the
+    /// current stack is not compatible with a new one pushed or if an expansion was ended.
+    fn exit_current_stack(&mut self) {
         let Some(element) = std::mem::take(&mut self.parent) else {
             panic!("exiting an element where there is no parent");
         };
-        let ElementStack { elements, parent, class } = Box::into_inner(element);
+        let ElementStack { elements, parent, class, pending_exit } = Box::into_inner(element);
 
         let old_elements = std::mem::take(&mut self.elements);
         self.elements = elements;
@@ -269,9 +305,11 @@ impl ElementStack {
             elements: old_elements,
             class: self.class,
             parent: None,
+            pending_exit: false,
         }));
         self.parent = parent;
         self.class = class;
+        self.pending_exit = pending_exit;
     }
 
     fn write_content(&self, out: &mut W, href_context: &Option>) {
@@ -306,16 +344,18 @@ impl ElementStack {
             // we generate the `` directly here.
             //
             // For other elements, the links will be generated in `write_elem_to`.
-            let href_context = if matches!(class, Class::Macro(_)) {
-                href_context
-            } else {
-                &None
-            };
-            string_without_closing_tag(out, "", Some(class), href_context, self.class != parent_class)
-                .expect(
-                    "internal error: enter_span was called with Some(class) but did not \
+            let href_context = if matches!(class, Class::Macro(_)) { href_context } else { &None };
+            string_without_closing_tag(
+                out,
+                "",
+                Some(class),
+                href_context,
+                self.class != parent_class,
+            )
+            .expect(
+                "internal error: enter_span was called with Some(class) but did not \
                     return a closing HTML tag",
-                )
+            )
         } else {
             ""
         };
@@ -427,7 +467,7 @@ impl TokenHandler<'_, '_, F> {
 
         // We inline everything into the top-most element.
         while self.element_stack.parent.is_some() {
-            self.element_stack.exit_elem();
+            self.element_stack.exit_current_stack();
             if let Some(ElementOrStack::Stack(stack)) = self.element_stack.elements.last()
                 && let Some(class) = stack.class
                 && class != Class::Original
@@ -450,6 +490,11 @@ impl TokenHandler<'_, '_, F> {
 impl Drop for TokenHandler<'_, '_, F> {
     /// When leaving, we need to flush all pending data to not have missing content.
     fn drop(&mut self) {
+        // We need to clean the hierarchy before displaying it, otherwise the parents won't see
+        // the last child.
+        while self.element_stack.parent.is_some() {
+            self.element_stack.exit_current_stack();
+        }
         self.element_stack.write_content(self.out, &self.href_context);
     }
 }
@@ -493,7 +538,7 @@ fn end_expansion<'a, W: Write>(
     expanded_codes: &'a [ExpandedCode],
     span: Span,
 ) -> Option<&'a ExpandedCode> {
-    token_handler.element_stack.exit_elem();
+    token_handler.element_stack.exit_current_stack();
     let expansion = get_next_expansion(expanded_codes, token_handler.line, span);
     if expansion.is_none() {
         token_handler.close_expansion();
@@ -617,7 +662,9 @@ pub(super) fn write_code(
                 }
             }
         }
-        Highlight::EnterSpan { class } => token_handler.element_stack.enter_elem(class),
+        Highlight::EnterSpan { class } => {
+            token_handler.element_stack.enter_stack(ElementStack::new_with_class(Some(class)))
+        }
         Highlight::ExitSpan => token_handler.element_stack.exit_elem(),
     });
 }

From b28eabc5e61af59bf7004fd5bf64f064c89411df Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Tue, 7 Oct 2025 14:33:39 +0200
Subject: [PATCH 076/170] Improve performance

---
 src/librustdoc/html/highlight.rs | 76 ++++++++++++++++----------------
 1 file changed, 39 insertions(+), 37 deletions(-)

diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 2fd5ef5608e8..03cbe379a42a 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -5,10 +5,12 @@
 //!
 //! Use the `render_with_highlighting` to highlight some rust code.
 
+use std::borrow::Cow;
 use std::collections::VecDeque;
 use std::fmt::{self, Display, Write};
-use std::{cmp, iter};
+use std::iter;
 
+use itertools::Either;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind};
 use rustc_span::BytePos;
@@ -134,8 +136,8 @@ fn can_merge(class1: Option, class2: Option, text: &str) -> bool {
 }
 
 #[derive(Debug)]
-struct Content {
-    text: String,
+struct Content<'a> {
+    text: Cow<'a, str>,
     /// If `Some` and the `span` is different from the parent, then it might generate a link so we
     /// need to keep this information.
     class: Option,
@@ -143,15 +145,15 @@ struct Content {
 }
 
 #[derive(Debug)]
-struct Element {
+struct Element<'a> {
     /// If `class` is `None`, then it's just plain text with no HTML tag.
     class: Option,
     /// Content for the current element.
-    content: Vec,
+    content: Vec>,
 }
 
-impl Element {
-    fn new(class: Option, text: String, needs_escape: bool) -> Self {
+impl<'a> Element<'a> {
+    fn new(class: Option, text: Cow<'a, str>, needs_escape: bool) -> Self {
         Self { class, content: vec![Content { text, class, needs_escape }] }
     }
 
@@ -168,8 +170,11 @@ impl Element {
         let mut prev = parent_class;
         let mut closing_tag = None;
         for part in &self.content {
-            let text: &dyn Display =
-                if part.needs_escape { &EscapeBodyText(&part.text) } else { &part.text };
+            let text = if part.needs_escape {
+                Either::Left(&EscapeBodyText(&part.text))
+            } else {
+                Either::Right(&part.text)
+            };
             if part.class.is_some() {
                 // We only try to generate links as the `` should have already be generated
                 // by the caller of `write_elem_to`.
@@ -199,9 +204,9 @@ impl Element {
 }
 
 #[derive(Debug)]
-enum ElementOrStack {
-    Element(Element),
-    Stack(ElementStack),
+enum ElementOrStack<'a> {
+    Element(Element<'a>),
+    Stack(ElementStack<'a>),
 }
 
 /// This represents the stack of HTML elements. For example a macro expansion
@@ -211,14 +216,14 @@ enum ElementOrStack {
 /// This allows to easily handle HTML tags instead of having a more complicated
 /// state machine to keep track of which tags are open.
 #[derive(Debug)]
-struct ElementStack {
-    elements: Vec,
-    parent: Option>,
+struct ElementStack<'a> {
+    elements: Vec>,
+    parent: Option>>,
     class: Option,
     pending_exit: bool,
 }
 
-impl ElementStack {
+impl<'a> ElementStack<'a> {
     fn new() -> Self {
         Self::new_with_class(None)
     }
@@ -227,7 +232,7 @@ impl ElementStack {
         Self { elements: Vec::new(), parent: None, class, pending_exit: false }
     }
 
-    fn push_element(&mut self, mut elem: Element) {
+    fn push_element(&mut self, mut elem: Element<'a>) {
         if self.pending_exit
             && !can_merge(self.class, elem.class, elem.content.first().map_or("", |c| &c.text))
         {
@@ -236,15 +241,13 @@ impl ElementStack {
         if let Some(ElementOrStack::Element(last)) = self.elements.last_mut()
             && last.can_merge(&elem)
         {
-            for part in elem.content.drain(..) {
-                last.content.push(part);
-            }
+            last.content.append(&mut elem.content);
         } else {
             self.elements.push(ElementOrStack::Element(elem));
         }
     }
 
-    fn empty_stack_and_set_new_heads(&mut self, class: Class, element: Element) {
+    fn empty_stack_and_set_new_heads(&mut self, class: Class, element: Element<'a>) {
         self.elements.clear();
         if let Some(parent) = &mut self.parent {
             parent.empty_stack_and_set_new_heads(class, element);
@@ -257,14 +260,12 @@ impl ElementStack {
 
     fn enter_stack(
         &mut self,
-        ElementStack { elements, parent, class, pending_exit }: ElementStack,
+        ElementStack { elements, parent, class, pending_exit }: ElementStack<'a>,
     ) {
         if self.pending_exit {
             if can_merge(self.class, class, "") {
                 self.pending_exit = false;
-                for elem in elements {
-                    self.elements.push(elem);
-                }
+                self.elements.extend(elements);
                 // Compatible stacks, nothing to be done here!
                 return;
             }
@@ -382,7 +383,7 @@ impl ElementStack {
 /// the various functions (which became its methods).
 struct TokenHandler<'a, 'tcx, F: Write> {
     out: &'a mut F,
-    element_stack: ElementStack,
+    element_stack: ElementStack<'a>,
     /// We need to keep the `Class` for each element because it could contain a `Span` which is
     /// used to generate links.
     href_context: Option>,
@@ -397,7 +398,7 @@ impl std::fmt::Debug for TokenHandler<'_, '_, F> {
     }
 }
 
-impl TokenHandler<'_, '_, F> {
+impl<'a, F: Write> TokenHandler<'a, '_, F> {
     fn handle_backline(&mut self) -> Option {
         self.line += 1;
         if self.line < self.max_lines {
@@ -409,20 +410,21 @@ impl TokenHandler<'_, '_, F> {
     fn push_element_without_backline_check(
         &mut self,
         class: Option,
-        text: String,
+        text: Cow<'a, str>,
         needs_escape: bool,
     ) {
         self.element_stack.push_element(Element::new(class, text, needs_escape))
     }
 
-    fn push_element(&mut self, class: Option, mut text: String) {
-        let needs_escape = if text == "\n"
+    fn push_element(&mut self, class: Option, text: Cow<'a, str>) {
+        let (needs_escape, text) = if text == "\n"
             && let Some(backline) = self.handle_backline()
         {
+            let mut text = text.into_owned();
             text.push_str(&backline);
-            false
+            (false, Cow::Owned(text))
         } else {
-            true
+            (true, text)
         };
 
         self.push_element_without_backline_check(class, text, needs_escape);
@@ -438,14 +440,14 @@ impl TokenHandler<'_, '_, F> {
             Element {
                 class: None,
                 content: vec![Content {
-                    text: format!(
+                    text: Cow::Owned(format!(
                         "",
                         self.line,
-                    ),
+                    )),
                     class: None,
                     needs_escape: false,
                 }],
@@ -456,7 +458,7 @@ impl TokenHandler<'_, '_, F> {
     fn add_expanded_code(&mut self, expanded_code: &ExpandedCode) {
         self.element_stack.push_element(Element::new(
             None,
-            format!("{}", expanded_code.code),
+            Cow::Owned(format!("{}", expanded_code.code)),
             false,
         ));
         self.element_stack.enter_stack(ElementStack::new_with_class(Some(Class::Original)));
@@ -615,7 +617,7 @@ pub(super) fn write_code(
         token_handler.line = line_info.start_line - 1;
         token_handler.max_lines = line_info.max_lines;
         if let Some(text) = token_handler.handle_backline() {
-            token_handler.push_element_without_backline_check(None, text, false);
+            token_handler.push_element_without_backline_check(None, Cow::Owned(text), false);
         }
     }
 
@@ -635,7 +637,7 @@ pub(super) fn write_code(
     )
     .highlight(&mut |span, highlight| match highlight {
         Highlight::Token { text, class } => {
-            token_handler.push_element(class, text.to_string());
+            token_handler.push_element(class, Cow::Borrowed(text));
 
             if text == "\n" {
                 if current_expansion.is_none() {

From 6ed9a9dd8fa444471f51c5050349712b9065b09b Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Wed, 8 Oct 2025 13:30:19 +0200
Subject: [PATCH 077/170] Flush elements when there are too many

---
 src/librustdoc/html/highlight.rs | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 03cbe379a42a..d7d68bfceb90 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -407,13 +407,26 @@ impl<'a, F: Write> TokenHandler<'a, '_, F> {
         None
     }
 
+    fn maybe_write_content(&mut self) {
+        if self.element_stack.parent.is_none()
+            && self.element_stack.class.is_none()
+            && !self.element_stack.pending_exit
+            // Completely random number.
+            && self.element_stack.elements.len() > 30
+        {
+            self.element_stack.write_content(self.out, &self.href_context);
+            self.element_stack.elements.clear();
+        }
+    }
+
     fn push_element_without_backline_check(
         &mut self,
         class: Option,
         text: Cow<'a, str>,
         needs_escape: bool,
     ) {
-        self.element_stack.push_element(Element::new(class, text, needs_escape))
+        self.element_stack.push_element(Element::new(class, text, needs_escape));
+        self.maybe_write_content();
     }
 
     fn push_element(&mut self, class: Option, text: Cow<'a, str>) {
@@ -486,6 +499,7 @@ impl<'a, F: Write> TokenHandler<'a, '_, F> {
         for class in old_stack.iter().rev() {
             self.element_stack.enter_stack(ElementStack::new_with_class(Some(*class)));
         }
+        self.maybe_write_content();
     }
 }
 

From 713cd50ea0b09691cf8496e91a24fa83c0cadcce Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Tue, 14 Oct 2025 23:01:27 +0200
Subject: [PATCH 078/170] Switch back to stream highlight processing

---
 src/librustdoc/html/highlight.rs | 543 ++++++++++++++-----------------
 1 file changed, 242 insertions(+), 301 deletions(-)

diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index d7d68bfceb90..ecc67fa065fe 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -136,79 +136,36 @@ fn can_merge(class1: Option, class2: Option, text: &str) -> bool {
 }
 
 #[derive(Debug)]
-struct Content<'a> {
-    text: Cow<'a, str>,
-    /// If `Some` and the `span` is different from the parent, then it might generate a link so we
-    /// need to keep this information.
-    class: Option,
-    needs_escape: bool,
+struct ClassInfo {
+    class: Class,
+    /// Set to true only when an item was written inside this tag.
+    open: bool,
+    /// Set to true when leaving the current item. The closing tag will be
+    /// written if:
+    ///
+    /// 1. `self.open` is true
+    /// 2. Only when the first non-mergeable item is pushed.
+    pending_exit: bool,
+    /// If `true`, it means it's ``, otherwise it's ``.
+    link_closing_tag: bool,
 }
 
-#[derive(Debug)]
-struct Element<'a> {
-    /// If `class` is `None`, then it's just plain text with no HTML tag.
-    class: Option,
-    /// Content for the current element.
-    content: Vec>,
-}
-
-impl<'a> Element<'a> {
-    fn new(class: Option, text: Cow<'a, str>, needs_escape: bool) -> Self {
-        Self { class, content: vec![Content { text, class, needs_escape }] }
+impl ClassInfo {
+    fn new(class: Class, pending_exit: bool) -> Self {
+        Self { class, open: pending_exit, pending_exit, link_closing_tag: false }
     }
 
-    fn can_merge(&self, other: &Self) -> bool {
-        other.content.iter().all(|c| can_merge(self.class, other.class, &c.text))
-    }
-
-    fn write_elem_to(
-        &self,
-        out: &mut W,
-        href_context: &Option>,
-        parent_class: Option,
-    ) {
-        let mut prev = parent_class;
-        let mut closing_tag = None;
-        for part in &self.content {
-            let text = if part.needs_escape {
-                Either::Left(&EscapeBodyText(&part.text))
+    fn close_tag(&self, out: &mut W) {
+        if self.open {
+            if self.link_closing_tag {
+                out.write_str("").unwrap();
             } else {
-                Either::Right(&part.text)
-            };
-            if part.class.is_some() {
-                // We only try to generate links as the `` should have already be generated
-                // by the caller of `write_elem_to`.
-                if let Some(new_closing_tag) = string_without_closing_tag(
-                    out,
-                    text,
-                    part.class,
-                    href_context,
-                    prev != part.class,
-                ) {
-                    if new_closing_tag == "" {
-                        out.write_str(new_closing_tag).unwrap();
-                        closing_tag = None;
-                    } else {
-                        closing_tag = Some(new_closing_tag);
-                    }
-                }
-                prev = part.class;
-            } else {
-                write!(out, "{text}").unwrap();
+                out.write_str("").unwrap();
             }
         }
-        if let Some(closing_tag) = closing_tag {
-            out.write_str(closing_tag).unwrap();
-        }
     }
 }
 
-#[derive(Debug)]
-enum ElementOrStack<'a> {
-    Element(Element<'a>),
-    Stack(ElementStack<'a>),
-}
-
 /// This represents the stack of HTML elements. For example a macro expansion
 /// will contain other elements which might themselves contain other elements
 /// (like macros).
@@ -216,166 +173,164 @@ enum ElementOrStack<'a> {
 /// This allows to easily handle HTML tags instead of having a more complicated
 /// state machine to keep track of which tags are open.
 #[derive(Debug)]
-struct ElementStack<'a> {
-    elements: Vec>,
-    parent: Option>>,
-    class: Option,
-    pending_exit: bool,
+struct ClassStack {
+    open_classes: Vec,
 }
 
-impl<'a> ElementStack<'a> {
+impl ClassStack {
     fn new() -> Self {
-        Self::new_with_class(None)
+        Self { open_classes: Vec::new() }
     }
 
-    fn new_with_class(class: Option) -> Self {
-        Self { elements: Vec::new(), parent: None, class, pending_exit: false }
-    }
-
-    fn push_element(&mut self, mut elem: Element<'a>) {
-        if self.pending_exit
-            && !can_merge(self.class, elem.class, elem.content.first().map_or("", |c| &c.text))
-        {
-            self.exit_current_stack();
-        }
-        if let Some(ElementOrStack::Element(last)) = self.elements.last_mut()
-            && last.can_merge(&elem)
-        {
-            last.content.append(&mut elem.content);
-        } else {
-            self.elements.push(ElementOrStack::Element(elem));
-        }
-    }
-
-    fn empty_stack_and_set_new_heads(&mut self, class: Class, element: Element<'a>) {
-        self.elements.clear();
-        if let Some(parent) = &mut self.parent {
-            parent.empty_stack_and_set_new_heads(class, element);
-        } else {
-            let mut new_stack = ElementStack::new_with_class(Some(class));
-            new_stack.elements.push(ElementOrStack::Element(element));
-            self.parent.replace(Box::new(new_stack));
-        }
-    }
-
-    fn enter_stack(
+    fn enter_elem(
         &mut self,
-        ElementStack { elements, parent, class, pending_exit }: ElementStack<'a>,
+        out: &mut W,
+        href_context: &Option>,
+        new_class: Class,
+        pending_exit: bool,
     ) {
-        if self.pending_exit {
-            if can_merge(self.class, class, "") {
-                self.pending_exit = false;
-                self.elements.extend(elements);
-                // Compatible stacks, nothing to be done here!
+        if let Some(current_class) = self.open_classes.last_mut() {
+            if can_merge(Some(current_class.class), Some(new_class), "") {
+                current_class.pending_exit = false;
                 return;
+            } else if current_class.pending_exit {
+                current_class.close_tag(out);
+                self.open_classes.pop();
             }
-            self.exit_current_stack();
         }
-        assert!(parent.is_none(), "`enter_stack` used with a non empty parent");
-        let parent_elements = std::mem::take(&mut self.elements);
-        let parent_parent = std::mem::take(&mut self.parent);
-        self.parent = Some(Box::new(ElementStack {
-            elements: parent_elements,
-            parent: parent_parent,
-            class: self.class,
-            pending_exit: self.pending_exit,
-        }));
-        self.class = class;
-        self.elements = elements;
-        self.pending_exit = pending_exit;
+        let mut class_info = ClassInfo::new(new_class, pending_exit);
+        if pending_exit {
+            class_info.open = true;
+        } else if matches!(new_class, Class::Decoration(_) | Class::Original) {
+            // We open it right away to ensure it always come at the expected location.
+            // FIXME: Should we instead add a new boolean field to `ClassInfo` to force a non-open
+            // tags to be added if another one comes before it's open?
+            write!(out, "", new_class.as_html()).unwrap();
+            class_info.open = true;
+        } else if new_class.get_span().is_some()
+            && let Some(closing_tag) =
+                string_without_closing_tag(out, "", Some(class_info.class), href_context, false)
+            && !closing_tag.is_empty()
+        {
+            class_info.open = true;
+            class_info.link_closing_tag = closing_tag == "";
+        }
+
+        self.open_classes.push(class_info);
     }
 
     /// This sets the `pending_exit` field to `true`. Meaning that if we try to push another stack
     /// which is not compatible with this one, it will exit the current one before adding the new
     /// one.
     fn exit_elem(&mut self) {
-        self.pending_exit = true;
-    }
-
-    /// Unlike `exit_elem`, this method directly exits the current stack. It is called when the
-    /// current stack is not compatible with a new one pushed or if an expansion was ended.
-    fn exit_current_stack(&mut self) {
-        let Some(element) = std::mem::take(&mut self.parent) else {
-            panic!("exiting an element where there is no parent");
-        };
-        let ElementStack { elements, parent, class, pending_exit } = Box::into_inner(element);
-
-        let old_elements = std::mem::take(&mut self.elements);
-        self.elements = elements;
-        self.elements.push(ElementOrStack::Stack(ElementStack {
-            elements: old_elements,
-            class: self.class,
-            parent: None,
-            pending_exit: false,
-        }));
-        self.parent = parent;
-        self.class = class;
-        self.pending_exit = pending_exit;
-    }
-
-    fn write_content(&self, out: &mut W, href_context: &Option>) {
-        let mut elem = self;
-
-        // We get the top most item.
-        while let Some(parent) = &elem.parent {
-            elem = parent;
+        let current_class =
+            self.open_classes.last_mut().expect("`exit_elem` called on empty class stack");
+        if !current_class.pending_exit {
+            current_class.pending_exit = true;
+            return;
         }
-        // Now we can output the whole content.
-        elem.write_to(out, href_context, None);
+        // If the current class was already closed, it means we are actually closing its parent.
+        self.open_classes.pop();
+        let current_class =
+            self.open_classes.last_mut().expect("`exit_elem` called on empty class stack parent");
+        current_class.pending_exit = true;
     }
 
-    fn write_to(
-        &self,
+    fn last_class(&self) -> Option {
+        self.open_classes.last().map(|c| c.class)
+    }
+
+    fn last_class_is_open(&self) -> bool {
+        if let Some(last) = self.open_classes.last() {
+            last.open
+        } else {
+            // If there is no class, then it's already open.
+            true
+        }
+    }
+
+    fn close_last_if_needed(&mut self, out: &mut W) {
+        if let Some(last) = self.open_classes.last()
+            && last.pending_exit
+            && last.open
+        {
+            last.close_tag(out);
+            self.open_classes.pop();
+        }
+    }
+
+    fn push(
+        &mut self,
         out: &mut W,
         href_context: &Option>,
-        parent_class: Option,
+        class: Option,
+        text: Cow<'_, str>,
+        needs_escape: bool,
     ) {
-        // If it only contains stack, it means it has no content of its own so no need to generate
-        // a tag.
-        let closing_tag = if let Some(Class::Expansion) = self.class {
-            out.write_str("").unwrap();
-            ""
-        } else if let Some(class) = self.class
-            // `PreludeTy` can never include more than an ident so it should not generate
-            // a wrapping `span`.
-            && !matches!(class, Class::PreludeTy(_))
-        {
-            // Macro is the only `ElementStack` that can generate a link to definition to its
-            // whole content, so to prevent having ``,
-            // we generate the `` directly here.
-            //
-            // For other elements, the links will be generated in `write_elem_to`.
-            let href_context = if matches!(class, Class::Macro(_)) { href_context } else { &None };
-            string_without_closing_tag(
-                out,
-                "",
-                Some(class),
-                href_context,
-                self.class != parent_class,
-            )
-            .expect(
-                "internal error: enter_span was called with Some(class) but did not \
-                    return a closing HTML tag",
-            )
-        } else {
-            ""
-        };
-
-        for child_elem in self.elements.iter() {
-            let child_elem = match child_elem {
-                ElementOrStack::Element(elem) => elem,
-                ElementOrStack::Stack(stack) => {
-                    stack.write_to(out, href_context, parent_class);
-                    continue;
-                }
-            };
-            if child_elem.content.is_empty() {
-                continue;
-            }
-            child_elem.write_elem_to(out, href_context, parent_class);
+        // If the new token cannot be merged with the currently open `Class`, we close the `Class`
+        // if possible.
+        if !can_merge(self.last_class(), class, &text) {
+            self.close_last_if_needed(out)
         }
 
-        out.write_str(closing_tag).unwrap();
+        let current_class = self.last_class();
+
+        // If we have a `Class` that hasn't been "open" yet (ie, we received only an `EnterSpan`
+        // event), we need to open the `Class` before going any further so the new token will be
+        // written inside it.
+        if class.is_none() && !self.last_class_is_open() {
+            if let Some(current_class_info) = self.open_classes.last_mut() {
+                let class_s = current_class_info.class.as_html();
+                if !class_s.is_empty() {
+                    write!(out, "").unwrap();
+                }
+                current_class_info.open = true;
+            }
+        }
+
+        let current_class_is_open = self.open_classes.last().is_some_and(|c| c.open);
+        let can_merge = can_merge(class, current_class, &text);
+        let should_open_tag = !current_class_is_open || !can_merge;
+
+        let text =
+            if needs_escape { Either::Left(&EscapeBodyText(&text)) } else { Either::Right(text) };
+
+        let closing_tag =
+            string_without_closing_tag(out, &text, class, href_context, should_open_tag);
+        if class.is_some() && should_open_tag && closing_tag.is_none() {
+            panic!(
+                "called `string_without_closing_tag` with a class but no closing tag was returned"
+            );
+        } else if let Some(closing_tag) = closing_tag
+            && !closing_tag.is_empty()
+        {
+            // If this is a link, we need to close it right away and not open a new `Class`,
+            // otherwise extra content would go into the `` HTML tag.
+            if closing_tag == "" {
+                out.write_str(closing_tag).unwrap();
+            // If the current `Class` is not compatible with this one, we create a new `Class`.
+            } else if let Some(class) = class
+                && !can_merge
+            {
+                self.enter_elem(out, href_context, class, true);
+            // Otherwise, we consider the actual `Class` to have been open.
+            } else if let Some(current_class_info) = self.open_classes.last_mut() {
+                current_class_info.open = true;
+            }
+        }
+    }
+
+    fn empty_stack(&mut self, out: &mut W) -> Vec {
+        let mut classes = Vec::with_capacity(self.open_classes.len());
+
+        // We close all open tags and only keep the ones that were not already waiting to be closed.
+        while let Some(class_info) = self.open_classes.pop() {
+            class_info.close_tag(out);
+            if !class_info.pending_exit {
+                classes.push(class_info.class);
+            }
+        }
+        classes
     }
 }
 
@@ -383,7 +338,7 @@ impl<'a> ElementStack<'a> {
 /// the various functions (which became its methods).
 struct TokenHandler<'a, 'tcx, F: Write> {
     out: &'a mut F,
-    element_stack: ElementStack<'a>,
+    class_stack: ClassStack,
     /// We need to keep the `Class` for each element because it could contain a `Span` which is
     /// used to generate links.
     href_context: Option>,
@@ -394,7 +349,7 @@ struct TokenHandler<'a, 'tcx, F: Write> {
 
 impl std::fmt::Debug for TokenHandler<'_, '_, F> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("TokenHandler").field("element_stack", &self.element_stack).finish()
+        f.debug_struct("TokenHandler").field("class_stack", &self.class_stack).finish()
     }
 }
 
@@ -407,111 +362,77 @@ impl<'a, F: Write> TokenHandler<'a, '_, F> {
         None
     }
 
-    fn maybe_write_content(&mut self) {
-        if self.element_stack.parent.is_none()
-            && self.element_stack.class.is_none()
-            && !self.element_stack.pending_exit
-            // Completely random number.
-            && self.element_stack.elements.len() > 30
-        {
-            self.element_stack.write_content(self.out, &self.href_context);
-            self.element_stack.elements.clear();
-        }
-    }
-
-    fn push_element_without_backline_check(
+    fn push_token_without_backline_check(
         &mut self,
         class: Option,
         text: Cow<'a, str>,
         needs_escape: bool,
     ) {
-        self.element_stack.push_element(Element::new(class, text, needs_escape));
-        self.maybe_write_content();
+        self.class_stack.push(self.out, &self.href_context, class, text, needs_escape);
     }
 
-    fn push_element(&mut self, class: Option, text: Cow<'a, str>) {
-        let (needs_escape, text) = if text == "\n"
+    fn push_token(&mut self, class: Option, text: Cow<'a, str>) {
+        if text == "\n"
             && let Some(backline) = self.handle_backline()
         {
-            let mut text = text.into_owned();
-            text.push_str(&backline);
-            (false, Cow::Owned(text))
+            self.out.write_str(&text).unwrap();
+            self.out.write_str(&backline).unwrap();
         } else {
-            (true, text)
-        };
-
-        self.push_element_without_backline_check(class, text, needs_escape);
+            self.push_token_without_backline_check(class, text, true);
+        }
     }
 
     fn start_expansion(&mut self) {
-        // We display everything.
-        self.element_stack.write_content(self.out, &self.href_context);
+        // We close all open tags.
+        let classes = self.class_stack.empty_stack(self.out);
 
-        // We remove everything and recreate the stack with the expansion at its head.
-        self.element_stack.empty_stack_and_set_new_heads(
-            Class::Expansion,
-            Element {
-                class: None,
-                content: vec![Content {
-                    text: Cow::Owned(format!(
-                        "",
-                        self.line,
-                    )),
-                    class: None,
-                    needs_escape: false,
-                }],
-            },
+                self.line,
+            )),
+            false,
         );
+
+        // We re-open all tags.
+        for class in classes.into_iter().rev() {
+            self.class_stack.enter_elem(self.out, &self.href_context, class, false);
+        }
     }
 
     fn add_expanded_code(&mut self, expanded_code: &ExpandedCode) {
-        self.element_stack.push_element(Element::new(
+        self.push_token_without_backline_check(
             None,
             Cow::Owned(format!("{}", expanded_code.code)),
             false,
-        ));
-        self.element_stack.enter_stack(ElementStack::new_with_class(Some(Class::Original)));
+        );
+        self.class_stack.enter_elem(self.out, &self.href_context, Class::Original, false);
     }
 
     fn close_expansion(&mut self) {
-        let mut old_stack = Vec::new();
+        // We close all open tags.
+        let classes = self.class_stack.empty_stack(self.out);
 
-        // We inline everything into the top-most element.
-        while self.element_stack.parent.is_some() {
-            self.element_stack.exit_current_stack();
-            if let Some(ElementOrStack::Stack(stack)) = self.element_stack.elements.last()
-                && let Some(class) = stack.class
-                && class != Class::Original
-            {
-                old_stack.push(class);
+        // We re-open all tags without expansion-related ones.
+        for class in classes.into_iter().rev() {
+            if !matches!(class, Class::Expansion | Class::Original) {
+                self.class_stack.enter_elem(self.out, &self.href_context, class, false);
             }
         }
-        // We display everything.
-        self.element_stack.write_content(self.out, &self.href_context);
-
-        // We recreate the tree but without the expansion node.
-        self.element_stack.elements.clear();
-        self.element_stack.class = None;
-        for class in old_stack.iter().rev() {
-            self.element_stack.enter_stack(ElementStack::new_with_class(Some(*class)));
-        }
-        self.maybe_write_content();
     }
 }
 
 impl Drop for TokenHandler<'_, '_, F> {
     /// When leaving, we need to flush all pending data to not have missing content.
     fn drop(&mut self) {
-        // We need to clean the hierarchy before displaying it, otherwise the parents won't see
-        // the last child.
-        while self.element_stack.parent.is_some() {
-            self.element_stack.exit_current_stack();
-        }
-        self.element_stack.write_content(self.out, &self.href_context);
+        self.class_stack.empty_stack(self.out);
     }
 }
 
@@ -554,7 +475,7 @@ fn end_expansion<'a, W: Write>(
     expanded_codes: &'a [ExpandedCode],
     span: Span,
 ) -> Option<&'a ExpandedCode> {
-    token_handler.element_stack.exit_current_stack();
+    token_handler.class_stack.exit_elem();
     let expansion = get_next_expansion(expanded_codes, token_handler.line, span);
     if expansion.is_none() {
         token_handler.close_expansion();
@@ -624,14 +545,14 @@ pub(super) fn write_code(
         },
         line: 0,
         max_lines: u32::MAX,
-        element_stack: ElementStack::new(),
+        class_stack: ClassStack::new(),
     };
 
     if let Some(line_info) = line_info {
         token_handler.line = line_info.start_line - 1;
         token_handler.max_lines = line_info.max_lines;
         if let Some(text) = token_handler.handle_backline() {
-            token_handler.push_element_without_backline_check(None, Cow::Owned(text), false);
+            token_handler.push_token_without_backline_check(None, Cow::Owned(text), false);
         }
     }
 
@@ -651,7 +572,7 @@ pub(super) fn write_code(
     )
     .highlight(&mut |span, highlight| match highlight {
         Highlight::Token { text, class } => {
-            token_handler.push_element(class, Cow::Borrowed(text));
+            token_handler.push_token(class, Cow::Borrowed(text));
 
             if text == "\n" {
                 if current_expansion.is_none() {
@@ -679,9 +600,16 @@ pub(super) fn write_code(
             }
         }
         Highlight::EnterSpan { class } => {
-            token_handler.element_stack.enter_stack(ElementStack::new_with_class(Some(class)))
+            token_handler.class_stack.enter_elem(
+                token_handler.out,
+                &token_handler.href_context,
+                class,
+                false,
+            );
+        }
+        Highlight::ExitSpan => {
+            token_handler.class_stack.exit_elem();
         }
-        Highlight::ExitSpan => token_handler.element_stack.exit_elem(),
     });
 }
 
@@ -872,12 +800,15 @@ impl<'a> PeekIter<'a> {
             None
         }
     }
+
+    fn stop_peeking(&mut self) {
+        self.peek_pos = 0;
+    }
 }
 
 impl<'a> Iterator for PeekIter<'a> {
     type Item = (TokenKind, &'a str);
     fn next(&mut self) -> Option {
-        self.peek_pos = 0;
         if let Some(first) = self.stored.pop_front() { Some(first) } else { self.iter.next() }
     }
 }
@@ -1257,31 +1188,35 @@ impl<'src> Classifier<'src> {
                 LiteralKind::Float { .. } | LiteralKind::Int { .. } => Class::Number,
             },
             TokenKind::GuardedStrPrefix => return no_highlight(sink),
-            TokenKind::Ident | TokenKind::RawIdent if lookahead == Some(TokenKind::Bang) => {
+            TokenKind::Ident | TokenKind::RawIdent
+                if self.peek_non_whitespace() == Some(TokenKind::Bang) =>
+            {
                 self.in_macro = true;
                 let span = new_span(before, text, file_span);
                 sink(DUMMY_SP, Highlight::EnterSpan { class: Class::Macro(span) });
                 sink(span, Highlight::Token { text, class: None });
                 return;
             }
-            TokenKind::Ident => match get_real_ident_class(text, false) {
-                None => match text {
-                    "Option" | "Result" => Class::PreludeTy(new_span(before, text, file_span)),
-                    "Some" | "None" | "Ok" | "Err" => {
-                        Class::PreludeVal(new_span(before, text, file_span))
-                    }
-                    // "union" is a weak keyword and is only considered as a keyword when declaring
-                    // a union type.
-                    "union" if self.check_if_is_union_keyword() => Class::KeyWord,
-                    _ if self.in_macro_nonterminal => {
-                        self.in_macro_nonterminal = false;
-                        Class::MacroNonTerminal
-                    }
-                    "self" | "Self" => Class::Self_(new_span(before, text, file_span)),
-                    _ => Class::Ident(new_span(before, text, file_span)),
-                },
-                Some(c) => c,
-            },
+            TokenKind::Ident => {
+                match get_real_ident_class(text, false) {
+                    None => match text {
+                        "Option" | "Result" => Class::PreludeTy(new_span(before, text, file_span)),
+                        "Some" | "None" | "Ok" | "Err" => {
+                            Class::PreludeVal(new_span(before, text, file_span))
+                        }
+                        // "union" is a weak keyword and is only considered as a keyword when declaring
+                        // a union type.
+                        "union" if self.check_if_is_union_keyword() => Class::KeyWord,
+                        _ if self.in_macro_nonterminal => {
+                            self.in_macro_nonterminal = false;
+                            Class::MacroNonTerminal
+                        }
+                        "self" | "Self" => Class::Self_(new_span(before, text, file_span)),
+                        _ => Class::Ident(new_span(before, text, file_span)),
+                    },
+                    Some(c) => c,
+                }
+            }
             TokenKind::RawIdent | TokenKind::UnknownPrefix | TokenKind::InvalidIdent => {
                 Class::Ident(new_span(before, text, file_span))
             }
@@ -1306,14 +1241,20 @@ impl<'src> Classifier<'src> {
         self.tokens.peek().map(|(token_kind, _text)| *token_kind)
     }
 
-    fn check_if_is_union_keyword(&mut self) -> bool {
-        while let Some(kind) = self.tokens.peek_next().map(|(token_kind, _text)| token_kind) {
-            if *kind == TokenKind::Whitespace {
-                continue;
+    fn peek_non_whitespace(&mut self) -> Option {
+        while let Some((token_kind, _)) = self.tokens.peek_next() {
+            if *token_kind != TokenKind::Whitespace {
+                let token_kind = *token_kind;
+                self.tokens.stop_peeking();
+                return Some(token_kind);
             }
-            return *kind == TokenKind::Ident;
         }
-        false
+        self.tokens.stop_peeking();
+        None
+    }
+
+    fn check_if_is_union_keyword(&mut self) -> bool {
+        self.peek_non_whitespace().is_some_and(|kind| kind == TokenKind::Ident)
     }
 }
 

From 03b8682865d8db2180096b7ca8d917cbb4c6bcbf Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Fri, 24 Oct 2025 01:06:27 +0800
Subject: [PATCH 079/170] Fix some typos

---
 .../crates/ide-completion/src/context/analysis.rs             | 4 ++--
 1 file changed, 2 insertions(+), 2 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 c01b544ff6ef..d39bff1577f3 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
@@ -891,8 +891,8 @@ fn classify_name_ref<'db>(
         return Some(make_res(kind));
     }
 
-    let field_expr_handle = |recviver, node| {
-        let receiver = find_opt_node_in_file(original_file, recviver);
+    let field_expr_handle = |receiver, node| {
+        let receiver = find_opt_node_in_file(original_file, receiver);
         let receiver_is_ambiguous_float_literal = match &receiver {
             Some(ast::Expr::Literal(l)) => matches! {
                 l.kind(),

From c1ecea6d7ee9ea3e00f88e09b61a26da1eb7a52a Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Thu, 23 Oct 2025 20:48:51 +0300
Subject: [PATCH 080/170] Implement `Interner::impl_specializes()`

Using specialization logic ported from rustc.
---
 .../rust-analyzer/crates/hir-ty/src/lib.rs    |   1 +
 .../crates/hir-ty/src/mir/eval/tests.rs       |   7 +-
 .../crates/hir-ty/src/next_solver/def_id.rs   |   2 +-
 .../crates/hir-ty/src/next_solver/interner.rs |  10 +-
 .../crates/hir-ty/src/specialization.rs       | 150 ++++++++++++++++++
 .../crates/intern/src/symbol/symbols.rs       |   2 +
 6 files changed, 163 insertions(+), 9 deletions(-)
 create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs

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 536c81ab03b2..96dd48b53aa8 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -27,6 +27,7 @@ mod infer;
 mod inhabitedness;
 mod lower;
 pub mod next_solver;
+mod specialization;
 mod target_feature;
 mod utils;
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
index 4eb4aa91598e..f242115afeff 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
@@ -636,16 +636,13 @@ fn main() {
     );
 }
 
-#[ignore = "
-FIXME(next-solver):
-This does not work currently because I replaced homemade selection with selection by the trait solver;
-This will work once we implement `Interner::impl_specializes()` properly.
-"]
 #[test]
 fn specialization_array_clone() {
     check_pass(
         r#"
 //- minicore: copy, derive, slice, index, coerce_unsized, panic
+#![feature(min_specialization)]
+
 impl Clone for [T; N] {
     #[inline]
     fn clone(&self) -> Self {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs
index 928e1321e738..0ff0b086a087 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs
@@ -211,7 +211,7 @@ macro_rules! declare_id_wrapper {
 
         impl std::fmt::Debug for $name {
             fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-                std::fmt::Debug::fmt(&self.0, f)
+                std::fmt::Debug::fmt(&SolverDefId::from(self.0), f)
             }
         }
 
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 42f1d926d7db..43b47398f96d 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
@@ -1922,10 +1922,14 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> {
 
     fn impl_specializes(
         self,
-        _specializing_impl_def_id: Self::ImplId,
-        _parent_impl_def_id: Self::ImplId,
+        specializing_impl_def_id: Self::ImplId,
+        parent_impl_def_id: Self::ImplId,
     ) -> bool {
-        false
+        crate::specialization::specializes(
+            self.db,
+            specializing_impl_def_id.0,
+            parent_impl_def_id.0,
+        )
     }
 
     fn next_trait_solver_globally(self) -> bool {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs
new file mode 100644
index 000000000000..611947b96b71
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs
@@ -0,0 +1,150 @@
+//! Impl specialization related things
+
+use hir_def::{ImplId, nameres::crate_def_map};
+use intern::sym;
+use tracing::debug;
+
+use crate::{
+    db::HirDatabase,
+    next_solver::{
+        DbInterner, TypingMode,
+        infer::{
+            DbInternerInferExt,
+            traits::{Obligation, ObligationCause},
+        },
+        obligation_ctxt::ObligationCtxt,
+    },
+};
+
+// rustc does not have a cycle handling for the `specializes` query, meaning a cycle is a bug,
+// and indeed I was unable to cause cycles even with erroneous code. However, in r-a we can
+// create a cycle if there is an error in the impl's where clauses. I believe well formed code
+// cannot create a cycle, but a cycle handler is required nevertheless.
+fn specializes_cycle(
+    _db: &dyn HirDatabase,
+    _specializing_impl_def_id: ImplId,
+    _parent_impl_def_id: ImplId,
+) -> bool {
+    false
+}
+
+/// Is `specializing_impl_def_id` a specialization of `parent_impl_def_id`?
+///
+/// For every type that could apply to `specializing_impl_def_id`, we prove that
+/// the `parent_impl_def_id` also applies (i.e. it has a valid impl header and
+/// its where-clauses hold).
+///
+/// For the purposes of const traits, we also check that the specializing
+/// impl is not more restrictive than the parent impl. That is, if the
+/// `parent_impl_def_id` is a const impl (conditionally based off of some `[const]`
+/// bounds), then `specializing_impl_def_id` must also be const for the same
+/// set of types.
+#[salsa::tracked(cycle_result = specializes_cycle)]
+pub(crate) fn specializes(
+    db: &dyn HirDatabase,
+    specializing_impl_def_id: ImplId,
+    parent_impl_def_id: ImplId,
+) -> bool {
+    let module = specializing_impl_def_id.loc(db).container;
+
+    // We check that the specializing impl comes from a crate that has specialization enabled.
+    //
+    // We don't really care if the specialized impl (the parent) is in a crate that has
+    // specialization enabled, since it's not being specialized.
+    //
+    // rustc also checks whether the specializing impls comes from a macro marked
+    // `#[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());
+    if !def_map.is_unstable_feature_enabled(&sym::specialization)
+        && !def_map.is_unstable_feature_enabled(&sym::min_specialization)
+    {
+        return false;
+    }
+
+    let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block());
+
+    let specializing_impl_signature = db.impl_signature(specializing_impl_def_id);
+    let parent_impl_signature = db.impl_signature(parent_impl_def_id);
+
+    // We determine whether there's a subset relationship by:
+    //
+    // - replacing bound vars with placeholders in impl1,
+    // - assuming the where clauses for impl1,
+    // - instantiating impl2 with fresh inference variables,
+    // - unifying,
+    // - attempting to prove the where clauses for impl2
+    //
+    // The last three steps are encapsulated in `fulfill_implication`.
+    //
+    // See RFC 1210 for more details and justification.
+
+    // Currently we do not allow e.g., a negative impl to specialize a positive one
+    if specializing_impl_signature.is_negative() != parent_impl_signature.is_negative() {
+        return false;
+    }
+
+    // create a parameter environment corresponding to an identity instantiation of the specializing impl,
+    // i.e. the most generic instantiation of the specializing impl.
+    let param_env = db.trait_environment(specializing_impl_def_id.into()).env;
+
+    // Create an infcx, taking the predicates of the specializing impl as assumptions:
+    let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
+
+    let specializing_impl_trait_ref =
+        db.impl_trait(specializing_impl_def_id).unwrap().instantiate_identity();
+    let cause = &ObligationCause::dummy();
+    debug!(
+        "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)",
+        param_env, specializing_impl_trait_ref, parent_impl_def_id
+    );
+
+    // Attempt to prove that the parent impl applies, given all of the above.
+
+    let mut ocx = ObligationCtxt::new(&infcx);
+
+    let parent_args = infcx.fresh_args_for_item(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")
+        .instantiate(interner, parent_args);
+
+    // do the impls unify? If not, no specialization.
+    let Ok(()) = ocx.eq(cause, param_env, specializing_impl_trait_ref, parent_impl_trait_ref)
+    else {
+        return false;
+    };
+
+    // Now check that the source trait ref satisfies all the where clauses of the target impl.
+    // This is not just for correctness; we also need this to constrain any params that may
+    // only be referenced via projection predicates.
+    if let Some(predicates) =
+        db.generic_predicates(parent_impl_def_id.into()).instantiate(interner, parent_args)
+    {
+        ocx.register_obligations(
+            predicates
+                .map(|predicate| Obligation::new(interner, cause.clone(), param_env, predicate)),
+        );
+    }
+
+    let errors = ocx.evaluate_obligations_error_on_ambiguity();
+    if !errors.is_empty() {
+        // no dice!
+        debug!(
+            "fulfill_implication: for impls on {:?} and {:?}, \
+                 could not fulfill: {:?} given {:?}",
+            specializing_impl_trait_ref, parent_impl_trait_ref, errors, param_env
+        );
+        return false;
+    }
+
+    // FIXME: Check impl constness (when we implement const impls).
+
+    debug!(
+        "fulfill_implication: an impl for {:?} specializes {:?}",
+        specializing_impl_trait_ref, parent_impl_trait_ref
+    );
+
+    true
+}
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 920bdd9568fc..756377fe56f7 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -517,4 +517,6 @@ define_symbols! {
     precision,
     width,
     never_type_fallback,
+    specialization,
+    min_specialization,
 }

From 5976015e7299aa2fec889e24a0c6b9d52d8a8b0b Mon Sep 17 00:00:00 2001
From: Jynn Nelson 
Date: Thu, 23 Oct 2025 13:57:21 -0400
Subject: [PATCH 081/170] compiletest: show output in debug logging

I had a test I was confused by; the root issue is that `error-pattern`
runs before normalization, even though `//~ ERROR` runs after
normalization. This logging caught the issue immediately.
---
 src/tools/compiletest/src/runtest/ui.rs | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs
index d683a325c866..c0adb6302e9d 100644
--- a/src/tools/compiletest/src/runtest/ui.rs
+++ b/src/tools/compiletest/src/runtest/ui.rs
@@ -207,8 +207,12 @@ impl TestCx<'_> {
 
         debug!(
             "run_ui_test: explicit={:?} config.compare_mode={:?} \
-               proc_res.status={:?} props.error_patterns={:?}",
-            explicit, self.config.compare_mode, proc_res.status, self.props.error_patterns
+               proc_res.status={:?} props.error_patterns={:?} output_to_check={:?}",
+            explicit,
+            self.config.compare_mode,
+            proc_res.status,
+            self.props.error_patterns,
+            output_to_check,
         );
 
         // Compiler diagnostics (expected errors) are always tied to the compile-time ProcRes.

From ab1dcee5f2749a0d32d1ebaab2023aad5f61d1c4 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Thu, 23 Oct 2025 18:09:10 +0200
Subject: [PATCH 082/170] Merge `ClassInfo::open` and
 `ClassInfo::link_closing_tag` fields into `closing_tags` Improve
 documentation Improve code

---
 src/librustdoc/html/highlight.rs | 98 ++++++++++++++++----------------
 1 file changed, 49 insertions(+), 49 deletions(-)

diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index ecc67fa065fe..c37736f137df 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -138,32 +138,30 @@ fn can_merge(class1: Option, class2: Option, text: &str) -> bool {
 #[derive(Debug)]
 struct ClassInfo {
     class: Class,
-    /// Set to true only when an item was written inside this tag.
-    open: bool,
-    /// Set to true when leaving the current item. The closing tag will be
-    /// written if:
+    /// If `Some`, then it means the tag was opened and needs to be closed.
+    closing_tag: Option<&'static str>,
+    /// Set to `true` by `exit_elem` to signal that all the elements of this class have been pushed.
     ///
-    /// 1. `self.open` is true
-    /// 2. Only when the first non-mergeable item is pushed.
+    /// The class will be closed and removed from the stack when the next non-mergeable item is
+    /// pushed. When it is removed, the closing tag will be written if (and only if)
+    /// `self.closing_tag` is `Some`.
     pending_exit: bool,
-    /// If `true`, it means it's ``, otherwise it's ``.
-    link_closing_tag: bool,
 }
 
 impl ClassInfo {
-    fn new(class: Class, pending_exit: bool) -> Self {
-        Self { class, open: pending_exit, pending_exit, link_closing_tag: false }
+    fn new(class: Class, closing_tag: Option<&'static str>) -> Self {
+        Self { class, closing_tag, pending_exit: closing_tag.is_some() }
     }
 
     fn close_tag(&self, out: &mut W) {
-        if self.open {
-            if self.link_closing_tag {
-                out.write_str("").unwrap();
-            } else {
-                out.write_str("").unwrap();
-            }
+        if let Some(closing_tag) = self.closing_tag {
+            out.write_str(closing_tag).unwrap();
         }
     }
+
+    fn is_open(&self) -> bool {
+        self.closing_tag.is_some()
+    }
 }
 
 /// This represents the stack of HTML elements. For example a macro expansion
@@ -187,7 +185,7 @@ impl ClassStack {
         out: &mut W,
         href_context: &Option>,
         new_class: Class,
-        pending_exit: bool,
+        closing_tag: Option<&'static str>,
     ) {
         if let Some(current_class) = self.open_classes.last_mut() {
             if can_merge(Some(current_class.class), Some(new_class), "") {
@@ -198,22 +196,22 @@ impl ClassStack {
                 self.open_classes.pop();
             }
         }
-        let mut class_info = ClassInfo::new(new_class, pending_exit);
-        if pending_exit {
-            class_info.open = true;
-        } else if matches!(new_class, Class::Decoration(_) | Class::Original) {
-            // We open it right away to ensure it always come at the expected location.
-            // FIXME: Should we instead add a new boolean field to `ClassInfo` to force a non-open
-            // tags to be added if another one comes before it's open?
-            write!(out, "", new_class.as_html()).unwrap();
-            class_info.open = true;
-        } else if new_class.get_span().is_some()
-            && let Some(closing_tag) =
-                string_without_closing_tag(out, "", Some(class_info.class), href_context, false)
-            && !closing_tag.is_empty()
-        {
-            class_info.open = true;
-            class_info.link_closing_tag = closing_tag == "";
+        let mut class_info = ClassInfo::new(new_class, closing_tag);
+        if closing_tag.is_none() {
+            if matches!(new_class, Class::Decoration(_) | Class::Original) {
+                // Even if a whitespace characters follows, we need to open the class right away
+                // as these characters are part of the element.
+                // FIXME: Should we instead add a new boolean field to `ClassInfo` to force a
+                // non-open tag to be added if another one comes before it's open?
+                write!(out, "", new_class.as_html()).unwrap();
+                class_info.closing_tag = Some("");
+            } else if new_class.get_span().is_some()
+                && let Some(closing_tag) =
+                    string_without_closing_tag(out, "", Some(class_info.class), href_context, false)
+                && !closing_tag.is_empty()
+            {
+                class_info.closing_tag = Some(closing_tag);
+            }
         }
 
         self.open_classes.push(class_info);
@@ -242,7 +240,7 @@ impl ClassStack {
 
     fn last_class_is_open(&self) -> bool {
         if let Some(last) = self.open_classes.last() {
-            last.open
+            last.is_open()
         } else {
             // If there is no class, then it's already open.
             true
@@ -250,12 +248,9 @@ impl ClassStack {
     }
 
     fn close_last_if_needed(&mut self, out: &mut W) {
-        if let Some(last) = self.open_classes.last()
-            && last.pending_exit
-            && last.open
+        if let Some(last) = self.open_classes.pop_if(|class| class.pending_exit && class.is_open())
         {
             last.close_tag(out);
-            self.open_classes.pop();
         }
     }
 
@@ -284,11 +279,11 @@ impl ClassStack {
                 if !class_s.is_empty() {
                     write!(out, "").unwrap();
                 }
-                current_class_info.open = true;
+                current_class_info.closing_tag = Some("");
             }
         }
 
-        let current_class_is_open = self.open_classes.last().is_some_and(|c| c.open);
+        let current_class_is_open = self.open_classes.last().is_some_and(|c| c.is_open());
         let can_merge = can_merge(class, current_class, &text);
         let should_open_tag = !current_class_is_open || !can_merge;
 
@@ -312,14 +307,20 @@ impl ClassStack {
             } else if let Some(class) = class
                 && !can_merge
             {
-                self.enter_elem(out, href_context, class, true);
+                self.enter_elem(out, href_context, class, Some(""));
             // Otherwise, we consider the actual `Class` to have been open.
             } else if let Some(current_class_info) = self.open_classes.last_mut() {
-                current_class_info.open = true;
+                current_class_info.closing_tag = Some("");
             }
         }
     }
 
+    /// This method closes all open tags and returns the list of `Class` which were not already
+    /// closed (ie `pending_exit` set to `true`).
+    ///
+    /// It is used when starting a macro expansion: we need to close all HTML tags and then to
+    /// reopen them inside the newly created expansion HTML tag. Same goes when we close the
+    /// expansion.
     fn empty_stack(&mut self, out: &mut W) -> Vec {
         let mut classes = Vec::with_capacity(self.open_classes.len());
 
@@ -387,7 +388,7 @@ impl<'a, F: Write> TokenHandler<'a, '_, F> {
         let classes = self.class_stack.empty_stack(self.out);
 
         // We start the expansion tag.
-        self.class_stack.enter_elem(self.out, &self.href_context, Class::Expansion, false);
+        self.class_stack.enter_elem(self.out, &self.href_context, Class::Expansion, None);
         self.push_token_without_backline_check(
             Some(Class::Expansion),
             Cow::Owned(format!(
@@ -401,9 +402,9 @@ impl<'a, F: Write> TokenHandler<'a, '_, F> {
             false,
         );
 
-        // We re-open all tags.
+        // We re-open all tags that didn't have `pending_exit` set to `true`.
         for class in classes.into_iter().rev() {
-            self.class_stack.enter_elem(self.out, &self.href_context, class, false);
+            self.class_stack.enter_elem(self.out, &self.href_context, class, None);
         }
     }
 
@@ -413,7 +414,7 @@ impl<'a, F: Write> TokenHandler<'a, '_, F> {
             Cow::Owned(format!("{}", expanded_code.code)),
             false,
         );
-        self.class_stack.enter_elem(self.out, &self.href_context, Class::Original, false);
+        self.class_stack.enter_elem(self.out, &self.href_context, Class::Original, None);
     }
 
     fn close_expansion(&mut self) {
@@ -423,7 +424,7 @@ impl<'a, F: Write> TokenHandler<'a, '_, F> {
         // We re-open all tags without expansion-related ones.
         for class in classes.into_iter().rev() {
             if !matches!(class, Class::Expansion | Class::Original) {
-                self.class_stack.enter_elem(self.out, &self.href_context, class, false);
+                self.class_stack.enter_elem(self.out, &self.href_context, class, None);
             }
         }
     }
@@ -604,7 +605,7 @@ pub(super) fn write_code(
                 token_handler.out,
                 &token_handler.href_context,
                 class,
-                false,
+                None,
             );
         }
         Highlight::ExitSpan => {
@@ -706,7 +707,6 @@ impl Class {
             | Self::Lifetime
             | Self::QuestionMark
             | Self::Decoration(_)
-            // | Self::Backline(_)
             | Self::Original
             | Self::Expansion => None,
         }

From 0ab44184a42a27970f6d0611af98a389f9b34a5f Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Wed, 15 Oct 2025 18:48:31 +0800
Subject: [PATCH 083/170] Add applicable on assignment for add_braces

```rust
fn foo() {
    let x =$0 n + 100;
}
```
->
```rust
fn foo() {
    let x = {
        n + 100
    };
}
```
---
 .../ide-assists/src/handlers/add_braces.rs    | 62 ++++++++++++++-----
 1 file changed, 46 insertions(+), 16 deletions(-)

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 d855fb771846..f5bbe8dda8c5 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
@@ -1,7 +1,8 @@
 use either::Either;
 use syntax::{
-    AstNode,
+    AstNode, T,
     ast::{self, edit::AstNodeEdit, syntax_factory::SyntaxFactory},
+    match_ast,
 };
 
 use crate::{AssistContext, AssistId, Assists};
@@ -37,6 +38,7 @@ pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
         match expr_type {
             ParentType::ClosureExpr => "Add braces to this closure body",
             ParentType::MatchArmExpr => "Add braces to this match arm expression",
+            ParentType::Assignment => "Add braces to this assignment expression",
         },
         expr.syntax().text_range(),
         |builder| {
@@ -57,29 +59,38 @@ pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
 enum ParentType {
     MatchArmExpr,
     ClosureExpr,
+    Assignment,
 }
 
 fn get_replacement_node(ctx: &AssistContext<'_>) -> Option<(ParentType, ast::Expr)> {
-    let node = ctx.find_node_at_offset::>()?;
-    if let Either::Left(match_arm) = &node {
+    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()?;
+        let body = match_ast! {
+            match parent {
+                ast::LetStmt(it) => it.initializer()?,
+                ast::LetExpr(it) => it.expr()?,
+                ast::Static(it) => it.body()?,
+                ast::Const(it) => it.body()?,
+                _ => return None,
+            }
+        };
+        (ParentType::Assignment, body)
+    } else if let Some(Either::Left(match_arm)) = &node {
         let match_arm_expr = match_arm.expr()?;
-
-        if matches!(match_arm_expr, ast::Expr::BlockExpr(_)) {
-            return None;
-        }
-
-        return Some((ParentType::MatchArmExpr, match_arm_expr));
-    } else if let Either::Right(closure_expr) = &node {
+        (ParentType::MatchArmExpr, match_arm_expr)
+    } else if let Some(Either::Right(closure_expr)) = &node {
         let body = closure_expr.body()?;
+        (ParentType::ClosureExpr, body)
+    } else {
+        return None;
+    };
 
-        if matches!(body, ast::Expr::BlockExpr(_)) {
-            return None;
-        }
-
-        return Some((ParentType::ClosureExpr, body));
+    if matches!(body, ast::Expr::BlockExpr(_)) {
+        return None;
     }
 
-    None
+    Some((parent_type, body))
 }
 
 #[cfg(test)]
@@ -134,6 +145,25 @@ fn foo() {
         );
     }
 
+    #[test]
+    fn suggest_add_braces_for_assignment() {
+        check_assist(
+            add_braces,
+            r#"
+fn foo() {
+    let x =$0 n + 100;
+}
+"#,
+            r#"
+fn foo() {
+    let x = {
+        n + 100
+    };
+}
+"#,
+        );
+    }
+
     #[test]
     fn no_assist_for_closures_with_braces() {
         check_assist_not_applicable(

From 71851588e9075c623c5fd55540d21127e43c64ec Mon Sep 17 00:00:00 2001
From: Johannes Altmanninger 
Date: Thu, 23 Oct 2025 19:57:56 +0200
Subject: [PATCH 084/170] Fix rustfmt for files that use 2024-edition syntax

"cargo fmt" works fine but "rustfmt" fails to format some files.

	$ rustfmt crates/ide-db/src/search.rs
	error: let chains are only allowed in Rust 2024 or later
	   --> /home/johannes/git/rust-analyzer/crates/ide-db/src/search.rs:298:12
	    |
	298 |         if let &Definition::Module(module) = self
	    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

I guess I could work around this by setting my format command to
"cargo fmt -- $filename" instead of "rustfmt $filename".

But it'd be nice if this worked OOTB. Make it so by adding specifying
the edition in rustfmt.toml.  We already have several other places
specifying the edition.

changelog internal
---
 .../crates/syntax/src/ast/generated/nodes.rs  | 926 +++---------------
 .../crates/syntax/src/ast/generated/tokens.rs |  62 +-
 src/tools/rust-analyzer/rustfmt.toml          |   1 +
 3 files changed, 167 insertions(+), 822 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index d60196d492fc..6c1dcf336ac5 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -2,9 +2,9 @@
 
 #![allow(non_snake_case)]
 use crate::{
-    ast::{self, support, AstChildren, AstNode},
     SyntaxKind::{self, *},
     SyntaxNode, SyntaxToken, T,
+    ast::{self, AstChildren, AstNode, support},
 };
 use std::{fmt, hash};
 pub struct Abi {
@@ -2262,11 +2262,7 @@ impl AstNode for Abi {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ABI }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2298,11 +2294,7 @@ impl AstNode for ArgList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ARG_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2334,11 +2326,7 @@ impl AstNode for ArrayExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ARRAY_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2370,11 +2358,7 @@ impl AstNode for ArrayType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ARRAY_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2406,11 +2390,7 @@ impl AstNode for AsmClobberAbi {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CLOBBER_ABI }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2442,11 +2422,7 @@ impl AstNode for AsmConst {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CONST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2478,11 +2454,7 @@ impl AstNode for AsmDirSpec {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_DIR_SPEC }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2514,11 +2486,7 @@ impl AstNode for AsmExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2550,11 +2518,7 @@ impl AstNode for AsmLabel {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_LABEL }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2586,11 +2550,7 @@ impl AstNode for AsmOperandExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2622,11 +2582,7 @@ impl AstNode for AsmOperandNamed {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_NAMED }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2658,11 +2614,7 @@ impl AstNode for AsmOption {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTION }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2694,11 +2646,7 @@ impl AstNode for AsmOptions {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTIONS }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2730,11 +2678,7 @@ impl AstNode for AsmRegOperand {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_OPERAND }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2766,11 +2710,7 @@ impl AstNode for AsmRegSpec {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_SPEC }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2802,11 +2742,7 @@ impl AstNode for AsmSym {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_SYM }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2838,11 +2774,7 @@ impl AstNode for AssocItemList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASSOC_ITEM_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2874,11 +2806,7 @@ impl AstNode for AssocTypeArg {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASSOC_TYPE_ARG }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2910,11 +2838,7 @@ impl AstNode for Attr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ATTR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2946,11 +2870,7 @@ impl AstNode for AwaitExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == AWAIT_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -2982,11 +2902,7 @@ impl AstNode for BecomeExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == BECOME_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3018,11 +2934,7 @@ impl AstNode for BinExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == BIN_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3054,11 +2966,7 @@ impl AstNode for BlockExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == BLOCK_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3090,11 +2998,7 @@ impl AstNode for BoxPat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == BOX_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3126,11 +3030,7 @@ impl AstNode for BreakExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == BREAK_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3162,11 +3062,7 @@ impl AstNode for CallExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == CALL_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3198,11 +3094,7 @@ impl AstNode for CastExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == CAST_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3234,11 +3126,7 @@ impl AstNode for ClosureExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == CLOSURE_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3270,11 +3158,7 @@ impl AstNode for Const {
     fn can_cast(kind: SyntaxKind) -> bool { kind == CONST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3306,11 +3190,7 @@ impl AstNode for ConstArg {
     fn can_cast(kind: SyntaxKind) -> bool { kind == CONST_ARG }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3342,11 +3222,7 @@ impl AstNode for ConstBlockPat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == CONST_BLOCK_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3378,11 +3254,7 @@ impl AstNode for ConstParam {
     fn can_cast(kind: SyntaxKind) -> bool { kind == CONST_PARAM }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3414,11 +3286,7 @@ impl AstNode for ContinueExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == CONTINUE_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3450,11 +3318,7 @@ impl AstNode for DynTraitType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == DYN_TRAIT_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3486,11 +3350,7 @@ impl AstNode for Enum {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ENUM }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3522,11 +3382,7 @@ impl AstNode for ExprStmt {
     fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_STMT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3558,11 +3414,7 @@ impl AstNode for ExternBlock {
     fn can_cast(kind: SyntaxKind) -> bool { kind == EXTERN_BLOCK }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3594,11 +3446,7 @@ impl AstNode for ExternCrate {
     fn can_cast(kind: SyntaxKind) -> bool { kind == EXTERN_CRATE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3630,11 +3478,7 @@ impl AstNode for ExternItemList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == EXTERN_ITEM_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3666,11 +3510,7 @@ impl AstNode for FieldExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == FIELD_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3702,11 +3542,7 @@ impl AstNode for Fn {
     fn can_cast(kind: SyntaxKind) -> bool { kind == FN }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3738,11 +3574,7 @@ impl AstNode for FnPtrType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == FN_PTR_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3774,11 +3606,7 @@ impl AstNode for ForBinder {
     fn can_cast(kind: SyntaxKind) -> bool { kind == FOR_BINDER }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3810,11 +3638,7 @@ impl AstNode for ForExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == FOR_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3846,11 +3670,7 @@ impl AstNode for ForType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == FOR_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3882,11 +3702,7 @@ impl AstNode for FormatArgsArg {
     fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_ARG }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3918,11 +3734,7 @@ impl AstNode for FormatArgsExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3954,11 +3766,7 @@ impl AstNode for GenericArgList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == GENERIC_ARG_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -3990,11 +3798,7 @@ impl AstNode for GenericParamList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == GENERIC_PARAM_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4026,11 +3830,7 @@ impl AstNode for IdentPat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == IDENT_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4062,11 +3862,7 @@ impl AstNode for IfExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == IF_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4098,11 +3894,7 @@ impl AstNode for Impl {
     fn can_cast(kind: SyntaxKind) -> bool { kind == IMPL }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4134,11 +3926,7 @@ impl AstNode for ImplTraitType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == IMPL_TRAIT_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4170,11 +3958,7 @@ impl AstNode for IndexExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == INDEX_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4206,11 +3990,7 @@ impl AstNode for InferType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == INFER_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4242,11 +4022,7 @@ impl AstNode for ItemList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == ITEM_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4278,11 +4054,7 @@ impl AstNode for Label {
     fn can_cast(kind: SyntaxKind) -> bool { kind == LABEL }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4314,11 +4086,7 @@ impl AstNode for LetElse {
     fn can_cast(kind: SyntaxKind) -> bool { kind == LET_ELSE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4350,11 +4118,7 @@ impl AstNode for LetExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == LET_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4386,11 +4150,7 @@ impl AstNode for LetStmt {
     fn can_cast(kind: SyntaxKind) -> bool { kind == LET_STMT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4422,11 +4182,7 @@ impl AstNode for Lifetime {
     fn can_cast(kind: SyntaxKind) -> bool { kind == LIFETIME }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4458,11 +4214,7 @@ impl AstNode for LifetimeArg {
     fn can_cast(kind: SyntaxKind) -> bool { kind == LIFETIME_ARG }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4494,11 +4246,7 @@ impl AstNode for LifetimeParam {
     fn can_cast(kind: SyntaxKind) -> bool { kind == LIFETIME_PARAM }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4530,11 +4278,7 @@ impl AstNode for Literal {
     fn can_cast(kind: SyntaxKind) -> bool { kind == LITERAL }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4566,11 +4310,7 @@ impl AstNode for LiteralPat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == LITERAL_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4602,11 +4342,7 @@ impl AstNode for LoopExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == LOOP_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4638,11 +4374,7 @@ impl AstNode for MacroCall {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_CALL }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4674,11 +4406,7 @@ impl AstNode for MacroDef {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_DEF }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4710,11 +4438,7 @@ impl AstNode for MacroExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4746,11 +4470,7 @@ impl AstNode for MacroItems {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_ITEMS }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4782,11 +4502,7 @@ impl AstNode for MacroPat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4818,11 +4534,7 @@ impl AstNode for MacroRules {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_RULES }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4854,11 +4566,7 @@ impl AstNode for MacroStmts {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_STMTS }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4890,11 +4598,7 @@ impl AstNode for MacroType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4926,11 +4630,7 @@ impl AstNode for MatchArm {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_ARM }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4962,11 +4662,7 @@ impl AstNode for MatchArmList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_ARM_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -4998,11 +4694,7 @@ impl AstNode for MatchExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5034,11 +4726,7 @@ impl AstNode for MatchGuard {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_GUARD }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5070,11 +4758,7 @@ impl AstNode for Meta {
     fn can_cast(kind: SyntaxKind) -> bool { kind == META }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5106,11 +4790,7 @@ impl AstNode for MethodCallExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == METHOD_CALL_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5142,11 +4822,7 @@ impl AstNode for Module {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MODULE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5178,11 +4854,7 @@ impl AstNode for Name {
     fn can_cast(kind: SyntaxKind) -> bool { kind == NAME }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5214,11 +4886,7 @@ impl AstNode for NameRef {
     fn can_cast(kind: SyntaxKind) -> bool { kind == NAME_REF }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5250,11 +4918,7 @@ impl AstNode for NeverType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == NEVER_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5286,11 +4950,7 @@ impl AstNode for OffsetOfExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == OFFSET_OF_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5322,11 +4982,7 @@ impl AstNode for OrPat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == OR_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5358,11 +5014,7 @@ impl AstNode for Param {
     fn can_cast(kind: SyntaxKind) -> bool { kind == PARAM }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5394,11 +5046,7 @@ impl AstNode for ParamList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == PARAM_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5430,11 +5078,7 @@ impl AstNode for ParenExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5466,11 +5110,7 @@ impl AstNode for ParenPat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5502,11 +5142,7 @@ impl AstNode for ParenType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5538,11 +5174,7 @@ impl AstNode for ParenthesizedArgList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == PARENTHESIZED_ARG_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5574,11 +5206,7 @@ impl AstNode for Path {
     fn can_cast(kind: SyntaxKind) -> bool { kind == PATH }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5610,11 +5238,7 @@ impl AstNode for PathExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5646,11 +5270,7 @@ impl AstNode for PathPat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5682,11 +5302,7 @@ impl AstNode for PathSegment {
     fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_SEGMENT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5718,11 +5334,7 @@ impl AstNode for PathType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5754,11 +5366,7 @@ impl AstNode for PrefixExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == PREFIX_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5790,11 +5398,7 @@ impl AstNode for PtrType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == PTR_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5826,11 +5430,7 @@ impl AstNode for RangeExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == RANGE_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5862,11 +5462,7 @@ impl AstNode for RangePat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == RANGE_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5898,11 +5494,7 @@ impl AstNode for RecordExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5934,11 +5526,7 @@ impl AstNode for RecordExprField {
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_EXPR_FIELD }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -5970,11 +5558,7 @@ impl AstNode for RecordExprFieldList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_EXPR_FIELD_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6006,11 +5590,7 @@ impl AstNode for RecordField {
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_FIELD }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6042,11 +5622,7 @@ impl AstNode for RecordFieldList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_FIELD_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6078,11 +5654,7 @@ impl AstNode for RecordPat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6114,11 +5686,7 @@ impl AstNode for RecordPatField {
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_PAT_FIELD }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6150,11 +5718,7 @@ impl AstNode for RecordPatFieldList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_PAT_FIELD_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6186,11 +5750,7 @@ impl AstNode for RefExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == REF_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6222,11 +5782,7 @@ impl AstNode for RefPat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == REF_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6258,11 +5814,7 @@ impl AstNode for RefType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == REF_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6294,11 +5846,7 @@ impl AstNode for Rename {
     fn can_cast(kind: SyntaxKind) -> bool { kind == RENAME }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6330,11 +5878,7 @@ impl AstNode for RestPat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == REST_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6366,11 +5910,7 @@ impl AstNode for RetType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == RET_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6402,11 +5942,7 @@ impl AstNode for ReturnExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == RETURN_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6438,11 +5974,7 @@ impl AstNode for ReturnTypeSyntax {
     fn can_cast(kind: SyntaxKind) -> bool { kind == RETURN_TYPE_SYNTAX }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6474,11 +6006,7 @@ impl AstNode for SelfParam {
     fn can_cast(kind: SyntaxKind) -> bool { kind == SELF_PARAM }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6510,11 +6038,7 @@ impl AstNode for SlicePat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == SLICE_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6546,11 +6070,7 @@ impl AstNode for SliceType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == SLICE_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6582,11 +6102,7 @@ impl AstNode for SourceFile {
     fn can_cast(kind: SyntaxKind) -> bool { kind == SOURCE_FILE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6618,11 +6134,7 @@ impl AstNode for Static {
     fn can_cast(kind: SyntaxKind) -> bool { kind == STATIC }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6654,11 +6166,7 @@ impl AstNode for StmtList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6690,11 +6198,7 @@ impl AstNode for Struct {
     fn can_cast(kind: SyntaxKind) -> bool { kind == STRUCT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6726,11 +6230,7 @@ impl AstNode for TokenTree {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TOKEN_TREE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6762,11 +6262,7 @@ impl AstNode for Trait {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TRAIT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6798,11 +6294,7 @@ impl AstNode for TryExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TRY_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6834,11 +6326,7 @@ impl AstNode for TupleExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6870,11 +6358,7 @@ impl AstNode for TupleField {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_FIELD }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6906,11 +6390,7 @@ impl AstNode for TupleFieldList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_FIELD_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6942,11 +6422,7 @@ impl AstNode for TuplePat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -6978,11 +6454,7 @@ impl AstNode for TupleStructPat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_STRUCT_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7014,11 +6486,7 @@ impl AstNode for TupleType {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_TYPE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7050,11 +6518,7 @@ impl AstNode for TypeAlias {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_ALIAS }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7086,11 +6550,7 @@ impl AstNode for TypeAnchor {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_ANCHOR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7122,11 +6582,7 @@ impl AstNode for TypeArg {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_ARG }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7158,11 +6614,7 @@ impl AstNode for TypeBound {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_BOUND }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7194,11 +6646,7 @@ impl AstNode for TypeBoundList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_BOUND_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7230,11 +6678,7 @@ impl AstNode for TypeParam {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_PARAM }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7266,11 +6710,7 @@ impl AstNode for UnderscoreExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == UNDERSCORE_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7302,11 +6742,7 @@ impl AstNode for Union {
     fn can_cast(kind: SyntaxKind) -> bool { kind == UNION }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7338,11 +6774,7 @@ impl AstNode for Use {
     fn can_cast(kind: SyntaxKind) -> bool { kind == USE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7374,11 +6806,7 @@ impl AstNode for UseBoundGenericArgs {
     fn can_cast(kind: SyntaxKind) -> bool { kind == USE_BOUND_GENERIC_ARGS }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7410,11 +6838,7 @@ impl AstNode for UseTree {
     fn can_cast(kind: SyntaxKind) -> bool { kind == USE_TREE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7446,11 +6870,7 @@ impl AstNode for UseTreeList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == USE_TREE_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7482,11 +6902,7 @@ impl AstNode for Variant {
     fn can_cast(kind: SyntaxKind) -> bool { kind == VARIANT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7518,11 +6934,7 @@ impl AstNode for VariantList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == VARIANT_LIST }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7554,11 +6966,7 @@ impl AstNode for Visibility {
     fn can_cast(kind: SyntaxKind) -> bool { kind == VISIBILITY }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7590,11 +6998,7 @@ impl AstNode for WhereClause {
     fn can_cast(kind: SyntaxKind) -> bool { kind == WHERE_CLAUSE }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7626,11 +7030,7 @@ impl AstNode for WherePred {
     fn can_cast(kind: SyntaxKind) -> bool { kind == WHERE_PRED }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7662,11 +7062,7 @@ impl AstNode for WhileExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == WHILE_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7698,11 +7094,7 @@ impl AstNode for WildcardPat {
     fn can_cast(kind: SyntaxKind) -> bool { kind == WILDCARD_PAT }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7734,11 +7126,7 @@ impl AstNode for YeetExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == YEET_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
@@ -7770,11 +7158,7 @@ impl AstNode for YieldExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == YIELD_EXPR }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
index b2f56c0b1dbf..3dca0db82630 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
@@ -1,9 +1,9 @@
 //! Generated by `cargo xtask codegen grammar`, do not edit by hand.
 
 use crate::{
-    ast::AstToken,
     SyntaxKind::{self, *},
     SyntaxToken,
+    ast::AstToken,
 };
 use std::{fmt, hash};
 pub struct Byte {
@@ -17,11 +17,7 @@ impl std::fmt::Display for Byte {
 impl AstToken for Byte {
     fn can_cast(kind: SyntaxKind) -> bool { kind == BYTE }
     fn cast(syntax: SyntaxToken) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     fn syntax(&self) -> &SyntaxToken { &self.syntax }
 }
@@ -51,11 +47,7 @@ impl std::fmt::Display for ByteString {
 impl AstToken for ByteString {
     fn can_cast(kind: SyntaxKind) -> bool { kind == BYTE_STRING }
     fn cast(syntax: SyntaxToken) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     fn syntax(&self) -> &SyntaxToken { &self.syntax }
 }
@@ -85,11 +77,7 @@ impl std::fmt::Display for CString {
 impl AstToken for CString {
     fn can_cast(kind: SyntaxKind) -> bool { kind == C_STRING }
     fn cast(syntax: SyntaxToken) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     fn syntax(&self) -> &SyntaxToken { &self.syntax }
 }
@@ -119,11 +107,7 @@ impl std::fmt::Display for Char {
 impl AstToken for Char {
     fn can_cast(kind: SyntaxKind) -> bool { kind == CHAR }
     fn cast(syntax: SyntaxToken) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     fn syntax(&self) -> &SyntaxToken { &self.syntax }
 }
@@ -153,11 +137,7 @@ impl std::fmt::Display for Comment {
 impl AstToken for Comment {
     fn can_cast(kind: SyntaxKind) -> bool { kind == COMMENT }
     fn cast(syntax: SyntaxToken) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     fn syntax(&self) -> &SyntaxToken { &self.syntax }
 }
@@ -187,11 +167,7 @@ impl std::fmt::Display for FloatNumber {
 impl AstToken for FloatNumber {
     fn can_cast(kind: SyntaxKind) -> bool { kind == FLOAT_NUMBER }
     fn cast(syntax: SyntaxToken) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     fn syntax(&self) -> &SyntaxToken { &self.syntax }
 }
@@ -221,11 +197,7 @@ impl std::fmt::Display for Ident {
 impl AstToken for Ident {
     fn can_cast(kind: SyntaxKind) -> bool { kind == IDENT }
     fn cast(syntax: SyntaxToken) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     fn syntax(&self) -> &SyntaxToken { &self.syntax }
 }
@@ -255,11 +227,7 @@ impl std::fmt::Display for IntNumber {
 impl AstToken for IntNumber {
     fn can_cast(kind: SyntaxKind) -> bool { kind == INT_NUMBER }
     fn cast(syntax: SyntaxToken) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     fn syntax(&self) -> &SyntaxToken { &self.syntax }
 }
@@ -289,11 +257,7 @@ impl std::fmt::Display for String {
 impl AstToken for String {
     fn can_cast(kind: SyntaxKind) -> bool { kind == STRING }
     fn cast(syntax: SyntaxToken) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     fn syntax(&self) -> &SyntaxToken { &self.syntax }
 }
@@ -323,11 +287,7 @@ impl std::fmt::Display for Whitespace {
 impl AstToken for Whitespace {
     fn can_cast(kind: SyntaxKind) -> bool { kind == WHITESPACE }
     fn cast(syntax: SyntaxToken) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
+        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
     }
     fn syntax(&self) -> &SyntaxToken { &self.syntax }
 }
diff --git a/src/tools/rust-analyzer/rustfmt.toml b/src/tools/rust-analyzer/rustfmt.toml
index 20bf59547b86..3ee7fdabdc21 100644
--- a/src/tools/rust-analyzer/rustfmt.toml
+++ b/src/tools/rust-analyzer/rustfmt.toml
@@ -1,2 +1,3 @@
+edition = "2024"
 reorder_modules = true
 use_small_heuristics = "Max"

From 617683f8d4d593141ada647debea854cb0d4d5be Mon Sep 17 00:00:00 2001
From: Steven Malis 
Date: Thu, 23 Oct 2025 19:18:18 -0400
Subject: [PATCH 085/170] Remove hir-ty/src/next_solver/mapping.rs

---
 src/tools/rust-analyzer/crates/hir-ty/src/db.rs |  4 ++--
 .../rust-analyzer/crates/hir-ty/src/display.rs  | 17 +++++++----------
 .../rust-analyzer/crates/hir-ty/src/infer.rs    | 12 ++++--------
 .../rust-analyzer/crates/hir-ty/src/lib.rs      | 12 +++---------
 .../crates/hir-ty/src/next_solver.rs            |  1 -
 .../crates/hir-ty/src/next_solver/interner.rs   |  3 +--
 .../crates/hir-ty/src/next_solver/mapping.rs    | 13 -------------
 .../crates/hir-ty/src/next_solver/ty.rs         | 11 ++++-------
 8 files changed, 21 insertions(+), 52 deletions(-)
 delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs

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 2ef796332299..9b58abbe4f92 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -277,7 +277,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
 
     // Interned IDs for solver integration
     #[salsa::interned]
-    fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId;
+    fn intern_impl_trait_id(&self, id: ImplTraitId<'_>) -> InternedOpaqueTyId;
 
     #[salsa::interned]
     fn intern_closure(&self, id: InternedClosure) -> InternedClosureId;
@@ -322,7 +322,7 @@ pub struct InternedConstParamId {
 #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)]
 #[derive(PartialOrd, Ord)]
 pub struct InternedOpaqueTyId {
-    pub loc: ImplTraitId,
+    pub loc: ImplTraitId<'db>,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
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 e807ce62e8cf..f8d9add42a8b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -58,7 +58,6 @@ use crate::{
         TraitRef, Ty, TyKind, TypingMode,
         abi::Safety,
         infer::{DbInternerInferExt, traits::ObligationCause},
-        mapping::ChalkToNextSolver,
     },
     primitive,
     utils::{self, detect_variant_from_bytes},
@@ -1126,9 +1125,9 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
                             let datas = db
                                 .return_type_impl_traits(func)
                                 .expect("impl trait id without data");
-                            let data = (*datas).as_ref().map_bound(|rpit| {
-                                &rpit.impl_traits[idx.to_nextsolver(interner)].predicates
-                            });
+                            let data = (*datas)
+                                .as_ref()
+                                .map_bound(|rpit| &rpit.impl_traits[idx].predicates);
                             let bounds =
                                 || data.iter_instantiated_copied(f.interner, ty.args.as_slice());
                             let mut len = bounds().count();
@@ -1358,9 +1357,8 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
                     ImplTraitId::ReturnTypeImplTrait(func, idx) => {
                         let datas =
                             db.return_type_impl_traits(func).expect("impl trait id without data");
-                        let data = (*datas).as_ref().map_bound(|rpit| {
-                            &rpit.impl_traits[idx.to_nextsolver(interner)].predicates
-                        });
+                        let data =
+                            (*datas).as_ref().map_bound(|rpit| &rpit.impl_traits[idx].predicates);
                         let bounds = data
                             .iter_instantiated_copied(interner, alias_ty.args.as_slice())
                             .collect::>();
@@ -1377,9 +1375,8 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
                     ImplTraitId::TypeAliasImplTrait(alias, idx) => {
                         let datas =
                             db.type_alias_impl_traits(alias).expect("impl trait id without data");
-                        let data = (*datas).as_ref().map_bound(|rpit| {
-                            &rpit.impl_traits[idx.to_nextsolver(interner)].predicates
-                        });
+                        let data =
+                            (*datas).as_ref().map_bound(|rpit| &rpit.impl_traits[idx].predicates);
                         let bounds = data
                             .iter_instantiated_copied(interner, alias_ty.args.as_slice())
                             .collect::>();
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 9891f3f248bd..21b6e053cc3b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -73,7 +73,6 @@ use crate::{
         abi::Safety,
         fold::fold_tys,
         infer::traits::{Obligation, ObligationCause},
-        mapping::ChalkToNextSolver,
     },
     traits::FnTrait,
     utils::TargetFeatureIsSafeInTarget,
@@ -1228,9 +1227,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
                     if matches!(mode, ImplTraitReplacingMode::TypeAlias) {
                         // RPITs don't have `tait_coercion_table`, so use inserted inference
                         // vars for them.
-                        if let Some(ty) =
-                            self.result.type_of_rpit.get(idx.to_nextsolver(self.interner()))
-                        {
+                        if let Some(ty) = self.result.type_of_rpit.get(idx) {
                             return *ty;
                         }
                         return ty;
@@ -1251,10 +1248,9 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
             let Some(impl_traits) = impl_traits else {
                 return ty;
             };
-            let bounds = (*impl_traits).as_ref().map_bound(|its| {
-                its.impl_traits[idx.to_nextsolver(self.interner())].predicates.as_slice()
-            });
-            let var = match self.result.type_of_rpit.entry(idx.to_nextsolver(self.interner())) {
+            let bounds =
+                (*impl_traits).as_ref().map_bound(|its| its.impl_traits[idx].predicates.as_slice());
+            let var = match self.result.type_of_rpit.entry(idx) {
                 Entry::Occupied(entry) => return *entry.get(),
                 Entry::Vacant(entry) => *entry.insert(self.table.next_ty_var()),
             };
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 094a3e5326e9..ecca1ef04da5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -57,7 +57,6 @@ use hir_def::{CallableDefId, TypeOrConstParamId, hir::ExprId, type_ref::Rawness}
 use hir_expand::name::Name;
 use indexmap::{IndexMap, map::Entry};
 use intern::{Symbol, sym};
-use la_arena::Idx;
 use mir::{MirEvalError, VTableMap};
 use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
 use rustc_type_ir::{
@@ -332,17 +331,12 @@ impl FnAbi {
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
-pub enum ImplTraitId {
-    ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx), // FIXME(next-solver): Should be crate::nextsolver::ImplTraitIdx.
-    TypeAliasImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
+pub enum ImplTraitId<'db> {
+    ReturnTypeImplTrait(hir_def::FunctionId, next_solver::ImplTraitIdx<'db>),
+    TypeAliasImplTrait(hir_def::TypeAliasId, next_solver::ImplTraitIdx<'db>),
     AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
 }
 
-#[derive(PartialEq, Eq, Debug, Hash)]
-pub struct ImplTrait {}
-
-pub type ImplTraitIdx = Idx;
-
 /// 'Canonicalizes' the `t` by replacing any errors with new variables. Also
 /// ensures there are no unbound variables or inference variables anywhere in
 /// the `t`.
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 1fb9a82ac9e0..8c52a847d1e9 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
@@ -11,7 +11,6 @@ pub mod infer;
 pub(crate) mod inspect;
 pub mod interner;
 mod ir_print;
-pub mod mapping;
 pub mod normalize;
 pub mod obligation_ctxt;
 mod opaques;
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 a509fd893d3c..06d35ba93d95 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
@@ -47,7 +47,6 @@ use super::{
     abi::Safety,
     fold::{BoundVarReplacer, BoundVarReplacerDelegate, FnMutDelegate},
     generics::{Generics, generics},
-    mapping::ChalkToNextSolver,
     region::{
         BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, PlaceholderRegion, Region,
     },
@@ -1883,7 +1882,7 @@ impl<'db> Interner for DbInterner<'db> {
                 match impl_trait_id {
                     crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
                         let infer = self.db().infer(func.into());
-                        EarlyBinder::bind(infer.type_of_rpit[idx.to_nextsolver(self)])
+                        EarlyBinder::bind(infer.type_of_rpit[idx])
                     }
                     crate::ImplTraitId::TypeAliasImplTrait(..)
                     | crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs
deleted file mode 100644
index 2b29561393ee..000000000000
--- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-//! Things useful for mapping to/from Chalk and next-trait-solver types.
-
-use crate::next_solver::interner::DbInterner;
-
-pub(crate) trait ChalkToNextSolver<'db, Out> {
-    fn to_nextsolver(&self, interner: DbInterner<'db>) -> Out;
-}
-
-impl<'db> ChalkToNextSolver<'db, crate::lower::ImplTraitIdx<'db>> for crate::ImplTraitIdx {
-    fn to_nextsolver(&self, _interner: DbInterner<'db>) -> crate::lower::ImplTraitIdx<'db> {
-        crate::lower::ImplTraitIdx::from_raw(self.into_raw())
-    }
-}
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 3abbd2865746..95ee00d2754b 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
@@ -32,7 +32,6 @@ use crate::{
         CoroutineIdWrapper, FnSig, GenericArg, PolyFnSig, Region, TraitRef, TypeAliasIdWrapper,
         abi::Safety,
         interner::InternedWrapperNoDebug,
-        mapping::ChalkToNextSolver,
         util::{CoroutineArgsExt, IntegerTypeExt},
     },
 };
@@ -533,18 +532,16 @@ impl<'db> Ty<'db> {
                 match db.lookup_intern_impl_trait_id(opaque_ty.def_id.expect_opaque_ty()) {
                     ImplTraitId::ReturnTypeImplTrait(func, idx) => {
                         db.return_type_impl_traits(func).map(|it| {
-                            let data = (*it).as_ref().map_bound(|rpit| {
-                                &rpit.impl_traits[idx.to_nextsolver(interner)].predicates
-                            });
+                            let data =
+                                (*it).as_ref().map_bound(|rpit| &rpit.impl_traits[idx].predicates);
                             data.iter_instantiated_copied(interner, opaque_ty.args.as_slice())
                                 .collect()
                         })
                     }
                     ImplTraitId::TypeAliasImplTrait(alias, idx) => {
                         db.type_alias_impl_traits(alias).map(|it| {
-                            let data = (*it).as_ref().map_bound(|rpit| {
-                                &rpit.impl_traits[idx.to_nextsolver(interner)].predicates
-                            });
+                            let data =
+                                (*it).as_ref().map_bound(|rpit| &rpit.impl_traits[idx].predicates);
                             data.iter_instantiated_copied(interner, opaque_ty.args.as_slice())
                                 .collect()
                         })

From 8badb14d822b724ab4e63c9b32ff37481204b628 Mon Sep 17 00:00:00 2001
From: Manuel Drehwald 
Date: Fri, 24 Oct 2025 03:09:24 +0200
Subject: [PATCH 086/170] Add the gpu version of the kernel which was missing
 in the docs

---
 src/doc/rustc-dev-guide/src/offload/usage.md | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/doc/rustc-dev-guide/src/offload/usage.md b/src/doc/rustc-dev-guide/src/offload/usage.md
index 9f519984d9bc..7d1a5c9e2e0e 100644
--- a/src/doc/rustc-dev-guide/src/offload/usage.md
+++ b/src/doc/rustc-dev-guide/src/offload/usage.md
@@ -56,6 +56,13 @@ fn main() {
 unsafe extern "C" {
     pub fn kernel_1(array_b: *mut [f64; 256]);
 }
+
+#[cfg(not(target_os = "linux"))]
+#[unsafe(no_mangle)]
+#[inline(never)]
+pub extern "gpu-kernel" fn kernel_1(x: *mut [f64; 256]) {
+    unsafe { (*x)[0] = 21.0 };
+}
 ```
 
 ## Compile instructions

From aec6d517d7c7d1f402a98329af30f3833c6cad52 Mon Sep 17 00:00:00 2001
From: Ed Page 
Date: Thu, 23 Oct 2025 20:18:52 -0500
Subject: [PATCH 087/170] refactor(rustdoc): Remove redundant langstr checks

These same checks feed into `doctest.can_be_merged`,
making them redundant.
---
 src/librustdoc/doctest.rs | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 51f79047f991..0d551a969d63 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1016,9 +1016,6 @@ impl CreateRunnableDocTests {
             .span(scraped_test.span)
             .build(dcx);
         let is_standalone = !doctest.can_be_merged
-            || scraped_test.langstr.compile_fail
-            || scraped_test.langstr.test_harness
-            || scraped_test.langstr.standalone_crate
             || self.rustdoc_options.nocapture
             || self.rustdoc_options.test_args.iter().any(|arg| arg == "--show-output");
         if is_standalone {

From c859e76f57711b689aef85faec746d665057b93e Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Thu, 23 Oct 2025 22:04:37 +0300
Subject: [PATCH 088/170] Represent async blocks as `TyKind::Coroutine`, not as
 opaques

---
 .../crates/hir-ty/src/display.rs              | 88 +++++++++--------
 .../rust-analyzer/crates/hir-ty/src/infer.rs  |  1 -
 .../crates/hir-ty/src/infer/expr.rs           | 33 ++++---
 .../rust-analyzer/crates/hir-ty/src/lib.rs    |  3 +-
 .../hir-ty/src/next_solver/generic_arg.rs     |  1 -
 .../crates/hir-ty/src/next_solver/generics.rs | 26 +----
 .../crates/hir-ty/src/next_solver/interner.rs |  3 +-
 .../crates/hir-ty/src/next_solver/ty.rs       | 37 +++----
 .../crates/hir-ty/src/next_solver/util.rs     | 99 +------------------
 9 files changed, 94 insertions(+), 197 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 f8d9add42a8b..c749a3d24a25 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -48,7 +48,7 @@ use triomphe::Arc;
 
 use crate::{
     CallableDefId, FnAbi, ImplTraitId, MemoryMap, TraitEnvironment, consteval,
-    db::{HirDatabase, InternedClosure},
+    db::{HirDatabase, InternedClosure, InternedCoroutine},
     generics::generics,
     layout::Layout,
     mir::pad16,
@@ -1389,33 +1389,6 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
                             SizedByDefault::Sized { anchor: krate },
                         )?;
                     }
-                    ImplTraitId::AsyncBlockTypeImplTrait(body, ..) => {
-                        let future_trait =
-                            LangItem::Future.resolve_trait(db, body.module(db).krate());
-                        let output = future_trait.and_then(|t| {
-                            t.trait_items(db)
-                                .associated_type_by_name(&Name::new_symbol_root(sym::Output))
-                        });
-                        write!(f, "impl ")?;
-                        if let Some(t) = future_trait {
-                            f.start_location_link(t.into());
-                        }
-                        write!(f, "Future")?;
-                        if future_trait.is_some() {
-                            f.end_location_link();
-                        }
-                        write!(f, "<")?;
-                        if let Some(t) = output {
-                            f.start_location_link(t.into());
-                        }
-                        write!(f, "Output")?;
-                        if output.is_some() {
-                            f.end_location_link();
-                        }
-                        write!(f, " = ")?;
-                        alias_ty.args.type_at(0).hir_fmt(f)?;
-                        write!(f, ">")?;
-                    }
                 }
             }
             TyKind::Closure(id, substs) => {
@@ -1567,23 +1540,56 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
                 }
             }
             TyKind::Infer(..) => write!(f, "_")?,
-            TyKind::Coroutine(_, subst) => {
-                if f.display_kind.is_source_code() {
-                    return Err(HirDisplayError::DisplaySourceCodeError(
-                        DisplaySourceCodeError::Coroutine,
-                    ));
-                }
+            TyKind::Coroutine(coroutine_id, subst) => {
+                let InternedCoroutine(owner, expr_id) = coroutine_id.0.loc(db);
                 let CoroutineArgsParts { resume_ty, yield_ty, return_ty, .. } =
                     subst.split_coroutine_args();
-                write!(f, "|")?;
-                resume_ty.hir_fmt(f)?;
-                write!(f, "|")?;
+                let body = db.body(owner);
+                match &body[expr_id] {
+                    hir_def::hir::Expr::Async { .. } => {
+                        let future_trait =
+                            LangItem::Future.resolve_trait(db, owner.module(db).krate());
+                        let output = future_trait.and_then(|t| {
+                            t.trait_items(db)
+                                .associated_type_by_name(&Name::new_symbol_root(sym::Output))
+                        });
+                        write!(f, "impl ")?;
+                        if let Some(t) = future_trait {
+                            f.start_location_link(t.into());
+                        }
+                        write!(f, "Future")?;
+                        if future_trait.is_some() {
+                            f.end_location_link();
+                        }
+                        write!(f, "<")?;
+                        if let Some(t) = output {
+                            f.start_location_link(t.into());
+                        }
+                        write!(f, "Output")?;
+                        if output.is_some() {
+                            f.end_location_link();
+                        }
+                        write!(f, " = ")?;
+                        return_ty.hir_fmt(f)?;
+                        write!(f, ">")?;
+                    }
+                    _ => {
+                        if f.display_kind.is_source_code() {
+                            return Err(HirDisplayError::DisplaySourceCodeError(
+                                DisplaySourceCodeError::Coroutine,
+                            ));
+                        }
+                        write!(f, "|")?;
+                        resume_ty.hir_fmt(f)?;
+                        write!(f, "|")?;
 
-                write!(f, " yields ")?;
-                yield_ty.hir_fmt(f)?;
+                        write!(f, " yields ")?;
+                        yield_ty.hir_fmt(f)?;
 
-                write!(f, " -> ")?;
-                return_ty.hir_fmt(f)?;
+                        write!(f, " -> ")?;
+                        return_ty.hir_fmt(f)?;
+                    }
+                }
             }
             TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?,
             TyKind::Pat(_, _) => write!(f, "{{pat}}")?,
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 21b6e053cc3b..361e66522df6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -1243,7 +1243,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
                     }
                     (self.db.type_alias_impl_traits(def), idx)
                 }
-                _ => unreachable!(),
             };
             let Some(impl_traits) = impl_traits else {
                 return ty;
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 efb7244ff637..fd4e374d9c89 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
@@ -18,7 +18,7 @@ use hir_expand::name::Name;
 use intern::sym;
 use rustc_ast_ir::Mutability;
 use rustc_type_ir::{
-    AliasTyKind, InferTy, Interner,
+    CoroutineArgs, CoroutineArgsParts, InferTy, Interner,
     inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Ty as _},
 };
 use syntax::ast::RangeOp;
@@ -29,6 +29,7 @@ use crate::{
     IncorrectGenericsLenKind, Rawness, TraitEnvironment,
     autoderef::overloaded_deref_ty,
     consteval,
+    db::InternedCoroutine,
     generics::generics,
     infer::{
         AllowTwoPhase, BreakableKind,
@@ -43,7 +44,7 @@ use crate::{
     },
     method_resolution::{self, VisibleFromModule},
     next_solver::{
-        AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, TraitRef, Ty, TyKind,
+        Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, TraitRef, Ty, TyKind,
         TypeError,
         infer::{
             InferOk,
@@ -1132,18 +1133,26 @@ impl<'db> InferenceContext<'_, 'db> {
         inner_ty: Ty<'db>,
         tgt_expr: ExprId,
     ) -> Ty<'db> {
-        // Use the first type parameter as the output type of future.
-        // existential type AsyncBlockImplTrait: Future
-        let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr);
-        let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
-        Ty::new_alias(
+        let coroutine_id = InternedCoroutine(self.owner, tgt_expr);
+        let coroutine_id = self.db.intern_coroutine(coroutine_id).into();
+        let parent_args = GenericArgs::identity_for_item(self.interner(), self.generic_def.into());
+        Ty::new_coroutine(
             self.interner(),
-            AliasTyKind::Opaque,
-            AliasTy::new(
+            coroutine_id,
+            CoroutineArgs::new(
                 self.interner(),
-                opaque_ty_id,
-                GenericArgs::new_from_iter(self.interner(), [inner_ty.into()]),
-            ),
+                CoroutineArgsParts {
+                    parent_args,
+                    kind_ty: self.types.unit,
+                    // rustc uses a special lang item type for the resume ty. I don't believe this can cause us problems.
+                    resume_ty: self.types.unit,
+                    yield_ty: self.types.unit,
+                    return_ty: inner_ty,
+                    // FIXME: Infer upvars.
+                    tupled_upvars_ty: self.types.unit,
+                },
+            )
+            .args,
         )
     }
 
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 ecca1ef04da5..2942c0f7a9d3 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -53,7 +53,7 @@ mod variance;
 
 use std::hash::Hash;
 
-use hir_def::{CallableDefId, TypeOrConstParamId, hir::ExprId, type_ref::Rawness};
+use hir_def::{CallableDefId, TypeOrConstParamId, type_ref::Rawness};
 use hir_expand::name::Name;
 use indexmap::{IndexMap, map::Entry};
 use intern::{Symbol, sym};
@@ -334,7 +334,6 @@ impl FnAbi {
 pub enum ImplTraitId<'db> {
     ReturnTypeImplTrait(hir_def::FunctionId, next_solver::ImplTraitIdx<'db>),
     TypeAliasImplTrait(hir_def::TypeAliasId, next_solver::ImplTraitIdx<'db>),
-    AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
 }
 
 /// 'Canonicalizes' the `t` by replacing any errors with new variables. Also
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 24f22bcb0c3e..90bd44aee86f 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
@@ -446,7 +446,6 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs<
                 signature_parts_ty,
                 tupled_upvars_ty,
                 coroutine_captures_by_ref_ty,
-                _coroutine_witness_ty,
             ] => rustc_type_ir::CoroutineClosureArgsParts {
                 parent_args: GenericArgs::new_from_iter(
                     DbInterner::conjure(),
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 d5a9a6f527bb..4d164a7e3bc5 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
@@ -2,10 +2,7 @@
 
 use hir_def::{
     ConstParamId, GenericDefId, GenericParamId, LifetimeParamId, TypeOrConstParamId, TypeParamId,
-    hir::generics::{
-        GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData, TypeParamData,
-        TypeParamProvenance,
-    },
+    hir::generics::{GenericParams, TypeOrConstParamData},
 };
 
 use crate::{db::HirDatabase, generics::parent_generic_def};
@@ -67,27 +64,6 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics {
                 crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => {
                     (Some(type_alias_id.into()), Vec::new())
                 }
-                crate::ImplTraitId::AsyncBlockTypeImplTrait(_def, _) => {
-                    let param = TypeOrConstParamData::TypeParamData(TypeParamData {
-                        name: None,
-                        default: None,
-                        provenance: TypeParamProvenance::TypeParamList,
-                    });
-                    // Yes, there is a parent but we don't include it in the generics
-                    // FIXME: It seems utterly sensitive to fake a generic param here.
-                    // Also, what a horrible mess!
-                    (
-                        None,
-                        vec![mk_ty(
-                            GenericDefId::FunctionId(salsa::plumbing::FromId::from_id(unsafe {
-                                salsa::Id::from_index(salsa::Id::MAX_U32 - 1)
-                            })),
-                            0,
-                            LocalTypeOrConstParamId::from_raw(la_arena::RawIdx::from_u32(0)),
-                            ¶m,
-                        )],
-                    )
-                }
             }
         }
         _ => panic!("No generics for {def:?}"),
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 06d35ba93d95..e3c65689d3fd 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
@@ -1884,8 +1884,7 @@ impl<'db> Interner for DbInterner<'db> {
                         let infer = self.db().infer(func.into());
                         EarlyBinder::bind(infer.type_of_rpit[idx])
                     }
-                    crate::ImplTraitId::TypeAliasImplTrait(..)
-                    | crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
+                    crate::ImplTraitId::TypeAliasImplTrait(..) => {
                         // FIXME(next-solver)
                         EarlyBinder::bind(Ty::new_error(self, ErrorGuaranteed))
                     }
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 95ee00d2754b..1443e2f0b312 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
@@ -26,7 +26,7 @@ use rustc_type_ir::{
 
 use crate::{
     ImplTraitId,
-    db::HirDatabase,
+    db::{HirDatabase, InternedCoroutine},
     next_solver::{
         AdtDef, AliasTy, Binder, CallableIdWrapper, Clause, ClauseKind, ClosureIdWrapper, Const,
         CoroutineIdWrapper, FnSig, GenericArg, PolyFnSig, Region, TraitRef, TypeAliasIdWrapper,
@@ -546,23 +546,6 @@ impl<'db> Ty<'db> {
                                 .collect()
                         })
                     }
-                    ImplTraitId::AsyncBlockTypeImplTrait(def, _) => {
-                        let krate = def.module(db).krate();
-                        if let Some(future_trait) = LangItem::Future.resolve_trait(db, krate) {
-                            // This is only used by type walking.
-                            // Parameters will be walked outside, and projection predicate is not used.
-                            // So just provide the Future trait.
-                            let impl_bound = TraitRef::new(
-                                interner,
-                                future_trait.into(),
-                                GenericArgs::new_from_iter(interner, []),
-                            )
-                            .upcast(interner);
-                            Some(vec![impl_bound])
-                        } else {
-                            None
-                        }
-                    }
                 }
             }
             TyKind::Param(param) => {
@@ -592,6 +575,24 @@ impl<'db> Ty<'db> {
                     _ => None,
                 }
             }
+            TyKind::Coroutine(coroutine_id, _args) => {
+                let InternedCoroutine(owner, _) = coroutine_id.0.loc(db);
+                let krate = owner.module(db).krate();
+                if let Some(future_trait) = LangItem::Future.resolve_trait(db, krate) {
+                    // This is only used by type walking.
+                    // Parameters will be walked outside, and projection predicate is not used.
+                    // So just provide the Future trait.
+                    let impl_bound = TraitRef::new(
+                        interner,
+                        future_trait.into(),
+                        GenericArgs::new_from_iter(interner, []),
+                    )
+                    .upcast(interner);
+                    Some(vec![impl_bound])
+                } else {
+                    None
+                }
+            }
             _ => None,
         }
     }
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 bb0d0552c710..d113f76a327d 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
@@ -7,11 +7,10 @@ use std::{
 
 use base_db::Crate;
 use hir_def::{BlockId, HasModule, lang_item::LangItem};
-use intern::sym;
 use la_arena::Idx;
 use rustc_abi::{Float, HasDataLayout, Integer, IntegerType, Primitive, ReprOptions};
 use rustc_type_ir::{
-    ConstKind, CoroutineArgs, DebruijnIndex, FloatTy, GenericArgKind, INNERMOST, IntTy, Interner,
+    ConstKind, CoroutineArgs, DebruijnIndex, FloatTy, INNERMOST, IntTy, Interner,
     PredicatePolarity, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable,
     TypeVisitableExt, TypeVisitor, UintTy, UniverseIndex,
     inherent::{
@@ -32,9 +31,8 @@ use crate::{
 };
 
 use super::{
-    AliasTerm, AliasTy, Binder, BoundRegion, BoundTy, BoundTyKind, BoundVarKind, BoundVarKinds,
-    Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder, GenericArgs, Predicate,
-    PredicateKind, ProjectionPredicate, Region, SolverDefId, Term, TraitPredicate, TraitRef, Ty,
+    Binder, BoundRegion, BoundTy, Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder,
+    GenericArgs, Predicate, PredicateKind, Region, SolverDefId, TraitPredicate, TraitRef, Ty,
     TyKind,
     fold::{BoundVarReplacer, FnMutDelegate},
 };
@@ -578,98 +576,9 @@ pub fn explicit_item_bounds<'db>(
                     let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())];
                     EarlyBinder::bind(Clauses::new_from_iter(interner, data.predicates.clone()))
                 }
-                crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
-                    if let Some((future_trait, future_output)) = LangItem::Future
-                        .resolve_trait(db, interner.krate.expect("Must have interner.krate"))
-                        .and_then(|trait_| {
-                            let alias = trait_.trait_items(db).associated_type_by_name(
-                                &hir_expand::name::Name::new_symbol_root(sym::Output.clone()),
-                            )?;
-                            Some((trait_, alias))
-                        })
-                    {
-                        let args = GenericArgs::identity_for_item(interner, def_id);
-                        let out = args.as_slice()[0];
-                        let mut predicates = vec![];
-
-                        let item_ty = Ty::new_alias(
-                            interner,
-                            rustc_type_ir::AliasTyKind::Opaque,
-                            AliasTy::new_from_args(interner, def_id, args),
-                        );
-
-                        let kind = PredicateKind::Clause(ClauseKind::Trait(TraitPredicate {
-                            polarity: rustc_type_ir::PredicatePolarity::Positive,
-                            trait_ref: TraitRef::new_from_args(
-                                interner,
-                                future_trait.into(),
-                                GenericArgs::new_from_iter(interner, [item_ty.into()]),
-                            ),
-                        }));
-                        predicates.push(Clause(Predicate::new(
-                            interner,
-                            Binder::bind_with_vars(
-                                kind,
-                                BoundVarKinds::new_from_iter(
-                                    interner,
-                                    [BoundVarKind::Ty(BoundTyKind::Anon)],
-                                ),
-                            ),
-                        )));
-                        let sized_trait = LangItem::Sized
-                            .resolve_trait(db, interner.krate.expect("Must have interner.krate"));
-                        if let Some(sized_trait_) = sized_trait {
-                            let kind = PredicateKind::Clause(ClauseKind::Trait(TraitPredicate {
-                                polarity: rustc_type_ir::PredicatePolarity::Positive,
-                                trait_ref: TraitRef::new_from_args(
-                                    interner,
-                                    sized_trait_.into(),
-                                    GenericArgs::new_from_iter(interner, [item_ty.into()]),
-                                ),
-                            }));
-                            predicates.push(Clause(Predicate::new(
-                                interner,
-                                Binder::bind_with_vars(
-                                    kind,
-                                    BoundVarKinds::new_from_iter(
-                                        interner,
-                                        [BoundVarKind::Ty(BoundTyKind::Anon)],
-                                    ),
-                                ),
-                            )));
-                        }
-                        let kind =
-                            PredicateKind::Clause(ClauseKind::Projection(ProjectionPredicate {
-                                projection_term: AliasTerm::new_from_args(
-                                    interner,
-                                    future_output.into(),
-                                    GenericArgs::new_from_iter(interner, [item_ty.into()]),
-                                ),
-                                term: match out.kind() {
-                                    GenericArgKind::Lifetime(_lt) => panic!(),
-                                    GenericArgKind::Type(ty) => Term::Ty(ty),
-                                    GenericArgKind::Const(const_) => Term::Const(const_),
-                                },
-                            }));
-                        predicates.push(Clause(Predicate::new(
-                            interner,
-                            Binder::bind_with_vars(
-                                kind,
-                                BoundVarKinds::new_from_iter(
-                                    interner,
-                                    [BoundVarKind::Ty(BoundTyKind::Anon)],
-                                ),
-                            ),
-                        )));
-                        EarlyBinder::bind(Clauses::new_from_iter(interner, predicates))
-                    } else {
-                        // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback.
-                        EarlyBinder::bind(Clauses::new_from_iter(interner, []))
-                    }
-                }
             }
         }
-        _ => panic!("Unexpected GeneridDefId"),
+        _ => panic!("Unexpected GenericDefId"),
     }
 }
 

From 3a47c64664d2ee97e9f5c6d76c87d4fe60cc1bd0 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Fri, 24 Oct 2025 01:11:50 +0300
Subject: [PATCH 089/170] Lower async closures to `TyKind::CoroutineClosure`

Instead of `TyKind::Closure`.

Note: the same `InternedCoroutineId` is used both for the *async closure* as well as for the *async block it returns*. When used in `TyKind::CoroutineClosure`, it represents the closure. When used in `TyKind::Coroutine`, it represents the async block. The generic args are different, though.

Also noteworthy is that we distinguish between the different kinds of coroutines (general coroutines, async coroutines and eventually gen coroutines too) via the expression producing them (stored in the `InternedCoroutineId`). It might be worth it to introduce a `CoroutineKind` field to `InternedCoroutineId`, although this is not done in this PR.
---
 .../crates/hir-ty/src/display.rs              | 92 +++++++++++++++--
 .../crates/hir-ty/src/infer/closure.rs        | 79 ++++++++++++---
 .../crates/hir-ty/src/next_solver/interner.rs | 72 ++++++++++----
 .../crates/hir-ty/src/next_solver/ty.rs       | 42 +++++++-
 .../crates/hir-ty/src/tests/simple.rs         |  4 +-
 .../crates/hir-ty/src/tests/traits.rs         | 12 +--
 src/tools/rust-analyzer/crates/hir/src/lib.rs | 98 ++++++++++++-------
 .../src/handlers/convert_closure_to_fn.rs     |  1 -
 8 files changed, 314 insertions(+), 86 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 c749a3d24a25..dd1b212d4c29 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -38,7 +38,8 @@ use rustc_apfloat::{
 use rustc_ast_ir::FloatTy;
 use rustc_hash::FxHashSet;
 use rustc_type_ir::{
-    AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, RegionKind, Upcast,
+    AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, CoroutineClosureArgsParts, RegionKind,
+    Upcast,
     inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _, Tys as _},
 };
 use smallvec::SmallVec;
@@ -1444,14 +1445,83 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
                     }
                     if f.closure_style == ClosureStyle::RANotation || !sig.output().is_unit() {
                         write!(f, " -> ")?;
-                        // FIXME: We display `AsyncFn` as `-> impl Future`, but this is hard to fix because
-                        // we don't have a trait environment here, required to normalize `::Output`.
                         sig.output().hir_fmt(f)?;
                     }
                 } else {
                     write!(f, "{{closure}}")?;
                 }
             }
+            TyKind::CoroutineClosure(id, args) => {
+                let id = id.0;
+                if f.display_kind.is_source_code() {
+                    if !f.display_kind.allows_opaque() {
+                        return Err(HirDisplayError::DisplaySourceCodeError(
+                            DisplaySourceCodeError::OpaqueType,
+                        ));
+                    } else if f.closure_style != ClosureStyle::ImplFn {
+                        never!("Only `impl Fn` is valid for displaying closures in source code");
+                    }
+                }
+                match f.closure_style {
+                    ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"),
+                    ClosureStyle::ClosureWithId => {
+                        return write!(
+                            f,
+                            "{{async closure#{:?}}}",
+                            salsa::plumbing::AsId::as_id(&id).index()
+                        );
+                    }
+                    ClosureStyle::ClosureWithSubst => {
+                        write!(
+                            f,
+                            "{{async closure#{:?}}}",
+                            salsa::plumbing::AsId::as_id(&id).index()
+                        )?;
+                        return hir_fmt_generics(f, args.as_slice(), None, None);
+                    }
+                    _ => (),
+                }
+                let CoroutineClosureArgsParts { closure_kind_ty, signature_parts_ty, .. } =
+                    args.split_coroutine_closure_args();
+                let kind = closure_kind_ty.to_opt_closure_kind().unwrap();
+                let kind = match kind {
+                    rustc_type_ir::ClosureKind::Fn => "AsyncFn",
+                    rustc_type_ir::ClosureKind::FnMut => "AsyncFnMut",
+                    rustc_type_ir::ClosureKind::FnOnce => "AsyncFnOnce",
+                };
+                let TyKind::FnPtr(coroutine_sig, _) = signature_parts_ty.kind() else {
+                    unreachable!("invalid coroutine closure signature");
+                };
+                let coroutine_sig = coroutine_sig.skip_binder();
+                let coroutine_inputs = coroutine_sig.inputs();
+                let TyKind::Tuple(coroutine_inputs) = coroutine_inputs.as_slice()[1].kind() else {
+                    unreachable!("invalid coroutine closure signature");
+                };
+                let TyKind::Tuple(coroutine_output) = coroutine_sig.output().kind() else {
+                    unreachable!("invalid coroutine closure signature");
+                };
+                let coroutine_output = coroutine_output.as_slice()[1];
+                match f.closure_style {
+                    ClosureStyle::ImplFn => write!(f, "impl {kind}(")?,
+                    ClosureStyle::RANotation => write!(f, "async |")?,
+                    _ => unreachable!(),
+                }
+                if coroutine_inputs.is_empty() {
+                } else if f.should_truncate() {
+                    write!(f, "{TYPE_HINT_TRUNCATION}")?;
+                } else {
+                    f.write_joined(coroutine_inputs, ", ")?;
+                };
+                match f.closure_style {
+                    ClosureStyle::ImplFn => write!(f, ")")?,
+                    ClosureStyle::RANotation => write!(f, "|")?,
+                    _ => unreachable!(),
+                }
+                if f.closure_style == ClosureStyle::RANotation || !coroutine_output.is_unit() {
+                    write!(f, " -> ")?;
+                    coroutine_output.hir_fmt(f)?;
+                }
+            }
             TyKind::Placeholder(_) => write!(f, "{{placeholder}}")?,
             TyKind::Param(param) => {
                 // FIXME: We should not access `param.id`, it should be removed, and we should know the
@@ -1545,8 +1615,13 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
                 let CoroutineArgsParts { resume_ty, yield_ty, return_ty, .. } =
                     subst.split_coroutine_args();
                 let body = db.body(owner);
-                match &body[expr_id] {
-                    hir_def::hir::Expr::Async { .. } => {
+                let expr = &body[expr_id];
+                match expr {
+                    hir_def::hir::Expr::Closure {
+                        closure_kind: hir_def::hir::ClosureKind::Async,
+                        ..
+                    }
+                    | hir_def::hir::Expr::Async { .. } => {
                         let future_trait =
                             LangItem::Future.resolve_trait(db, owner.module(db).krate());
                         let output = future_trait.and_then(|t| {
@@ -1573,7 +1648,10 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
                         return_ty.hir_fmt(f)?;
                         write!(f, ">")?;
                     }
-                    _ => {
+                    hir_def::hir::Expr::Closure {
+                        closure_kind: hir_def::hir::ClosureKind::Coroutine(..),
+                        ..
+                    } => {
                         if f.display_kind.is_source_code() {
                             return Err(HirDisplayError::DisplaySourceCodeError(
                                 DisplaySourceCodeError::Coroutine,
@@ -1589,12 +1667,12 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
                         write!(f, " -> ")?;
                         return_ty.hir_fmt(f)?;
                     }
+                    _ => panic!("invalid expr for coroutine: {expr:?}"),
                 }
             }
             TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?,
             TyKind::Pat(_, _) => write!(f, "{{pat}}")?,
             TyKind::UnsafeBinder(_) => write!(f, "{{unsafe binder}}")?,
-            TyKind::CoroutineClosure(_, _) => write!(f, "{{coroutine closure}}")?,
             TyKind::Alias(_, _) => write!(f, "{{alias}}")?,
         }
         Ok(())
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 3dc277023a32..06f8307eb0ab 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
@@ -11,8 +11,9 @@ use hir_def::{
     type_ref::TypeRefId,
 };
 use rustc_type_ir::{
-    ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, Interner, TypeSuperVisitable,
-    TypeVisitable, TypeVisitableExt, TypeVisitor,
+    ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, CoroutineClosureArgs,
+    CoroutineClosureArgsParts, Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
+    TypeVisitor,
     inherent::{BoundExistentialPredicates, GenericArgs as _, IntoKind, SliceLike, Ty as _},
 };
 use tracing::debug;
@@ -22,8 +23,9 @@ use crate::{
     db::{InternedClosure, InternedCoroutine},
     infer::{BreakableKind, Diverges, coerce::CoerceMany},
     next_solver::{
-        AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig,
-        PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, Ty, TyKind,
+        AliasTy, Binder, BoundRegionKind, BoundVarKind, BoundVarKinds, ClauseKind, DbInterner,
+        ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig, PolyProjectionPredicate, Predicate,
+        PredicateKind, SolverDefId, Ty, TyKind,
         abi::Safety,
         infer::{
             BoundRegionConversionTime, InferOk, InferResult,
@@ -72,6 +74,8 @@ impl<'db> InferenceContext<'_, 'db> {
         let sig_ty = Ty::new_fn_ptr(interner, bound_sig);
 
         let parent_args = GenericArgs::identity_for_item(interner, self.generic_def.into());
+        // FIXME: Make this an infer var and infer it later.
+        let tupled_upvars_ty = self.types.unit;
         let (id, ty, resume_yield_tys) = match closure_kind {
             ClosureKind::Coroutine(_) => {
                 let yield_ty = self.table.next_ty_var();
@@ -80,11 +84,11 @@ impl<'db> InferenceContext<'_, 'db> {
                 // FIXME: Infer the upvars later.
                 let parts = CoroutineArgsParts {
                     parent_args,
-                    kind_ty: Ty::new_unit(interner),
+                    kind_ty: self.types.unit,
                     resume_ty,
                     yield_ty,
                     return_ty: body_ret_ty,
-                    tupled_upvars_ty: Ty::new_unit(interner),
+                    tupled_upvars_ty,
                 };
 
                 let coroutine_id =
@@ -97,9 +101,7 @@ impl<'db> InferenceContext<'_, 'db> {
 
                 (None, coroutine_ty, Some((resume_ty, yield_ty)))
             }
-            // FIXME(next-solver): `ClosureKind::Async` should really be a separate arm that creates a `CoroutineClosure`.
-            // But for now we treat it as a closure.
-            ClosureKind::Closure | ClosureKind::Async => {
+            ClosureKind::Closure => {
                 let closure_id = self.db.intern_closure(InternedClosure(self.owner, tgt_expr));
                 match expected_kind {
                     Some(kind) => {
@@ -117,7 +119,7 @@ impl<'db> InferenceContext<'_, 'db> {
                     }
                     None => {}
                 };
-                // FIXME: Infer the kind and the upvars later when needed.
+                // FIXME: Infer the kind later if needed.
                 let parts = ClosureArgsParts {
                     parent_args,
                     closure_kind_ty: Ty::from_closure_kind(
@@ -125,7 +127,7 @@ impl<'db> InferenceContext<'_, 'db> {
                         expected_kind.unwrap_or(rustc_type_ir::ClosureKind::Fn),
                     ),
                     closure_sig_as_fn_ptr_ty: sig_ty,
-                    tupled_upvars_ty: Ty::new_unit(interner),
+                    tupled_upvars_ty,
                 };
                 let closure_ty = Ty::new_closure(
                     interner,
@@ -136,6 +138,61 @@ impl<'db> InferenceContext<'_, 'db> {
                 self.add_current_closure_dependency(closure_id);
                 (Some(closure_id), closure_ty, None)
             }
+            ClosureKind::Async => {
+                // async closures always return the type ascribed after the `->` (if present),
+                // and yield `()`.
+                let bound_return_ty = bound_sig.skip_binder().output();
+                let bound_yield_ty = self.types.unit;
+                // rustc uses a special lang item type for the resume ty. I don't believe this can cause us problems.
+                let resume_ty = self.types.unit;
+
+                // FIXME: Infer the kind later if needed.
+                let closure_kind_ty = Ty::from_closure_kind(
+                    interner,
+                    expected_kind.unwrap_or(rustc_type_ir::ClosureKind::Fn),
+                );
+
+                // FIXME: Infer captures later.
+                // `for<'env> fn() -> ()`, for no captures.
+                let coroutine_captures_by_ref_ty = Ty::new_fn_ptr(
+                    interner,
+                    Binder::bind_with_vars(
+                        interner.mk_fn_sig([], self.types.unit, false, Safety::Safe, FnAbi::Rust),
+                        BoundVarKinds::new_from_iter(
+                            interner,
+                            [BoundVarKind::Region(BoundRegionKind::ClosureEnv)],
+                        ),
+                    ),
+                );
+                let closure_args = CoroutineClosureArgs::new(
+                    interner,
+                    CoroutineClosureArgsParts {
+                        parent_args,
+                        closure_kind_ty,
+                        signature_parts_ty: Ty::new_fn_ptr(
+                            interner,
+                            bound_sig.map_bound(|sig| {
+                                interner.mk_fn_sig(
+                                    [
+                                        resume_ty,
+                                        Ty::new_tup_from_iter(interner, sig.inputs().iter()),
+                                    ],
+                                    Ty::new_tup(interner, &[bound_yield_ty, bound_return_ty]),
+                                    sig.c_variadic,
+                                    sig.safety,
+                                    sig.abi,
+                                )
+                            }),
+                        ),
+                        tupled_upvars_ty,
+                        coroutine_captures_by_ref_ty,
+                    },
+                );
+
+                let coroutine_id =
+                    self.db.intern_coroutine(InternedCoroutine(self.owner, tgt_expr)).into();
+                (None, Ty::new_coroutine_closure(interner, coroutine_id, closure_args.args), None)
+            }
         };
 
         // Now go through the argument patterns
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 e3c65689d3fd..081865a99e5c 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
@@ -17,8 +17,8 @@ use rustc_abi::{ReprFlags, ReprOptions};
 use rustc_hash::FxHashSet;
 use rustc_index::bit_set::DenseBitSet;
 use rustc_type_ir::{
-    AliasTermKind, AliasTyKind, BoundVar, CollectAndApply, DebruijnIndex, EarlyBinder,
-    FlagComputation, Flags, GenericArgKind, ImplPolarity, InferTy, Interner, TraitRef,
+    AliasTermKind, AliasTyKind, BoundVar, CollectAndApply, CoroutineWitnessTypes, DebruijnIndex,
+    EarlyBinder, FlagComputation, Flags, GenericArgKind, ImplPolarity, InferTy, Interner, TraitRef,
     TypeVisitableExt, UniverseIndex, Upcast, Variance,
     elaborate::elaborate,
     error::TypeError,
@@ -29,7 +29,7 @@ use rustc_type_ir::{
 
 use crate::{
     FnAbi,
-    db::HirDatabase,
+    db::{HirDatabase, InternedCoroutine},
     method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint},
     next_solver::{
         AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
@@ -1205,12 +1205,28 @@ impl<'db> Interner for DbInterner<'db> {
         self.db().callable_item_signature(def_id.0)
     }
 
-    fn coroutine_movability(self, _def_id: Self::CoroutineId) -> rustc_ast_ir::Movability {
-        unimplemented!()
+    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 InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db);
+        let body = self.db.body(owner);
+        let expr = &body[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::Movability::Static => rustc_ast_ir::Movability::Static,
+                    hir_def::hir::Movability::Movable => rustc_ast_ir::Movability::Movable,
+                },
+                hir_def::hir::ClosureKind::Async => rustc_ast_ir::Movability::Static,
+                _ => panic!("unexpected expression for a coroutine: {expr:?}"),
+            },
+            hir_def::hir::Expr::Async { .. } => rustc_ast_ir::Movability::Static,
+            _ => panic!("unexpected expression for a coroutine: {expr:?}"),
+        }
     }
 
-    fn coroutine_for_closure(self, _def_id: Self::CoroutineId) -> Self::CoroutineId {
-        unimplemented!()
+    fn coroutine_for_closure(self, def_id: Self::CoroutineClosureId) -> Self::CoroutineId {
+        def_id
     }
 
     fn generics_require_sized_self(self, def_id: Self::DefId) -> bool {
@@ -1725,23 +1741,39 @@ impl<'db> Interner for DbInterner<'db> {
         panic!("Bug encountered in next-trait-solver: {}", msg.to_string())
     }
 
-    fn is_general_coroutine(self, _coroutine_def_id: Self::CoroutineId) -> bool {
-        // FIXME(next-solver)
-        true
+    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 InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db);
+        let body = self.db.body(owner);
+        matches!(
+            body[expr_id],
+            hir_def::hir::Expr::Closure {
+                closure_kind: hir_def::hir::ClosureKind::Coroutine(_),
+                ..
+            }
+        )
     }
 
-    fn coroutine_is_async(self, _coroutine_def_id: Self::CoroutineId) -> bool {
-        // FIXME(next-solver)
-        true
+    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 InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db);
+        let body = self.db.body(owner);
+        matches!(
+            body[expr_id],
+            hir_def::hir::Expr::Closure { closure_kind: hir_def::hir::ClosureKind::Async, .. }
+                | hir_def::hir::Expr::Async { .. }
+        )
     }
 
     fn coroutine_is_gen(self, _coroutine_def_id: Self::CoroutineId) -> bool {
-        // FIXME(next-solver)
+        // We don't handle gen coroutines yet.
         false
     }
 
     fn coroutine_is_async_gen(self, _coroutine_def_id: Self::CoroutineId) -> bool {
-        // FIXME(next-solver)
+        // We don't handle gen coroutines yet.
         false
     }
 
@@ -1897,10 +1929,12 @@ impl<'db> Interner for DbInterner<'db> {
     fn coroutine_hidden_types(
         self,
         _def_id: Self::CoroutineId,
-    ) -> EarlyBinder>>
-    {
-        // FIXME(next-solver)
-        unimplemented!()
+    ) -> EarlyBinder>> {
+        // FIXME: Actually implement this.
+        EarlyBinder::bind(Binder::dummy(CoroutineWitnessTypes {
+            types: Tys::default(),
+            assumptions: RegionAssumptions::default(),
+        }))
     }
 
     fn is_default_trait(self, def_id: Self::TraitId) -> bool {
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 1443e2f0b312..b8406fecda31 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
@@ -11,10 +11,10 @@ use hir_def::{TraitId, type_ref::Rawness};
 use rustc_abi::{Float, Integer, Size};
 use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult};
 use rustc_type_ir::{
-    AliasTyKind, BoundVar, BoundVarIndexKind, ClosureKind, DebruijnIndex, FlagComputation, Flags,
-    FloatTy, FloatVid, InferTy, IntTy, IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable,
-    TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast,
-    WithCachedTypeInfo,
+    AliasTyKind, BoundVar, BoundVarIndexKind, ClosureKind, CoroutineArgs, CoroutineArgsParts,
+    DebruijnIndex, FlagComputation, Flags, FloatTy, FloatVid, InferTy, IntTy, IntVid, Interner,
+    TyVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
+    TypeVisitor, UintTy, Upcast, WithCachedTypeInfo,
     inherent::{
         AdtDef as _, BoundExistentialPredicates, BoundVarLike, Const as _, GenericArgs as _,
         IntoKind, ParamLike, PlaceholderLike, Safety as _, SliceLike, Ty as _,
@@ -404,6 +404,40 @@ impl<'db> Ty<'db> {
                 .split_closure_args_untupled()
                 .closure_sig_as_fn_ptr_ty
                 .callable_sig(interner),
+            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(
+                        interner,
+                        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,
+                    );
+                    FnSig {
+                        inputs_and_output: Tys::new_from_iter(
+                            interner,
+                            sig.tupled_inputs_ty
+                                .tuple_fields()
+                                .iter()
+                                .chain(std::iter::once(return_ty)),
+                        ),
+                        c_variadic: sig.c_variadic,
+                        safety: sig.safety,
+                        abi: sig.abi,
+                    }
+                }))
+            }
             _ => None,
         }
     }
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 38af7cb7248f..c2392b36baba 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
@@ -3856,9 +3856,9 @@ fn main() {
             74..75 'f': F
             80..82 '{}': ()
             94..191 '{     ... }); }': ()
-            100..113 'async_closure': fn async_closure(impl FnOnce(i32))
+            100..113 'async_closure': fn async_closure(impl AsyncFnOnce(i32))
             100..147 'async_...    })': ()
-            114..146 'async ...     }': impl FnOnce(i32)
+            114..146 'async ...     }': impl AsyncFnOnce(i32)
             121..124 'arg': i32
             126..146 '{     ...     }': ()
             136..139 'arg': 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 0cf723e8514d..f72ca22fd229 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
@@ -85,7 +85,6 @@ async fn test() {
 }
 
 #[test]
-#[ignore = "FIXME(next-solver): fix async closures"]
 fn infer_async_closure() {
     check_types(
         r#"
@@ -93,7 +92,7 @@ fn infer_async_closure() {
 async fn test() {
     let f = async move |x: i32| x + 42;
     f;
-//  ^ impl Fn(i32) -> impl Future
+//  ^ impl AsyncFn(i32) -> i32
     let a = f(4);
     a;
 //  ^ impl Future
@@ -102,7 +101,7 @@ async fn test() {
 //  ^ i32
     let f = async move || 42;
     f;
-//  ^ impl Fn() -> impl Future
+//  ^ impl AsyncFn() -> i32
     let a = f();
     a;
 //  ^ impl Future
@@ -119,7 +118,7 @@ async fn test() {
     };
     let _: Option = c().await;
     c;
-//  ^ impl Fn() -> impl Future>
+//  ^ impl AsyncFn() -> Option
 }
 "#,
     );
@@ -4930,7 +4929,6 @@ fn main() {
 
 #[test]
 fn async_fn_return_type() {
-    // FIXME(next-solver): Async closures are lowered as closures currently. We should fix that.
     check_infer(
         r#"
 //- minicore: async_fn
@@ -4948,9 +4946,9 @@ fn main() {
             46..53 'loop {}': !
             51..53 '{}': ()
             67..97 '{     ...()); }': ()
-            73..76 'foo': fn foo(impl Fn())
+            73..76 'foo': fn foo(impl AsyncFn())
             73..94 'foo(as...|| ())': ()
-            77..93 'async ... || ()': impl Fn()
+            77..93 'async ... || ()': impl AsyncFn()
             91..93 '()': ()
         "#]],
     );
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 941890312317..2bb2f80ecc05 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -75,7 +75,7 @@ use hir_ty::{
     TraitEnvironment, TyDefId, TyLoweringDiagnostic, ValueTyDefId, all_super_traits, autoderef,
     check_orphan_rules,
     consteval::try_const_usize,
-    db::InternedClosureId,
+    db::{InternedClosureId, InternedCoroutineId},
     diagnostics::BodyValidationDiagnostic,
     direct_super_traits, known_const_to_ast,
     layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
@@ -92,7 +92,7 @@ use itertools::Itertools;
 use rustc_hash::FxHashSet;
 use rustc_type_ir::{
     AliasTyKind, TypeSuperVisitable, TypeVisitable, TypeVisitor,
-    inherent::{AdtDef, IntoKind, SliceLike, Term as _, Ty as _},
+    inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _},
 };
 use smallvec::SmallVec;
 use span::{AstIdNode, Edition, FileId};
@@ -4558,16 +4558,27 @@ impl<'db> TraitRef<'db> {
     }
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+enum AnyClosureId {
+    ClosureId(InternedClosureId),
+    CoroutineClosureId(InternedCoroutineId),
+}
+
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub struct Closure<'db> {
-    id: InternedClosureId,
+    id: AnyClosureId,
     subst: GenericArgs<'db>,
 }
 
 impl<'db> Closure<'db> {
     fn as_ty(&self, db: &'db dyn HirDatabase) -> Ty<'db> {
         let interner = DbInterner::new_with(db, None, None);
-        Ty::new_closure(interner, self.id.into(), self.subst)
+        match self.id {
+            AnyClosureId::ClosureId(id) => Ty::new_closure(interner, id.into(), self.subst),
+            AnyClosureId::CoroutineClosureId(id) => {
+                Ty::new_coroutine_closure(interner, id.into(), self.subst)
+            }
+        }
     }
 
     pub fn display_with_id(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
@@ -4585,20 +4596,28 @@ impl<'db> Closure<'db> {
     }
 
     pub fn captured_items(&self, db: &'db dyn HirDatabase) -> Vec> {
-        let owner = db.lookup_intern_closure(self.id).0;
+        let AnyClosureId::ClosureId(id) = self.id else {
+            // FIXME: Infer coroutine closures' captures.
+            return Vec::new();
+        };
+        let owner = db.lookup_intern_closure(id).0;
         let infer = db.infer(owner);
-        let info = infer.closure_info(self.id);
+        let info = infer.closure_info(id);
         info.0
             .iter()
             .cloned()
-            .map(|capture| ClosureCapture { owner, closure: self.id, capture })
+            .map(|capture| ClosureCapture { owner, closure: id, capture })
             .collect()
     }
 
     pub fn capture_types(&self, db: &'db dyn HirDatabase) -> Vec> {
-        let owner = db.lookup_intern_closure(self.id).0;
+        let AnyClosureId::ClosureId(id) = self.id else {
+            // FIXME: Infer coroutine closures' captures.
+            return Vec::new();
+        };
+        let owner = db.lookup_intern_closure(id).0;
         let infer = db.infer(owner);
-        let (captures, _) = infer.closure_info(self.id);
+        let (captures, _) = infer.closure_info(id);
         let env = db.trait_environment_for_body(owner);
         captures
             .iter()
@@ -4607,10 +4626,22 @@ impl<'db> Closure<'db> {
     }
 
     pub fn fn_trait(&self, db: &dyn HirDatabase) -> FnTrait {
-        let owner = db.lookup_intern_closure(self.id).0;
-        let infer = db.infer(owner);
-        let info = infer.closure_info(self.id);
-        info.1
+        match self.id {
+            AnyClosureId::ClosureId(id) => {
+                let owner = db.lookup_intern_closure(id).0;
+                let infer = db.infer(owner);
+                let info = infer.closure_info(id);
+                info.1
+            }
+            AnyClosureId::CoroutineClosureId(_id) => {
+                // FIXME: Infer kind for coroutine closures.
+                match self.subst.as_coroutine_closure().kind() {
+                    rustc_type_ir::ClosureKind::Fn => FnTrait::AsyncFn,
+                    rustc_type_ir::ClosureKind::FnMut => FnTrait::AsyncFnMut,
+                    rustc_type_ir::ClosureKind::FnOnce => FnTrait::AsyncFnOnce,
+                }
+            }
+        }
     }
 }
 
@@ -5124,28 +5155,14 @@ impl<'db> Type<'db> {
         let interner = DbInterner::new_with(db, None, None);
         let callee = match self.ty.kind() {
             TyKind::Closure(id, subst) => Callee::Closure(id.0, subst),
+            TyKind::CoroutineClosure(id, subst) => Callee::CoroutineClosure(id.0, subst),
             TyKind::FnPtr(..) => Callee::FnPtr,
             TyKind::FnDef(id, _) => Callee::Def(id.0),
-            kind => {
-                // This will happen when it implements fn or fn mut, since we add an autoborrow adjustment
-                let (ty, kind) = if let TyKind::Ref(_, ty, _) = kind {
-                    (ty, ty.kind())
-                } else {
-                    (self.ty, kind)
-                };
-                if let TyKind::Closure(closure, subst) = kind {
-                    let sig = subst
-                        .split_closure_args_untupled()
-                        .closure_sig_as_fn_ptr_ty
-                        .callable_sig(interner)?;
-                    return Some(Callable {
-                        ty: self.clone(),
-                        sig,
-                        callee: Callee::Closure(closure.0, subst),
-                        is_bound_method: false,
-                    });
-                }
-                let (fn_trait, sig) = hir_ty::callable_sig_from_fn_trait(ty, self.env.clone(), db)?;
+            // This will happen when it implements fn or fn mut, since we add an autoborrow adjustment
+            TyKind::Ref(_, inner_ty, _) => return self.derived(inner_ty).as_callable(db),
+            _ => {
+                let (fn_trait, sig) =
+                    hir_ty::callable_sig_from_fn_trait(self.ty, self.env.clone(), db)?;
                 return Some(Callable {
                     ty: self.clone(),
                     sig,
@@ -5165,7 +5182,12 @@ impl<'db> Type<'db> {
 
     pub fn as_closure(&self) -> Option> {
         match self.ty.kind() {
-            TyKind::Closure(id, subst) => Some(Closure { id: id.0, subst }),
+            TyKind::Closure(id, subst) => {
+                Some(Closure { id: AnyClosureId::ClosureId(id.0), subst })
+            }
+            TyKind::CoroutineClosure(id, subst) => {
+                Some(Closure { id: AnyClosureId::CoroutineClosureId(id.0), subst })
+            }
             _ => None,
         }
     }
@@ -5824,6 +5846,7 @@ pub struct Callable<'db> {
 enum Callee<'db> {
     Def(CallableDefId),
     Closure(InternedClosureId, GenericArgs<'db>),
+    CoroutineClosure(InternedCoroutineId, GenericArgs<'db>),
     FnPtr,
     FnImpl(FnTrait),
 }
@@ -5845,7 +5868,12 @@ impl<'db> Callable<'db> {
             Callee::Def(CallableDefId::EnumVariantId(it)) => {
                 CallableKind::TupleEnumVariant(it.into())
             }
-            Callee::Closure(id, ref subst) => CallableKind::Closure(Closure { id, subst: *subst }),
+            Callee::Closure(id, subst) => {
+                CallableKind::Closure(Closure { id: AnyClosureId::ClosureId(id), subst })
+            }
+            Callee::CoroutineClosure(id, subst) => {
+                CallableKind::Closure(Closure { id: AnyClosureId::CoroutineClosureId(id), subst })
+            }
             Callee::FnPtr => CallableKind::FnPtr,
             Callee::FnImpl(fn_) => CallableKind::FnImpl(fn_),
         }
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 2cda6d6f1c0a..ca142332d97e 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
@@ -805,7 +805,6 @@ impl A {
         );
     }
 
-    #[ignore = "FIXME(next-solver): Fix async closures"]
     #[test]
     fn replaces_async_closure_with_async_fn() {
         check_assist(

From 76b7c79e326a2917fe3e04ffbaac0fde61d6b4dd Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Fri, 12 Sep 2025 20:55:14 +0800
Subject: [PATCH 090/170] Support else completion for more expressions

- Support else completion in ArrayExpr, ReturnExpr and PrefixExpr etc
- Support else completion after MatchArm expression

Before this PR, the else branch could not be completed in most expressions

Example
---
```rust
fn foo() -> [i32; 1] {
    [if true {
        2
    } $0]
}
```
->
```rust
fn foo() -> [i32; 1] {
    [if true {
        2
    } else {
        $0
    }]
}
```

---

```rust
fn foo() -> i32 {
    match () {
        () => if true {
            2
        } $0
    }
}
```
->
```rust
fn foo() -> i32 {
    match () {
        () => if true {
            2
        } else {
            $0
        }
    }
}
```
---
 .../ide-completion/src/completions/pattern.rs |   5 +
 .../crates/ide-completion/src/context.rs      |   1 +
 .../ide-completion/src/context/analysis.rs    |  19 +-
 .../ide-completion/src/tests/expression.rs    | 292 ++++++++++++++++++
 4 files changed, 312 insertions(+), 5 deletions(-)

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 0ce81d02b409..dcddc24890ac 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
@@ -42,6 +42,11 @@ pub(crate) fn complete_pattern(
         }
     }
 
+    if pattern_ctx.after_if_expr {
+        add_keyword("else", "else {\n    $0\n}");
+        add_keyword("else if", "else if $1 {\n    $0\n}");
+    }
+
     if pattern_ctx.record_pat.is_some() {
         return;
     }
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 91f19f6b4370..2f166b718451 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -279,6 +279,7 @@ pub(crate) struct PatternContext {
     pub(crate) param_ctx: Option,
     pub(crate) has_type_ascription: bool,
     pub(crate) should_suggest_name: bool,
+    pub(crate) after_if_expr: bool,
     pub(crate) parent_pat: Option,
     pub(crate) ref_token: Option,
     pub(crate) mut_token: Option,
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 d39bff1577f3..42772ef2d04f 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
@@ -999,10 +999,6 @@ fn classify_name_ref<'db>(
             }
         }
     };
-    let after_if_expr = |node: SyntaxNode| {
-        let prev_expr = prev_expr(node);
-        matches!(prev_expr, Some(ast::Expr::IfExpr(_)))
-    };
     let after_incomplete_let = |node: SyntaxNode| {
         prev_expr(node).and_then(|it| it.syntax().parent()).and_then(ast::LetStmt::cast)
     };
@@ -1242,7 +1238,7 @@ fn classify_name_ref<'db>(
         let it = expr.syntax();
         let in_block_expr = is_in_block(it);
         let (in_loop_body, innermost_breakable) = is_in_breakable(it).unzip();
-        let after_if_expr = after_if_expr(it.clone());
+        let after_if_expr = is_after_if_expr(it.clone());
         let ref_expr_parent =
             path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast);
         let after_amp = non_trivia_sibling(it.clone().into(), Direction::Prev)
@@ -1763,6 +1759,7 @@ fn pattern_context_for(
         param_ctx,
         has_type_ascription,
         should_suggest_name,
+        after_if_expr: is_after_if_expr(pat.syntax().clone()),
         parent_pat: pat.syntax().parent().and_then(ast::Pat::cast),
         mut_token,
         ref_token,
@@ -1933,6 +1930,18 @@ fn has_in_newline_expr_first(node: &SyntaxNode) -> bool {
     }
 }
 
+fn is_after_if_expr(node: SyntaxNode) -> bool {
+    let node = match node.parent().and_then(Either::::cast) {
+        Some(stmt) => stmt.syntax().clone(),
+        None => node,
+    };
+    let prev_sibling =
+        non_trivia_sibling(node.into(), Direction::Prev).and_then(NodeOrToken::into_node);
+    iter::successors(prev_sibling, |it| it.last_child_or_token()?.into_node())
+        .find_map(ast::IfExpr::cast)
+        .is_some()
+}
+
 fn next_non_trivia_token(e: impl Into) -> Option {
     let mut token = match e.into() {
         SyntaxElement::Node(n) => n.last_token()?,
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 09af635f01ca..67c84f42c1ae 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
@@ -1869,6 +1869,298 @@ fn foo() { let x = if foo {} $0 else if true {} else {}; }
             sn ppd
         "#]],
     );
+    check(
+        r#"
+fn foo() { [if foo {} $0]}
+"#,
+        expect![[r#"
+            fn foo()  fn()
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw else
+            kw else if
+            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
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { [if foo {} el$0]}
+"#,
+        expect![[r#"
+            fn foo()  fn()
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw else
+            kw else if
+            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
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { 2 + if foo {} $0 }
+"#,
+        expect![[r#"
+            fn foo()  fn()
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw else
+            kw else if
+            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
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { -if foo {} $0 }
+"#,
+        expect![[r#"
+            fn foo()  fn()
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw else
+            kw else if
+            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
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { &mut if foo {} $0 }
+"#,
+        expect![[r#"
+            fn foo()  fn()
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw else
+            kw else if
+            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
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { return if foo {} $0 }
+"#,
+        expect![[r#"
+            fn foo()  fn()
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw else
+            kw else if
+            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
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { match () { () => if foo {} $0 } }
+"#,
+        expect![[r#"
+            kw else
+            kw else if
+            kw mut
+            kw ref
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { match () { () => if foo {} $0, } }
+"#,
+        expect![[r#"
+            kw else
+            kw else if
+            kw mut
+            kw ref
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { match () { () => if foo {} $0, _ => (), } }
+"#,
+        expect![[r#"
+            kw else
+            kw else if
+            kw mut
+            kw ref
+        "#]],
+    );
+    // FIXME: support else completion after ast::RecordExprField
 }
 
 #[test]

From a15ef424881aa419b3381e04b708753ef7717aa4 Mon Sep 17 00:00:00 2001
From: Camille Gillot 
Date: Wed, 8 Oct 2025 19:44:41 +0000
Subject: [PATCH 091/170] Only load pin field once.

---
 compiler/rustc_mir_transform/src/coroutine.rs |  53 ++++---
 .../rustc_mir_transform/src/coroutine/drop.rs |   7 +-
 ...#0}.coroutine_drop_async.0.panic-abort.mir |  14 +-
 ...0}.coroutine_drop_async.0.panic-unwind.mir |  16 ++-
 ...await.a-{closure#0}.coroutine_resume.0.mir |   6 +-
 ...await.b-{closure#0}.coroutine_resume.0.mir |  26 ++--
 ....main-{closure#0}.StateTransform.after.mir |  30 ++--
 ....main-{closure#1}.StateTransform.after.mir |  30 ++--
 ...ny.main-{closure#0}.coroutine_resume.0.mir |  10 +-
 ...y.run2-{closure#0}.Inline.panic-abort.diff | 120 ++++++----------
 ....run2-{closure#0}.Inline.panic-unwind.diff | 134 +++++++-----------
 .../async-closure.stdout                      |  20 +--
 12 files changed, 196 insertions(+), 270 deletions(-)

diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index e5e9cd192afa..a5c2f8b53cf6 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -132,8 +132,8 @@ struct SelfArgVisitor<'tcx> {
 }
 
 impl<'tcx> SelfArgVisitor<'tcx> {
-    fn new(tcx: TyCtxt<'tcx>, elem: ProjectionElem>) -> Self {
-        Self { tcx, new_base: Place { local: SELF_ARG, projection: tcx.mk_place_elems(&[elem]) } }
+    fn new(tcx: TyCtxt<'tcx>, new_base: Place<'tcx>) -> Self {
+        Self { tcx, new_base }
     }
 }
 
@@ -146,16 +146,14 @@ impl<'tcx> MutVisitor<'tcx> for SelfArgVisitor<'tcx> {
         assert_ne!(*local, SELF_ARG);
     }
 
-    fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
+    fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, _: Location) {
         if place.local == SELF_ARG {
             replace_base(place, self.new_base, self.tcx);
-        } else {
-            self.visit_local(&mut place.local, context, location);
+        }
 
-            for elem in place.projection.iter() {
-                if let PlaceElem::Index(local) = elem {
-                    assert_ne!(local, SELF_ARG);
-                }
+        for elem in place.projection.iter() {
+            if let PlaceElem::Index(local) = elem {
+                assert_ne!(local, SELF_ARG);
             }
         }
     }
@@ -515,20 +513,22 @@ fn make_aggregate_adt<'tcx>(
 
 #[tracing::instrument(level = "trace", skip(tcx, body))]
 fn make_coroutine_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-    let coroutine_ty = body.local_decls.raw[1].ty;
+    let coroutine_ty = body.local_decls[SELF_ARG].ty;
 
     let ref_coroutine_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty);
 
     // Replace the by value coroutine argument
-    body.local_decls.raw[1].ty = ref_coroutine_ty;
+    body.local_decls[SELF_ARG].ty = ref_coroutine_ty;
 
     // Add a deref to accesses of the coroutine state
-    SelfArgVisitor::new(tcx, ProjectionElem::Deref).visit_body(body);
+    SelfArgVisitor::new(tcx, tcx.mk_place_deref(SELF_ARG.into())).visit_body(body);
 }
 
 #[tracing::instrument(level = "trace", skip(tcx, body))]
 fn make_coroutine_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-    let ref_coroutine_ty = body.local_decls.raw[1].ty;
+    let coroutine_ty = body.local_decls[SELF_ARG].ty;
+
+    let ref_coroutine_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty);
 
     let pin_did = tcx.require_lang_item(LangItem::Pin, body.span);
     let pin_adt_ref = tcx.adt_def(pin_did);
@@ -536,11 +536,26 @@ fn make_coroutine_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body
     let pin_ref_coroutine_ty = Ty::new_adt(tcx, pin_adt_ref, args);
 
     // Replace the by ref coroutine argument
-    body.local_decls.raw[1].ty = pin_ref_coroutine_ty;
+    body.local_decls[SELF_ARG].ty = pin_ref_coroutine_ty;
+
+    let unpinned_local = body.local_decls.push(LocalDecl::new(ref_coroutine_ty, body.span));
 
     // Add the Pin field access to accesses of the coroutine state
-    SelfArgVisitor::new(tcx, ProjectionElem::Field(FieldIdx::ZERO, ref_coroutine_ty))
-        .visit_body(body);
+    SelfArgVisitor::new(tcx, tcx.mk_place_deref(unpinned_local.into())).visit_body(body);
+
+    let source_info = SourceInfo::outermost(body.span);
+    let pin_field = tcx.mk_place_field(SELF_ARG.into(), FieldIdx::ZERO, ref_coroutine_ty);
+
+    body.basic_blocks_mut()[START_BLOCK].statements.insert(
+        0,
+        Statement::new(
+            source_info,
+            StatementKind::Assign(Box::new((
+                unpinned_local.into(),
+                Rvalue::Use(Operand::Copy(pin_field)),
+            ))),
+        ),
+    );
 }
 
 /// Transforms the `body` of the coroutine applying the following transforms:
@@ -1292,8 +1307,6 @@ fn create_coroutine_resume_function<'tcx>(
     let default_block = insert_term_block(body, TerminatorKind::Unreachable);
     insert_switch(body, cases, &transform, default_block);
 
-    make_coroutine_state_argument_indirect(tcx, body);
-
     match transform.coroutine_kind {
         CoroutineKind::Coroutine(_)
         | CoroutineKind::Desugared(CoroutineDesugaring::Async | CoroutineDesugaring::AsyncGen, _) =>
@@ -1302,7 +1315,9 @@ fn create_coroutine_resume_function<'tcx>(
         }
         // Iterator::next doesn't accept a pinned argument,
         // unlike for all other coroutine kinds.
-        CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {}
+        CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {
+            make_coroutine_state_argument_indirect(tcx, body);
+        }
     }
 
     // Make sure we remove dead blocks to remove
diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs
index 41c588f0d8d3..2699a051a8fe 100644
--- a/compiler/rustc_mir_transform/src/coroutine/drop.rs
+++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs
@@ -684,12 +684,13 @@ pub(super) fn create_coroutine_drop_shim_async<'tcx>(
     let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()]));
     body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info);
 
-    make_coroutine_state_argument_indirect(tcx, &mut body);
-
     match transform.coroutine_kind {
         // Iterator::next doesn't accept a pinned argument,
         // unlike for all other coroutine kinds.
-        CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {}
+        CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {
+            make_coroutine_state_argument_indirect(tcx, &mut body);
+        }
+
         _ => {
             make_coroutine_state_argument_pinned(tcx, &mut body);
         }
diff --git a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir
index 541b9b9895b3..435c1532895c 100644
--- a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir
+++ b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir
@@ -2,7 +2,7 @@
 
 fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) -> Poll<()> {
     debug _task_context => _2;
-    debug x => ((*(_1.0: &mut {async fn body of a()})).0: T);
+    debug x => ((*_20).0: T);
     let mut _0: std::task::Poll<()>;
     let _3: T;
     let mut _4: impl std::future::Future;
@@ -21,12 +21,14 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>)
     let mut _17: isize;
     let mut _18: ();
     let mut _19: u32;
+    let mut _20: &mut {async fn body of a()};
     scope 1 {
-        debug x => (((*(_1.0: &mut {async fn body of a()})) as variant#4).0: T);
+        debug x => (((*_20) as variant#4).0: T);
     }
 
     bb0: {
-        _19 = discriminant((*(_1.0: &mut {async fn body of a()})));
+        _20 = copy (_1.0: &mut {async fn body of a()});
+        _19 = discriminant((*_20));
         switchInt(move _19) -> [0: bb9, 3: bb12, 4: bb13, otherwise: bb14];
     }
 
@@ -43,13 +45,13 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>)
 
     bb3: {
         _0 = Poll::<()>::Pending;
-        discriminant((*(_1.0: &mut {async fn body of a()}))) = 4;
+        discriminant((*_20)) = 4;
         return;
     }
 
     bb4: {
         StorageLive(_16);
-        _15 = &mut (((*(_1.0: &mut {async fn body of a()})) as variant#4).1: impl std::future::Future);
+        _15 = &mut (((*_20) as variant#4).1: impl std::future::Future);
         _16 = Pin::<&mut impl Future>::new_unchecked(move _15) -> [return: bb7, unwind unreachable];
     }
 
@@ -81,7 +83,7 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>)
     }
 
     bb11: {
-        drop(((*(_1.0: &mut {async fn body of a()})).0: T)) -> [return: bb10, unwind unreachable];
+        drop(((*_20).0: T)) -> [return: bb10, unwind unreachable];
     }
 
     bb12: {
diff --git a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir
index f2e1fb099756..1dc1d0813629 100644
--- a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir
+++ b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir
@@ -2,7 +2,7 @@
 
 fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) -> Poll<()> {
     debug _task_context => _2;
-    debug x => ((*(_1.0: &mut {async fn body of a()})).0: T);
+    debug x => ((*_20).0: T);
     let mut _0: std::task::Poll<()>;
     let _3: T;
     let mut _4: impl std::future::Future;
@@ -21,12 +21,14 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>)
     let mut _17: isize;
     let mut _18: ();
     let mut _19: u32;
+    let mut _20: &mut {async fn body of a()};
     scope 1 {
-        debug x => (((*(_1.0: &mut {async fn body of a()})) as variant#4).0: T);
+        debug x => (((*_20) as variant#4).0: T);
     }
 
     bb0: {
-        _19 = discriminant((*(_1.0: &mut {async fn body of a()})));
+        _20 = copy (_1.0: &mut {async fn body of a()});
+        _19 = discriminant((*_20));
         switchInt(move _19) -> [0: bb12, 2: bb18, 3: bb16, 4: bb17, otherwise: bb19];
     }
 
@@ -57,13 +59,13 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>)
 
     bb6: {
         _0 = Poll::<()>::Pending;
-        discriminant((*(_1.0: &mut {async fn body of a()}))) = 4;
+        discriminant((*_20)) = 4;
         return;
     }
 
     bb7: {
         StorageLive(_16);
-        _15 = &mut (((*(_1.0: &mut {async fn body of a()})) as variant#4).1: impl std::future::Future);
+        _15 = &mut (((*_20) as variant#4).1: impl std::future::Future);
         _16 = Pin::<&mut impl Future>::new_unchecked(move _15) -> [return: bb10, unwind: bb15];
     }
 
@@ -95,11 +97,11 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>)
     }
 
     bb14: {
-        drop(((*(_1.0: &mut {async fn body of a()})).0: T)) -> [return: bb13, unwind: bb4];
+        drop(((*_20).0: T)) -> [return: bb13, unwind: bb4];
     }
 
     bb15 (cleanup): {
-        discriminant((*(_1.0: &mut {async fn body of a()}))) = 2;
+        discriminant((*_20)) = 2;
         resume;
     }
 
diff --git a/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir
index 2e2876cb3fcf..6cad5b105d3e 100644
--- a/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir
+++ b/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir
@@ -14,9 +14,11 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) ->
     let mut _0: std::task::Poll<()>;
     let mut _3: ();
     let mut _4: u32;
+    let mut _5: &mut {async fn body of a()};
 
     bb0: {
-        _4 = discriminant((*(_1.0: &mut {async fn body of a()})));
+        _5 = copy (_1.0: &mut {async fn body of a()});
+        _4 = discriminant((*_5));
         switchInt(move _4) -> [0: bb1, 1: bb4, otherwise: bb5];
     }
 
@@ -27,7 +29,7 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) ->
 
     bb2: {
         _0 = Poll::<()>::Ready(move _3);
-        discriminant((*(_1.0: &mut {async fn body of a()}))) = 1;
+        discriminant((*_5)) = 1;
         return;
     }
 
diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir
index 20fc4112ef28..96ee37185db1 100644
--- a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir
+++ b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir
@@ -86,15 +86,16 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) ->
     let mut _36: ();
     let mut _37: ();
     let mut _38: u32;
+    let mut _39: &mut {async fn body of b()};
     scope 1 {
-        debug __awaitee => (((*(_1.0: &mut {async fn body of b()})) as variant#3).0: {async fn body of a()});
+        debug __awaitee => (((*_39) as variant#3).0: {async fn body of a()});
         let _17: ();
         scope 2 {
             debug result => _17;
         }
     }
     scope 3 {
-        debug __awaitee => (((*(_1.0: &mut {async fn body of b()})) as variant#4).0: {async fn body of a()});
+        debug __awaitee => (((*_39) as variant#4).0: {async fn body of a()});
         let _33: ();
         scope 4 {
             debug result => _33;
@@ -102,7 +103,8 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) ->
     }
 
     bb0: {
-        _38 = discriminant((*(_1.0: &mut {async fn body of b()})));
+        _39 = copy (_1.0: &mut {async fn body of b()});
+        _38 = discriminant((*_39));
         switchInt(move _38) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb8];
     }
 
@@ -121,7 +123,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) ->
         StorageDead(_5);
         PlaceMention(_4);
         nop;
-        (((*(_1.0: &mut {async fn body of b()})) as variant#3).0: {async fn body of a()}) = move _4;
+        (((*_39) as variant#3).0: {async fn body of a()}) = move _4;
         goto -> bb4;
     }
 
@@ -131,7 +133,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) ->
         StorageLive(_10);
         StorageLive(_11);
         StorageLive(_12);
-        _12 = &mut (((*(_1.0: &mut {async fn body of b()})) as variant#3).0: {async fn body of a()});
+        _12 = &mut (((*_39) as variant#3).0: {async fn body of a()});
         _11 = &mut (*_12);
         _10 = Pin::<&mut {async fn body of a()}>::new_unchecked(move _11) -> [return: bb5, unwind unreachable];
     }
@@ -178,7 +180,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) ->
         StorageDead(_4);
         StorageDead(_19);
         StorageDead(_20);
-        discriminant((*(_1.0: &mut {async fn body of b()}))) = 3;
+        discriminant((*_39)) = 3;
         return;
     }
 
@@ -191,7 +193,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) ->
         StorageDead(_12);
         StorageDead(_9);
         StorageDead(_8);
-        drop((((*(_1.0: &mut {async fn body of b()})) as variant#3).0: {async fn body of a()})) -> [return: bb12, unwind unreachable];
+        drop((((*_39) as variant#3).0: {async fn body of a()})) -> [return: bb12, unwind unreachable];
     }
 
     bb11: {
@@ -223,7 +225,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) ->
         StorageDead(_22);
         PlaceMention(_21);
         nop;
-        (((*(_1.0: &mut {async fn body of b()})) as variant#4).0: {async fn body of a()}) = move _21;
+        (((*_39) as variant#4).0: {async fn body of a()}) = move _21;
         goto -> bb16;
     }
 
@@ -233,7 +235,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) ->
         StorageLive(_26);
         StorageLive(_27);
         StorageLive(_28);
-        _28 = &mut (((*(_1.0: &mut {async fn body of b()})) as variant#4).0: {async fn body of a()});
+        _28 = &mut (((*_39) as variant#4).0: {async fn body of a()});
         _27 = &mut (*_28);
         _26 = Pin::<&mut {async fn body of a()}>::new_unchecked(move _27) -> [return: bb17, unwind unreachable];
     }
@@ -275,7 +277,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) ->
         StorageDead(_21);
         StorageDead(_35);
         StorageDead(_36);
-        discriminant((*(_1.0: &mut {async fn body of b()}))) = 4;
+        discriminant((*_39)) = 4;
         return;
     }
 
@@ -288,7 +290,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) ->
         StorageDead(_28);
         StorageDead(_25);
         StorageDead(_24);
-        drop((((*(_1.0: &mut {async fn body of b()})) as variant#4).0: {async fn body of a()})) -> [return: bb23, unwind unreachable];
+        drop((((*_39) as variant#4).0: {async fn body of a()})) -> [return: bb23, unwind unreachable];
     }
 
     bb22: {
@@ -311,7 +313,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) ->
 
     bb25: {
         _0 = Poll::<()>::Ready(move _37);
-        discriminant((*(_1.0: &mut {async fn body of b()}))) = 1;
+        discriminant((*_39)) = 1;
         return;
     }
 
diff --git a/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir b/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir
index cd776ad7170a..b61215dc28cb 100644
--- a/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir
+++ b/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir
@@ -23,7 +23,7 @@
 } */
 
 fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> {
-    debug arg => (((*(_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18})) as variant#4).0: std::string::String);
+    debug arg => (((*_18) as variant#4).0: std::string::String);
     let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>;
     let _3: std::string::String;
     let mut _4: (&str, std::string::String, &std::panic::Location<'_>);
@@ -41,13 +41,6 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2
     let mut _16: ();
     let mut _17: u32;
     let mut _18: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18};
-    let mut _19: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18};
-    let mut _20: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18};
-    let mut _21: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18};
-    let mut _22: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18};
-    let mut _23: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18};
-    let mut _24: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18};
-    let mut _25: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18};
 
     bb0: {
         _18 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18});
@@ -56,14 +49,12 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2
     }
 
     bb1: {
-        _19 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18});
-        (((*_19) as variant#4).0: std::string::String) = move _2;
+        (((*_18) as variant#4).0: std::string::String) = move _2;
         StorageLive(_3);
         StorageLive(_4);
         StorageLive(_5);
         StorageLive(_6);
-        _20 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18});
-        _6 = &(((*_20) as variant#4).0: std::string::String);
+        _6 = &(((*_18) as variant#4).0: std::string::String);
         _5 = ::clone(move _6) -> [return: bb2, unwind unreachable];
     }
 
@@ -84,8 +75,7 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2
         _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4);
         StorageDead(_3);
         StorageDead(_4);
-        _21 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18});
-        discriminant((*_21)) = 3;
+        discriminant((*_18)) = 3;
         return;
     }
 
@@ -108,8 +98,7 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2
         _10 = &(*_11);
         StorageLive(_12);
         StorageLive(_13);
-        _22 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18});
-        _13 = &(((*_22) as variant#4).0: std::string::String);
+        _13 = &(((*_18) as variant#4).0: std::string::String);
         _12 = ::clone(move _13) -> [return: bb8, unwind unreachable];
     }
 
@@ -135,8 +124,7 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2
         StorageDead(_9);
         StorageDead(_11);
         StorageDead(_15);
-        _23 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18});
-        discriminant((*_23)) = 4;
+        discriminant((*_18)) = 4;
         return;
     }
 
@@ -154,8 +142,7 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2
         StorageDead(_11);
         StorageDead(_8);
         _16 = const ();
-        _24 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18});
-        drop((((*_24) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable];
+        drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable];
     }
 
     bb14: {
@@ -164,8 +151,7 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2
 
     bb15: {
         _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16);
-        _25 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18});
-        discriminant((*_25)) = 1;
+        discriminant((*_18)) = 1;
         return;
     }
 
diff --git a/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir b/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir
index 981bc21dc878..aac028a9e6c0 100644
--- a/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir
+++ b/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir
@@ -23,7 +23,7 @@
 } */
 
 fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> {
-    debug arg => (((*(_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18})) as variant#4).0: std::string::String);
+    debug arg => (((*_18) as variant#4).0: std::string::String);
     let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>;
     let _3: std::string::String;
     let mut _4: (&str, std::string::String, &std::panic::Location<'_>);
@@ -41,13 +41,6 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2
     let mut _16: ();
     let mut _17: u32;
     let mut _18: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18};
-    let mut _19: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18};
-    let mut _20: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18};
-    let mut _21: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18};
-    let mut _22: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18};
-    let mut _23: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18};
-    let mut _24: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18};
-    let mut _25: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18};
 
     bb0: {
         _18 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18});
@@ -56,14 +49,12 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2
     }
 
     bb1: {
-        _19 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18});
-        (((*_19) as variant#4).0: std::string::String) = move _2;
+        (((*_18) as variant#4).0: std::string::String) = move _2;
         StorageLive(_3);
         StorageLive(_4);
         StorageLive(_5);
         StorageLive(_6);
-        _20 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18});
-        _6 = &(((*_20) as variant#4).0: std::string::String);
+        _6 = &(((*_18) as variant#4).0: std::string::String);
         _5 = ::clone(move _6) -> [return: bb2, unwind unreachable];
     }
 
@@ -84,8 +75,7 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2
         _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4);
         StorageDead(_3);
         StorageDead(_4);
-        _21 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18});
-        discriminant((*_21)) = 3;
+        discriminant((*_18)) = 3;
         return;
     }
 
@@ -108,8 +98,7 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2
         _10 = &(*_11);
         StorageLive(_12);
         StorageLive(_13);
-        _22 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18});
-        _13 = &(((*_22) as variant#4).0: std::string::String);
+        _13 = &(((*_18) as variant#4).0: std::string::String);
         _12 = ::clone(move _13) -> [return: bb8, unwind unreachable];
     }
 
@@ -135,8 +124,7 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2
         StorageDead(_9);
         StorageDead(_11);
         StorageDead(_15);
-        _23 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18});
-        discriminant((*_23)) = 4;
+        discriminant((*_18)) = 4;
         return;
     }
 
@@ -154,8 +142,7 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2
         StorageDead(_11);
         StorageDead(_8);
         _16 = const ();
-        _24 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18});
-        drop((((*_24) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable];
+        drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable];
     }
 
     bb14: {
@@ -164,8 +151,7 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2
 
     bb15: {
         _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16);
-        _25 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18});
-        discriminant((*_25)) = 1;
+        discriminant((*_18)) = 1;
         return;
     }
 
diff --git a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir
index 9905cc3e00f9..222c7144ef07 100644
--- a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir
+++ b/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir
@@ -32,18 +32,20 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13}
     let _8: ();
     let mut _9: ();
     let mut _10: u32;
+    let mut _11: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13};
     scope 1 {
-        debug _d => (((*(_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13})) as variant#3).0: HasDrop);
+        debug _d => (((*_11) as variant#3).0: HasDrop);
     }
 
     bb0: {
-        _10 = discriminant((*(_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13})));
+        _11 = copy (_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13});
+        _10 = discriminant((*_11));
         switchInt(move _10) -> [0: bb1, 3: bb5, otherwise: bb6];
     }
 
     bb1: {
         nop;
-        (((*(_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13})) as variant#3).0: HasDrop) = HasDrop;
+        (((*_11) as variant#3).0: HasDrop) = HasDrop;
         StorageLive(_4);
         goto -> bb2;
     }
@@ -56,7 +58,7 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13}
         StorageDead(_4);
         StorageDead(_6);
         StorageDead(_7);
-        discriminant((*(_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13}))) = 3;
+        discriminant((*_11)) = 3;
         return;
     }
 
diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff
index 4ca8dbbc76f0..4c1b25c786ef 100644
--- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff
+++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff
@@ -41,13 +41,6 @@
 +                 let mut _30: ();
 +                 let mut _31: u32;
 +                 let mut _32: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _33: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _34: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _35: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _36: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _37: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _38: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _39: &mut {async fn body of ActionPermit<'_, T>::perform()};
 +                 scope 7 {
 +                     let mut _15: std::future::Ready<()>;
 +                     scope 8 {
@@ -57,37 +50,37 @@
 +                         scope 12 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) {
 +                         }
 +                         scope 13 (inlined  as Future>::poll) {
-+                             let mut _41: ();
-+                             let mut _42: std::option::Option<()>;
-+                             let mut _43: &mut std::option::Option<()>;
-+                             let mut _44: &mut std::future::Ready<()>;
-+                             let mut _45: &mut std::pin::Pin<&mut std::future::Ready<()>>;
++                             let mut _34: ();
++                             let mut _35: std::option::Option<()>;
++                             let mut _36: &mut std::option::Option<()>;
++                             let mut _37: &mut std::future::Ready<()>;
++                             let mut _38: &mut std::pin::Pin<&mut std::future::Ready<()>>;
 +                             scope 14 (inlined > as DerefMut>::deref_mut) {
-+                                 let mut _46: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>;
-+                                 let mut _47: *mut std::pin::Pin<&mut std::future::Ready<()>>;
++                                 let mut _39: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>;
++                                 let mut _40: *mut std::pin::Pin<&mut std::future::Ready<()>>;
 +                                 scope 15 (inlined > as pin::helper::PinDerefMutHelper>::deref_mut) {
-+                                     let mut _48: &mut &mut std::future::Ready<()>;
++                                     let mut _41: &mut &mut std::future::Ready<()>;
 +                                     scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) {
 +                                     }
 +                                 }
 +                             }
 +                             scope 17 (inlined Option::<()>::take) {
-+                                 let mut _49: std::option::Option<()>;
++                                 let mut _42: std::option::Option<()>;
 +                                 scope 18 (inlined std::mem::replace::>) {
 +                                     scope 19 {
 +                                     }
 +                                 }
 +                             }
 +                             scope 20 (inlined #[track_caller] Option::<()>::expect) {
-+                                 let mut _50: isize;
-+                                 let mut _51: !;
++                                 let mut _43: isize;
++                                 let mut _44: !;
 +                                 scope 21 {
 +                                 }
 +                             }
 +                         }
 +                     }
 +                     scope 10 (inlined ready::<()>) {
-+                         let mut _40: std::option::Option<()>;
++                         let mut _33: std::option::Option<()>;
 +                     }
 +                     scope 11 (inlined  as IntoFuture>::into_future) {
 +                     }
@@ -132,13 +125,6 @@
 +         StorageLive(_30);
 +         StorageLive(_31);
 +         StorageLive(_32);
-+         StorageLive(_33);
-+         StorageLive(_34);
-+         StorageLive(_35);
-+         StorageLive(_36);
-+         StorageLive(_37);
-+         StorageLive(_38);
-+         StorageLive(_39);
 +         _32 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
 +         _31 = discriminant((*_32));
 +         switchInt(move _31) -> [0: bb3, 1: bb10, 3: bb9, otherwise: bb5];
@@ -151,13 +137,6 @@
 +     }
 + 
 +     bb2: {
-+         StorageDead(_39);
-+         StorageDead(_38);
-+         StorageDead(_37);
-+         StorageDead(_36);
-+         StorageDead(_35);
-+         StorageDead(_34);
-+         StorageDead(_33);
 +         StorageDead(_32);
 +         StorageDead(_31);
 +         StorageDead(_30);
@@ -175,22 +154,19 @@
       }
   
 +     bb3: {
-+         _33 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         _34 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         (((*_33) as variant#3).0: ActionPermit<'_, T>) = move ((*_34).0: ActionPermit<'_, T>);
++         (((*_32) as variant#3).0: ActionPermit<'_, T>) = move ((*_32).0: ActionPermit<'_, T>);
 +         StorageLive(_12);
 +         StorageLive(_13);
 +         StorageLive(_14);
 +         _14 = ();
-+         StorageLive(_40);
-+         _40 = Option::<()>::Some(copy _14);
-+         _13 = std::future::Ready::<()>(move _40);
-+         StorageDead(_40);
++         StorageLive(_33);
++         _33 = Option::<()>::Some(copy _14);
++         _13 = std::future::Ready::<()>(move _33);
++         StorageDead(_33);
 +         StorageDead(_14);
 +         _12 = move _13;
 +         StorageDead(_13);
-+         _35 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         (((*_35) as variant#3).1: std::future::Ready<()>) = move _12;
++         (((*_32) as variant#3).1: std::future::Ready<()>) = move _12;
 +         goto -> bb4;
 +     }
 + 
@@ -202,8 +178,7 @@
 +         StorageLive(_19);
 +         StorageLive(_20);
 +         StorageLive(_21);
-+         _36 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         _21 = &mut (((*_36) as variant#3).1: std::future::Ready<()>);
++         _21 = &mut (((*_32) as variant#3).1: std::future::Ready<()>);
 +         _20 = &mut (*_21);
 +         _19 = Pin::<&mut std::future::Ready<()>> { pointer: copy _20 };
 +         StorageDead(_20);
@@ -214,24 +189,24 @@
 +         _23 = move _24;
 +         _22 = &mut (*_23);
 +         StorageDead(_24);
++         StorageLive(_37);
++         StorageLive(_39);
 +         StorageLive(_44);
-+         StorageLive(_46);
-+         StorageLive(_51);
-+         StorageLive(_41);
++         StorageLive(_34);
++         StorageLive(_35);
++         StorageLive(_40);
++         _40 = &raw mut _19;
++         _39 = copy _40 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr);
++         StorageDead(_40);
++         _37 = copy ((*_39).0: &mut std::future::Ready<()>);
 +         StorageLive(_42);
-+         StorageLive(_47);
-+         _47 = &raw mut _19;
-+         _46 = copy _47 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr);
-+         StorageDead(_47);
-+         _44 = copy ((*_46).0: &mut std::future::Ready<()>);
-+         StorageLive(_49);
-+         _49 = Option::<()>::None;
-+         _42 = copy ((*_44).0: std::option::Option<()>);
-+         ((*_44).0: std::option::Option<()>) = copy _49;
-+         StorageDead(_49);
-+         StorageLive(_50);
-+         _50 = discriminant(_42);
-+         switchInt(move _50) -> [0: bb11, 1: bb12, otherwise: bb5];
++         _42 = Option::<()>::None;
++         _35 = copy ((*_37).0: std::option::Option<()>);
++         ((*_37).0: std::option::Option<()>) = copy _42;
++         StorageDead(_42);
++         StorageLive(_43);
++         _43 = discriminant(_35);
++         switchInt(move _43) -> [0: bb11, 1: bb12, otherwise: bb5];
       }
 + 
 +     bb5: {
@@ -251,8 +226,7 @@
 +         StorageDead(_12);
 +         StorageDead(_28);
 +         StorageDead(_29);
-+         _37 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         discriminant((*_37)) = 3;
++         discriminant((*_32)) = 3;
 +         goto -> bb2;
 +     }
 + 
@@ -266,14 +240,12 @@
 +         StorageDead(_18);
 +         StorageDead(_17);
 +         StorageDead(_12);
-+         _38 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         drop((((*_38) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb8, unwind unreachable];
++         drop((((*_32) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb8, unwind unreachable];
 +     }
 + 
 +     bb8: {
 +         _7 = Poll::<()>::Ready(move _30);
-+         _39 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         discriminant((*_39)) = 1;
++         discriminant((*_32)) = 1;
 +         goto -> bb2;
 +     }
 + 
@@ -294,18 +266,18 @@
 +     }
 + 
 +     bb11: {
-+         _51 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable;
++         _44 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable;
 +     }
 + 
 +     bb12: {
-+         _41 = move ((_42 as Some).0: ());
-+         StorageDead(_50);
-+         StorageDead(_42);
-+         _18 = Poll::<()>::Ready(move _41);
-+         StorageDead(_41);
-+         StorageDead(_51);
-+         StorageDead(_46);
++         _34 = move ((_35 as Some).0: ());
++         StorageDead(_43);
++         StorageDead(_35);
++         _18 = Poll::<()>::Ready(move _34);
++         StorageDead(_34);
 +         StorageDead(_44);
++         StorageDead(_39);
++         StorageDead(_37);
 +         StorageDead(_22);
 +         StorageDead(_19);
 +         _25 = discriminant(_18);
diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff
index 6ce8693007b9..59417ce64651 100644
--- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff
+++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff
@@ -41,15 +41,6 @@
 +                 let mut _30: ();
 +                 let mut _31: u32;
 +                 let mut _32: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _33: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _34: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _35: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _36: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _37: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _38: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _39: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _40: &mut {async fn body of ActionPermit<'_, T>::perform()};
-+                 let mut _41: &mut {async fn body of ActionPermit<'_, T>::perform()};
 +                 scope 7 {
 +                     let mut _15: std::future::Ready<()>;
 +                     scope 8 {
@@ -59,37 +50,37 @@
 +                         scope 12 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) {
 +                         }
 +                         scope 13 (inlined  as Future>::poll) {
-+                             let mut _43: ();
-+                             let mut _44: std::option::Option<()>;
-+                             let mut _45: &mut std::option::Option<()>;
-+                             let mut _46: &mut std::future::Ready<()>;
-+                             let mut _47: &mut std::pin::Pin<&mut std::future::Ready<()>>;
++                             let mut _34: ();
++                             let mut _35: std::option::Option<()>;
++                             let mut _36: &mut std::option::Option<()>;
++                             let mut _37: &mut std::future::Ready<()>;
++                             let mut _38: &mut std::pin::Pin<&mut std::future::Ready<()>>;
 +                             scope 14 (inlined > as DerefMut>::deref_mut) {
-+                                 let mut _48: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>;
-+                                 let mut _49: *mut std::pin::Pin<&mut std::future::Ready<()>>;
++                                 let mut _39: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>;
++                                 let mut _40: *mut std::pin::Pin<&mut std::future::Ready<()>>;
 +                                 scope 15 (inlined > as pin::helper::PinDerefMutHelper>::deref_mut) {
-+                                     let mut _50: &mut &mut std::future::Ready<()>;
++                                     let mut _41: &mut &mut std::future::Ready<()>;
 +                                     scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) {
 +                                     }
 +                                 }
 +                             }
 +                             scope 17 (inlined Option::<()>::take) {
-+                                 let mut _51: std::option::Option<()>;
++                                 let mut _42: std::option::Option<()>;
 +                                 scope 18 (inlined std::mem::replace::>) {
 +                                     scope 19 {
 +                                     }
 +                                 }
 +                             }
 +                             scope 20 (inlined #[track_caller] Option::<()>::expect) {
-+                                 let mut _52: isize;
-+                                 let mut _53: !;
++                                 let mut _43: isize;
++                                 let mut _44: !;
 +                                 scope 21 {
 +                                 }
 +                             }
 +                         }
 +                     }
 +                     scope 10 (inlined ready::<()>) {
-+                         let mut _42: std::option::Option<()>;
++                         let mut _33: std::option::Option<()>;
 +                     }
 +                     scope 11 (inlined  as IntoFuture>::into_future) {
 +                     }
@@ -134,15 +125,6 @@
 +         StorageLive(_30);
 +         StorageLive(_31);
 +         StorageLive(_32);
-+         StorageLive(_33);
-+         StorageLive(_34);
-+         StorageLive(_35);
-+         StorageLive(_36);
-+         StorageLive(_37);
-+         StorageLive(_38);
-+         StorageLive(_39);
-+         StorageLive(_40);
-+         StorageLive(_41);
 +         _32 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
 +         _31 = discriminant((*_32));
 +         switchInt(move _31) -> [0: bb5, 1: bb15, 2: bb14, 3: bb13, otherwise: bb7];
@@ -163,15 +145,6 @@
 +     }
 + 
 +     bb4: {
-+         StorageDead(_41);
-+         StorageDead(_40);
-+         StorageDead(_39);
-+         StorageDead(_38);
-+         StorageDead(_37);
-+         StorageDead(_36);
-+         StorageDead(_35);
-+         StorageDead(_34);
-+         StorageDead(_33);
 +         StorageDead(_32);
 +         StorageDead(_31);
 +         StorageDead(_30);
@@ -192,22 +165,19 @@
 -         StorageDead(_2);
 -         return;
 +     bb5: {
-+         _33 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         _34 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         (((*_33) as variant#3).0: ActionPermit<'_, T>) = move ((*_34).0: ActionPermit<'_, T>);
++         (((*_32) as variant#3).0: ActionPermit<'_, T>) = move ((*_32).0: ActionPermit<'_, T>);
 +         StorageLive(_12);
 +         StorageLive(_13);
 +         StorageLive(_14);
 +         _14 = ();
-+         StorageLive(_42);
-+         _42 = Option::<()>::Some(copy _14);
-+         _13 = std::future::Ready::<()>(move _42);
-+         StorageDead(_42);
++         StorageLive(_33);
++         _33 = Option::<()>::Some(copy _14);
++         _13 = std::future::Ready::<()>(move _33);
++         StorageDead(_33);
 +         StorageDead(_14);
 +         _12 = move _13;
 +         StorageDead(_13);
-+         _35 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         (((*_35) as variant#3).1: std::future::Ready<()>) = move _12;
++         (((*_32) as variant#3).1: std::future::Ready<()>) = move _12;
 +         goto -> bb6;
       }
   
@@ -219,8 +189,7 @@
 +         StorageLive(_19);
 +         StorageLive(_20);
 +         StorageLive(_21);
-+         _36 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         _21 = &mut (((*_36) as variant#3).1: std::future::Ready<()>);
++         _21 = &mut (((*_32) as variant#3).1: std::future::Ready<()>);
 +         _20 = &mut (*_21);
 +         _19 = Pin::<&mut std::future::Ready<()>> { pointer: copy _20 };
 +         StorageDead(_20);
@@ -231,24 +200,24 @@
 +         _23 = move _24;
 +         _22 = &mut (*_23);
 +         StorageDead(_24);
-+         StorageLive(_46);
-+         StorageLive(_48);
-+         StorageLive(_53);
-+         StorageLive(_43);
++         StorageLive(_37);
++         StorageLive(_39);
 +         StorageLive(_44);
-+         StorageLive(_49);
-+         _49 = &raw mut _19;
-+         _48 = copy _49 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr);
-+         StorageDead(_49);
-+         _46 = copy ((*_48).0: &mut std::future::Ready<()>);
-+         StorageLive(_51);
-+         _51 = Option::<()>::None;
-+         _44 = copy ((*_46).0: std::option::Option<()>);
-+         ((*_46).0: std::option::Option<()>) = copy _51;
-+         StorageDead(_51);
-+         StorageLive(_52);
-+         _52 = discriminant(_44);
-+         switchInt(move _52) -> [0: bb16, 1: bb17, otherwise: bb7];
++         StorageLive(_34);
++         StorageLive(_35);
++         StorageLive(_40);
++         _40 = &raw mut _19;
++         _39 = copy _40 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr);
++         StorageDead(_40);
++         _37 = copy ((*_39).0: &mut std::future::Ready<()>);
++         StorageLive(_42);
++         _42 = Option::<()>::None;
++         _35 = copy ((*_37).0: std::option::Option<()>);
++         ((*_37).0: std::option::Option<()>) = copy _42;
++         StorageDead(_42);
++         StorageLive(_43);
++         _43 = discriminant(_35);
++         switchInt(move _43) -> [0: bb16, 1: bb17, otherwise: bb7];
       }
   
 -     bb6 (cleanup): {
@@ -270,8 +239,7 @@
 +         StorageDead(_12);
 +         StorageDead(_28);
 +         StorageDead(_29);
-+         _37 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         discriminant((*_37)) = 3;
++         discriminant((*_32)) = 3;
 +         goto -> bb4;
 +     }
 + 
@@ -285,14 +253,12 @@
 +         StorageDead(_18);
 +         StorageDead(_17);
 +         StorageDead(_12);
-+         _38 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         drop((((*_38) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb10, unwind: bb12];
++         drop((((*_32) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb10, unwind: bb12];
 +     }
 + 
 +     bb10: {
 +         _7 = Poll::<()>::Ready(move _30);
-+         _39 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         discriminant((*_39)) = 1;
++         discriminant((*_32)) = 1;
 +         goto -> bb4;
 +     }
 + 
@@ -304,13 +270,11 @@
 +         StorageDead(_18);
 +         StorageDead(_17);
 +         StorageDead(_12);
-+         _40 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         drop((((*_40) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb12, unwind terminate(cleanup)];
++         drop((((*_32) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb12, unwind terminate(cleanup)];
 +     }
 + 
 +     bb12 (cleanup): {
-+         _41 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()});
-+         discriminant((*_41)) = 2;
++         discriminant((*_32)) = 2;
 +         goto -> bb2;
 +     }
 + 
@@ -335,18 +299,18 @@
 +     }
 + 
 +     bb16: {
-+         _53 = option::expect_failed(const "`Ready` polled after completion") -> bb11;
++         _44 = option::expect_failed(const "`Ready` polled after completion") -> bb11;
 +     }
 + 
 +     bb17: {
-+         _43 = move ((_44 as Some).0: ());
-+         StorageDead(_52);
-+         StorageDead(_44);
-+         _18 = Poll::<()>::Ready(move _43);
++         _34 = move ((_35 as Some).0: ());
 +         StorageDead(_43);
-+         StorageDead(_53);
-+         StorageDead(_48);
-+         StorageDead(_46);
++         StorageDead(_35);
++         _18 = Poll::<()>::Ready(move _34);
++         StorageDead(_34);
++         StorageDead(_44);
++         StorageDead(_39);
++         StorageDead(_37);
 +         StorageDead(_22);
 +         StorageDead(_19);
 +         _25 = discriminant(_18);
diff --git a/tests/ui/rustc_public-ir-print/async-closure.stdout b/tests/ui/rustc_public-ir-print/async-closure.stdout
index 550f0512569d..3ec816b657f4 100644
--- a/tests/ui/rustc_public-ir-print/async-closure.stdout
+++ b/tests/ui/rustc_public-ir-print/async-closure.stdout
@@ -42,10 +42,8 @@ fn foo::{closure#0}::{closure#0}(_1: Pin<&mut {async closure body@$DIR/async-clo
     let mut _5: ();
     let mut _6: u32;
     let mut _7: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6};
-    let mut _8: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6};
-    let mut _9: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6};
     debug _task_context => _2;
-    debug y => (*((*(_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})).0: &i32));
+    debug y => (*((*_7).0: &i32));
     debug y => _3;
     bb0: {
         _7 = (_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6});
@@ -54,14 +52,12 @@ fn foo::{closure#0}::{closure#0}(_1: Pin<&mut {async closure body@$DIR/async-clo
     }
     bb1: {
         StorageLive(_3);
-        _8 = (_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6});
-        _4 = ((*_8).0: &i32);
+        _4 = ((*_7).0: &i32);
         _3 = (*_4);
         _5 = ();
         StorageDead(_3);
         _0 = std::task::Poll::Ready(move _5);
-        _9 = (_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6});
-        discriminant((*_9)) = 1;
+        discriminant((*_7)) = 1;
         return;
     }
     bb2: {
@@ -78,10 +74,8 @@ fn foo::{closure#0}::{synthetic#0}(_1: Pin<&mut {async closure body@$DIR/async-c
     let mut _5: ();
     let mut _6: u32;
     let mut _7: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6};
-    let mut _8: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6};
-    let mut _9: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6};
     debug _task_context => _2;
-    debug y => (*((*(_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})).0: &i32));
+    debug y => (*((*_7).0: &i32));
     debug y => _3;
     bb0: {
         _7 = (_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6});
@@ -90,14 +84,12 @@ fn foo::{closure#0}::{synthetic#0}(_1: Pin<&mut {async closure body@$DIR/async-c
     }
     bb1: {
         StorageLive(_3);
-        _8 = (_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6});
-        _4 = ((*_8).0: &i32);
+        _4 = ((*_7).0: &i32);
         _3 = (*_4);
         _5 = ();
         StorageDead(_3);
         _0 = std::task::Poll::Ready(move _5);
-        _9 = (_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6});
-        discriminant((*_9)) = 1;
+        discriminant((*_7)) = 1;
         return;
     }
     bb2: {

From ed85b96bb5c354b04b15323fda81efa8a17701ce Mon Sep 17 00:00:00 2001
From: Camille Gillot 
Date: Wed, 8 Oct 2025 22:45:09 +0000
Subject: [PATCH 092/170] Insert assignment after Retag.

---
 compiler/rustc_mir_transform/src/coroutine.rs | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index a5c2f8b53cf6..2ccd8178e667 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -546,8 +546,15 @@ fn make_coroutine_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body
     let source_info = SourceInfo::outermost(body.span);
     let pin_field = tcx.mk_place_field(SELF_ARG.into(), FieldIdx::ZERO, ref_coroutine_ty);
 
-    body.basic_blocks_mut()[START_BLOCK].statements.insert(
-        0,
+    let statements = &mut body.basic_blocks.as_mut_preserves_cfg()[START_BLOCK].statements;
+    // Miri requires retags to be the very first thing in the body.
+    // We insert this assignment just after.
+    let insert_point = statements
+        .iter()
+        .position(|stmt| !matches!(stmt.kind, StatementKind::Retag(..)))
+        .unwrap_or(statements.len());
+    statements.insert(
+        insert_point,
         Statement::new(
             source_info,
             StatementKind::Assign(Box::new((

From b67453fccd7919af0e59b2134e6b3a5e26e76378 Mon Sep 17 00:00:00 2001
From: Camille GILLOT 
Date: Sat, 21 Jun 2025 14:44:59 +0000
Subject: [PATCH 093/170] Stop passing resolver disambiguator state to AST
 lowering.

---
 compiler/rustc_ast_lowering/src/expr.rs           |  9 ++++++++-
 compiler/rustc_ast_lowering/src/lib.rs            | 15 +++++++++++++--
 compiler/rustc_ast_lowering/src/pat.rs            |  4 +++-
 compiler/rustc_hir/src/definitions.rs             | 13 ++++++++++++-
 compiler/rustc_middle/src/ty/mod.rs               |  3 ---
 compiler/rustc_middle/src/ty/print/pretty.rs      |  1 +
 compiler/rustc_resolve/src/lib.rs                 |  1 -
 .../src/cfi/typeid/itanium_cxx_abi/encode.rs      |  4 +++-
 compiler/rustc_symbol_mangling/src/v0.rs          |  4 +++-
 ...emit-type-metadata-id-itanium-cxx-abi-paths.rs |  6 +++---
 10 files changed, 46 insertions(+), 14 deletions(-)

diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 4a9b9f544b53..c443b1e3a03c 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -7,6 +7,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir as hir;
 use rustc_hir::attrs::AttributeKind;
 use rustc_hir::def::{DefKind, Res};
+use rustc_hir::definitions::DefPathData;
 use rustc_hir::{HirId, Target, find_attr};
 use rustc_middle::span_bug;
 use rustc_middle::ty::TyCtxt;
@@ -461,7 +462,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
         for (idx, arg) in args.iter().cloned().enumerate() {
             if legacy_args_idx.contains(&idx) {
                 let node_id = self.next_node_id();
-                self.create_def(node_id, None, DefKind::AnonConst, f.span);
+                self.create_def(
+                    node_id,
+                    None,
+                    DefKind::AnonConst,
+                    DefPathData::LateAnonConst,
+                    f.span,
+                );
                 let mut visitor = WillCreateDefIdsVisitor {};
                 let const_value = if let ControlFlow::Break(span) = visitor.visit_expr(&arg) {
                     Box::new(Expr {
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index d959657e7fe5..be9db9257356 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -51,6 +51,7 @@ use rustc_data_structures::tagged_ptr::TaggedRef;
 use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle};
 use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
+use rustc_hir::definitions::{DefPathData, DisambiguatorState};
 use rustc_hir::lints::DelayedLint;
 use rustc_hir::{
     self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LifetimeSource,
@@ -93,6 +94,7 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 struct LoweringContext<'a, 'hir> {
     tcx: TyCtxt<'hir>,
     resolver: &'a mut ResolverAstLowering,
+    disambiguator: DisambiguatorState,
 
     /// Used to allocate HIR nodes.
     arena: &'hir hir::Arena<'hir>,
@@ -155,6 +157,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             // Pseudo-globals.
             tcx,
             resolver,
+            disambiguator: DisambiguatorState::new(),
             arena: tcx.hir_arena,
 
             // HirId handling.
@@ -546,6 +549,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         node_id: ast::NodeId,
         name: Option,
         def_kind: DefKind,
+        def_path_data: DefPathData,
         span: Span,
     ) -> LocalDefId {
         let parent = self.current_hir_id_owner.def_id;
@@ -561,7 +565,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let def_id = self
             .tcx
             .at(span)
-            .create_def(parent, name, def_kind, None, &mut self.resolver.disambiguator)
+            .create_def(parent, name, def_kind, Some(def_path_data), &mut self.disambiguator)
             .def_id();
 
         debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id);
@@ -846,6 +850,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     param,
                     Some(kw::UnderscoreLifetime),
                     DefKind::LifetimeParam,
+                    DefPathData::DesugaredAnonymousLifetime,
                     ident.span,
                 );
                 debug!(?_def_id);
@@ -2290,7 +2295,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             // We're lowering a const argument that was originally thought to be a type argument,
             // so the def collector didn't create the def ahead of time. That's why we have to do
             // it here.
-            let def_id = self.create_def(node_id, None, DefKind::AnonConst, span);
+            let def_id = self.create_def(
+                node_id,
+                None,
+                DefKind::AnonConst,
+                DefPathData::LateAnonConst,
+                span,
+            );
             let hir_id = self.lower_node_id(node_id);
 
             let path_expr = Expr {
diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs
index 7e4fa840f252..815338c84fa6 100644
--- a/compiler/rustc_ast_lowering/src/pat.rs
+++ b/compiler/rustc_ast_lowering/src/pat.rs
@@ -3,6 +3,7 @@ use std::sync::Arc;
 use rustc_ast::*;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir::def::{DefKind, Res};
+use rustc_hir::definitions::DefPathData;
 use rustc_hir::{self as hir, LangItem, Target};
 use rustc_middle::span_bug;
 use rustc_span::source_map::{Spanned, respan};
@@ -527,7 +528,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // We're generating a range end that didn't exist in the AST,
         // so the def collector didn't create the def ahead of time. That's why we have to do
         // it here.
-        let def_id = self.create_def(node_id, None, DefKind::AnonConst, span);
+        let def_id =
+            self.create_def(node_id, None, DefKind::AnonConst, DefPathData::LateAnonConst, span);
         let hir_id = self.lower_node_id(node_id);
 
         let unstable_span = self.mark_span_with_reason(
diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs
index 698406d53a43..07d5bcdd6ee9 100644
--- a/compiler/rustc_hir/src/definitions.rs
+++ b/compiler/rustc_hir/src/definitions.rs
@@ -302,6 +302,10 @@ pub enum DefPathData {
     Ctor,
     /// A constant expression (see `{ast,hir}::AnonConst`).
     AnonConst,
+    /// A constant expression created during AST->HIR lowering..
+    LateAnonConst,
+    /// A fresh anonymous lifetime created by desugaring elided lifetimes.
+    DesugaredAnonymousLifetime,
     /// An existential `impl Trait` type node.
     /// Argument position `impl Trait` have a `TypeNs` with their pretty-printed name.
     OpaqueTy,
@@ -454,6 +458,8 @@ impl DefPathData {
             TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name)
             | OpaqueLifetime(name) => Some(name),
 
+            DesugaredAnonymousLifetime => Some(kw::UnderscoreLifetime),
+
             Impl
             | ForeignMod
             | CrateRoot
@@ -462,6 +468,7 @@ impl DefPathData {
             | Closure
             | Ctor
             | AnonConst
+            | LateAnonConst
             | OpaqueTy
             | AnonAssocTy(..)
             | SyntheticCoroutineBody
@@ -475,6 +482,8 @@ impl DefPathData {
             TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) | AnonAssocTy(name)
             | OpaqueLifetime(name) => Some(name),
 
+            DesugaredAnonymousLifetime => Some(kw::UnderscoreLifetime),
+
             Impl
             | ForeignMod
             | CrateRoot
@@ -483,6 +492,7 @@ impl DefPathData {
             | Closure
             | Ctor
             | AnonConst
+            | LateAnonConst
             | OpaqueTy
             | SyntheticCoroutineBody
             | NestedStatic => None,
@@ -502,7 +512,8 @@ impl DefPathData {
             GlobalAsm => DefPathDataName::Anon { namespace: sym::global_asm },
             Closure => DefPathDataName::Anon { namespace: sym::closure },
             Ctor => DefPathDataName::Anon { namespace: sym::constructor },
-            AnonConst => DefPathDataName::Anon { namespace: sym::constant },
+            AnonConst | LateAnonConst => DefPathDataName::Anon { namespace: sym::constant },
+            DesugaredAnonymousLifetime => DefPathDataName::Named(kw::UnderscoreLifetime),
             OpaqueTy => DefPathDataName::Anon { namespace: sym::opaque },
             AnonAssocTy(..) => DefPathDataName::Anon { namespace: sym::anon_assoc },
             SyntheticCoroutineBody => DefPathDataName::Anon { namespace: sym::synthetic },
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index c3e1defef809..93d0c77c1dae 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -37,7 +37,6 @@ use rustc_errors::{Diag, ErrorGuaranteed, LintBuffer};
 use rustc_hir::attrs::{AttributeKind, StrippedCfgItem};
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res};
 use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap};
-use rustc_hir::definitions::DisambiguatorState;
 use rustc_hir::{LangItem, attrs as attr, find_attr};
 use rustc_index::IndexVec;
 use rustc_index::bit_set::BitMatrix;
@@ -211,8 +210,6 @@ pub struct ResolverAstLowering {
 
     pub node_id_to_def_id: NodeMap,
 
-    pub disambiguator: DisambiguatorState,
-
     pub trait_map: NodeMap>,
     /// List functions and methods for which lifetime elision was successful.
     pub lifetime_elision_allowed: FxHashSet,
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 0af7fe808ef9..06744ae6e242 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -2156,6 +2156,7 @@ fn guess_def_namespace(tcx: TyCtxt<'_>, def_id: DefId) -> Namespace {
 
         DefPathData::ValueNs(..)
         | DefPathData::AnonConst
+        | DefPathData::LateAnonConst
         | DefPathData::Closure
         | DefPathData::Ctor => Namespace::ValueNS,
 
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 1636605b234f..f6a4f59cb339 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -1791,7 +1791,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                 .into_items()
                 .map(|(k, f)| (k, f.key()))
                 .collect(),
-            disambiguator: self.disambiguator,
             trait_map: self.trait_map,
             lifetime_elision_allowed: self.lifetime_elision_allowed,
             lint_buffer: Steal::new(self.lint_buffer),
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
index 621cc0fb3ef1..60b3b42989b7 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
@@ -712,7 +712,8 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
             hir::definitions::DefPathData::ValueNs(..) => "v",
             hir::definitions::DefPathData::Closure => "C",
             hir::definitions::DefPathData::Ctor => "c",
-            hir::definitions::DefPathData::AnonConst => "k",
+            hir::definitions::DefPathData::AnonConst => "K",
+            hir::definitions::DefPathData::LateAnonConst => "k",
             hir::definitions::DefPathData::OpaqueTy => "i",
             hir::definitions::DefPathData::SyntheticCoroutineBody => "s",
             hir::definitions::DefPathData::NestedStatic => "n",
@@ -722,6 +723,7 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
             | hir::definitions::DefPathData::MacroNs(..)
             | hir::definitions::DefPathData::OpaqueLifetime(..)
             | hir::definitions::DefPathData::LifetimeNs(..)
+            | hir::definitions::DefPathData::DesugaredAnonymousLifetime
             | hir::definitions::DefPathData::AnonAssocTy(..) => {
                 bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data);
             }
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 2c71b22c4a2b..e40fc04d7664 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -877,7 +877,8 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> {
             DefPathData::ValueNs(_) => 'v',
             DefPathData::Closure => 'C',
             DefPathData::Ctor => 'c',
-            DefPathData::AnonConst => 'k',
+            DefPathData::AnonConst => 'K',
+            DefPathData::LateAnonConst => 'k',
             DefPathData::OpaqueTy => 'i',
             DefPathData::SyntheticCoroutineBody => 's',
             DefPathData::NestedStatic => 'n',
@@ -889,6 +890,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> {
             | DefPathData::Impl
             | DefPathData::MacroNs(_)
             | DefPathData::LifetimeNs(_)
+            | DefPathData::DesugaredAnonymousLifetime
             | DefPathData::OpaqueLifetime(_)
             | DefPathData::AnonAssocTy(..) => {
                 bug!("symbol_names: unexpected DefPathData: {:?}", disambiguated_data.data)
diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs
index f5846713bd53..4ce9c57070a7 100644
--- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs
+++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs
@@ -78,9 +78,9 @@ pub fn foo12(_: &Type4, _: &Type4, _: &Type4) {}
 // CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooEE"}
 // CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_E"}
 // CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_S0_E"}
-// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooEE"}
-// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_E"}
-// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_S0_E"}
+// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNKNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooEE"}
+// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNKNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_E"}
+// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNKNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_S0_E"}
 // CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barEE"}
 // CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_E"}
 // CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_S0_E"}

From 1a8055d77789ad04c688b8800394d8995d83f3e2 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Fri, 24 Oct 2025 13:32:54 +0800
Subject: [PATCH 094/170] minor: fix track_caller for ide-complpetion test
 utils

---
 .../rust-analyzer/crates/ide-completion/src/tests.rs     | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
index b32a89545726..83606d21f522 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
@@ -246,11 +246,10 @@ pub(crate) fn check_edit_with_config(
     let (db, position) = position(ra_fixture_before);
     let completions: Vec =
         hir::attach_db(&db, || crate::completions(&db, &config, position, None).unwrap());
-    let (completion,) = completions
-        .iter()
-        .filter(|it| it.lookup() == what)
-        .collect_tuple()
-        .unwrap_or_else(|| panic!("can't find {what:?} completion in {completions:#?}"));
+    let Some((completion,)) = completions.iter().filter(|it| it.lookup() == what).collect_tuple()
+    else {
+        panic!("can't find {what:?} completion in {completions:#?}")
+    };
     let mut actual = db.file_text(position.file_id).text(&db).to_string();
 
     let mut combined_edit = completion.text_edit.clone();

From 6d2271696a6654458c38dd5130bd8cddcb170dde Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Fri, 24 Oct 2025 18:02:30 +0800
Subject: [PATCH 095/170] minor: Update ide-assist docs for add_braces

---
 .../ide-assists/src/handlers/add_braces.rs    | 16 +++++++++++++++-
 .../crates/ide-assists/src/tests/generated.rs | 19 +++++++++++++++++++
 2 files changed, 34 insertions(+), 1 deletion(-)

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 f5bbe8dda8c5..99ee50fa5848 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
@@ -9,7 +9,7 @@ use crate::{AssistContext, AssistId, Assists};
 
 // Assist: add_braces
 //
-// Adds braces to closure bodies and match arm expressions.
+// Adds braces to closure bodies, match arm expressions and assignment bodies.
 //
 // ```
 // fn foo(n: i32) -> i32 {
@@ -30,6 +30,20 @@ use crate::{AssistContext, AssistId, Assists};
 //     }
 // }
 // ```
+// ---
+// ```
+// fn foo(n: i32) -> i32 {
+//     let x =$0 n + 2;
+// }
+// ```
+// ->
+// ```
+// fn foo(n: i32) -> i32 {
+//     let x = {
+//         n + 2
+//     };
+// }
+// ```
 pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let (expr_type, expr) = get_replacement_node(ctx)?;
 
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 c7ae44124f23..e8582aa19f9f 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
@@ -27,6 +27,25 @@ fn foo(n: i32) -> i32 {
     )
 }
 
+#[test]
+fn doctest_add_braces_1() {
+    check_doc_test(
+        "add_braces",
+        r#####"
+fn foo(n: i32) -> i32 {
+    let x =$0 n + 2;
+}
+"#####,
+        r#####"
+fn foo(n: i32) -> i32 {
+    let x = {
+        n + 2
+    };
+}
+"#####,
+    )
+}
+
 #[test]
 fn doctest_add_explicit_enum_discriminant() {
     check_doc_test(

From 5a8ffa4befdf6ccdf1df10a74422c4d21261046c Mon Sep 17 00:00:00 2001
From: bjorn3 <17426603+bjorn3@users.noreply.github.com>
Date: Thu, 23 Oct 2025 14:45:51 +0000
Subject: [PATCH 096/170] Skip codegen_crate call in check mode

---
 .../rustc_codegen_cranelift/src/driver/aot.rs   | 13 +------------
 .../rustc_codegen_cranelift/src/driver/jit.rs   |  4 +---
 compiler/rustc_codegen_ssa/src/back/write.rs    |  8 +-------
 compiler/rustc_codegen_ssa/src/base.rs          | 11 -----------
 compiler/rustc_interface/src/passes.rs          | 17 ++++++++++++++++-
 compiler/rustc_interface/src/queries.rs         | 10 +++++++++-
 6 files changed, 28 insertions(+), 35 deletions(-)

diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
index 7e77781dc2fc..7bf1efc10653 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
@@ -671,18 +671,7 @@ pub(crate) fn run_aot(tcx: TyCtxt<'_>) -> Box {
     }
     .to_owned();
 
-    let cgus = if tcx.sess.opts.output_types.should_codegen() {
-        tcx.collect_and_partition_mono_items(()).codegen_units
-    } else {
-        // If only `--emit metadata` is used, we shouldn't perform any codegen.
-        // Also `tcx.collect_and_partition_mono_items` may panic in that case.
-        return Box::new(OngoingCodegen {
-            modules: vec![],
-            allocator_module: None,
-            crate_info: CrateInfo::new(tcx, target_cpu),
-            concurrency_limiter: ConcurrencyLimiter::new(0),
-        });
-    };
+    let cgus = tcx.collect_and_partition_mono_items(()).codegen_units;
 
     if tcx.dep_graph.is_fully_enabled() {
         for cgu in cgus {
diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
index b3497503bf06..fec46bf26975 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
@@ -33,9 +33,7 @@ fn create_jit_module(tcx: TyCtxt<'_>) -> (UnwindModule, CodegenCx) {
 }
 
 pub(crate) fn run_jit(tcx: TyCtxt<'_>, jit_args: Vec) -> ! {
-    if !tcx.sess.opts.output_types.should_codegen() {
-        tcx.dcx().fatal("JIT mode doesn't work with `cargo check`");
-    }
+    // FIXME error on check mode or crate types other than bin in CodegenBackend::init()
 
     if !tcx.crate_types().contains(&rustc_session::config::CrateType::Executable) {
         tcx.dcx().fatal("can't jit non-executable crate");
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 7518bbaf16a1..368a2e307bb2 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -1274,13 +1274,7 @@ fn start_executing_work(
         })
         .expect("failed to spawn helper thread");
 
-    let ol =
-        if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
-            // If we know that we won’t be doing codegen, create target machines without optimisation.
-            config::OptLevel::No
-        } else {
-            tcx.backend_optimization_level(())
-        };
+    let ol = tcx.backend_optimization_level(());
     let backend_features = tcx.global_backend_features(());
 
     let remark_dir = if let Some(ref dir) = sess.opts.unstable_opts.remark_dir {
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index c35b05f798ea..f58be0a3b9a3 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -684,17 +684,6 @@ pub fn codegen_crate(
     tcx: TyCtxt<'_>,
     target_cpu: String,
 ) -> OngoingCodegen {
-    // Skip crate items and just output metadata in -Z no-codegen mode.
-    if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
-        let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, None);
-
-        ongoing_codegen.codegen_finished(tcx);
-
-        ongoing_codegen.check_for_errors(tcx.sess);
-
-        return ongoing_codegen;
-    }
-
     if tcx.sess.target.need_explicit_cpu && tcx.sess.opts.cg.target_cpu.is_none() {
         // The target has no default cpu, but none is set explicitly
         tcx.dcx().emit_fatal(errors::CpuRequired);
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index a41d6b858795..3243f2f55ac9 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -8,6 +8,7 @@ use std::{env, fs, iter};
 use rustc_ast as ast;
 use rustc_attr_parsing::{AttributeParser, ShouldEmit};
 use rustc_codegen_ssa::traits::CodegenBackend;
+use rustc_codegen_ssa::{CodegenResults, CrateInfo};
 use rustc_data_structures::jobserver::Proxy;
 use rustc_data_structures::steal::Steal;
 use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal};
@@ -1236,7 +1237,21 @@ pub(crate) fn start_codegen<'tcx>(
 
     let metadata = rustc_metadata::fs::encode_and_write_metadata(tcx);
 
-    let codegen = tcx.sess.time("codegen_crate", move || codegen_backend.codegen_crate(tcx));
+    let codegen = tcx.sess.time("codegen_crate", move || {
+        if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
+            // Skip crate items and just output metadata in -Z no-codegen mode.
+            tcx.sess.dcx().abort_if_errors();
+
+            // Linker::link will skip join_codegen in case of a CodegenResults Any value.
+            Box::new(CodegenResults {
+                modules: vec![],
+                allocator_module: None,
+                crate_info: CrateInfo::new(tcx, "".to_owned()),
+            })
+        } else {
+            codegen_backend.codegen_crate(tcx)
+        }
+    });
 
     info!("Post-codegen\n{:?}", tcx.debug_stats());
 
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index 280214ab4183..3799485077ef 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -3,6 +3,7 @@ use std::sync::Arc;
 
 use rustc_codegen_ssa::CodegenResults;
 use rustc_codegen_ssa::traits::CodegenBackend;
+use rustc_data_structures::indexmap::IndexMap;
 use rustc_data_structures::svh::Svh;
 use rustc_errors::timings::TimingSection;
 use rustc_hir::def_id::LOCAL_CRATE;
@@ -46,7 +47,14 @@ impl Linker {
 
     pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) {
         let (codegen_results, mut work_products) = sess.time("finish_ongoing_codegen", || {
-            codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames)
+            match self.ongoing_codegen.downcast::() {
+                // This was a check only build
+                Ok(codegen_results) => (*codegen_results, IndexMap::default()),
+
+                Err(ongoing_codegen) => {
+                    codegen_backend.join_codegen(ongoing_codegen, sess, &self.output_filenames)
+                }
+            }
         });
         sess.timings.end_section(sess.dcx(), TimingSection::Codegen);
 

From 52b62c8ec3ea4106b7cd926c75311e9c4f2fa43b Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Fri, 24 Oct 2025 14:08:01 +0300
Subject: [PATCH 097/170] Remove no-longer-necessary conversion

---
 src/tools/rust-analyzer/crates/hir-ty/src/lower.rs | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

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 6f7ca4829d52..a181ae0157cc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -475,10 +475,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
                             .opaque_type_data
                             .alloc(ImplTrait { predicates: Vec::default() });
 
-                        // FIXME(next-solver): this from_raw/into_raw dance isn't nice, but it's minimal
                         let impl_trait_id = origin.either(
-                            |f| ImplTraitId::ReturnTypeImplTrait(f, Idx::from_raw(idx.into_raw())),
-                            |a| ImplTraitId::TypeAliasImplTrait(a, Idx::from_raw(idx.into_raw())),
+                            |f| ImplTraitId::ReturnTypeImplTrait(f, idx),
+                            |a| ImplTraitId::TypeAliasImplTrait(a, idx),
                         );
                         let opaque_ty_id: SolverDefId =
                             self.db.intern_impl_trait_id(impl_trait_id).into();

From 561e3cede26b1f222788ddea2106346c4eeaca35 Mon Sep 17 00:00:00 2001
From: lcnr 
Date: Fri, 24 Oct 2025 14:59:54 +0200
Subject: [PATCH 098/170] gamer

---
 .../src/solve/eval_ctxt/mod.rs                | 30 -------------------
 .../fixpoint-exponential-growth.stderr        |  1 -
 2 files changed, 31 deletions(-)

diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index f72bd1b8c268..8d0a3ac94d5a 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -1308,36 +1308,6 @@ where
             },
         );
 
-        // HACK: We bail with overflow if the response would have too many non-region
-        // inference variables. This tends to only happen if we encounter a lot of
-        // ambiguous alias types which get replaced with fresh inference variables
-        // during generalization. This prevents hangs caused by an exponential blowup,
-        // see tests/ui/traits/next-solver/coherence-alias-hang.rs.
-        match self.current_goal_kind {
-            // We don't do so for `NormalizesTo` goals as we erased the expected term and
-            // bailing with overflow here would prevent us from detecting a type-mismatch,
-            // causing a coherence error in diesel, see #131969. We still bail with overflow
-            // when later returning from the parent AliasRelate goal.
-            CurrentGoalKind::NormalizesTo => {}
-            CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => {
-                let num_non_region_vars = canonical
-                    .variables
-                    .iter()
-                    .filter(|c| !c.is_region() && c.is_existential())
-                    .count();
-                if num_non_region_vars > self.cx().recursion_limit() {
-                    debug!(?num_non_region_vars, "too many inference variables -> overflow");
-                    return Ok(self.make_ambiguous_response_no_constraints(
-                        MaybeCause::Overflow {
-                            suggest_increasing_limit: true,
-                            keep_constraints: false,
-                        },
-                        OpaqueTypesJank::AllGood,
-                    ));
-                }
-            }
-        }
-
         Ok(canonical)
     }
 
diff --git a/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr b/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr
index 150100f2c531..8d7d8cee08ae 100644
--- a/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr
+++ b/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr
@@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `W<_>: Trait`
 LL |     impls::>();
    |             ^^^^
    |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`fixpoint_exponential_growth`)
 note: required by a bound in `impls`
   --> $DIR/fixpoint-exponential-growth.rs:30:13
    |

From 15557506eb90cb9ea3d0d8b0835d284fb4bbd9a5 Mon Sep 17 00:00:00 2001
From: Shoyu Vanilla 
Date: Fri, 24 Oct 2025 22:31:47 +0900
Subject: [PATCH 099/170] fix: Fix a bug on inhabitedness checks for arrays

---
 .../crates/hir-ty/src/inhabitedness.rs        |  2 +-
 .../src/handlers/non_exhaustive_let.rs        | 52 +++++++++++++++++++
 2 files changed, 53 insertions(+), 1 deletion(-)

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 8aed2608d6cd..5e742bba3ebe 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
@@ -90,7 +90,7 @@ impl<'db> TypeVisitor> for UninhabitedFrom<'_, 'db> {
             TyKind::Tuple(..) => ty.super_visit_with(self),
             TyKind::Array(item_ty, len) => match try_const_usize(self.infcx.interner.db, len) {
                 Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
-                Some(1..) => item_ty.super_visit_with(self),
+                Some(1..) => item_ty.visit_with(self),
             },
             _ => CONTINUE_OPAQUELY_INHABITED,
         };
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 e31367f3b14e..c86ecd2f03b9 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
@@ -152,6 +152,58 @@ impl Deref for Foo {
 fn test(x: Foo<(i32, bool)>) {
     let (_a, _b): &(i32, bool) = &x;
 }
+"#,
+        );
+    }
+
+    #[test]
+    fn uninhabited_variants() {
+        check_diagnostics(
+            r#"
+//- minicore: result
+enum Infallible {}
+
+trait Foo {
+    type Bar;
+}
+
+struct Wrapper {
+    error: T,
+}
+
+struct FooWrapper {
+    error: T::Bar,
+}
+
+fn foo>(result: Result) -> T {
+    let Ok(ok) = result;
+    ok
+}
+
+fn bar>(result: Result) -> T {
+    let Ok(ok) = result;
+    ok
+}
+
+fn baz>(result: Result>) -> T {
+    let Ok(ok) = result;
+    ok
+}
+
+fn qux>(result: Result>) -> T {
+    let Ok(ok) = result;
+    ok
+}
+
+fn quux>(result: Result) -> T {
+    let Ok(ok) = result;
+    ok
+}
+
+fn corge>(result: Result) -> T {
+    let Ok(ok) = result;
+    ok
+}
 "#,
         );
     }

From 4e3fe6dad59576e8f11e7eb138f6b5396c18c450 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Fri, 24 Oct 2025 17:10:23 +0300
Subject: [PATCH 100/170] Fix typo

---
 .../rust-analyzer/crates/ide-completion/src/context/analysis.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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 42772ef2d04f..b3d9ff004610 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
@@ -1907,7 +1907,7 @@ fn is_in_block(node: &SyntaxNode) -> bool {
         .unwrap_or(false)
 }
 
-/// Similar to `has_parens`, heuristic sensing incomplete statement before ambigiguous `Expr`
+/// Similar to `has_parens`, heuristic sensing incomplete statement before ambiguous `Expr`
 ///
 /// Heuristic:
 ///

From 82a4049844cfa328aa903afe78a974a5385a2c76 Mon Sep 17 00:00:00 2001
From: David Wood 
Date: Wed, 18 Jun 2025 17:03:15 +0000
Subject: [PATCH 101/170] hir_analysis: add missing sizedness bounds

Default sizedness bounds were not being added to
`explicit_super_predicates_of` and `explicit_implied_predicates_of`
which meant that a trait bound added to a associated type projection
would be missing the implied predicate of the default sizedness
supertrait of that trait.

An unexpected consequence of this change was that the check for multiple
principals was now finding an additional `MetaSized` principal when
eagerly expanding trait aliases. Instead of special-casing trait aliases
as different from traits and not adding a `MetaSized` supertrait to trait
aliases, filter out `MetaSized` when lowering `dyn Trait`.
---
 .../src/collect/item_bounds.rs                |  30 +++-
 .../src/collect/predicates_of.rs              |  34 ++--
 .../src/hir_ty_lowering/bounds.rs             | 163 ++++++++----------
 .../src/hir_ty_lowering/dyn_trait.rs          |  23 ++-
 .../src/hir_ty_lowering/mod.rs                |  18 +-
 .../bound-on-assoc-type-projection.rs         |  18 ++
 .../ui/sized-hierarchy/default-supertrait.rs  |   5 +-
 .../sized-hierarchy/default-supertrait.stderr |  37 +---
 .../ui/sized-hierarchy/elaboration-simple.rs  |  13 ++
 .../trait-alias-elaboration.rs                |  16 ++
 .../trait-alias-elaboration.stderr            |  17 ++
 .../normalize/normalize-param-env-2.stderr    |  19 +-
 .../normalize-param-env-4.next.stderr         |  16 +-
 13 files changed, 215 insertions(+), 194 deletions(-)
 create mode 100644 tests/ui/sized-hierarchy/bound-on-assoc-type-projection.rs
 create mode 100644 tests/ui/sized-hierarchy/elaboration-simple.rs
 create mode 100644 tests/ui/sized-hierarchy/trait-alias-elaboration.rs
 create mode 100644 tests/ui/sized-hierarchy/trait-alias-elaboration.stderr

diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index 67284e162156..6b51e3157961 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -12,7 +12,9 @@ use tracing::{debug, instrument};
 
 use super::ItemCtxt;
 use super::predicates_of::assert_only_contains_predicates_from;
-use crate::hir_ty_lowering::{HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter};
+use crate::hir_ty_lowering::{
+    HirTyLowerer, ImpliedBoundsContext, OverlappingAsssocItemConstraints, PredicateFilter,
+};
 
 /// For associated types we include both bounds written on the type
 /// (`type X: Trait`) and predicates from the trait: `where Self::X: Trait`.
@@ -52,15 +54,20 @@ fn associated_type_bounds<'tcx>(
             | PredicateFilter::SelfTraitThatDefines(_)
             | PredicateFilter::SelfAndAssociatedTypeBounds => {
                 // Implicit bounds are added to associated types unless a `?Trait` bound is found.
-                icx.lowerer().add_sizedness_bounds(
+                icx.lowerer().add_implicit_sizedness_bounds(
                     &mut bounds,
                     item_ty,
                     hir_bounds,
-                    None,
-                    None,
+                    ImpliedBoundsContext::AssociatedTypeOrImplTrait,
+                    span,
+                );
+                icx.lowerer().add_default_traits(
+                    &mut bounds,
+                    item_ty,
+                    hir_bounds,
+                    ImpliedBoundsContext::AssociatedTypeOrImplTrait,
                     span,
                 );
-                icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span);
 
                 // Also collect `where Self::Assoc: Trait` from the parent trait's where clauses.
                 let trait_def_id = tcx.local_parent(assoc_item_def_id);
@@ -372,15 +379,20 @@ fn opaque_type_bounds<'tcx>(
             | PredicateFilter::SelfOnly
             | PredicateFilter::SelfTraitThatDefines(_)
             | PredicateFilter::SelfAndAssociatedTypeBounds => {
-                icx.lowerer().add_sizedness_bounds(
+                icx.lowerer().add_implicit_sizedness_bounds(
                     &mut bounds,
                     item_ty,
                     hir_bounds,
-                    None,
-                    None,
+                    ImpliedBoundsContext::AssociatedTypeOrImplTrait,
+                    span,
+                );
+                icx.lowerer().add_default_traits(
+                    &mut bounds,
+                    item_ty,
+                    hir_bounds,
+                    ImpliedBoundsContext::AssociatedTypeOrImplTrait,
                     span,
                 );
-                icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span);
             }
             //`ConstIfConst` is only interested in `[const]` bounds.
             PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index e66accc7dcff..79aaa0cb7970 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -19,7 +19,8 @@ use crate::collect::ItemCtxt;
 use crate::constrained_generic_params as cgp;
 use crate::delegation::inherit_predicates_for_delegation_item;
 use crate::hir_ty_lowering::{
-    HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason,
+    HirTyLowerer, ImpliedBoundsContext, OverlappingAsssocItemConstraints, PredicateFilter,
+    RegionInferReason,
 };
 
 /// Returns a list of all type predicates (explicit and implicit) for the definition with
@@ -189,19 +190,18 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
             PredicateFilter::All,
             OverlappingAsssocItemConstraints::Allowed,
         );
-        icx.lowerer().add_sizedness_bounds(
+        icx.lowerer().add_implicit_sizedness_bounds(
             &mut bounds,
             tcx.types.self_param,
             self_bounds,
-            None,
-            Some(def_id),
+            ImpliedBoundsContext::TraitDef(def_id),
             span,
         );
-        icx.lowerer().add_default_super_traits(
-            def_id,
+        icx.lowerer().add_default_traits(
             &mut bounds,
+            tcx.types.self_param,
             self_bounds,
-            hir_generics,
+            ImpliedBoundsContext::TraitDef(def_id),
             span,
         );
         predicates.extend(bounds);
@@ -229,19 +229,18 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
                 let param_ty = icx.lowerer().lower_ty_param(param.hir_id);
                 let mut bounds = Vec::new();
                 // Implicit bounds are added to type params unless a `?Trait` bound is found
-                icx.lowerer().add_sizedness_bounds(
+                icx.lowerer().add_implicit_sizedness_bounds(
                     &mut bounds,
                     param_ty,
                     &[],
-                    Some((param.def_id, hir_generics.predicates)),
-                    None,
+                    ImpliedBoundsContext::TyParam(param.def_id, hir_generics.predicates),
                     param.span,
                 );
                 icx.lowerer().add_default_traits(
                     &mut bounds,
                     param_ty,
                     &[],
-                    Some((param.def_id, hir_generics.predicates)),
+                    ImpliedBoundsContext::TyParam(param.def_id, hir_generics.predicates),
                     param.span,
                 );
                 trace!(?bounds);
@@ -676,11 +675,18 @@ pub(super) fn implied_predicates_with_filter<'tcx>(
         | PredicateFilter::SelfOnly
         | PredicateFilter::SelfTraitThatDefines(_)
         | PredicateFilter::SelfAndAssociatedTypeBounds => {
-            icx.lowerer().add_default_super_traits(
-                trait_def_id,
+            icx.lowerer().add_implicit_sizedness_bounds(
                 &mut bounds,
+                self_param_ty,
                 superbounds,
-                generics,
+                ImpliedBoundsContext::TraitDef(trait_def_id),
+                item.span,
+            );
+            icx.lowerer().add_default_traits(
+                &mut bounds,
+                self_param_ty,
+                superbounds,
+                ImpliedBoundsContext::TraitDef(trait_def_id),
                 item.span,
             );
         }
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index a3cb49e32d80..1832e6e890b9 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -1,4 +1,3 @@
-use std::assert_matches::assert_matches;
 use std::ops::ControlFlow;
 
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
@@ -7,7 +6,7 @@ use rustc_errors::struct_span_code_err;
 use rustc_hir as hir;
 use rustc_hir::PolyTraitRef;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
+use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
 use rustc_middle::bug;
 use rustc_middle::ty::{
     self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
@@ -18,11 +17,10 @@ use rustc_trait_selection::traits;
 use smallvec::SmallVec;
 use tracing::{debug, instrument};
 
-use super::errors::GenericsArgsErrExtend;
 use crate::errors;
 use crate::hir_ty_lowering::{
-    AssocItemQSelf, FeedConstTy, HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter,
-    RegionInferReason,
+    AssocItemQSelf, FeedConstTy, GenericsArgsErrExtend, HirTyLowerer, ImpliedBoundsContext,
+    OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason,
 };
 
 #[derive(Debug, Default)]
@@ -62,7 +60,7 @@ impl CollectedSizednessBounds {
 
 fn search_bounds_for<'tcx>(
     hir_bounds: &'tcx [hir::GenericBound<'tcx>],
-    self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+    context: ImpliedBoundsContext<'tcx>,
     mut f: impl FnMut(&'tcx PolyTraitRef<'tcx>),
 ) {
     let mut search_bounds = |hir_bounds: &'tcx [hir::GenericBound<'tcx>]| {
@@ -76,7 +74,7 @@ fn search_bounds_for<'tcx>(
     };
 
     search_bounds(hir_bounds);
-    if let Some((self_ty, where_clause)) = self_ty_where_predicates {
+    if let ImpliedBoundsContext::TyParam(self_ty, where_clause) = context {
         for clause in where_clause {
             if let hir::WherePredicateKind::BoundPredicate(pred) = clause.kind
                 && pred.is_param_bound(self_ty.to_def_id())
@@ -89,10 +87,10 @@ fn search_bounds_for<'tcx>(
 
 fn collect_relaxed_bounds<'tcx>(
     hir_bounds: &'tcx [hir::GenericBound<'tcx>],
-    self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+    context: ImpliedBoundsContext<'tcx>,
 ) -> SmallVec<[&'tcx PolyTraitRef<'tcx>; 1]> {
     let mut relaxed_bounds: SmallVec<[_; 1]> = SmallVec::new();
-    search_bounds_for(hir_bounds, self_ty_where_predicates, |ptr| {
+    search_bounds_for(hir_bounds, context, |ptr| {
         if matches!(ptr.modifiers.polarity, hir::BoundPolarity::Maybe(_)) {
             relaxed_bounds.push(ptr);
         }
@@ -102,11 +100,11 @@ fn collect_relaxed_bounds<'tcx>(
 
 fn collect_bounds<'a, 'tcx>(
     hir_bounds: &'a [hir::GenericBound<'tcx>],
-    self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+    context: ImpliedBoundsContext<'tcx>,
     target_did: DefId,
 ) -> CollectedBound {
     let mut collect_into = CollectedBound::default();
-    search_bounds_for(hir_bounds, self_ty_where_predicates, |ptr| {
+    search_bounds_for(hir_bounds, context, |ptr| {
         if !matches!(ptr.trait_ref.path.res, Res::Def(DefKind::Trait, did) if did == target_did) {
             return;
         }
@@ -123,17 +121,17 @@ fn collect_bounds<'a, 'tcx>(
 fn collect_sizedness_bounds<'tcx>(
     tcx: TyCtxt<'tcx>,
     hir_bounds: &'tcx [hir::GenericBound<'tcx>],
-    self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+    context: ImpliedBoundsContext<'tcx>,
     span: Span,
 ) -> CollectedSizednessBounds {
     let sized_did = tcx.require_lang_item(hir::LangItem::Sized, span);
-    let sized = collect_bounds(hir_bounds, self_ty_where_predicates, sized_did);
+    let sized = collect_bounds(hir_bounds, context, sized_did);
 
     let meta_sized_did = tcx.require_lang_item(hir::LangItem::MetaSized, span);
-    let meta_sized = collect_bounds(hir_bounds, self_ty_where_predicates, meta_sized_did);
+    let meta_sized = collect_bounds(hir_bounds, context, meta_sized_did);
 
     let pointee_sized_did = tcx.require_lang_item(hir::LangItem::PointeeSized, span);
-    let pointee_sized = collect_bounds(hir_bounds, self_ty_where_predicates, pointee_sized_did);
+    let pointee_sized = collect_bounds(hir_bounds, context, pointee_sized_did);
 
     CollectedSizednessBounds { sized, meta_sized, pointee_sized }
 }
@@ -161,13 +159,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     ///   bounds are present.
     /// - On parameters, opaque type, associated types and trait aliases, add a `MetaSized` bound if
     ///   a `?Sized` bound is present.
-    pub(crate) fn add_sizedness_bounds(
+    pub(crate) fn add_implicit_sizedness_bounds(
         &self,
         bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
         self_ty: Ty<'tcx>,
         hir_bounds: &'tcx [hir::GenericBound<'tcx>],
-        self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
-        trait_did: Option,
+        context: ImpliedBoundsContext<'tcx>,
         span: Span,
     ) {
         let tcx = self.tcx();
@@ -181,33 +178,36 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let pointee_sized_did = tcx.require_lang_item(hir::LangItem::PointeeSized, span);
 
         // If adding sizedness bounds to a trait, then there are some relevant early exits
-        if let Some(trait_did) = trait_did {
-            let trait_did = trait_did.to_def_id();
-            // Never add a default supertrait to `PointeeSized`.
-            if trait_did == pointee_sized_did {
-                return;
+        match context {
+            ImpliedBoundsContext::TraitDef(trait_did) => {
+                let trait_did = trait_did.to_def_id();
+                // Never add a default supertrait to `PointeeSized`.
+                if trait_did == pointee_sized_did {
+                    return;
+                }
+                // Don't add default sizedness supertraits to auto traits because it isn't possible to
+                // relax an automatically added supertrait on the defn itself.
+                if tcx.trait_is_auto(trait_did) {
+                    return;
+                }
             }
-            // Don't add default sizedness supertraits to auto traits because it isn't possible to
-            // relax an automatically added supertrait on the defn itself.
-            if tcx.trait_is_auto(trait_did) {
-                return;
+            ImpliedBoundsContext::TyParam(..) | ImpliedBoundsContext::AssociatedTypeOrImplTrait => {
+                // Report invalid relaxed bounds.
+                // FIXME: Since we only call this validation function here in this function, we only
+                //        fully validate relaxed bounds in contexts where we perform
+                //        "sized elaboration". In most cases that doesn't matter because we *usually*
+                //        reject such relaxed bounds outright during AST lowering.
+                //        However, this can easily get out of sync! Ideally, we would perform this step
+                //        where we are guaranteed to catch *all* bounds like in
+                //        `Self::lower_poly_trait_ref`. List of concrete issues:
+                //        FIXME(more_maybe_bounds): We don't call this for trait object tys, supertrait
+                //                                  bounds, trait alias bounds, assoc type bounds (ATB)!
+                let bounds = collect_relaxed_bounds(hir_bounds, context);
+                self.reject_duplicate_relaxed_bounds(bounds);
             }
-        } else {
-            // Report invalid relaxed bounds.
-            // FIXME: Since we only call this validation function here in this function, we only
-            //        fully validate relaxed bounds in contexts where we perform
-            //        "sized elaboration". In most cases that doesn't matter because we *usually*
-            //        reject such relaxed bounds outright during AST lowering.
-            //        However, this can easily get out of sync! Ideally, we would perform this step
-            //        where we are guaranteed to catch *all* bounds like in
-            //        `Self::lower_poly_trait_ref`. List of concrete issues:
-            //        FIXME(more_maybe_bounds): We don't call this for trait object tys, supertrait
-            //                                  bounds, trait alias bounds, assoc type bounds (ATB)!
-            let bounds = collect_relaxed_bounds(hir_bounds, self_ty_where_predicates);
-            self.reject_duplicate_relaxed_bounds(bounds);
         }
 
-        let collected = collect_sizedness_bounds(tcx, hir_bounds, self_ty_where_predicates, span);
+        let collected = collect_sizedness_bounds(tcx, hir_bounds, context, span);
         if (collected.sized.maybe || collected.sized.negative)
             && !collected.sized.positive
             && !collected.meta_sized.any()
@@ -217,62 +217,33 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             // other explicit ones) - this can happen for trait aliases as well as bounds.
             add_trait_bound(tcx, bounds, self_ty, meta_sized_did, span);
         } else if !collected.any() {
-            if trait_did.is_some() {
-                // If there are no explicit sizedness bounds on a trait then add a default
-                // `MetaSized` supertrait.
-                add_trait_bound(tcx, bounds, self_ty, meta_sized_did, span);
-            } else {
-                // If there are no explicit sizedness bounds on a parameter then add a default
-                // `Sized` bound.
-                let sized_did = tcx.require_lang_item(hir::LangItem::Sized, span);
-                add_trait_bound(tcx, bounds, self_ty, sized_did, span);
+            match context {
+                ImpliedBoundsContext::TraitDef(..) => {
+                    // If there are no explicit sizedness bounds on a trait then add a default
+                    // `MetaSized` supertrait.
+                    add_trait_bound(tcx, bounds, self_ty, meta_sized_did, span);
+                }
+                ImpliedBoundsContext::TyParam(..)
+                | ImpliedBoundsContext::AssociatedTypeOrImplTrait => {
+                    // If there are no explicit sizedness bounds on a parameter then add a default
+                    // `Sized` bound.
+                    let sized_did = tcx.require_lang_item(hir::LangItem::Sized, span);
+                    add_trait_bound(tcx, bounds, self_ty, sized_did, span);
+                }
             }
         }
     }
 
-    /// Adds `experimental_default_bounds` bounds to the supertrait bounds.
-    pub(crate) fn add_default_super_traits(
-        &self,
-        trait_def_id: LocalDefId,
-        bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
-        hir_bounds: &'tcx [hir::GenericBound<'tcx>],
-        hir_generics: &'tcx hir::Generics<'tcx>,
-        span: Span,
-    ) {
-        assert_matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias);
-
-        // Supertraits for auto trait are unsound according to the unstable book:
-        // https://doc.rust-lang.org/beta/unstable-book/language-features/auto-traits.html#supertraits
-        if self.tcx().trait_is_auto(trait_def_id.to_def_id()) {
-            return;
-        }
-
-        self.add_default_traits(
-            bounds,
-            self.tcx().types.self_param,
-            hir_bounds,
-            Some((trait_def_id, hir_generics.predicates)),
-            span,
-        );
-    }
-
     pub(crate) fn add_default_traits(
         &self,
         bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
         self_ty: Ty<'tcx>,
         hir_bounds: &[hir::GenericBound<'tcx>],
-        self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+        context: ImpliedBoundsContext<'tcx>,
         span: Span,
     ) {
         self.tcx().default_traits().iter().for_each(|default_trait| {
-            self.add_default_trait(
-                *default_trait,
-                bounds,
-                self_ty,
-                hir_bounds,
-                self_ty_where_predicates,
-                span,
-            );
+            self.add_default_trait(*default_trait, bounds, self_ty, hir_bounds, context, span);
         });
     }
 
@@ -285,15 +256,23 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
         self_ty: Ty<'tcx>,
         hir_bounds: &[hir::GenericBound<'tcx>],
-        self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+        context: ImpliedBoundsContext<'tcx>,
         span: Span,
     ) {
         let tcx = self.tcx();
-        let trait_id = tcx.lang_items().get(trait_);
-        if let Some(trait_id) = trait_id
-            && self.should_add_default_traits(trait_id, hir_bounds, self_ty_where_predicates)
+
+        // Supertraits for auto trait are unsound according to the unstable book:
+        // https://doc.rust-lang.org/beta/unstable-book/language-features/auto-traits.html#supertraits
+        if let ImpliedBoundsContext::TraitDef(trait_did) = context
+            && self.tcx().trait_is_auto(trait_did.into())
         {
-            add_trait_bound(tcx, bounds, self_ty, trait_id, span);
+            return;
+        }
+
+        if let Some(trait_did) = tcx.lang_items().get(trait_)
+            && self.should_add_default_traits(trait_did, hir_bounds, context)
+        {
+            add_trait_bound(tcx, bounds, self_ty, trait_did, span);
         }
     }
 
@@ -302,9 +281,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         &self,
         trait_def_id: DefId,
         hir_bounds: &'a [hir::GenericBound<'tcx>],
-        self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+        context: ImpliedBoundsContext<'tcx>,
     ) -> bool {
-        let collected = collect_bounds(hir_bounds, self_ty_where_predicates, trait_def_id);
+        let collected = collect_bounds(hir_bounds, context, trait_def_id);
         !self.tcx().has_attr(CRATE_DEF_ID, sym::rustc_no_implicit_bounds) && !collected.any()
     }
 
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs
index c0b137730892..15d7da3a0070 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs
@@ -4,9 +4,9 @@ use rustc_errors::codes::*;
 use rustc_errors::{
     Applicability, Diag, EmissionGuarantee, StashKey, Suggestions, struct_span_code_err,
 };
-use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, LangItem};
 use rustc_lint_defs::builtin::{BARE_TRAIT_OBJECTS, UNUSED_ASSOCIATED_TYPE_BOUNDS};
 use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan;
 use rustc_middle::ty::{
@@ -24,7 +24,8 @@ use tracing::{debug, instrument};
 use super::HirTyLowerer;
 use crate::errors::SelfInTypeAlias;
 use crate::hir_ty_lowering::{
-    GenericArgCountMismatch, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason,
+    GenericArgCountMismatch, ImpliedBoundsContext, OverlappingAsssocItemConstraints,
+    PredicateFilter, RegionInferReason,
 };
 
 impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
@@ -76,12 +77,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 .iter()
                 .map(|&trait_ref| hir::GenericBound::Trait(trait_ref))
                 .collect::>(),
-            None,
+            ImpliedBoundsContext::AssociatedTypeOrImplTrait,
             span,
         );
 
-        let (elaborated_trait_bounds, elaborated_projection_bounds) =
+        let (mut elaborated_trait_bounds, elaborated_projection_bounds) =
             traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied());
+
+        // FIXME(sized-hierarchy): https://github.com/rust-lang/rust/pull/142712#issuecomment-3013231794
+        debug!(?user_written_bounds, ?elaborated_trait_bounds);
+        let meta_sized_did = tcx.require_lang_item(LangItem::MetaSized, span);
+        // Don't strip out `MetaSized` when the user wrote it explicitly, only when it was
+        // elaborated
+        if user_written_bounds
+            .iter()
+            .all(|(clause, _)| clause.as_trait_clause().map(|p| p.def_id()) != Some(meta_sized_did))
+        {
+            elaborated_trait_bounds.retain(|(pred, _)| pred.def_id() != meta_sized_did);
+        }
+        debug!(?user_written_bounds, ?elaborated_trait_bounds);
+
         let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = elaborated_trait_bounds
             .into_iter()
             .partition(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 00f909575780..82a931dcca2a 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -56,6 +56,19 @@ use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_
 use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
 use crate::middle::resolve_bound_vars as rbv;
 
+/// The context in which an implied bound is being added to a item being lowered (i.e. a sizedness
+/// trait or a default trait)
+#[derive(Clone, Copy)]
+pub(crate) enum ImpliedBoundsContext<'tcx> {
+    /// An implied bound is added to a trait definition (i.e. a new supertrait), used when adding
+    /// a default `MetaSized` supertrait
+    TraitDef(LocalDefId),
+    /// An implied bound is added to a type parameter
+    TyParam(LocalDefId, &'tcx [hir::WherePredicate<'tcx>]),
+    /// An implied bound being added in any other context
+    AssociatedTypeOrImplTrait,
+}
+
 /// A path segment that is semantically allowed to have generic arguments.
 #[derive(Debug)]
 pub struct GenericPathSegment(pub DefId, pub usize);
@@ -2513,12 +2526,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     PredicateFilter::All,
                     OverlappingAsssocItemConstraints::Allowed,
                 );
-                self.add_sizedness_bounds(
+                self.add_implicit_sizedness_bounds(
                     &mut bounds,
                     self_ty,
                     hir_bounds,
-                    None,
-                    None,
+                    ImpliedBoundsContext::AssociatedTypeOrImplTrait,
                     hir_ty.span,
                 );
                 self.register_trait_ascription_bounds(bounds, hir_ty.hir_id, hir_ty.span);
diff --git a/tests/ui/sized-hierarchy/bound-on-assoc-type-projection.rs b/tests/ui/sized-hierarchy/bound-on-assoc-type-projection.rs
new file mode 100644
index 000000000000..48d17ac9a3af
--- /dev/null
+++ b/tests/ui/sized-hierarchy/bound-on-assoc-type-projection.rs
@@ -0,0 +1,18 @@
+//@ check-pass
+#![crate_type = "lib"]
+#![feature(sized_hierarchy)]
+
+// Tests that a bound on an associated type projection, of a trait with a sizedness bound, will be
+// elaborated.
+
+trait FalseDeref {
+    type Target: std::marker::PointeeSized;
+}
+
+trait Bar {}
+
+fn foo()
+where
+    T::Target: Bar,
+{
+}
diff --git a/tests/ui/sized-hierarchy/default-supertrait.rs b/tests/ui/sized-hierarchy/default-supertrait.rs
index d5bf152b7963..b13f9d90081c 100644
--- a/tests/ui/sized-hierarchy/default-supertrait.rs
+++ b/tests/ui/sized-hierarchy/default-supertrait.rs
@@ -52,14 +52,11 @@ fn with_pointeesized_supertrait() {
     requires_pointeesized::();
 }
 
-// `T` won't inherit the `const MetaSized` implicit supertrait of `Bare`, so there is an error on
-// the bound, which is expected.
+// `T` inherits the `const MetaSized` implicit supertrait of `Bare`.
 fn with_bare_trait() {
-//~^ ERROR the size for values of type `T` cannot be known
     requires_sized::();
     //~^ ERROR the size for values of type `T` cannot be known
     requires_metasized::();
-    //~^ ERROR the size for values of type `T` cannot be known
     requires_pointeesized::();
 }
 
diff --git a/tests/ui/sized-hierarchy/default-supertrait.stderr b/tests/ui/sized-hierarchy/default-supertrait.stderr
index 2a521dce8b6b..35875163774d 100644
--- a/tests/ui/sized-hierarchy/default-supertrait.stderr
+++ b/tests/ui/sized-hierarchy/default-supertrait.stderr
@@ -62,22 +62,6 @@ LL | trait NegPointeeSized: ?PointeeSized { }
    |
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
-error[E0277]: the size for values of type `T` cannot be known
-  --> $DIR/default-supertrait.rs:57:38
-   |
-LL | fn with_bare_trait() {
-   |                                      ^^^^ doesn't have a known size
-   |
-note: required by a bound in `Bare`
-  --> $DIR/default-supertrait.rs:27:1
-   |
-LL | trait Bare {}
-   | ^^^^^^^^^^^^^ required by this bound in `Bare`
-help: consider further restricting type parameter `T` with unstable trait `MetaSized`
-   |
-LL | fn with_bare_trait() {
-   |                                           ++++++++++++++++++++++++
-
 error[E0277]: the size for values of type `T` cannot be known at compilation time
   --> $DIR/default-supertrait.rs:40:22
    |
@@ -123,11 +107,10 @@ LL | fn with_pointeesized_supertrait $DIR/default-supertrait.rs:59:22
+  --> $DIR/default-supertrait.rs:57:22
    |
 LL | fn with_bare_trait() {
    |                    - this type parameter needs to be `Sized`
-LL |
 LL |     requires_sized::();
    |                      ^ doesn't have a size known at compile-time
    |
@@ -137,22 +120,6 @@ note: required by a bound in `requires_sized`
 LL | fn requires_sized() {}
    |                      ^^^^^ required by this bound in `requires_sized`
 
-error[E0277]: the size for values of type `T` cannot be known
-  --> $DIR/default-supertrait.rs:61:26
-   |
-LL |     requires_metasized::();
-   |                          ^ doesn't have a known size
-   |
-note: required by a bound in `requires_metasized`
-  --> $DIR/default-supertrait.rs:30:26
-   |
-LL | fn requires_metasized() {}
-   |                          ^^^^^^^^^ required by this bound in `requires_metasized`
-help: consider further restricting type parameter `T` with unstable trait `MetaSized`
-   |
-LL | fn with_bare_trait() {
-   |                                           ++++++++++++++++++++++++
-
-error: aborting due to 15 previous errors
+error: aborting due to 13 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/sized-hierarchy/elaboration-simple.rs b/tests/ui/sized-hierarchy/elaboration-simple.rs
new file mode 100644
index 000000000000..87e100166a29
--- /dev/null
+++ b/tests/ui/sized-hierarchy/elaboration-simple.rs
@@ -0,0 +1,13 @@
+//@ check-pass
+//@ compile-flags: --crate-type=lib
+#![feature(sized_hierarchy)]
+
+// Test demonstrating that elaboration of sizedness bounds works in the simplest cases.
+
+trait Trait {}
+
+fn f() {
+    require_metasized::();
+}
+
+fn require_metasized() {}
diff --git a/tests/ui/sized-hierarchy/trait-alias-elaboration.rs b/tests/ui/sized-hierarchy/trait-alias-elaboration.rs
new file mode 100644
index 000000000000..a5b4443ffddd
--- /dev/null
+++ b/tests/ui/sized-hierarchy/trait-alias-elaboration.rs
@@ -0,0 +1,16 @@
+#![feature(sized_hierarchy, trait_alias)]
+use std::marker::MetaSized;
+
+// Trait aliases also have implicit `MetaSized` bounds, like traits. These are filtered out during
+// elaboration of trait aliases when lowering `dyn TraitAlias` - however, if the user explicitly
+// wrote `MetaSized` in the `dyn Trait` then that should still be an error so as not to accidentally
+// accept this going forwards.
+
+trait Qux = Clone;
+
+type Foo = dyn Qux + MetaSized;
+//~^ ERROR: only auto traits can be used as additional traits in a trait object
+
+type Bar = dyn Qux;
+
+fn main() {}
diff --git a/tests/ui/sized-hierarchy/trait-alias-elaboration.stderr b/tests/ui/sized-hierarchy/trait-alias-elaboration.stderr
new file mode 100644
index 000000000000..394aae6f8e32
--- /dev/null
+++ b/tests/ui/sized-hierarchy/trait-alias-elaboration.stderr
@@ -0,0 +1,17 @@
+error[E0225]: only auto traits can be used as additional traits in a trait object
+  --> $DIR/trait-alias-elaboration.rs:11:16
+   |
+LL | trait Qux = Clone;
+   | ------------------ additional non-auto trait
+LL |
+LL | type Foo = dyn Qux + MetaSized;
+   |                ^^^   --------- first non-auto trait
+   |                |
+   |                second non-auto trait comes from this alias
+   |
+   = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: MetaSized + MetaSized + Clone {}`
+   = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit 
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0225`.
diff --git a/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr b/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr
index 8d8909625ffc..d179c8059623 100644
--- a/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr
+++ b/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr
@@ -19,23 +19,6 @@ error[E0275]: overflow evaluating the requirement `<() as A>::Assoc: A`
 LL |         Self::Assoc: A,
    |                      ^^^^
 
-error[E0275]: overflow evaluating the requirement `<() as A>::Assoc: MetaSized`
-  --> $DIR/normalize-param-env-2.rs:24:22
-   |
-LL |         Self::Assoc: A,
-   |                      ^^^^
-   |
-note: required by a bound in `A`
-  --> $DIR/normalize-param-env-2.rs:9:1
-   |
-LL | / trait A {
-LL | |     type Assoc;
-LL | |
-LL | |     fn f()
-...  |
-LL | | }
-   | |_^ required by this bound in `A`
-
 error[E0275]: overflow evaluating the requirement `<() as A>::Assoc well-formed`
   --> $DIR/normalize-param-env-2.rs:24:22
    |
@@ -63,6 +46,6 @@ LL |     where
 LL |         Self::Assoc: A,
    |                      ^^^^ required by this bound in `A::f`
 
-error: aborting due to 6 previous errors
+error: aborting due to 5 previous errors
 
 For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr b/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr
index 9f7f74f94664..f5fd9ce9864c 100644
--- a/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr
+++ b/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr
@@ -4,20 +4,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc: Trait`
 LL |     ::Assoc: Trait,
    |                          ^^^^^
 
-error[E0275]: overflow evaluating the requirement `::Assoc: MetaSized`
-  --> $DIR/normalize-param-env-4.rs:19:26
-   |
-LL |     ::Assoc: Trait,
-   |                          ^^^^^
-   |
-note: required by a bound in `Trait`
-  --> $DIR/normalize-param-env-4.rs:7:1
-   |
-LL | / trait Trait {
-LL | |     type Assoc;
-LL | | }
-   | |_^ required by this bound in `Trait`
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
 For more information about this error, try `rustc --explain E0275`.

From 66b992d70526c1c6b2e2c38656585b40115f1824 Mon Sep 17 00:00:00 2001
From: Mads Marquart 
Date: Fri, 24 Oct 2025 16:32:34 +0200
Subject: [PATCH 102/170] Fix compiling CondVar::wait_timeout on 32-bit Apple
 platforms

---
 library/std/src/sys/pal/unix/sync/condvar.rs | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/library/std/src/sys/pal/unix/sync/condvar.rs b/library/std/src/sys/pal/unix/sync/condvar.rs
index b6c3ba4136f2..73768860723d 100644
--- a/library/std/src/sys/pal/unix/sync/condvar.rs
+++ b/library/std/src/sys/pal/unix/sync/condvar.rs
@@ -1,6 +1,10 @@
 use super::Mutex;
 use crate::cell::UnsafeCell;
 use crate::pin::Pin;
+#[cfg(not(target_os = "nto"))]
+use crate::sys::pal::time::TIMESPEC_MAX;
+#[cfg(target_os = "nto")]
+use crate::sys::pal::time::TIMESPEC_MAX_CAPPED;
 use crate::time::Duration;
 
 pub struct Condvar {
@@ -51,10 +55,6 @@ impl Condvar {
     /// * `mutex` must be locked by the current thread.
     /// * This condition variable may only be used with the same mutex.
     pub unsafe fn wait_timeout(&self, mutex: Pin<&Mutex>, dur: Duration) -> bool {
-        #[cfg(not(target_os = "nto"))]
-        use crate::sys::pal::time::TIMESPEC_MAX;
-        #[cfg(target_os = "nto")]
-        use crate::sys::pal::time::TIMESPEC_MAX_CAPPED;
         use crate::sys::pal::time::Timespec;
 
         let mutex = mutex.raw();
@@ -118,10 +118,12 @@ impl Condvar {
 
         let (dur, clamped) = if dur <= MAX_DURATION { (dur, false) } else { (MAX_DURATION, true) };
 
-        let timeout = libc::timespec {
-            // This cannot overflow because of the clamping above.
-            tv_sec: dur.as_secs() as i64,
-            tv_nsec: dur.subsec_nanos() as i64,
+        // This can overflow on 32-bit platforms, but not on 64-bit because of the clamping above.
+        let timeout = if let Ok(tv_sec) = dur.as_secs().try_into() {
+            libc::timespec { tv_sec, tv_nsec: dur.subsec_nanos() as _ }
+        } else {
+            // This is less than `MAX_DURATION` on 32-bit platforms.
+            TIMESPEC_MAX
         };
 
         let r = unsafe { libc::pthread_cond_timedwait_relative_np(self.raw(), mutex, &timeout) };

From fc5f0f7ee58a34ae0bc21e63a442ca069a0cf9d1 Mon Sep 17 00:00:00 2001
From: Ed Page 
Date: Fri, 24 Oct 2025 10:51:21 -0500
Subject: [PATCH 103/170] test(frontmatter): Rename tests to make coverage more
 obvious

When working on the stabilization report,
I found it annoying to determine what cases were covered because areas
of the frontmatter feature were either not in the file name or in
inconsistent locations.

This moves the area of frontmatter to the start of the file name and the
moves to more specific the later in the file name so coverage is easier
to see.
---
 ...ns-whitespace.rs => content-contains-whitespace.rs} |  0
 ...lexible-tokens.rs => content-non-lexible-tokens.rs} |  0
 .../{escape.rs => escape-hyphens-leading.rs}           |  0
 ...frontmatter-2.rs => escape-hyphens-nonleading-1.rs} |  0
 ...ner-hyphens-1.rs => escape-hyphens-nonleading-2.rs} |  0
 ...ner-hyphens-2.rs => escape-hyphens-nonleading-3.rs} |  0
 .../{extra-after-end.rs => fence-close-extra-after.rs} |  0
 ...after-end.stderr => fence-close-extra-after.stderr} |  2 +-
 ...tter-whitespace-2.rs => fence-indented-mismatch.rs} |  0
 ...tespace-2.stderr => fence-indented-mismatch.stderr} |  6 +++---
 .../{frontmatter-whitespace-1.rs => fence-indented.rs} |  0
 ...atter-whitespace-1.stderr => fence-indented.stderr} |  8 ++++----
 .../frontmatter/{mismatch-1.rs => fence-mismatch-1.rs} |  0
 .../{mismatch-1.stderr => fence-mismatch-1.stderr}     |  2 +-
 .../frontmatter/{mismatch-2.rs => fence-mismatch-2.rs} |  0
 .../{mismatch-2.stderr => fence-mismatch-2.stderr}     |  4 ++--
 .../frontmatter/{unclosed-1.rs => fence-unclosed-1.rs} |  0
 .../{unclosed-1.stderr => fence-unclosed-1.stderr}     |  4 ++--
 .../frontmatter/{unclosed-2.rs => fence-unclosed-2.rs} |  0
 .../{unclosed-2.stderr => fence-unclosed-2.stderr}     |  6 +++---
 .../frontmatter/{unclosed-3.rs => fence-unclosed-3.rs} |  0
 .../{unclosed-3.stderr => fence-unclosed-3.stderr}     | 10 +++++-----
 .../frontmatter/{unclosed-4.rs => fence-unclosed-4.rs} |  0
 .../{unclosed-4.stderr => fence-unclosed-4.stderr}     |  4 ++--
 .../frontmatter/{unclosed-5.rs => fence-unclosed-5.rs} |  0
 .../{unclosed-5.stderr => fence-unclosed-5.stderr}     |  6 +++---
 .../frontmatter/{unclosed-6.rs => fence-unclosed-6.rs} |  0
 .../{unclosed-6.stderr => fence-unclosed-6.stderr}     |  4 ++--
 ...-whitespace-3.rs => fence-whitespace-trailing-1.rs} |  0
 ...-whitespace-4.rs => fence-whitespace-trailing-2.rs} |  0
 .../{infostring-fail.rs => infostring-comma.rs}        |  0
 ...{infostring-fail.stderr => infostring-comma.stderr} |  2 +-
 ...infostring-leading.rs => infostring-dot-leading.rs} |  0
 ...ng-leading.stderr => infostring-dot-leading.stderr} |  2 +-
 ...ing-non-leading.rs => infostring-dot-nonleading.rs} |  0
 ...ostring-leading.rs => infostring-hyphen-leading.rs} |  0
 ...leading.stderr => infostring-hyphen-leading.stderr} |  2 +-
 ...-non-leading.rs => infostring-hyphen-nonleading.rs} |  0
 .../{space-in-infostring.rs => infostring-space.rs}    |  0
 ...ce-in-infostring.stderr => infostring-space.stderr} |  2 +-
 .../{shebang.rs => location-after-shebang.rs}          |  0
 ...matter-after-tokens.rs => location-after-tokens.rs} |  0
 ...fter-tokens.stderr => location-after-tokens.stderr} |  2 +-
 ...n-expr-ctxt.rs => location-include-in-expr-ctxt.rs} |  0
 ...n-item-ctxt.rs => location-include-in-item-ctxt.rs} |  0
 ...cro-observer.rs => location-proc-macro-observer.rs} |  0
 46 files changed, 33 insertions(+), 33 deletions(-)
 rename tests/ui/frontmatter/{frontmatter-contains-whitespace.rs => content-contains-whitespace.rs} (100%)
 rename tests/ui/frontmatter/{frontmatter-non-lexible-tokens.rs => content-non-lexible-tokens.rs} (100%)
 rename tests/ui/frontmatter/{escape.rs => escape-hyphens-leading.rs} (100%)
 rename tests/ui/frontmatter/{multifrontmatter-2.rs => escape-hyphens-nonleading-1.rs} (100%)
 rename tests/ui/frontmatter/{frontmatter-inner-hyphens-1.rs => escape-hyphens-nonleading-2.rs} (100%)
 rename tests/ui/frontmatter/{frontmatter-inner-hyphens-2.rs => escape-hyphens-nonleading-3.rs} (100%)
 rename tests/ui/frontmatter/{extra-after-end.rs => fence-close-extra-after.rs} (100%)
 rename tests/ui/frontmatter/{extra-after-end.stderr => fence-close-extra-after.stderr} (76%)
 rename tests/ui/frontmatter/{frontmatter-whitespace-2.rs => fence-indented-mismatch.rs} (100%)
 rename tests/ui/frontmatter/{frontmatter-whitespace-2.stderr => fence-indented-mismatch.stderr} (81%)
 rename tests/ui/frontmatter/{frontmatter-whitespace-1.rs => fence-indented.rs} (100%)
 rename tests/ui/frontmatter/{frontmatter-whitespace-1.stderr => fence-indented.stderr} (69%)
 rename tests/ui/frontmatter/{mismatch-1.rs => fence-mismatch-1.rs} (100%)
 rename tests/ui/frontmatter/{mismatch-1.stderr => fence-mismatch-1.stderr} (88%)
 rename tests/ui/frontmatter/{mismatch-2.rs => fence-mismatch-2.rs} (100%)
 rename tests/ui/frontmatter/{mismatch-2.stderr => fence-mismatch-2.stderr} (84%)
 rename tests/ui/frontmatter/{unclosed-1.rs => fence-unclosed-1.rs} (100%)
 rename tests/ui/frontmatter/{unclosed-1.stderr => fence-unclosed-1.stderr} (73%)
 rename tests/ui/frontmatter/{unclosed-2.rs => fence-unclosed-2.rs} (100%)
 rename tests/ui/frontmatter/{unclosed-2.stderr => fence-unclosed-2.stderr} (86%)
 rename tests/ui/frontmatter/{unclosed-3.rs => fence-unclosed-3.rs} (100%)
 rename tests/ui/frontmatter/{unclosed-3.stderr => fence-unclosed-3.stderr} (79%)
 rename tests/ui/frontmatter/{unclosed-4.rs => fence-unclosed-4.rs} (100%)
 rename tests/ui/frontmatter/{unclosed-4.stderr => fence-unclosed-4.stderr} (73%)
 rename tests/ui/frontmatter/{unclosed-5.rs => fence-unclosed-5.rs} (100%)
 rename tests/ui/frontmatter/{unclosed-5.stderr => fence-unclosed-5.stderr} (85%)
 rename tests/ui/frontmatter/{unclosed-6.rs => fence-unclosed-6.rs} (100%)
 rename tests/ui/frontmatter/{unclosed-6.stderr => fence-unclosed-6.stderr} (75%)
 rename tests/ui/frontmatter/{frontmatter-whitespace-3.rs => fence-whitespace-trailing-1.rs} (100%)
 rename tests/ui/frontmatter/{frontmatter-whitespace-4.rs => fence-whitespace-trailing-2.rs} (100%)
 rename tests/ui/frontmatter/{infostring-fail.rs => infostring-comma.rs} (100%)
 rename tests/ui/frontmatter/{infostring-fail.stderr => infostring-comma.stderr} (86%)
 rename tests/ui/frontmatter/{dot-in-infostring-leading.rs => infostring-dot-leading.rs} (100%)
 rename tests/ui/frontmatter/{dot-in-infostring-leading.stderr => infostring-dot-leading.stderr} (83%)
 rename tests/ui/frontmatter/{dot-in-infostring-non-leading.rs => infostring-dot-nonleading.rs} (100%)
 rename tests/ui/frontmatter/{hyphen-in-infostring-leading.rs => infostring-hyphen-leading.rs} (100%)
 rename tests/ui/frontmatter/{hyphen-in-infostring-leading.stderr => infostring-hyphen-leading.stderr} (82%)
 rename tests/ui/frontmatter/{hyphen-in-infostring-non-leading.rs => infostring-hyphen-nonleading.rs} (100%)
 rename tests/ui/frontmatter/{space-in-infostring.rs => infostring-space.rs} (100%)
 rename tests/ui/frontmatter/{space-in-infostring.stderr => infostring-space.stderr} (86%)
 rename tests/ui/frontmatter/{shebang.rs => location-after-shebang.rs} (100%)
 rename tests/ui/frontmatter/{frontmatter-after-tokens.rs => location-after-tokens.rs} (100%)
 rename tests/ui/frontmatter/{frontmatter-after-tokens.stderr => location-after-tokens.stderr} (84%)
 rename tests/ui/frontmatter/{include-in-expr-ctxt.rs => location-include-in-expr-ctxt.rs} (100%)
 rename tests/ui/frontmatter/{include-in-item-ctxt.rs => location-include-in-item-ctxt.rs} (100%)
 rename tests/ui/frontmatter/{proc-macro-observer.rs => location-proc-macro-observer.rs} (100%)

diff --git a/tests/ui/frontmatter/frontmatter-contains-whitespace.rs b/tests/ui/frontmatter/content-contains-whitespace.rs
similarity index 100%
rename from tests/ui/frontmatter/frontmatter-contains-whitespace.rs
rename to tests/ui/frontmatter/content-contains-whitespace.rs
diff --git a/tests/ui/frontmatter/frontmatter-non-lexible-tokens.rs b/tests/ui/frontmatter/content-non-lexible-tokens.rs
similarity index 100%
rename from tests/ui/frontmatter/frontmatter-non-lexible-tokens.rs
rename to tests/ui/frontmatter/content-non-lexible-tokens.rs
diff --git a/tests/ui/frontmatter/escape.rs b/tests/ui/frontmatter/escape-hyphens-leading.rs
similarity index 100%
rename from tests/ui/frontmatter/escape.rs
rename to tests/ui/frontmatter/escape-hyphens-leading.rs
diff --git a/tests/ui/frontmatter/multifrontmatter-2.rs b/tests/ui/frontmatter/escape-hyphens-nonleading-1.rs
similarity index 100%
rename from tests/ui/frontmatter/multifrontmatter-2.rs
rename to tests/ui/frontmatter/escape-hyphens-nonleading-1.rs
diff --git a/tests/ui/frontmatter/frontmatter-inner-hyphens-1.rs b/tests/ui/frontmatter/escape-hyphens-nonleading-2.rs
similarity index 100%
rename from tests/ui/frontmatter/frontmatter-inner-hyphens-1.rs
rename to tests/ui/frontmatter/escape-hyphens-nonleading-2.rs
diff --git a/tests/ui/frontmatter/frontmatter-inner-hyphens-2.rs b/tests/ui/frontmatter/escape-hyphens-nonleading-3.rs
similarity index 100%
rename from tests/ui/frontmatter/frontmatter-inner-hyphens-2.rs
rename to tests/ui/frontmatter/escape-hyphens-nonleading-3.rs
diff --git a/tests/ui/frontmatter/extra-after-end.rs b/tests/ui/frontmatter/fence-close-extra-after.rs
similarity index 100%
rename from tests/ui/frontmatter/extra-after-end.rs
rename to tests/ui/frontmatter/fence-close-extra-after.rs
diff --git a/tests/ui/frontmatter/extra-after-end.stderr b/tests/ui/frontmatter/fence-close-extra-after.stderr
similarity index 76%
rename from tests/ui/frontmatter/extra-after-end.stderr
rename to tests/ui/frontmatter/fence-close-extra-after.stderr
index c2770fdfd41f..a54a67152f97 100644
--- a/tests/ui/frontmatter/extra-after-end.stderr
+++ b/tests/ui/frontmatter/fence-close-extra-after.stderr
@@ -1,5 +1,5 @@
 error: extra characters after frontmatter close are not allowed
-  --> $DIR/extra-after-end.rs:2:1
+  --> $DIR/fence-close-extra-after.rs:2:1
    |
 LL | ---cargo
    | ^^^^^^^^
diff --git a/tests/ui/frontmatter/frontmatter-whitespace-2.rs b/tests/ui/frontmatter/fence-indented-mismatch.rs
similarity index 100%
rename from tests/ui/frontmatter/frontmatter-whitespace-2.rs
rename to tests/ui/frontmatter/fence-indented-mismatch.rs
diff --git a/tests/ui/frontmatter/frontmatter-whitespace-2.stderr b/tests/ui/frontmatter/fence-indented-mismatch.stderr
similarity index 81%
rename from tests/ui/frontmatter/frontmatter-whitespace-2.stderr
rename to tests/ui/frontmatter/fence-indented-mismatch.stderr
index 2ae63cdc6fe4..d85add165210 100644
--- a/tests/ui/frontmatter/frontmatter-whitespace-2.stderr
+++ b/tests/ui/frontmatter/fence-indented-mismatch.stderr
@@ -1,5 +1,5 @@
 error: unclosed frontmatter
-  --> $DIR/frontmatter-whitespace-2.rs:1:1
+  --> $DIR/fence-indented-mismatch.rs:1:1
    |
 LL | / ---cargo
 ...  |
@@ -7,13 +7,13 @@ LL | |
    | |_^
    |
 note: frontmatter opening here was not closed
-  --> $DIR/frontmatter-whitespace-2.rs:1:1
+  --> $DIR/fence-indented-mismatch.rs:1:1
    |
 LL | ---cargo
    | ^^^
 
 warning: use of a double negation
-  --> $DIR/frontmatter-whitespace-2.rs:9:6
+  --> $DIR/fence-indented-mismatch.rs:9:6
    |
 LL |     ---x
    |      ^^^
diff --git a/tests/ui/frontmatter/frontmatter-whitespace-1.rs b/tests/ui/frontmatter/fence-indented.rs
similarity index 100%
rename from tests/ui/frontmatter/frontmatter-whitespace-1.rs
rename to tests/ui/frontmatter/fence-indented.rs
diff --git a/tests/ui/frontmatter/frontmatter-whitespace-1.stderr b/tests/ui/frontmatter/fence-indented.stderr
similarity index 69%
rename from tests/ui/frontmatter/frontmatter-whitespace-1.stderr
rename to tests/ui/frontmatter/fence-indented.stderr
index f16788fa3992..777db0976d08 100644
--- a/tests/ui/frontmatter/frontmatter-whitespace-1.stderr
+++ b/tests/ui/frontmatter/fence-indented.stderr
@@ -1,17 +1,17 @@
 error: invalid preceding whitespace for frontmatter opening
-  --> $DIR/frontmatter-whitespace-1.rs:1:1
+  --> $DIR/fence-indented.rs:1:1
    |
 LL |   ---
    | ^^^^^
    |
 note: frontmatter opening should not be preceded by whitespace
-  --> $DIR/frontmatter-whitespace-1.rs:1:1
+  --> $DIR/fence-indented.rs:1:1
    |
 LL |   ---
    | ^^
 
 error: unclosed frontmatter
-  --> $DIR/frontmatter-whitespace-1.rs:1:3
+  --> $DIR/fence-indented.rs:1:3
    |
 LL | /   ---
 LL | |
@@ -21,7 +21,7 @@ LL | |
    | |_^
    |
 note: frontmatter opening here was not closed
-  --> $DIR/frontmatter-whitespace-1.rs:1:3
+  --> $DIR/fence-indented.rs:1:3
    |
 LL |   ---
    |   ^^^
diff --git a/tests/ui/frontmatter/mismatch-1.rs b/tests/ui/frontmatter/fence-mismatch-1.rs
similarity index 100%
rename from tests/ui/frontmatter/mismatch-1.rs
rename to tests/ui/frontmatter/fence-mismatch-1.rs
diff --git a/tests/ui/frontmatter/mismatch-1.stderr b/tests/ui/frontmatter/fence-mismatch-1.stderr
similarity index 88%
rename from tests/ui/frontmatter/mismatch-1.stderr
rename to tests/ui/frontmatter/fence-mismatch-1.stderr
index b6e29294d9e1..f971039522cd 100644
--- a/tests/ui/frontmatter/mismatch-1.stderr
+++ b/tests/ui/frontmatter/fence-mismatch-1.stderr
@@ -1,5 +1,5 @@
 error: frontmatter close does not match the opening
-  --> $DIR/mismatch-1.rs:1:1
+  --> $DIR/fence-mismatch-1.rs:1:1
    |
 LL |   ---cargo
    |   ^--
diff --git a/tests/ui/frontmatter/mismatch-2.rs b/tests/ui/frontmatter/fence-mismatch-2.rs
similarity index 100%
rename from tests/ui/frontmatter/mismatch-2.rs
rename to tests/ui/frontmatter/fence-mismatch-2.rs
diff --git a/tests/ui/frontmatter/mismatch-2.stderr b/tests/ui/frontmatter/fence-mismatch-2.stderr
similarity index 84%
rename from tests/ui/frontmatter/mismatch-2.stderr
rename to tests/ui/frontmatter/fence-mismatch-2.stderr
index 90bb7b80bcea..8378ae20901e 100644
--- a/tests/ui/frontmatter/mismatch-2.stderr
+++ b/tests/ui/frontmatter/fence-mismatch-2.stderr
@@ -1,5 +1,5 @@
 error: frontmatter close does not match the opening
-  --> $DIR/mismatch-2.rs:1:1
+  --> $DIR/fence-mismatch-2.rs:1:1
    |
 LL |   ----cargo
    |   ^---
@@ -13,7 +13,7 @@ LL | | ---cargo
    |   ...while the close has 3 dashes
 
 error: extra characters after frontmatter close are not allowed
-  --> $DIR/mismatch-2.rs:3:1
+  --> $DIR/fence-mismatch-2.rs:3:1
    |
 LL | ---cargo
    | ^^^^^^^^
diff --git a/tests/ui/frontmatter/unclosed-1.rs b/tests/ui/frontmatter/fence-unclosed-1.rs
similarity index 100%
rename from tests/ui/frontmatter/unclosed-1.rs
rename to tests/ui/frontmatter/fence-unclosed-1.rs
diff --git a/tests/ui/frontmatter/unclosed-1.stderr b/tests/ui/frontmatter/fence-unclosed-1.stderr
similarity index 73%
rename from tests/ui/frontmatter/unclosed-1.stderr
rename to tests/ui/frontmatter/fence-unclosed-1.stderr
index 04031d128398..f96da8cc6689 100644
--- a/tests/ui/frontmatter/unclosed-1.stderr
+++ b/tests/ui/frontmatter/fence-unclosed-1.stderr
@@ -1,5 +1,5 @@
 error: unclosed frontmatter
-  --> $DIR/unclosed-1.rs:1:1
+  --> $DIR/fence-unclosed-1.rs:1:1
    |
 LL | / ----cargo
 ...  |
@@ -7,7 +7,7 @@ LL | |
    | |_^
    |
 note: frontmatter opening here was not closed
-  --> $DIR/unclosed-1.rs:1:1
+  --> $DIR/fence-unclosed-1.rs:1:1
    |
 LL | ----cargo
    | ^^^^
diff --git a/tests/ui/frontmatter/unclosed-2.rs b/tests/ui/frontmatter/fence-unclosed-2.rs
similarity index 100%
rename from tests/ui/frontmatter/unclosed-2.rs
rename to tests/ui/frontmatter/fence-unclosed-2.rs
diff --git a/tests/ui/frontmatter/unclosed-2.stderr b/tests/ui/frontmatter/fence-unclosed-2.stderr
similarity index 86%
rename from tests/ui/frontmatter/unclosed-2.stderr
rename to tests/ui/frontmatter/fence-unclosed-2.stderr
index 0a4022c1557b..7eeb30bed528 100644
--- a/tests/ui/frontmatter/unclosed-2.stderr
+++ b/tests/ui/frontmatter/fence-unclosed-2.stderr
@@ -1,5 +1,5 @@
 error: unclosed frontmatter
-  --> $DIR/unclosed-2.rs:1:1
+  --> $DIR/fence-unclosed-2.rs:1:1
    |
 LL | / ----cargo
 ...  |
@@ -8,13 +8,13 @@ LL | | }
    | |__^
    |
 note: frontmatter opening here was not closed
-  --> $DIR/unclosed-2.rs:1:1
+  --> $DIR/fence-unclosed-2.rs:1:1
    |
 LL | ----cargo
    | ^^^^
 
 error[E0658]: frontmatters are experimental
-  --> $DIR/unclosed-2.rs:1:1
+  --> $DIR/fence-unclosed-2.rs:1:1
    |
 LL | / ----cargo
 ...  |
diff --git a/tests/ui/frontmatter/unclosed-3.rs b/tests/ui/frontmatter/fence-unclosed-3.rs
similarity index 100%
rename from tests/ui/frontmatter/unclosed-3.rs
rename to tests/ui/frontmatter/fence-unclosed-3.rs
diff --git a/tests/ui/frontmatter/unclosed-3.stderr b/tests/ui/frontmatter/fence-unclosed-3.stderr
similarity index 79%
rename from tests/ui/frontmatter/unclosed-3.stderr
rename to tests/ui/frontmatter/fence-unclosed-3.stderr
index cd69cb000408..aa3e8fa3c01d 100644
--- a/tests/ui/frontmatter/unclosed-3.stderr
+++ b/tests/ui/frontmatter/fence-unclosed-3.stderr
@@ -1,17 +1,17 @@
 error: invalid preceding whitespace for frontmatter close
-  --> $DIR/unclosed-3.rs:12:1
+  --> $DIR/fence-unclosed-3.rs:12:1
    |
 LL |     ---x
    | ^^^^^^^^
    |
 note: frontmatter close should not be preceded by whitespace
-  --> $DIR/unclosed-3.rs:12:1
+  --> $DIR/fence-unclosed-3.rs:12:1
    |
 LL |     ---x
    | ^^^^
 
 error: frontmatter close does not match the opening
-  --> $DIR/unclosed-3.rs:1:1
+  --> $DIR/fence-unclosed-3.rs:1:1
    |
 LL |   ----cargo
    |   ^---
@@ -26,13 +26,13 @@ LL | |     ---x
    |   ...while the close has 3 dashes
 
 error: extra characters after frontmatter close are not allowed
-  --> $DIR/unclosed-3.rs:12:1
+  --> $DIR/fence-unclosed-3.rs:12:1
    |
 LL |     ---x
    | ^^^^^^^^
 
 error: unexpected closing delimiter: `}`
-  --> $DIR/unclosed-3.rs:15:1
+  --> $DIR/fence-unclosed-3.rs:15:1
    |
 LL | }
    | ^ unexpected closing delimiter
diff --git a/tests/ui/frontmatter/unclosed-4.rs b/tests/ui/frontmatter/fence-unclosed-4.rs
similarity index 100%
rename from tests/ui/frontmatter/unclosed-4.rs
rename to tests/ui/frontmatter/fence-unclosed-4.rs
diff --git a/tests/ui/frontmatter/unclosed-4.stderr b/tests/ui/frontmatter/fence-unclosed-4.stderr
similarity index 73%
rename from tests/ui/frontmatter/unclosed-4.stderr
rename to tests/ui/frontmatter/fence-unclosed-4.stderr
index b3ba56937bba..cb597d16de28 100644
--- a/tests/ui/frontmatter/unclosed-4.stderr
+++ b/tests/ui/frontmatter/fence-unclosed-4.stderr
@@ -1,5 +1,5 @@
 error: unclosed frontmatter
-  --> $DIR/unclosed-4.rs:1:1
+  --> $DIR/fence-unclosed-4.rs:1:1
    |
 LL | / ----cargo
 LL | |
@@ -7,7 +7,7 @@ LL | |
    | |_^
    |
 note: frontmatter opening here was not closed
-  --> $DIR/unclosed-4.rs:1:1
+  --> $DIR/fence-unclosed-4.rs:1:1
    |
 LL | ----cargo
    | ^^^^
diff --git a/tests/ui/frontmatter/unclosed-5.rs b/tests/ui/frontmatter/fence-unclosed-5.rs
similarity index 100%
rename from tests/ui/frontmatter/unclosed-5.rs
rename to tests/ui/frontmatter/fence-unclosed-5.rs
diff --git a/tests/ui/frontmatter/unclosed-5.stderr b/tests/ui/frontmatter/fence-unclosed-5.stderr
similarity index 85%
rename from tests/ui/frontmatter/unclosed-5.stderr
rename to tests/ui/frontmatter/fence-unclosed-5.stderr
index e904014a175a..09531f3c0341 100644
--- a/tests/ui/frontmatter/unclosed-5.stderr
+++ b/tests/ui/frontmatter/fence-unclosed-5.stderr
@@ -1,5 +1,5 @@
 error: unclosed frontmatter
-  --> $DIR/unclosed-5.rs:1:1
+  --> $DIR/fence-unclosed-5.rs:1:1
    |
 LL | / ----cargo
 ...  |
@@ -7,13 +7,13 @@ LL | |
    | |_^
    |
 note: frontmatter opening here was not closed
-  --> $DIR/unclosed-5.rs:1:1
+  --> $DIR/fence-unclosed-5.rs:1:1
    |
 LL | ----cargo
    | ^^^^
 
 error[E0658]: frontmatters are experimental
-  --> $DIR/unclosed-5.rs:1:1
+  --> $DIR/fence-unclosed-5.rs:1:1
    |
 LL | / ----cargo
 ...  |
diff --git a/tests/ui/frontmatter/unclosed-6.rs b/tests/ui/frontmatter/fence-unclosed-6.rs
similarity index 100%
rename from tests/ui/frontmatter/unclosed-6.rs
rename to tests/ui/frontmatter/fence-unclosed-6.rs
diff --git a/tests/ui/frontmatter/unclosed-6.stderr b/tests/ui/frontmatter/fence-unclosed-6.stderr
similarity index 75%
rename from tests/ui/frontmatter/unclosed-6.stderr
rename to tests/ui/frontmatter/fence-unclosed-6.stderr
index 01a13e87268c..6b653f0781c6 100644
--- a/tests/ui/frontmatter/unclosed-6.stderr
+++ b/tests/ui/frontmatter/fence-unclosed-6.stderr
@@ -1,5 +1,5 @@
 error: unclosed frontmatter
-  --> $DIR/unclosed-6.rs:1:1
+  --> $DIR/fence-unclosed-6.rs:1:1
    |
 LL | / ---
 LL | |
@@ -10,7 +10,7 @@ LL | |
    | |_^
    |
 note: frontmatter opening here was not closed
-  --> $DIR/unclosed-6.rs:1:1
+  --> $DIR/fence-unclosed-6.rs:1:1
    |
 LL | ---
    | ^^^
diff --git a/tests/ui/frontmatter/frontmatter-whitespace-3.rs b/tests/ui/frontmatter/fence-whitespace-trailing-1.rs
similarity index 100%
rename from tests/ui/frontmatter/frontmatter-whitespace-3.rs
rename to tests/ui/frontmatter/fence-whitespace-trailing-1.rs
diff --git a/tests/ui/frontmatter/frontmatter-whitespace-4.rs b/tests/ui/frontmatter/fence-whitespace-trailing-2.rs
similarity index 100%
rename from tests/ui/frontmatter/frontmatter-whitespace-4.rs
rename to tests/ui/frontmatter/fence-whitespace-trailing-2.rs
diff --git a/tests/ui/frontmatter/infostring-fail.rs b/tests/ui/frontmatter/infostring-comma.rs
similarity index 100%
rename from tests/ui/frontmatter/infostring-fail.rs
rename to tests/ui/frontmatter/infostring-comma.rs
diff --git a/tests/ui/frontmatter/infostring-fail.stderr b/tests/ui/frontmatter/infostring-comma.stderr
similarity index 86%
rename from tests/ui/frontmatter/infostring-fail.stderr
rename to tests/ui/frontmatter/infostring-comma.stderr
index 6b264abc90f1..4463dd414c4f 100644
--- a/tests/ui/frontmatter/infostring-fail.stderr
+++ b/tests/ui/frontmatter/infostring-comma.stderr
@@ -1,5 +1,5 @@
 error: invalid infostring for frontmatter
-  --> $DIR/infostring-fail.rs:1:4
+  --> $DIR/infostring-comma.rs:1:4
    |
 LL | ---cargo,clippy
    |    ^^^^^^^^^^^^
diff --git a/tests/ui/frontmatter/dot-in-infostring-leading.rs b/tests/ui/frontmatter/infostring-dot-leading.rs
similarity index 100%
rename from tests/ui/frontmatter/dot-in-infostring-leading.rs
rename to tests/ui/frontmatter/infostring-dot-leading.rs
diff --git a/tests/ui/frontmatter/dot-in-infostring-leading.stderr b/tests/ui/frontmatter/infostring-dot-leading.stderr
similarity index 83%
rename from tests/ui/frontmatter/dot-in-infostring-leading.stderr
rename to tests/ui/frontmatter/infostring-dot-leading.stderr
index bc86bd80eece..8c15f8f7536e 100644
--- a/tests/ui/frontmatter/dot-in-infostring-leading.stderr
+++ b/tests/ui/frontmatter/infostring-dot-leading.stderr
@@ -1,5 +1,5 @@
 error: invalid infostring for frontmatter
-  --> $DIR/dot-in-infostring-leading.rs:1:4
+  --> $DIR/infostring-dot-leading.rs:1:4
    |
 LL | ---.toml
    |    ^^^^^
diff --git a/tests/ui/frontmatter/dot-in-infostring-non-leading.rs b/tests/ui/frontmatter/infostring-dot-nonleading.rs
similarity index 100%
rename from tests/ui/frontmatter/dot-in-infostring-non-leading.rs
rename to tests/ui/frontmatter/infostring-dot-nonleading.rs
diff --git a/tests/ui/frontmatter/hyphen-in-infostring-leading.rs b/tests/ui/frontmatter/infostring-hyphen-leading.rs
similarity index 100%
rename from tests/ui/frontmatter/hyphen-in-infostring-leading.rs
rename to tests/ui/frontmatter/infostring-hyphen-leading.rs
diff --git a/tests/ui/frontmatter/hyphen-in-infostring-leading.stderr b/tests/ui/frontmatter/infostring-hyphen-leading.stderr
similarity index 82%
rename from tests/ui/frontmatter/hyphen-in-infostring-leading.stderr
rename to tests/ui/frontmatter/infostring-hyphen-leading.stderr
index 167b537d62be..646936b92830 100644
--- a/tests/ui/frontmatter/hyphen-in-infostring-leading.stderr
+++ b/tests/ui/frontmatter/infostring-hyphen-leading.stderr
@@ -1,5 +1,5 @@
 error: invalid infostring for frontmatter
-  --> $DIR/hyphen-in-infostring-leading.rs:1:4
+  --> $DIR/infostring-hyphen-leading.rs:1:4
    |
 LL | --- -toml
    |    ^^^^^^
diff --git a/tests/ui/frontmatter/hyphen-in-infostring-non-leading.rs b/tests/ui/frontmatter/infostring-hyphen-nonleading.rs
similarity index 100%
rename from tests/ui/frontmatter/hyphen-in-infostring-non-leading.rs
rename to tests/ui/frontmatter/infostring-hyphen-nonleading.rs
diff --git a/tests/ui/frontmatter/space-in-infostring.rs b/tests/ui/frontmatter/infostring-space.rs
similarity index 100%
rename from tests/ui/frontmatter/space-in-infostring.rs
rename to tests/ui/frontmatter/infostring-space.rs
diff --git a/tests/ui/frontmatter/space-in-infostring.stderr b/tests/ui/frontmatter/infostring-space.stderr
similarity index 86%
rename from tests/ui/frontmatter/space-in-infostring.stderr
rename to tests/ui/frontmatter/infostring-space.stderr
index b876ddae782e..aa8d08d60904 100644
--- a/tests/ui/frontmatter/space-in-infostring.stderr
+++ b/tests/ui/frontmatter/infostring-space.stderr
@@ -1,5 +1,5 @@
 error: invalid infostring for frontmatter
-  --> $DIR/space-in-infostring.rs:1:4
+  --> $DIR/infostring-space.rs:1:4
    |
 LL | --- cargo clippy
    |    ^^^^^^^^^^^^^
diff --git a/tests/ui/frontmatter/shebang.rs b/tests/ui/frontmatter/location-after-shebang.rs
similarity index 100%
rename from tests/ui/frontmatter/shebang.rs
rename to tests/ui/frontmatter/location-after-shebang.rs
diff --git a/tests/ui/frontmatter/frontmatter-after-tokens.rs b/tests/ui/frontmatter/location-after-tokens.rs
similarity index 100%
rename from tests/ui/frontmatter/frontmatter-after-tokens.rs
rename to tests/ui/frontmatter/location-after-tokens.rs
diff --git a/tests/ui/frontmatter/frontmatter-after-tokens.stderr b/tests/ui/frontmatter/location-after-tokens.stderr
similarity index 84%
rename from tests/ui/frontmatter/frontmatter-after-tokens.stderr
rename to tests/ui/frontmatter/location-after-tokens.stderr
index 919456924d02..b8b38167ace5 100644
--- a/tests/ui/frontmatter/frontmatter-after-tokens.stderr
+++ b/tests/ui/frontmatter/location-after-tokens.stderr
@@ -1,5 +1,5 @@
 error: expected item, found `-`
-  --> $DIR/frontmatter-after-tokens.rs:3:1
+  --> $DIR/location-after-tokens.rs:3:1
    |
 LL | ---
    | ^ expected item
diff --git a/tests/ui/frontmatter/include-in-expr-ctxt.rs b/tests/ui/frontmatter/location-include-in-expr-ctxt.rs
similarity index 100%
rename from tests/ui/frontmatter/include-in-expr-ctxt.rs
rename to tests/ui/frontmatter/location-include-in-expr-ctxt.rs
diff --git a/tests/ui/frontmatter/include-in-item-ctxt.rs b/tests/ui/frontmatter/location-include-in-item-ctxt.rs
similarity index 100%
rename from tests/ui/frontmatter/include-in-item-ctxt.rs
rename to tests/ui/frontmatter/location-include-in-item-ctxt.rs
diff --git a/tests/ui/frontmatter/proc-macro-observer.rs b/tests/ui/frontmatter/location-proc-macro-observer.rs
similarity index 100%
rename from tests/ui/frontmatter/proc-macro-observer.rs
rename to tests/ui/frontmatter/location-proc-macro-observer.rs

From 72217cb30453a7da073960b4257b379daf8875c5 Mon Sep 17 00:00:00 2001
From: Daniel Paoliello 
Date: Fri, 24 Oct 2025 09:09:37 -0700
Subject: [PATCH 104/170] Bump dependencies to remove indirect dependencies on
 windows-sys 0.52 and 0.59

---
 src/tools/rust-analyzer/Cargo.lock | 36 ++++++++----------------------
 1 file changed, 9 insertions(+), 27 deletions(-)

diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index b557b10e5c77..535833d9e2a9 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -796,11 +796,11 @@ dependencies = [
 
 [[package]]
 name = "home"
-version = "0.5.11"
+version = "0.5.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
+checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
 dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.61.0",
 ]
 
 [[package]]
@@ -1385,14 +1385,14 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "1.0.4"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
+checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
 dependencies = [
  "libc",
  "log",
  "wasi",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.0",
 ]
 
 [[package]]
@@ -1448,11 +1448,11 @@ checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
 
 [[package]]
 name = "nu-ansi-term"
-version = "0.50.1"
+version = "0.50.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
+checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
 dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
@@ -2973,24 +2973,6 @@ dependencies = [
  "windows-link 0.1.3",
 ]
 
-[[package]]
-name = "windows-sys"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.59.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
 [[package]]
 name = "windows-sys"
 version = "0.60.2"

From e150280005b232f9e4fe64504f21d1466c67075c Mon Sep 17 00:00:00 2001
From: Josh Stone 
Date: Fri, 24 Oct 2025 11:12:06 -0700
Subject: [PATCH 105/170] Bump the version number to 1.93.0

---
 src/version | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/version b/src/version
index 7f229af96476..95784efddbc4 100644
--- a/src/version
+++ b/src/version
@@ -1 +1 @@
-1.92.0
+1.93.0

From 616fff7a52f79cad7d92b64449bff45d22c83ea1 Mon Sep 17 00:00:00 2001
From: Alona Enraght-Moony 
Date: Wed, 22 Oct 2025 21:35:35 +0000
Subject: [PATCH 106/170] compiletest: pass rustdoc mode as param, rather than
 implicitly

Spun out of https://www.github.com/rust-lang/rust/pull/142642

In the future, I want the rustdoc-json test suite to invoke rustdoc
twice, once with `--output-format=json`, and once with the (not yet
implemented) `--output-format=postcard` flag.

Doing that requires being able to explicitly tell the `.document()`
function which format to use, rather then implicitly using json in the
rustdoc-json suite, and HTML in all others.
---
 src/tools/compiletest/src/runtest.rs          | 26 +++++++++++++++----
 src/tools/compiletest/src/runtest/js_doc.rs   |  4 +--
 src/tools/compiletest/src/runtest/rustdoc.rs  |  4 +--
 .../compiletest/src/runtest/rustdoc_json.rs   |  4 +--
 4 files changed, 27 insertions(+), 11 deletions(-)

diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index ff275df9b31c..b2953a6a6b2e 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1000,8 +1000,15 @@ impl<'test> TestCx<'test> {
 
     /// `root_out_dir` and `root_testpaths` refer to the parameters of the actual test being run.
     /// Auxiliaries, no matter how deep, have the same root_out_dir and root_testpaths.
-    fn document(&self, root_out_dir: &Utf8Path, root_testpaths: &TestPaths) -> ProcRes {
+    fn document(
+        &self,
+        root_out_dir: &Utf8Path,
+        root_testpaths: &TestPaths,
+        kind: DocKind,
+    ) -> ProcRes {
         if self.props.build_aux_docs {
+            assert_eq!(kind, DocKind::Html, "build-aux-docs only make sense for html output");
+
             for rel_ab in &self.props.aux.builds {
                 let aux_testpaths = self.compute_aux_test_paths(root_testpaths, rel_ab);
                 let props_for_aux =
@@ -1018,7 +1025,7 @@ impl<'test> TestCx<'test> {
                 create_dir_all(aux_cx.output_base_dir()).unwrap();
                 // use root_testpaths here, because aux-builds should have the
                 // same --out-dir and auxiliary directory.
-                let auxres = aux_cx.document(&root_out_dir, root_testpaths);
+                let auxres = aux_cx.document(&root_out_dir, root_testpaths, kind);
                 if !auxres.status.success() {
                     return auxres;
                 }
@@ -1063,8 +1070,11 @@ impl<'test> TestCx<'test> {
             .args(&self.props.compile_flags)
             .args(&self.props.doc_flags);
 
-        if self.config.mode == TestMode::RustdocJson {
-            rustdoc.arg("--output-format").arg("json").arg("-Zunstable-options");
+        match kind {
+            DocKind::Html => {}
+            DocKind::Json => {
+                rustdoc.arg("--output-format").arg("json").arg("-Zunstable-options");
+            }
         }
 
         if let Some(ref linker) = self.config.target_linker {
@@ -2203,7 +2213,7 @@ impl<'test> TestCx<'test> {
         let aux_dir = new_rustdoc.aux_output_dir();
         new_rustdoc.build_all_auxiliary(&new_rustdoc.testpaths, &aux_dir, &mut rustc);
 
-        let proc_res = new_rustdoc.document(&compare_dir, &new_rustdoc.testpaths);
+        let proc_res = new_rustdoc.document(&compare_dir, &new_rustdoc.testpaths, DocKind::Html);
         if !proc_res.status.success() {
             writeln!(self.stderr, "failed to run nightly rustdoc");
             return;
@@ -3121,6 +3131,12 @@ enum CompareOutcome {
     Differed,
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+enum DocKind {
+    Html,
+    Json,
+}
+
 impl CompareOutcome {
     fn should_error(&self) -> bool {
         matches!(self, CompareOutcome::Differed)
diff --git a/src/tools/compiletest/src/runtest/js_doc.rs b/src/tools/compiletest/src/runtest/js_doc.rs
index fd53f01ca174..93b05617e6f8 100644
--- a/src/tools/compiletest/src/runtest/js_doc.rs
+++ b/src/tools/compiletest/src/runtest/js_doc.rs
@@ -1,13 +1,13 @@
 use std::process::Command;
 
-use super::TestCx;
+use super::{DocKind, TestCx};
 
 impl TestCx<'_> {
     pub(super) fn run_rustdoc_js_test(&self) {
         if let Some(nodejs) = &self.config.nodejs {
             let out_dir = self.output_base_dir();
 
-            self.document(&out_dir, &self.testpaths);
+            self.document(&out_dir, &self.testpaths, DocKind::Html);
 
             let file_stem = self.testpaths.file.file_stem().expect("no file stem");
             let res = self.run_command_to_procres(
diff --git a/src/tools/compiletest/src/runtest/rustdoc.rs b/src/tools/compiletest/src/runtest/rustdoc.rs
index 4a143aa5824e..32b1823961be 100644
--- a/src/tools/compiletest/src/runtest/rustdoc.rs
+++ b/src/tools/compiletest/src/runtest/rustdoc.rs
@@ -1,6 +1,6 @@
 use std::process::Command;
 
-use super::{TestCx, remove_and_create_dir_all};
+use super::{DocKind, TestCx, remove_and_create_dir_all};
 
 impl TestCx<'_> {
     pub(super) fn run_rustdoc_test(&self) {
@@ -11,7 +11,7 @@ impl TestCx<'_> {
             panic!("failed to remove and recreate output directory `{out_dir}`: {e}")
         });
 
-        let proc_res = self.document(&out_dir, &self.testpaths);
+        let proc_res = self.document(&out_dir, &self.testpaths, DocKind::Html);
         if !proc_res.status.success() {
             self.fatal_proc_rec("rustdoc failed!", &proc_res);
         }
diff --git a/src/tools/compiletest/src/runtest/rustdoc_json.rs b/src/tools/compiletest/src/runtest/rustdoc_json.rs
index b8da6e2ac528..dd7ebe9efaee 100644
--- a/src/tools/compiletest/src/runtest/rustdoc_json.rs
+++ b/src/tools/compiletest/src/runtest/rustdoc_json.rs
@@ -1,6 +1,6 @@
 use std::process::Command;
 
-use super::{TestCx, remove_and_create_dir_all};
+use super::{DocKind, TestCx, remove_and_create_dir_all};
 
 impl TestCx<'_> {
     pub(super) fn run_rustdoc_json_test(&self) {
@@ -13,7 +13,7 @@ impl TestCx<'_> {
             panic!("failed to remove and recreate output directory `{out_dir}`: {e}")
         });
 
-        let proc_res = self.document(&out_dir, &self.testpaths);
+        let proc_res = self.document(&out_dir, &self.testpaths, DocKind::Json);
         if !proc_res.status.success() {
             self.fatal_proc_rec("rustdoc failed!", &proc_res);
         }

From 354b9779bf9b021c56f3ea14ed338e34377aa575 Mon Sep 17 00:00:00 2001
From: Scott Schafer 
Date: Sat, 29 Mar 2025 09:48:39 -0600
Subject: [PATCH 107/170] chore: Update to the latest annotate-snippets

---
 Cargo.lock                                    |  34 +-
 compiler/rustc_errors/Cargo.toml              |   2 +-
 .../src/annotate_snippet_emitter_writer.rs    | 786 +++++++++++++++---
 compiler/rustc_session/src/session.rs         |  19 +-
 tests/ui/annotate-snippet/missing-type.stderr |  15 +
 .../ui/annotate-snippet/multiple-files.stderr |   7 +-
 tests/ui/annotate-snippet/multispan.stderr    |  51 ++
 7 files changed, 786 insertions(+), 128 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 2c290b392d59..2320e33bc4bf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -75,7 +75,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4"
 dependencies = [
  "anstyle",
- "unicode-width 0.2.1",
+ "unicode-width 0.2.2",
+]
+
+[[package]]
+name = "annotate-snippets"
+version = "0.12.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47224528f74de27d1d06aad6a5dda4f865b6ebe2e56c538943d746a7270cb67e"
+dependencies = [
+ "anstyle",
+ "unicode-width 0.2.2",
 ]
 
 [[package]]
@@ -136,7 +146,7 @@ dependencies = [
  "anstyle-lossy",
  "anstyle-parse",
  "html-escape",
- "unicode-width 0.2.1",
+ "unicode-width 0.2.2",
 ]
 
 [[package]]
@@ -677,7 +687,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
 dependencies = [
  "serde",
  "termcolor",
- "unicode-width 0.2.1",
+ "unicode-width 0.2.2",
 ]
 
 [[package]]
@@ -808,7 +818,7 @@ dependencies = [
  "encode_unicode",
  "libc",
  "once_cell",
- "unicode-width 0.2.1",
+ "unicode-width 0.2.2",
  "windows-sys 0.59.0",
 ]
 
@@ -1485,7 +1495,7 @@ version = "0.2.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df"
 dependencies = [
- "unicode-width 0.2.1",
+ "unicode-width 0.2.2",
 ]
 
 [[package]]
@@ -1887,7 +1897,7 @@ dependencies = [
  "console",
  "number_prefix",
  "portable-atomic",
- "unicode-width 0.2.1",
+ "unicode-width 0.2.2",
  "web-time",
 ]
 
@@ -3756,7 +3766,7 @@ dependencies = [
 name = "rustc_errors"
 version = "0.0.0"
 dependencies = [
- "annotate-snippets 0.11.5",
+ "annotate-snippets 0.12.7",
  "anstream",
  "anstyle",
  "derive_setters",
@@ -4331,7 +4341,7 @@ dependencies = [
  "thin-vec",
  "tracing",
  "unicode-normalization",
- "unicode-width 0.2.1",
+ "unicode-width 0.2.2",
 ]
 
 [[package]]
@@ -4590,7 +4600,7 @@ dependencies = [
  "sha1",
  "sha2",
  "tracing",
- "unicode-width 0.2.1",
+ "unicode-width 0.2.2",
 ]
 
 [[package]]
@@ -5936,9 +5946,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
 
 [[package]]
 name = "unicode-width"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
+checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
 
 [[package]]
 name = "unicode-xid"
@@ -6223,7 +6233,7 @@ dependencies = [
  "bumpalo",
  "leb128fmt",
  "memchr",
- "unicode-width 0.2.1",
+ "unicode-width 0.2.2",
  "wasm-encoder 0.240.0",
 ]
 
diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml
index b7b5cbd35741..6ade87ea3b25 100644
--- a/compiler/rustc_errors/Cargo.toml
+++ b/compiler/rustc_errors/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-annotate-snippets = "0.11"
+annotate-snippets = "0.12.7"
 anstream = "0.6.20"
 anstyle = "1.0.13"
 derive_setters = "0.1.6"
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index 2eb3c23259ff..854e3ddf15e4 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -5,32 +5,70 @@
 //!
 //! [annotate_snippets]: https://docs.rs/crate/annotate-snippets/
 
+use std::borrow::Cow;
+use std::error::Report;
+use std::fmt::Debug;
+use std::io;
+use std::io::Write;
 use std::sync::Arc;
 
-use annotate_snippets::{Renderer, Snippet};
-use rustc_error_messages::FluentArgs;
-use rustc_span::SourceFile;
+use annotate_snippets::renderer::DEFAULT_TERM_WIDTH;
+use annotate_snippets::{AnnotationKind, Group, Origin, Padding, Patch, Renderer, Snippet};
+use anstream::ColorChoice;
+use derive_setters::Setters;
+use rustc_data_structures::sync::IntoDynSyncSend;
+use rustc_error_messages::{FluentArgs, SpanLabel};
+use rustc_lint_defs::pluralize;
 use rustc_span::source_map::SourceMap;
+use rustc_span::{BytePos, FileName, Pos, SourceFile, Span};
+use tracing::debug;
 
-use crate::emitter::FileWithAnnotatedLines;
+use crate::emitter::{
+    ConfusionType, Destination, MAX_SUGGESTIONS, OutputTheme, detect_confusion_type, is_different,
+    normalize_whitespace, should_show_source_code,
+};
 use crate::registry::Registry;
-use crate::snippet::Line;
 use crate::translation::{Translator, to_fluent_args};
 use crate::{
     CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, Level, MultiSpan, Style, Subdiag,
+    SuggestionStyle, TerminalUrl,
 };
 
 /// Generates diagnostics using annotate-snippet
+#[derive(Setters)]
 pub struct AnnotateSnippetEmitter {
-    source_map: Option>,
+    #[setters(skip)]
+    dst: IntoDynSyncSend,
+    sm: Option>,
+    #[setters(skip)]
     translator: Translator,
-
-    /// If true, hides the longer explanation text
     short_message: bool,
-    /// If true, will normalize line numbers with `LL` to prevent noise in UI test diffs.
     ui_testing: bool,
+    ignored_directories_in_source_blocks: Vec,
+    diagnostic_width: Option,
 
     macro_backtrace: bool,
+    track_diagnostics: bool,
+    terminal_url: TerminalUrl,
+    theme: OutputTheme,
+}
+
+impl Debug for AnnotateSnippetEmitter {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("AnnotateSnippetEmitter")
+            .field("short_message", &self.short_message)
+            .field("ui_testing", &self.ui_testing)
+            .field(
+                "ignored_directories_in_source_blocks",
+                &self.ignored_directories_in_source_blocks,
+            )
+            .field("diagnostic_width", &self.diagnostic_width)
+            .field("macro_backtrace", &self.macro_backtrace)
+            .field("track_diagnostics", &self.track_diagnostics)
+            .field("terminal_url", &self.terminal_url)
+            .field("theme", &self.theme)
+            .finish()
+    }
 }
 
 impl Emitter for AnnotateSnippetEmitter {
@@ -38,6 +76,10 @@ impl Emitter for AnnotateSnippetEmitter {
     fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) {
         let fluent_args = to_fluent_args(diag.args.iter());
 
+        if self.track_diagnostics && diag.span.has_primary_spans() && !diag.span.is_dummy() {
+            diag.children.insert(0, diag.emitted_at_sub_diag());
+        }
+
         let mut suggestions = diag.suggestions.unwrap_tag();
         self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);
 
@@ -55,12 +97,12 @@ impl Emitter for AnnotateSnippetEmitter {
             &diag.code,
             &diag.span,
             &diag.children,
-            &suggestions,
+            suggestions,
         );
     }
 
     fn source_map(&self) -> Option<&SourceMap> {
-        self.source_map.as_deref()
+        self.sm.as_deref()
     }
 
     fn should_show_explain(&self) -> bool {
@@ -70,128 +112,648 @@ impl Emitter for AnnotateSnippetEmitter {
     fn translator(&self) -> &Translator {
         &self.translator
     }
+
+    fn supports_color(&self) -> bool {
+        false
+    }
 }
 
-/// Provides the source string for the given `line` of `file`
-fn source_string(file: Arc, line: &Line) -> String {
-    file.get_line(line.line_index - 1).map(|a| a.to_string()).unwrap_or_default()
-}
-
-/// Maps [`crate::Level`] to [`annotate_snippets::Level`]
-fn annotation_level_for_level(level: Level) -> annotate_snippets::Level {
+fn annotation_level_for_level(level: Level) -> annotate_snippets::level::Level<'static> {
     match level {
-        Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => {
-            annotate_snippets::Level::Error
+        Level::Bug | Level::DelayedBug => {
+            annotate_snippets::Level::ERROR.with_name("error: internal compiler error")
         }
-        Level::ForceWarning | Level::Warning => annotate_snippets::Level::Warning,
-        Level::Note | Level::OnceNote => annotate_snippets::Level::Note,
-        Level::Help | Level::OnceHelp => annotate_snippets::Level::Help,
-        // FIXME(#59346): Not sure how to map this level
-        Level::FailureNote => annotate_snippets::Level::Error,
+        Level::Fatal | Level::Error => annotate_snippets::level::ERROR,
+        Level::ForceWarning | Level::Warning => annotate_snippets::Level::WARNING,
+        Level::Note | Level::OnceNote => annotate_snippets::Level::NOTE,
+        Level::Help | Level::OnceHelp => annotate_snippets::Level::HELP,
+        Level::FailureNote => annotate_snippets::Level::NOTE.no_name(),
         Level::Allow => panic!("Should not call with Allow"),
         Level::Expect => panic!("Should not call with Expect"),
     }
 }
 
 impl AnnotateSnippetEmitter {
-    pub fn new(
-        source_map: Option>,
-        translator: Translator,
-        short_message: bool,
-        macro_backtrace: bool,
-    ) -> Self {
-        Self { source_map, translator, short_message, ui_testing: false, macro_backtrace }
-    }
-
-    /// Allows to modify `Self` to enable or disable the `ui_testing` flag.
-    ///
-    /// If this is set to true, line numbers will be normalized as `LL` in the output.
-    pub fn ui_testing(mut self, ui_testing: bool) -> Self {
-        self.ui_testing = ui_testing;
-        self
+    pub fn new(dst: Destination, translator: Translator) -> Self {
+        Self {
+            dst: IntoDynSyncSend(dst),
+            sm: None,
+            translator,
+            short_message: false,
+            ui_testing: false,
+            ignored_directories_in_source_blocks: Vec::new(),
+            diagnostic_width: None,
+            macro_backtrace: false,
+            track_diagnostics: false,
+            terminal_url: TerminalUrl::No,
+            theme: OutputTheme::Ascii,
+        }
     }
 
     fn emit_messages_default(
         &mut self,
         level: &Level,
-        messages: &[(DiagMessage, Style)],
+        msgs: &[(DiagMessage, Style)],
         args: &FluentArgs<'_>,
         code: &Option,
         msp: &MultiSpan,
-        _children: &[Subdiag],
-        _suggestions: &[CodeSuggestion],
+        children: &[Subdiag],
+        suggestions: Vec,
     ) {
-        let message = self.translator.translate_messages(messages, args);
-        if let Some(source_map) = &self.source_map {
-            // Make sure our primary file comes first
-            let primary_lo = if let Some(primary_span) = msp.primary_span().as_ref() {
-                if primary_span.is_dummy() {
-                    // FIXME(#59346): Not sure when this is the case and what
-                    // should be done if it happens
-                    return;
-                } else {
-                    source_map.lookup_char_pos(primary_span.lo())
-                }
-            } else {
-                // FIXME(#59346): Not sure when this is the case and what
-                // should be done if it happens
-                return;
-            };
-            let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp);
-            if let Ok(pos) =
-                annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name))
-            {
-                annotated_files.swap(0, pos);
-            }
-            // owned: file name, line source, line index, annotations
-            type Owned = (String, String, usize, Vec);
-            let annotated_files: Vec = annotated_files
-                .into_iter()
-                .flat_map(|annotated_file| {
-                    let file = annotated_file.file;
-                    annotated_file
-                        .lines
-                        .into_iter()
-                        .map(|line| {
-                            // Ensure the source file is present before we try
-                            // to load a string from it.
-                            // FIXME(#115869): support -Z ignore-directory-in-diagnostics-source-blocks
-                            source_map.ensure_source_file_source_present(&file);
-                            (
-                                format!("{}", source_map.filename_for_diagnostics(&file.name)),
-                                source_string(Arc::clone(&file), &line),
-                                line.line_index,
-                                line.annotations,
-                            )
-                        })
-                        .collect::>()
-                })
-                .collect();
-            let code = code.map(|code| code.to_string());
+        let renderer = self.renderer();
+        let annotation_level = annotation_level_for_level(*level);
 
-            let snippets =
-                annotated_files.iter().map(|(file_name, source, line_index, annotations)| {
-                    Snippet::source(source)
-                        .line_start(*line_index)
-                        .origin(file_name)
-                        // FIXME(#59346): Not really sure when `fold` should be true or false
-                        .fold(false)
-                        .annotations(annotations.iter().map(|annotation| {
-                            annotation_level_for_level(*level)
-                                .span(annotation.start_col.display..annotation.end_col.display)
-                                .label(annotation.label.as_deref().unwrap_or_default())
-                        }))
-                });
-            let mut message = annotation_level_for_level(*level).title(&message).snippets(snippets);
-            if let Some(code) = code.as_deref() {
-                message = message.id(code)
+        // If at least one portion of the message is styled, we need to
+        // "pre-style" the message
+        let mut title = if msgs.iter().any(|(_, style)| style != &crate::Style::NoStyle) {
+            annotation_level
+                .clone()
+                .secondary_title(Cow::Owned(self.pre_style_msgs(msgs, *level, args)))
+        } else {
+            annotation_level.clone().primary_title(self.translator.translate_messages(msgs, args))
+        };
+
+        if let Some(c) = code {
+            title = title.id(c.to_string());
+            if let TerminalUrl::Yes = self.terminal_url {
+                title = title.id_url(format!("https://doc.rust-lang.org/error_codes/{c}.html"));
             }
-            // FIXME(#59346): Figure out if we can _always_ print to stderr or not.
-            // `emitter.rs` has the `Destination` enum that lists various possible output
-            // destinations.
-            let renderer = Renderer::plain().anonymized_line_numbers(self.ui_testing);
-            eprintln!("{}", renderer.render(message))
         }
-        // FIXME(#59346): Is it ok to return None if there's no source_map?
+
+        let mut report = vec![];
+        let mut group = Group::with_title(title);
+
+        // If we don't have span information, emit and exit
+        let Some(sm) = self.sm.as_ref() else {
+            group = group.elements(children.iter().map(|c| {
+                let msg = self.translator.translate_messages(&c.messages, args).to_string();
+                let level = annotation_level_for_level(c.level);
+                level.message(msg)
+            }));
+
+            report.push(group);
+            if let Err(e) = emit_to_destination(
+                renderer.render(&report),
+                level,
+                &mut self.dst,
+                self.short_message,
+            ) {
+                panic!("failed to emit error: {e}");
+            }
+            return;
+        };
+
+        let mut file_ann = collect_annotations(args, msp, sm, &self.translator);
+
+        // Make sure our primary file comes first
+        let primary_span = msp.primary_span().unwrap_or_default();
+        if !primary_span.is_dummy() {
+            let primary_lo = sm.lookup_char_pos(primary_span.lo());
+            if let Ok(pos) = file_ann.binary_search_by(|(f, _)| f.name.cmp(&primary_lo.file.name)) {
+                file_ann.swap(0, pos);
+            }
+
+            for (file_idx, (file, annotations)) in file_ann.into_iter().enumerate() {
+                if should_show_source_code(&self.ignored_directories_in_source_blocks, sm, &file) {
+                    if let Some(snippet) = self.annotated_snippet(annotations, &file.name, sm) {
+                        group = group.element(snippet);
+                    }
+                // we can't annotate anything if the source is unavailable.
+                } else if !self.short_message {
+                    // We'll just print unannotated messages
+                    group = self.unannotated_messages(
+                        annotations,
+                        &file.name,
+                        sm,
+                        file_idx,
+                        &mut report,
+                        group,
+                        &annotation_level,
+                    );
+                    // If this is the last annotation for a file, and
+                    // this is the last file, and the first child is a
+                    // "secondary" message, we need to add padding
+                    // ╭▸ /rustc/FAKE_PREFIX/library/core/src/clone.rs:236:13
+                    // │
+                    // ├ note: the late bound lifetime parameter
+                    // │ (<- It adds *this*)
+                    // ╰ warning: this was previously accepted
+                    if let Some(c) = children.first()
+                        && (!c.span.has_primary_spans() && !c.span.has_span_labels())
+                    {
+                        group = group.element(Padding);
+                    }
+                }
+            }
+        }
+
+        for c in children {
+            let level = annotation_level_for_level(c.level);
+
+            // If at least one portion of the message is styled, we need to
+            // "pre-style" the message
+            let msg = if c.messages.iter().any(|(_, style)| style != &crate::Style::NoStyle) {
+                Cow::Owned(self.pre_style_msgs(&c.messages, c.level, args))
+            } else {
+                self.translator.translate_messages(&c.messages, args)
+            };
+
+            // This is a secondary message with no span info
+            if !c.span.has_primary_spans() && !c.span.has_span_labels() {
+                group = group.element(level.clone().message(msg));
+                continue;
+            }
+
+            report.push(std::mem::replace(
+                &mut group,
+                Group::with_title(level.clone().secondary_title(msg)),
+            ));
+
+            let mut file_ann = collect_annotations(args, &c.span, sm, &self.translator);
+            let primary_span = c.span.primary_span().unwrap_or_default();
+            if !primary_span.is_dummy() {
+                let primary_lo = sm.lookup_char_pos(primary_span.lo());
+                if let Ok(pos) =
+                    file_ann.binary_search_by(|(f, _)| f.name.cmp(&primary_lo.file.name))
+                {
+                    file_ann.swap(0, pos);
+                }
+            }
+
+            for (file_idx, (file, annotations)) in file_ann.into_iter().enumerate() {
+                if should_show_source_code(&self.ignored_directories_in_source_blocks, sm, &file) {
+                    if let Some(snippet) = self.annotated_snippet(annotations, &file.name, sm) {
+                        group = group.element(snippet);
+                    }
+                // we can't annotate anything if the source is unavailable.
+                } else if !self.short_message {
+                    // We'll just print unannotated messages
+                    group = self.unannotated_messages(
+                        annotations,
+                        &file.name,
+                        sm,
+                        file_idx,
+                        &mut report,
+                        group,
+                        &level,
+                    );
+                }
+            }
+        }
+
+        let suggestions_expected = suggestions
+            .iter()
+            .filter(|s| {
+                matches!(
+                    s.style,
+                    SuggestionStyle::HideCodeInline
+                        | SuggestionStyle::ShowCode
+                        | SuggestionStyle::ShowAlways
+                )
+            })
+            .count();
+        for suggestion in suggestions {
+            match suggestion.style {
+                SuggestionStyle::CompletelyHidden => {
+                    // do not display this suggestion, it is meant only for tools
+                }
+                SuggestionStyle::HideCodeAlways => {
+                    let msg = self
+                        .translator
+                        .translate_messages(&[(suggestion.msg.to_owned(), Style::HeaderMsg)], args);
+                    group = group.element(annotate_snippets::Level::HELP.message(msg));
+                }
+                SuggestionStyle::HideCodeInline
+                | SuggestionStyle::ShowCode
+                | SuggestionStyle::ShowAlways => {
+                    let substitutions = suggestion
+                        .substitutions
+                        .into_iter()
+                        .filter_map(|mut subst| {
+                            // Suggestions coming from macros can have malformed spans. This is a heavy
+                            // handed approach to avoid ICEs by ignoring the suggestion outright.
+                            let invalid =
+                                subst.parts.iter().any(|item| sm.is_valid_span(item.span).is_err());
+                            if invalid {
+                                debug!("suggestion contains an invalid span: {:?}", subst);
+                            }
+
+                            // Assumption: all spans are in the same file, and all spans
+                            // are disjoint. Sort in ascending order.
+                            subst.parts.sort_by_key(|part| part.span.lo());
+                            // Verify the assumption that all spans are disjoint
+                            assert_eq!(
+                                subst.parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
+                                None,
+                                "all spans must be disjoint",
+                            );
+
+                            // Account for cases where we are suggesting the same code that's already
+                            // there. This shouldn't happen often, but in some cases for multipart
+                            // suggestions it's much easier to handle it here than in the origin.
+                            subst.parts.retain(|p| is_different(sm, &p.snippet, p.span));
+
+                            let item_span = subst.parts.first()?;
+                            let file = sm.lookup_source_file(item_span.span.lo());
+                            if !invalid
+                                && should_show_source_code(
+                                    &self.ignored_directories_in_source_blocks,
+                                    sm,
+                                    &file,
+                                )
+                            {
+                                Some(subst)
+                            } else {
+                                None
+                            }
+                        })
+                        .collect::>();
+
+                    if substitutions.is_empty() {
+                        continue;
+                    }
+                    let mut msg = self
+                        .translator
+                        .translate_message(&suggestion.msg, args)
+                        .map_err(Report::new)
+                        .unwrap()
+                        .to_string();
+
+                    let lo = substitutions
+                        .iter()
+                        .find_map(|sub| sub.parts.first().map(|p| p.span.lo()))
+                        .unwrap();
+                    let file = sm.lookup_source_file(lo);
+
+                    let filename =
+                        sm.filename_for_diagnostics(&file.name).to_string_lossy().to_string();
+
+                    let other_suggestions = substitutions.len().saturating_sub(MAX_SUGGESTIONS);
+
+                    let subs = substitutions
+                        .into_iter()
+                        .take(MAX_SUGGESTIONS)
+                        .filter_map(|sub| {
+                            let mut confusion_type = ConfusionType::None;
+                            for part in &sub.parts {
+                                let part_confusion =
+                                    detect_confusion_type(sm, &part.snippet, part.span);
+                                confusion_type = confusion_type.combine(part_confusion);
+                            }
+
+                            if !matches!(confusion_type, ConfusionType::None) {
+                                msg.push_str(confusion_type.label_text());
+                            }
+
+                            let mut parts = sub
+                                .parts
+                                .into_iter()
+                                .filter_map(|p| {
+                                    if is_different(sm, &p.snippet, p.span) {
+                                        Some((p.span, p.snippet))
+                                    } else {
+                                        None
+                                    }
+                                })
+                                .collect::>();
+
+                            if parts.is_empty() {
+                                None
+                            } else {
+                                let spans = parts.iter().map(|(span, _)| *span).collect::>();
+                                // The suggestion adds an entire line of code, ending on a newline, so we'll also
+                                // print the *following* line, to provide context of what we're advising people to
+                                // do. Otherwise you would only see contextless code that can be confused for
+                                // already existing code, despite the colors and UI elements.
+                                // We special case `#[derive(_)]\n` and other attribute suggestions, because those
+                                // are the ones where context is most useful.
+                                let fold = if let [(p, snippet)] = &mut parts[..]
+                                    && snippet.trim().starts_with("#[")
+                                    // This allows for spaces to come between the attribute and the newline
+                                    && snippet.trim().ends_with("]")
+                                    && snippet.ends_with('\n')
+                                    && p.hi() == p.lo()
+                                    && let Ok(b) = sm.span_to_prev_source(*p)
+                                    && let b = b.rsplit_once('\n').unwrap_or_else(|| ("", &b)).1
+                                    && b.trim().is_empty()
+                                {
+                                    // FIXME: This is a hack:
+                                    // The span for attribute suggestions often times points to the
+                                    // beginning of an item, disregarding leading whitespace. This
+                                    // causes the attribute to be properly indented, but leaves original
+                                    // item without indentation when rendered.
+                                    // This fixes that problem by adjusting the span to point to the start
+                                    // of the whitespace, and adds the whitespace to the replacement.
+                                    //
+                                    // Source: "    extern "custom" fn negate(a: i64) -> i64 {\n"
+                                    // Span: 4..4
+                                    // Replacement: "#[unsafe(naked)]\n"
+                                    //
+                                    // Before:
+                                    // help: convert this to an `#[unsafe(naked)]` function
+                                    //    |
+                                    // LL +     #[unsafe(naked)]
+                                    // LL | extern "custom" fn negate(a: i64) -> i64 {
+                                    //    |
+                                    //
+                                    // After
+                                    // help: convert this to an `#[unsafe(naked)]` function
+                                    //    |
+                                    // LL +     #[unsafe(naked)]
+                                    // LL |     extern "custom" fn negate(a: i64) -> i64 {
+                                    //    |
+                                    if !b.is_empty() && !snippet.ends_with(b) {
+                                        snippet.insert_str(0, b);
+                                        let offset = BytePos(b.len() as u32);
+                                        *p = p.with_lo(p.lo() - offset).shrink_to_lo();
+                                    }
+                                    false
+                                } else {
+                                    true
+                                };
+
+                                if let Some((bounding_span, source, line_offset)) =
+                                    shrink_file(spans.as_slice(), &file.name, sm)
+                                {
+                                    let adj_lo = bounding_span.lo().to_usize();
+                                    Some(
+                                        Snippet::source(source)
+                                            .line_start(line_offset)
+                                            .path(filename.clone())
+                                            .fold(fold)
+                                            .patches(parts.into_iter().map(
+                                                |(span, replacement)| {
+                                                    let lo =
+                                                        span.lo().to_usize().saturating_sub(adj_lo);
+                                                    let hi =
+                                                        span.hi().to_usize().saturating_sub(adj_lo);
+
+                                                    Patch::new(lo..hi, replacement)
+                                                },
+                                            )),
+                                    )
+                                } else {
+                                    None
+                                }
+                            }
+                        })
+                        .collect::>();
+                    if !subs.is_empty() {
+                        report.push(std::mem::replace(
+                            &mut group,
+                            Group::with_title(annotate_snippets::Level::HELP.secondary_title(msg)),
+                        ));
+
+                        group = group.elements(subs);
+                        if other_suggestions > 0 {
+                            group = group.element(
+                                annotate_snippets::Level::NOTE.no_name().message(format!(
+                                    "and {} other candidate{}",
+                                    other_suggestions,
+                                    pluralize!(other_suggestions)
+                                )),
+                            );
+                        }
+                    }
+                }
+            }
+        }
+
+        // FIXME: This hack should be removed once annotate_snippets is the
+        // default emitter.
+        if suggestions_expected > 0 && report.is_empty() {
+            group = group.element(Padding);
+        }
+
+        if !group.is_empty() {
+            report.push(group);
+        }
+        if let Err(e) =
+            emit_to_destination(renderer.render(&report), level, &mut self.dst, self.short_message)
+        {
+            panic!("failed to emit error: {e}");
+        }
+    }
+
+    fn renderer(&self) -> Renderer {
+        let width = if let Some(width) = self.diagnostic_width {
+            width
+        } else if self.ui_testing || cfg!(miri) {
+            DEFAULT_TERM_WIDTH
+        } else {
+            termize::dimensions().map(|(w, _)| w).unwrap_or(DEFAULT_TERM_WIDTH)
+        };
+        let decor_style = match self.theme {
+            OutputTheme::Ascii => annotate_snippets::renderer::DecorStyle::Ascii,
+            OutputTheme::Unicode => annotate_snippets::renderer::DecorStyle::Unicode,
+        };
+
+        match self.dst.current_choice() {
+            ColorChoice::AlwaysAnsi | ColorChoice::Always | ColorChoice::Auto => Renderer::styled(),
+            ColorChoice::Never => Renderer::plain(),
+        }
+        .term_width(width)
+        .anonymized_line_numbers(self.ui_testing)
+        .decor_style(decor_style)
+        .short_message(self.short_message)
+    }
+
+    fn pre_style_msgs(
+        &self,
+        msgs: &[(DiagMessage, Style)],
+        level: Level,
+        args: &FluentArgs<'_>,
+    ) -> String {
+        msgs.iter()
+            .filter_map(|(m, style)| {
+                let text = self.translator.translate_message(m, args).map_err(Report::new).unwrap();
+                let style = style.anstyle(level);
+                if text.is_empty() { None } else { Some(format!("{style}{text}{style:#}")) }
+            })
+            .collect()
+    }
+
+    fn annotated_snippet<'a>(
+        &self,
+        annotations: Vec,
+        file_name: &FileName,
+        sm: &Arc,
+    ) -> Option>> {
+        let spans = annotations.iter().map(|a| a.span).collect::>();
+        if let Some((bounding_span, source, offset_line)) = shrink_file(&spans, file_name, sm) {
+            let adj_lo = bounding_span.lo().to_usize();
+            let filename = sm.filename_for_diagnostics(file_name).to_string_lossy().to_string();
+            Some(Snippet::source(source).line_start(offset_line).path(filename).annotations(
+                annotations.into_iter().map(move |a| {
+                    let lo = a.span.lo().to_usize().saturating_sub(adj_lo);
+                    let hi = a.span.hi().to_usize().saturating_sub(adj_lo);
+                    let ann = a.kind.span(lo..hi);
+                    if let Some(label) = a.label { ann.label(label) } else { ann }
+                }),
+            ))
+        } else {
+            None
+        }
+    }
+
+    fn unannotated_messages<'a>(
+        &self,
+        annotations: Vec,
+        file_name: &FileName,
+        sm: &Arc,
+        file_idx: usize,
+        report: &mut Vec>,
+        mut group: Group<'a>,
+        level: &annotate_snippets::level::Level<'static>,
+    ) -> Group<'a> {
+        let filename = sm.filename_for_diagnostics(file_name).to_string_lossy().to_string();
+        let mut line_tracker = vec![];
+        for (i, a) in annotations.into_iter().enumerate() {
+            let lo = sm.lookup_char_pos(a.span.lo());
+            let hi = sm.lookup_char_pos(a.span.hi());
+            if i == 0 || (a.label.is_some()) {
+                // Render each new file after the first in its own Group
+                //    ╭▸ $DIR/deriving-meta-unknown-trait.rs:1:10
+                //    │
+                // LL │ #[derive(Eqr)]
+                //    │          ━━━
+                //    ╰╴ (<- It makes it so *this* will get printed)
+                //    ╭▸ $SRC_DIR/core/src/option.rs:594:0
+                //    ⸬  $SRC_DIR/core/src/option.rs:602:4
+                //    │
+                //    ╰ note: not covered
+                if i == 0 && file_idx != 0 {
+                    report.push(std::mem::replace(&mut group, Group::with_level(level.clone())));
+                }
+
+                if !line_tracker.contains(&lo.line) {
+                    line_tracker.push(lo.line);
+                    // ╭▸ $SRC_DIR/core/src/option.rs:594:0 (<- It adds *this*)
+                    // ⸬  $SRC_DIR/core/src/option.rs:602:4
+                    // │
+                    // ╰ note: not covered
+                    group = group.element(
+                        Origin::path(filename.clone())
+                            .line(sm.doctest_offset_line(file_name, lo.line))
+                            .char_column(lo.col_display),
+                    );
+                }
+
+                if hi.line > lo.line
+                    && a.label.as_ref().is_some_and(|l| !l.is_empty())
+                    && !line_tracker.contains(&hi.line)
+                {
+                    line_tracker.push(hi.line);
+                    // ╭▸ $SRC_DIR/core/src/option.rs:594:0
+                    // ⸬  $SRC_DIR/core/src/option.rs:602:4 (<- It adds *this*)
+                    // │
+                    // ╰ note: not covered
+                    group = group.element(
+                        Origin::path(filename.clone())
+                            .line(sm.doctest_offset_line(file_name, hi.line))
+                            .char_column(hi.col_display),
+                    );
+                }
+
+                if let Some(label) = a.label
+                    && !label.is_empty()
+                {
+                    // ╭▸ $SRC_DIR/core/src/option.rs:594:0
+                    // ⸬  $SRC_DIR/core/src/option.rs:602:4
+                    // │ (<- It adds *this*)
+                    // ╰ note: not covered (<- and *this*)
+                    group = group
+                        .element(Padding)
+                        .element(annotate_snippets::Level::NOTE.message(label));
+                }
+            }
+        }
+        group
     }
 }
+
+fn emit_to_destination(
+    rendered: String,
+    lvl: &Level,
+    dst: &mut Destination,
+    short_message: bool,
+) -> io::Result<()> {
+    use crate::lock;
+    let _buffer_lock = lock::acquire_global_lock("rustc_errors");
+    writeln!(dst, "{rendered}")?;
+    if !short_message && !lvl.is_failure_note() {
+        writeln!(dst)?;
+    }
+    dst.flush()?;
+    Ok(())
+}
+
+#[derive(Debug)]
+struct Annotation {
+    kind: AnnotationKind,
+    span: Span,
+    label: Option,
+}
+
+fn collect_annotations(
+    args: &FluentArgs<'_>,
+    msp: &MultiSpan,
+    sm: &Arc,
+    translator: &Translator,
+) -> Vec<(Arc, Vec)> {
+    let mut output: Vec<(Arc, Vec)> = vec![];
+
+    for SpanLabel { span, is_primary, label } in msp.span_labels() {
+        // If we don't have a useful span, pick the primary span if that exists.
+        // Worst case we'll just print an error at the top of the main file.
+        let span = match (span.is_dummy(), msp.primary_span()) {
+            (_, None) | (false, _) => span,
+            (true, Some(span)) => span,
+        };
+        let file = sm.lookup_source_file(span.lo());
+
+        let kind = if is_primary { AnnotationKind::Primary } else { AnnotationKind::Context };
+
+        let label = label.as_ref().map(|m| {
+            normalize_whitespace(
+                &translator.translate_message(m, args).map_err(Report::new).unwrap(),
+            )
+        });
+
+        let ann = Annotation { kind, span, label };
+        if sm.is_valid_span(ann.span).is_ok() {
+            // Look through each of our files for the one we're adding to. We
+            // use each files `stable_id` to avoid issues with file name
+            // collisions when multiple versions of the same crate are present
+            // in the dependency graph
+            if let Some((_, annotations)) =
+                output.iter_mut().find(|(f, _)| f.stable_id == file.stable_id)
+            {
+                annotations.push(ann);
+            } else {
+                output.push((file, vec![ann]));
+            }
+        }
+    }
+    output
+}
+
+fn shrink_file(
+    spans: &[Span],
+    file_name: &FileName,
+    sm: &Arc,
+) -> Option<(Span, String, usize)> {
+    let lo_byte = spans.iter().map(|s| s.lo()).min()?;
+    let lo_loc = sm.lookup_char_pos(lo_byte);
+    let lo = lo_loc.file.line_bounds(lo_loc.line.saturating_sub(1)).start;
+
+    let hi_byte = spans.iter().map(|s| s.hi()).max()?;
+    let hi_loc = sm.lookup_char_pos(hi_byte);
+    let hi = lo_loc.file.line_bounds(hi_loc.line.saturating_sub(1)).end;
+
+    let bounding_span = Span::with_root_ctxt(lo, hi);
+    let source = sm.span_to_snippet(bounding_span).unwrap_or_default();
+    let offset_line = sm.doctest_offset_line(file_name, lo_loc.line);
+
+    Some((bounding_span, source, offset_line))
+}
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index f243c8be2088..522faf1db9ff 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -955,7 +955,24 @@ fn default_emitter(
 
             if let HumanReadableErrorType::AnnotateSnippet = kind {
                 let emitter =
-                    AnnotateSnippetEmitter::new(source_map, translator, short, macro_backtrace);
+                    AnnotateSnippetEmitter::new(stderr_destination(color_config), translator)
+                        .sm(source_map)
+                        .short_message(short)
+                        .diagnostic_width(sopts.diagnostic_width)
+                        .macro_backtrace(macro_backtrace)
+                        .track_diagnostics(track_diagnostics)
+                        .terminal_url(terminal_url)
+                        .theme(if let HumanReadableErrorType::Unicode = kind {
+                            OutputTheme::Unicode
+                        } else {
+                            OutputTheme::Ascii
+                        })
+                        .ignored_directories_in_source_blocks(
+                            sopts
+                                .unstable_opts
+                                .ignore_directory_in_diagnostics_source_blocks
+                                .clone(),
+                        );
                 Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
             } else {
                 let emitter = HumanEmitter::new(stderr_destination(color_config), translator)
diff --git a/tests/ui/annotate-snippet/missing-type.stderr b/tests/ui/annotate-snippet/missing-type.stderr
index c16f022a77fa..5cc9cc9529f4 100644
--- a/tests/ui/annotate-snippet/missing-type.stderr
+++ b/tests/ui/annotate-snippet/missing-type.stderr
@@ -4,3 +4,18 @@ error[E0412]: cannot find type `Iter` in this scope
 LL |     let x: Iter;
    |            ^^^^ not found in this scope
    |
+help: consider importing one of these structs
+   |
+LL + use std::collections::binary_heap::Iter;
+   |
+LL + use std::collections::btree_map::Iter;
+   |
+LL + use std::collections::btree_set::Iter;
+   |
+LL + use std::collections::hash_map::Iter;
+   |
+   = and 9 other candidates
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0412`.
diff --git a/tests/ui/annotate-snippet/multiple-files.stderr b/tests/ui/annotate-snippet/multiple-files.stderr
index 4236ec811d04..ffdc9482bbf2 100644
--- a/tests/ui/annotate-snippet/multiple-files.stderr
+++ b/tests/ui/annotate-snippet/multiple-files.stderr
@@ -7,5 +7,8 @@ LL |     other_file::WithPrivateMethod.private_method();
   ::: $DIR/auxiliary/other_file.rs:5:5
    |
 LL |     fn private_method(&self) {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^ private method defined here
-   |
+   |     ------------------------ private method defined here
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0624`.
diff --git a/tests/ui/annotate-snippet/multispan.stderr b/tests/ui/annotate-snippet/multispan.stderr
index baed54c59a4e..8acb60e27a19 100644
--- a/tests/ui/annotate-snippet/multispan.stderr
+++ b/tests/ui/annotate-snippet/multispan.stderr
@@ -4,39 +4,90 @@ error: hello to you, too!
 LL |     hello!(hi);
    |     ^^^^^^^^^^
    |
+note: found these 'hi's
+  --> $DIR/multispan.rs:15:12
+   |
+LL |     hello!(hi);
+   |            ^^
+   = note: this error originates in the macro `hello` (in Nightly builds, run with -Z macro-backtrace for more info)
+
 error: hello to you, too!
   --> $DIR/multispan.rs:18:5
    |
 LL |     hello!(hi hi);
    |     ^^^^^^^^^^^^^
    |
+note: found these 'hi's
+  --> $DIR/multispan.rs:18:12
+   |
+LL |     hello!(hi hi);
+   |            ^^ ^^
+   = note: this error originates in the macro `hello` (in Nightly builds, run with -Z macro-backtrace for more info)
+
 error: hello to you, too!
   --> $DIR/multispan.rs:21:5
    |
 LL |     hello!(hi hi hi);
    |     ^^^^^^^^^^^^^^^^
    |
+note: found these 'hi's
+  --> $DIR/multispan.rs:21:12
+   |
+LL |     hello!(hi hi hi);
+   |            ^^ ^^ ^^
+   = note: this error originates in the macro `hello` (in Nightly builds, run with -Z macro-backtrace for more info)
+
 error: hello to you, too!
   --> $DIR/multispan.rs:24:5
    |
 LL |     hello!(hi hey hi yo hi beep beep hi hi);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
+note: found these 'hi's
+  --> $DIR/multispan.rs:24:12
+   |
+LL |     hello!(hi hey hi yo hi beep beep hi hi);
+   |            ^^     ^^    ^^           ^^ ^^
+   = note: this error originates in the macro `hello` (in Nightly builds, run with -Z macro-backtrace for more info)
+
 error: hello to you, too!
   --> $DIR/multispan.rs:25:5
    |
 LL |     hello!(hi there, hi how are you? hi... hi.);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
+note: found these 'hi's
+  --> $DIR/multispan.rs:25:12
+   |
+LL |     hello!(hi there, hi how are you? hi... hi.);
+   |            ^^        ^^              ^^    ^^
+   = note: this error originates in the macro `hello` (in Nightly builds, run with -Z macro-backtrace for more info)
+
 error: hello to you, too!
   --> $DIR/multispan.rs:26:5
    |
 LL |     hello!(whoah. hi di hi di ho);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
+note: found these 'hi's
+  --> $DIR/multispan.rs:26:19
+   |
+LL |     hello!(whoah. hi di hi di ho);
+   |                   ^^    ^^
+   = note: this error originates in the macro `hello` (in Nightly builds, run with -Z macro-backtrace for more info)
+
 error: hello to you, too!
   --> $DIR/multispan.rs:27:5
    |
 LL |     hello!(hi good hi and good bye);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
+note: found these 'hi's
+  --> $DIR/multispan.rs:27:12
+   |
+LL |     hello!(hi good hi and good bye);
+   |            ^^      ^^
+   = note: this error originates in the macro `hello` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 7 previous errors
+

From 38a17905b78238e554b05ce1dc8c51914fa1e9ae Mon Sep 17 00:00:00 2001
From: Martin Nordholts 
Date: Sat, 18 Oct 2025 09:29:21 +0200
Subject: [PATCH 108/170] tests/ui/sanitizer/hwaddress.rs: Run on aarch64 and
 remove cgu hack

To avoid linker errors like

    relocation truncated to fit: R_AARCH64_ADR_PREL_PG_HI21 against `.data.rel.ro..L.hwasan'

we need to have `-C target-feature=+tagged-globals`, which is documented here:
https://doc.rust-lang.org/beta/unstable-book/compiler-flags/sanitizer.html#hwaddresssanitizer
---
 tests/ui/sanitizer/hwaddress.rs     | 8 +++-----
 tests/ui/sanitizer/hwaddress.stderr | 7 +++++++
 2 files changed, 10 insertions(+), 5 deletions(-)
 create mode 100644 tests/ui/sanitizer/hwaddress.stderr

diff --git a/tests/ui/sanitizer/hwaddress.rs b/tests/ui/sanitizer/hwaddress.rs
index 05fcab17506b..8666e7de4492 100644
--- a/tests/ui/sanitizer/hwaddress.rs
+++ b/tests/ui/sanitizer/hwaddress.rs
@@ -1,11 +1,7 @@
 //@ needs-sanitizer-support
 //@ needs-sanitizer-hwaddress
 //
-// FIXME(#83706): this test triggers errors on aarch64-gnu
-//@ ignore-aarch64-unknown-linux-gnu
-//
-// FIXME(#83989): codegen-units=1 triggers linker errors on aarch64-gnu
-//@ compile-flags: -Z sanitizer=hwaddress -O -g -C codegen-units=16 -C unsafe-allow-abi-mismatch=sanitizer
+//@ compile-flags: -Z sanitizer=hwaddress -O -g -C target-feature=+tagged-globals -C unsafe-allow-abi-mismatch=sanitizer
 //
 //@ run-fail
 //@ error-pattern: HWAddressSanitizer: tag-mismatch
@@ -19,3 +15,5 @@ fn main() {
     let code = unsafe { *xs.offset(4) };
     std::process::exit(code);
 }
+
+//~? WARN unknown and unstable feature specified for `-Ctarget-feature`: `tagged-globals`
diff --git a/tests/ui/sanitizer/hwaddress.stderr b/tests/ui/sanitizer/hwaddress.stderr
new file mode 100644
index 000000000000..37afe0bd779e
--- /dev/null
+++ b/tests/ui/sanitizer/hwaddress.stderr
@@ -0,0 +1,7 @@
+warning: unknown and unstable feature specified for `-Ctarget-feature`: `tagged-globals`
+   |
+   = note: it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
+   = help: consider filing a feature request
+
+warning: 1 warning emitted
+

From 9470a0fdc90f098862952b1e393ecd6d5dba7fe7 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Fri, 24 Oct 2025 21:10:34 +1100
Subject: [PATCH 109/170] Add concrete examples for `TestPaths` paths

---
 src/tools/compiletest/src/common.rs | 27 ++++++++++++++++++++++++---
 1 file changed, 24 insertions(+), 3 deletions(-)

diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 0a2f4340bbea..53a6aaa206cf 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -1075,10 +1075,31 @@ fn query_rustc_output(config: &Config, args: &[&str], envs: HashMap
Date: Fri, 24 Oct 2025 22:35:55 +1100
Subject: [PATCH 110/170] Add concrete examples for several config paths

---
 src/tools/compiletest/src/common.rs | 45 ++++++++++++++++++++++++-----
 1 file changed, 37 insertions(+), 8 deletions(-)

diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 53a6aaa206cf..8e47d51ee4db 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -248,6 +248,9 @@ pub struct Config {
 
     /// Path to libraries needed to run the *staged* `rustc`-under-test on the **host** platform.
     ///
+    /// For example:
+    /// - `/home/ferris/rust/build/x86_64-unknown-linux-gnu/stage1/bin/lib`
+    ///
     /// FIXME: maybe rename this to reflect (1) which target platform (host, not target), and (2)
     /// which `rustc` (the `rustc`-under-test, not the stage 0 `rustc` unless forced).
     pub compile_lib_path: Utf8PathBuf,
@@ -255,6 +258,9 @@ pub struct Config {
     /// Path to libraries needed to run the compiled executable for the **target** platform. This
     /// corresponds to the **target** sysroot libraries, including the **target** standard library.
     ///
+    /// For example:
+    /// - `/home/ferris/rust/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/i686-unknown-linux-gnu/lib`
+    ///
     /// FIXME: maybe rename this to reflect (1) which target platform (target, not host), and (2)
     /// what "run libraries" are against.
     ///
@@ -266,6 +272,9 @@ pub struct Config {
     /// Path to the *staged*  `rustc`-under-test. Unless forced, this `rustc` is *staged*, and must
     /// not be confused with [`Self::stage0_rustc_path`].
     ///
+    /// For example:
+    /// - `/home/ferris/rust/build/x86_64-unknown-linux-gnu/stage1/bin/rustc`
+    ///
     /// FIXME: maybe rename this to reflect that this is the `rustc`-under-test.
     pub rustc_path: Utf8PathBuf,
 
@@ -274,11 +283,17 @@ pub struct Config {
     /// *not* used to compile the test recipes), and so must be staged as there may be differences
     /// between e.g. beta `cargo` vs in-tree `cargo`.
     ///
+    /// For example:
+    /// - `/home/ferris/rust/build/x86_64-unknown-linux-gnu/stage1-tools-bin/cargo`
+    ///
     /// FIXME: maybe rename this to reflect that this is a *staged* host cargo.
     pub cargo_path: Option,
 
     /// Path to the stage 0 `rustc` used to build `run-make` recipes. This must not be confused with
     /// [`Self::rustc_path`].
+    ///
+    /// For example:
+    /// - `/home/ferris/rust/build/x86_64-unknown-linux-gnu/stage0/bin/rustc`
     pub stage0_rustc_path: Option,
 
     /// Path to the stage 1 or higher `rustc` used to obtain target information via
@@ -312,6 +327,9 @@ pub struct Config {
     pub llvm_filecheck: Option,
 
     /// Path to a host LLVM bintools directory.
+    ///
+    /// For example:
+    /// - `/home/ferris/rust/build/x86_64-unknown-linux-gnu/llvm/bin`
     pub llvm_bin_dir: Option,
 
     /// The path to the **target** `clang` executable to run `clang`-based tests with. If `None`,
@@ -321,28 +339,39 @@ pub struct Config {
     /// Path to the directory containing the sources. This corresponds to the root folder of a
     /// `rust-lang/rust` checkout.
     ///
+    /// For example:
+    /// - `/home/ferris/rust`
+    ///
     /// FIXME: this name is confusing, because this is actually `$checkout_root`, **not** the
     /// `$checkout_root/src/` folder.
     pub src_root: Utf8PathBuf,
 
-    /// Path to the directory containing the test suites sources. This corresponds to the
-    /// `$src_root/tests/` folder.
+    /// Absolute path to the test suite directory.
     ///
-    /// Must be an immediate subdirectory of [`Self::src_root`].
-    ///
-    /// FIXME: this name is also confusing, maybe just call it `tests_root`.
+    /// For example:
+    /// - `/home/ferris/rust/tests/ui`
+    /// - `/home/ferris/rust/tests/coverage`
     pub src_test_suite_root: Utf8PathBuf,
 
-    /// Path to the build directory (e.g. `build/`).
+    /// Path to the top-level build directory used by bootstrap.
+    ///
+    /// For example:
+    /// - `/home/ferris/rust/build`
     pub build_root: Utf8PathBuf,
 
-    /// Path to the test suite specific build directory (e.g. `build/host/test/ui/`).
+    /// Path to the build directory used by the current test suite.
     ///
-    /// Must be a subdirectory of [`Self::build_root`].
+    /// For example:
+    /// - `/home/ferris/rust/build/x86_64-unknown-linux-gnu/test/ui`
+    /// - `/home/ferris/rust/build/x86_64-unknown-linux-gnu/test/coverage`
     pub build_test_suite_root: Utf8PathBuf,
 
     /// Path to the directory containing the sysroot of the `rustc`-under-test.
     ///
+    /// For example:
+    /// - `/home/ferris/rust/build/x86_64-unknown-linux-gnu/stage1`
+    /// - `/home/ferris/rust/build/x86_64-unknown-linux-gnu/stage2`
+    ///
     /// When stage 0 is forced, this will correspond to the sysroot *of* that specified stage 0
     /// `rustc`.
     ///

From 2ab3580300cebc323c5ac96b17e6a4a24a0a7022 Mon Sep 17 00:00:00 2001
From: yukang 
Date: Sat, 25 Oct 2025 09:14:35 +0800
Subject: [PATCH 111/170] Use tracing error when received compiler message for
 unknown package

---
 .../crates/project-model/src/build_dependencies.rs              | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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 5eda5af3ace0..3a682d5a4d83 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
@@ -142,7 +142,7 @@ impl WorkspaceBuildScripts {
                 if let Some(&(package, workspace)) = by_id.get(package) {
                     cb(&workspaces[workspace][package].name, &mut res[workspace].outputs[package]);
                 } else {
-                    never!("Received compiler message for unknown package: {}", package);
+                    tracing::error!("Received compiler message for unknown package: {}", package);
                 }
             },
             progress,

From 2db19f27dafeb3d7c6349363729fbd434f138c87 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Sat, 25 Oct 2025 12:51:30 +1100
Subject: [PATCH 112/170] Don't pass `Arc` to `runtest::run`

This function doesn't need its own clone of the `Arc`, and can just take a
reference instead.
---
 src/tools/compiletest/src/executor.rs | 2 +-
 src/tools/compiletest/src/runtest.rs  | 3 +--
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs
index c7aca6d1c5aa..733b97787208 100644
--- a/src/tools/compiletest/src/executor.rs
+++ b/src/tools/compiletest/src/executor.rs
@@ -224,7 +224,7 @@ impl RunnableTest {
     fn run(&self, stdout: &dyn ConsoleOut, stderr: &dyn ConsoleOut) {
         __rust_begin_short_backtrace(|| {
             crate::runtest::run(
-                Arc::clone(&self.config),
+                &self.config,
                 stdout,
                 stderr,
                 &self.testpaths,
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index ff275df9b31c..bfae1ed370d1 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -6,7 +6,6 @@ use std::hash::{DefaultHasher, Hash, Hasher};
 use std::io::prelude::*;
 use std::io::{self, BufReader};
 use std::process::{Child, Command, ExitStatus, Output, Stdio};
-use std::sync::Arc;
 use std::{env, fmt, iter, str};
 
 use build_helper::fs::remove_and_create_dir_all;
@@ -110,7 +109,7 @@ fn dylib_name(name: &str) -> String {
 }
 
 pub fn run(
-    config: Arc,
+    config: &Config,
     stdout: &dyn ConsoleOut,
     stderr: &dyn ConsoleOut,
     testpaths: &TestPaths,

From f988679d23bae53633e58d2640ed63ca67b2ed9c Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Sat, 25 Oct 2025 12:49:50 +1100
Subject: [PATCH 113/170] Rename `RunnableTest` to `TestThreadArgs` and inline
 its methods

---
 src/tools/compiletest/src/executor.rs | 66 +++++++++++++--------------
 1 file changed, 33 insertions(+), 33 deletions(-)

diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs
index 733b97787208..06c83fbc9d07 100644
--- a/src/tools/compiletest/src/executor.rs
+++ b/src/tools/compiletest/src/executor.rs
@@ -107,23 +107,33 @@ fn spawn_test_thread(
         return None;
     }
 
-    let runnable_test = RunnableTest::new(test);
+    let args = TestThreadArgs {
+        config: Arc::clone(&test.config),
+        testpaths: test.testpaths.clone(),
+        revision: test.revision.clone(),
+    };
     let should_panic = test.desc.should_panic;
-    let run_test = move || run_test_inner(id, should_panic, runnable_test, completion_tx);
+    let run_test = move || test_thread_main(id, should_panic, args, completion_tx);
 
     let thread_builder = thread::Builder::new().name(test.desc.name.clone());
     let join_handle = thread_builder.spawn(run_test).unwrap();
     Some(join_handle)
 }
 
+struct TestThreadArgs {
+    config: Arc,
+    testpaths: TestPaths,
+    revision: Option,
+}
+
 /// Runs a single test, within the dedicated thread spawned by the caller.
-fn run_test_inner(
+fn test_thread_main(
     id: TestId,
     should_panic: ShouldPanic,
-    runnable_test: RunnableTest,
+    args: TestThreadArgs,
     completion_sender: mpsc::Sender,
 ) {
-    let capture = CaptureKind::for_config(&runnable_test.config);
+    let capture = CaptureKind::for_config(&args.config);
 
     // Install a panic-capture buffer for use by the custom panic hook.
     if capture.should_set_panic_hook() {
@@ -133,7 +143,24 @@ fn run_test_inner(
     let stdout = capture.stdout();
     let stderr = capture.stderr();
 
-    let panic_payload = panic::catch_unwind(move || runnable_test.run(stdout, stderr)).err();
+    // Run the test, catching any panics so that we can gracefully report
+    // failure (or success).
+    //
+    // FIXME(Zalathar): Ideally we would report test failures with `Result`,
+    // and use panics only for bugs within compiletest itself, but that would
+    // require a major overhaul of error handling in the test runners.
+    let panic_payload = panic::catch_unwind(|| {
+        __rust_begin_short_backtrace(|| {
+            crate::runtest::run(
+                &args.config,
+                stdout,
+                stderr,
+                &args.testpaths,
+                args.revision.as_deref(),
+            );
+        });
+    })
+    .err();
 
     if let Some(panic_buf) = panic_hook::take_capture_buf() {
         let panic_buf = panic_buf.lock().unwrap_or_else(|e| e.into_inner());
@@ -207,33 +234,6 @@ impl CaptureKind {
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 struct TestId(usize);
 
-struct RunnableTest {
-    config: Arc,
-    testpaths: TestPaths,
-    revision: Option,
-}
-
-impl RunnableTest {
-    fn new(test: &CollectedTest) -> Self {
-        let config = Arc::clone(&test.config);
-        let testpaths = test.testpaths.clone();
-        let revision = test.revision.clone();
-        Self { config, testpaths, revision }
-    }
-
-    fn run(&self, stdout: &dyn ConsoleOut, stderr: &dyn ConsoleOut) {
-        __rust_begin_short_backtrace(|| {
-            crate::runtest::run(
-                &self.config,
-                stdout,
-                stderr,
-                &self.testpaths,
-                self.revision.as_deref(),
-            );
-        });
-    }
-}
-
 /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
 #[inline(never)]
 fn __rust_begin_short_backtrace T>(f: F) -> T {

From 665dbb373923cde80eddce31d653abd726b071a5 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Sat, 25 Oct 2025 13:02:57 +1100
Subject: [PATCH 114/170] Move all other test thread args into `TestThreadArgs`

---
 src/tools/compiletest/src/executor.rs | 30 ++++++++++++++-------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs
index 06c83fbc9d07..d6dbdbf8639e 100644
--- a/src/tools/compiletest/src/executor.rs
+++ b/src/tools/compiletest/src/executor.rs
@@ -98,41 +98,42 @@ pub(crate) fn run_tests(config: &Config, tests: Vec) -> bool {
 fn spawn_test_thread(
     id: TestId,
     test: &CollectedTest,
-    completion_tx: mpsc::Sender,
+    completion_sender: mpsc::Sender,
 ) -> Option> {
     if test.desc.ignore && !test.config.run_ignored {
-        completion_tx
+        completion_sender
             .send(TestCompletion { id, outcome: TestOutcome::Ignored, stdout: None })
             .unwrap();
         return None;
     }
 
     let args = TestThreadArgs {
+        id,
         config: Arc::clone(&test.config),
         testpaths: test.testpaths.clone(),
         revision: test.revision.clone(),
+        should_panic: test.desc.should_panic,
+        completion_sender,
     };
-    let should_panic = test.desc.should_panic;
-    let run_test = move || test_thread_main(id, should_panic, args, completion_tx);
-
     let thread_builder = thread::Builder::new().name(test.desc.name.clone());
-    let join_handle = thread_builder.spawn(run_test).unwrap();
+    let join_handle = thread_builder.spawn(move || test_thread_main(args)).unwrap();
     Some(join_handle)
 }
 
+/// All of the owned data needed by `test_thread_main`.
 struct TestThreadArgs {
+    id: TestId,
+
     config: Arc,
     testpaths: TestPaths,
     revision: Option,
+    should_panic: ShouldPanic,
+
+    completion_sender: mpsc::Sender,
 }
 
 /// Runs a single test, within the dedicated thread spawned by the caller.
-fn test_thread_main(
-    id: TestId,
-    should_panic: ShouldPanic,
-    args: TestThreadArgs,
-    completion_sender: mpsc::Sender,
-) {
+fn test_thread_main(args: TestThreadArgs) {
     let capture = CaptureKind::for_config(&args.config);
 
     // Install a panic-capture buffer for use by the custom panic hook.
@@ -168,7 +169,8 @@ fn test_thread_main(
         write!(stderr, "{panic_buf}");
     }
 
-    let outcome = match (should_panic, panic_payload) {
+    // Interpret the presence/absence of a panic as test failure/success.
+    let outcome = match (args.should_panic, panic_payload) {
         (ShouldPanic::No, None) | (ShouldPanic::Yes, Some(_)) => TestOutcome::Succeeded,
         (ShouldPanic::No, Some(_)) => TestOutcome::Failed { message: None },
         (ShouldPanic::Yes, None) => {
@@ -177,7 +179,7 @@ fn test_thread_main(
     };
 
     let stdout = capture.into_inner();
-    completion_sender.send(TestCompletion { id, outcome, stdout }).unwrap();
+    args.completion_sender.send(TestCompletion { id: args.id, outcome, stdout }).unwrap();
 }
 
 enum CaptureKind {

From bb20367178be12ba9afb556104df4f55dad88d40 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Sat, 25 Oct 2025 13:35:58 +1100
Subject: [PATCH 115/170] Rename `ShouldPanic` to `ShouldFail`

The old name was a holdover from libtest, but in compiletest we only use it for
`//@ should-fail` tests, which are tests of compiletest itself.
---
 src/tools/compiletest/src/directives.rs       | 12 +++++------
 src/tools/compiletest/src/directives/tests.rs |  6 +++---
 src/tools/compiletest/src/executor.rs         | 21 ++++++++++---------
 3 files changed, 20 insertions(+), 19 deletions(-)

diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs
index 51eac58c971e..0318ed2b3d11 100644
--- a/src/tools/compiletest/src/directives.rs
+++ b/src/tools/compiletest/src/directives.rs
@@ -18,7 +18,7 @@ use crate::directives::line::{DirectiveLine, line_directive};
 use crate::directives::needs::CachedNeedsConditions;
 use crate::edition::{Edition, parse_edition};
 use crate::errors::ErrorKind;
-use crate::executor::{CollectedTestDesc, ShouldPanic};
+use crate::executor::{CollectedTestDesc, ShouldFail};
 use crate::util::static_regex;
 use crate::{fatal, help};
 
@@ -1366,10 +1366,10 @@ pub(crate) fn make_test_description(
     // The `should-fail` annotation doesn't apply to pretty tests,
     // since we run the pretty printer across all tests by default.
     // If desired, we could add a `should-fail-pretty` annotation.
-    let should_panic = match config.mode {
-        TestMode::Pretty => ShouldPanic::No,
-        _ if should_fail => ShouldPanic::Yes,
-        _ => ShouldPanic::No,
+    let should_fail = if should_fail && config.mode != TestMode::Pretty {
+        ShouldFail::Yes
+    } else {
+        ShouldFail::No
     };
 
     CollectedTestDesc {
@@ -1377,7 +1377,7 @@ pub(crate) fn make_test_description(
         filterable_path: filterable_path.to_owned(),
         ignore,
         ignore_message,
-        should_panic,
+        should_fail,
     }
 }
 
diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs
index 98249e69601b..0bf2bcd3af43 100644
--- a/src/tools/compiletest/src/directives/tests.rs
+++ b/src/tools/compiletest/src/directives/tests.rs
@@ -7,7 +7,7 @@ use crate::directives::{
     extract_llvm_version, extract_version_range, iter_directives, line_directive, parse_edition,
     parse_normalize_rule,
 };
-use crate::executor::{CollectedTestDesc, ShouldPanic};
+use crate::executor::{CollectedTestDesc, ShouldFail};
 
 fn make_test_description(
     config: &Config,
@@ -247,9 +247,9 @@ fn should_fail() {
     let p = Utf8Path::new("a.rs");
 
     let d = make_test_description(&config, tn.clone(), p, p, "", None);
-    assert_eq!(d.should_panic, ShouldPanic::No);
+    assert_eq!(d.should_fail, ShouldFail::No);
     let d = make_test_description(&config, tn, p, p, "//@ should-fail", None);
-    assert_eq!(d.should_panic, ShouldPanic::Yes);
+    assert_eq!(d.should_fail, ShouldFail::Yes);
 }
 
 #[test]
diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs
index d6dbdbf8639e..5a2136c55b05 100644
--- a/src/tools/compiletest/src/executor.rs
+++ b/src/tools/compiletest/src/executor.rs
@@ -112,7 +112,7 @@ fn spawn_test_thread(
         config: Arc::clone(&test.config),
         testpaths: test.testpaths.clone(),
         revision: test.revision.clone(),
-        should_panic: test.desc.should_panic,
+        should_fail: test.desc.should_fail,
         completion_sender,
     };
     let thread_builder = thread::Builder::new().name(test.desc.name.clone());
@@ -127,7 +127,7 @@ struct TestThreadArgs {
     config: Arc,
     testpaths: TestPaths,
     revision: Option,
-    should_panic: ShouldPanic,
+    should_fail: ShouldFail,
 
     completion_sender: mpsc::Sender,
 }
@@ -170,11 +170,11 @@ fn test_thread_main(args: TestThreadArgs) {
     }
 
     // Interpret the presence/absence of a panic as test failure/success.
-    let outcome = match (args.should_panic, panic_payload) {
-        (ShouldPanic::No, None) | (ShouldPanic::Yes, Some(_)) => TestOutcome::Succeeded,
-        (ShouldPanic::No, Some(_)) => TestOutcome::Failed { message: None },
-        (ShouldPanic::Yes, None) => {
-            TestOutcome::Failed { message: Some("test did not panic as expected") }
+    let outcome = match (args.should_fail, panic_payload) {
+        (ShouldFail::No, None) | (ShouldFail::Yes, Some(_)) => TestOutcome::Succeeded,
+        (ShouldFail::No, Some(_)) => TestOutcome::Failed { message: None },
+        (ShouldFail::Yes, None) => {
+            TestOutcome::Failed { message: Some("`//@ should-fail` test did not fail as expected") }
         }
     };
 
@@ -338,7 +338,7 @@ pub(crate) struct CollectedTestDesc {
     pub(crate) filterable_path: Utf8PathBuf,
     pub(crate) ignore: bool,
     pub(crate) ignore_message: Option>,
-    pub(crate) should_panic: ShouldPanic,
+    pub(crate) should_fail: ShouldFail,
 }
 
 /// Whether console output should be colored or not.
@@ -350,9 +350,10 @@ pub enum ColorConfig {
     NeverColor,
 }
 
-/// Whether test is expected to panic or not.
+/// Tests with `//@ should-fail` are tests of compiletest itself, and should
+/// be reported as successful if and only if they would have _failed_.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-pub(crate) enum ShouldPanic {
+pub(crate) enum ShouldFail {
     No,
     Yes,
 }

From e0fd5743f9293e74843d09446f15a9616bbfea3d Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Sat, 27 Sep 2025 16:33:05 +0800
Subject: [PATCH 116/170] Fix untyped syntax tree ans casts for
 convert_to_guarded_return

---
 .../src/handlers/convert_to_guarded_return.rs | 26 ++++++++++---------
 1 file changed, 14 insertions(+), 12 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 ae13f83fbc34..2a0ed4dc9e92 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
@@ -75,8 +75,8 @@ fn if_expr_to_guarded_return(
 
     let let_chains = flat_let_chain(cond);
 
-    let then_block = if_expr.then_branch()?;
-    let then_block = then_block.stmt_list()?;
+    let then_branch = if_expr.then_branch()?;
+    let then_block = then_branch.stmt_list()?;
 
     let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
 
@@ -84,17 +84,8 @@ fn if_expr_to_guarded_return(
         return None;
     }
 
-    // FIXME: This relies on untyped syntax tree and casts to much. It should be
-    // rewritten to use strongly-typed APIs.
-
     // check for early return and continue
-    let first_in_then_block = then_block.syntax().first_child()?;
-    if ast::ReturnExpr::can_cast(first_in_then_block.kind())
-        || ast::ContinueExpr::can_cast(first_in_then_block.kind())
-        || first_in_then_block
-            .children()
-            .any(|x| ast::ReturnExpr::can_cast(x.kind()) || ast::ContinueExpr::can_cast(x.kind()))
-    {
+    if is_early_block(&then_block) || is_never_block(&ctx.sema, &then_branch) {
         return None;
     }
 
@@ -284,6 +275,17 @@ fn clean_stmt_block(block: &ast::BlockExpr) -> ast::BlockExpr {
     }
 }
 
+fn is_early_block(then_block: &ast::StmtList) -> bool {
+    let is_early_expr =
+        |expr| matches!(expr, ast::Expr::ReturnExpr(_) | ast::Expr::ContinueExpr(_));
+    let into_expr = |stmt| match stmt {
+        ast::Stmt::ExprStmt(expr_stmt) => expr_stmt.expr(),
+        _ => None,
+    };
+    then_block.tail_expr().is_some_and(is_early_expr)
+        || then_block.statements().filter_map(into_expr).any(is_early_expr)
+}
+
 #[cfg(test)]
 mod tests {
     use crate::tests::{check_assist, check_assist_not_applicable};

From 79c0897aef8080b2ad7f613aa1d93a828c64c996 Mon Sep 17 00:00:00 2001
From: Azzybana Raccoon <121582001+Azzybana@users.noreply.github.com>
Date: Sat, 25 Oct 2025 02:54:28 -0400
Subject: [PATCH 117/170] Update layout.rs

perf: removed unnecessary let for return only
---
 library/core/src/alloc/layout.rs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs
index cd5fd77f8659..1f37c978fecf 100644
--- a/library/core/src/alloc/layout.rs
+++ b/library/core/src/alloc/layout.rs
@@ -316,8 +316,7 @@ impl Layout {
         // Size 1 Align MAX or Size isize::MAX Align 2 round up to `isize::MAX + 1`.)
         unsafe {
             let align_m1 = unchecked_sub(align.as_usize(), 1);
-            let size_rounded_up = unchecked_add(self.size, align_m1) & !align_m1;
-            size_rounded_up
+            unchecked_add(self.size, align_m1) & !align_m1
         }
     }
 

From 5fda166acfc7c7c4916b42d9a6810dce0e5b905d Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Sat, 25 Oct 2025 15:24:37 +0800
Subject: [PATCH 118/170] Fix not complete `let` before expr in condition

Example
---
```rust
fn f() {
    if $0foo.bar() {}
}
```

**Before this PR**

"let" not in completion list

**After this PR**

```rust
fn f() {
    if let $1 = $0foo.bar() {}
}
```
---
 .../crates/ide-completion/src/completions/postfix.rs        | 2 ++
 .../crates/ide-completion/src/tests/expression.rs           | 6 ++++++
 2 files changed, 8 insertions(+)

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 8c2bb961c586..ab3f619fd7f5 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
@@ -460,6 +460,8 @@ pub(crate) fn is_in_condition(it: &ast::Expr) -> bool {
                 ast::MatchGuard(guard) => guard.condition()? == *it,
                 ast::BinExpr(bin_expr) => (bin_expr.op_token()?.kind() == T![&&])
                     .then(|| is_in_condition(&bin_expr.into()))?,
+                ast::Expr(expr) => (expr.syntax().text_range().start() == it.syntax().text_range().start())
+                    .then(|| is_in_condition(&expr))?,
                 _ => return None,
             } })
         })
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 67c84f42c1ae..4033aa5d9c5e 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
@@ -3268,6 +3268,12 @@ fn foo() -> (i32, i32) {
 #[test]
 fn let_in_condition() {
     check_edit("let", r#"fn f() { if $0 {} }"#, r#"fn f() { if let $1 = $0 {} }"#);
+    check_edit("let", r#"fn f() { if $0x {} }"#, r#"fn f() { if let $1 = $0x {} }"#);
+    check_edit(
+        "let",
+        r#"fn f() { if $0foo.bar() {} }"#,
+        r#"fn f() { if let $1 = $0foo.bar() {} }"#,
+    );
 }
 
 #[test]

From 70876ee42be27738b0cea4bee17138b12679c5f6 Mon Sep 17 00:00:00 2001
From: Romain Perier 
Date: Fri, 17 Oct 2025 19:27:21 +0200
Subject: [PATCH 119/170] Unify and deduplicate max recip float tests

---
 library/coretests/tests/floats/f128.rs | 12 ------------
 library/coretests/tests/floats/f16.rs  |  8 +-------
 library/coretests/tests/floats/mod.rs  |  8 ++++++++
 3 files changed, 9 insertions(+), 19 deletions(-)

diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs
index 62278bf96c3c..8e4f0c9899e1 100644
--- a/library/coretests/tests/floats/f128.rs
+++ b/library/coretests/tests/floats/f128.rs
@@ -1,8 +1,6 @@
 // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy
 #![cfg(target_has_reliable_f128)]
 
-#[cfg(any(miri, target_has_reliable_f128_math))]
-use super::assert_approx_eq;
 use super::assert_biteq;
 
 // Note these tolerances make sense around zero, but not for more extreme exponents.
@@ -20,16 +18,6 @@ const TOL_PRECISE: f128 = 1e-28;
 // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support
 // the intrinsics.
 
-#[test]
-#[cfg(any(miri, target_has_reliable_f128_math))]
-fn test_max_recip() {
-    assert_approx_eq!(
-        f128::MAX.recip(),
-        8.40525785778023376565669454330438228902076605e-4933,
-        1e-4900
-    );
-}
-
 #[test]
 fn test_from() {
     assert_biteq!(f128::from(false), 0.0);
diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs
index 7ffafd467a51..3cff4259de54 100644
--- a/library/coretests/tests/floats/f16.rs
+++ b/library/coretests/tests/floats/f16.rs
@@ -1,7 +1,7 @@
 // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy
 #![cfg(target_has_reliable_f16)]
 
-use super::{assert_approx_eq, assert_biteq};
+use super::assert_biteq;
 
 /// Tolerance for results on the order of 10.0e-2
 #[allow(unused)]
@@ -22,12 +22,6 @@ const TOL_P4: f16 = 10.0;
 // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support
 // the intrinsics.
 
-#[test]
-#[cfg(any(miri, target_has_reliable_f16_math))]
-fn test_max_recip() {
-    assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4);
-}
-
 #[test]
 fn test_from() {
     assert_biteq!(f16::from(false), 0.0);
diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs
index 0348065d17fe..63d5b8fb2c6e 100644
--- a/library/coretests/tests/floats/mod.rs
+++ b/library/coretests/tests/floats/mod.rs
@@ -38,6 +38,8 @@ trait TestableFloat: Sized {
     const MUL_ADD_RESULT: Self;
     /// The result of (-12.3).mul_add(-4.5, -6.7)
     const NEG_MUL_ADD_RESULT: Self;
+    /// Reciprocal of the maximum val
+    const MAX_RECIP: Self;
 }
 
 impl TestableFloat for f16 {
@@ -64,6 +66,7 @@ impl TestableFloat for f16 {
     const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xcb20);
     const MUL_ADD_RESULT: Self = 62.031;
     const NEG_MUL_ADD_RESULT: Self = 48.625;
+    const MAX_RECIP: Self = 1.526624e-5;
 }
 
 impl TestableFloat for f32 {
@@ -92,6 +95,7 @@ impl TestableFloat for f32 {
     const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc1640000);
     const MUL_ADD_RESULT: Self = 62.05;
     const NEG_MUL_ADD_RESULT: Self = 48.65;
+    const MAX_RECIP: Self = 2.938736e-39;
 }
 
 impl TestableFloat for f64 {
@@ -116,6 +120,7 @@ impl TestableFloat for f64 {
     const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc02c800000000000);
     const MUL_ADD_RESULT: Self = 62.050000000000004;
     const NEG_MUL_ADD_RESULT: Self = 48.650000000000006;
+    const MAX_RECIP: Self = 5.562684646268003e-309;
 }
 
 impl TestableFloat for f128 {
@@ -140,6 +145,7 @@ impl TestableFloat for f128 {
     const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc002c800000000000000000000000000);
     const MUL_ADD_RESULT: Self = 62.0500000000000000000000000000000037;
     const NEG_MUL_ADD_RESULT: Self = 48.6500000000000000000000000000000049;
+    const MAX_RECIP: Self = 8.40525785778023376565669454330438228902076605e-4933;
 }
 
 /// Determine the tolerance for values of the argument type.
@@ -1425,6 +1431,7 @@ float_test! {
         let nan: Float = Float::NAN;
         let inf: Float = Float::INFINITY;
         let neg_inf: Float = Float::NEG_INFINITY;
+        let max: Float = Float::MAX;
         assert_biteq!((1.0 as Float).recip(), 1.0);
         assert_biteq!((2.0 as Float).recip(), 0.5);
         assert_biteq!((-0.4 as Float).recip(), -2.5);
@@ -1432,6 +1439,7 @@ float_test! {
         assert!(nan.recip().is_nan());
         assert_biteq!(inf.recip(), 0.0);
         assert_biteq!(neg_inf.recip(), -0.0);
+        assert_biteq!(max.recip(), Float::MAX_RECIP);
     }
 }
 

From f28ed4ca5040feddc65998e71d7c74066dd46d22 Mon Sep 17 00:00:00 2001
From: Mark Rousskov 
Date: Wed, 22 Oct 2025 20:38:15 -0700
Subject: [PATCH 120/170] 1.91.0 release notes

---
 RELEASES.md | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 211 insertions(+)

diff --git a/RELEASES.md b/RELEASES.md
index aa5f37fffebf..992d0ffbd380 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,3 +1,214 @@
+Version 1.91.0 (2025-10-30)
+==========================
+
+
+
+Language
+--------
+
+- [Lower pattern bindings in the order they're written and base drop order on primary bindings' order](https://github.com/rust-lang/rust/pull/143764)
+- [Stabilize declaration of C-style variadic functions for `sysv64`, `win64`, `efiapi`, and `aapcs` ABIs](https://github.com/rust-lang/rust/pull/144066).
+  This brings these ABIs in line with the C ABI: variadic functions can be declared in extern blocks but not defined.
+- [Add `dangling_pointers_from_locals` lint to warn against dangling pointers from local variables](https://github.com/rust-lang/rust/pull/144322)
+- [Upgrade `semicolon_in_expressions_from_macros` from warn to deny](https://github.com/rust-lang/rust/pull/144369)
+- [Stabilize LoongArch32 inline assembly](https://github.com/rust-lang/rust/pull/144402)
+- [Add warn-by-default `integer_to_ptr_transmutes` lint against integer-to-pointer transmutes](https://github.com/rust-lang/rust/pull/144531)
+- [Stabilize `sse4a` and `tbm` target features](https://github.com/rust-lang/rust/pull/144542)
+- [Add `target_env = "macabi"` and `target_env = "sim"` cfgs](https://github.com/rust-lang/rust/pull/139451) as replacements for the `target_abi` cfgs with the same values.
+
+
+
+Compiler
+--------
+
+- [Don't warn on never-to-any `as` casts as unreachable](https://github.com/rust-lang/rust/pull/144804)
+
+
+
+Platform Support
+----------------
+
+- [Promote `aarch64-pc-windows-gnullvm` and `x86_64-pc-windows-gnullvm` to Tier 2 with host tools.](https://github.com/rust-lang/rust/pull/143031)
+  Note: llvm-tools and MSI installers are missing but will be added in future releases.
+- [Promote `aarch64-pc-windows-msvc` to Tier 1](https://github.com/rust-lang/rust/pull/145682)
+
+Refer to Rust's [platform support page][platform-support-doc]
+for more information on Rust's tiered platform support.
+
+[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html
+
+
+
+Libraries
+---------
+
+- [Print thread ID in panic message](https://github.com/rust-lang/rust/pull/115746)
+- [Fix overly restrictive lifetime in `core::panic::Location::file` return type](https://github.com/rust-lang/rust/pull/132087)
+- [Guarantee parameter order for `_by()` variants of `min` / `max`/ `minmax` in `std::cmp`](https://github.com/rust-lang/rust/pull/139357)
+- [Document assumptions about `Clone` and `Eq` traits](https://github.com/rust-lang/rust/pull/144330/)
+- [`std::thread`: Return error if setting thread stack size fails](https://github.com/rust-lang/rust/pull/144210)
+  This used to panic within the standard library.
+
+
+
+Stabilized APIs
+---------------
+
+- [`Path::file_prefix`](https://doc.rust-lang.org/stable/std/path/struct.Path.html#method.file_prefix)
+- [`AtomicPtr::fetch_ptr_add`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_ptr_add)
+- [`AtomicPtr::fetch_ptr_sub`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_ptr_sub)
+- [`AtomicPtr::fetch_byte_add`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_byte_add)
+- [`AtomicPtr::fetch_byte_sub`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_byte_sub)
+- [`AtomicPtr::fetch_or`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_or)
+- [`AtomicPtr::fetch_and`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_and)
+- [`AtomicPtr::fetch_xor`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.fetch_xor)
+- [`{integer}::strict_add`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_add)
+- [`{integer}::strict_sub`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_sub)
+- [`{integer}::strict_mul`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_mul)
+- [`{integer}::strict_div`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_div)
+- [`{integer}::strict_div_euclid`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_div_euclid)
+- [`{integer}::strict_rem`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_rem)
+- [`{integer}::strict_rem_euclid`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_rem_euclid)
+- [`{integer}::strict_neg`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_neg)
+- [`{integer}::strict_shl`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_shl)
+- [`{integer}::strict_shr`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_shr)
+- [`{integer}::strict_pow`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_pow)
+- [`i{N}::strict_add_unsigned`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_add_unsigned)
+- [`i{N}::strict_sub_unsigned`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_sub_unsigned)
+- [`i{N}::strict_abs`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_abs)
+- [`u{N}::strict_add_signed`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_add_signed)
+- [`u{N}::strict_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_sub_signed)
+- [`PanicHookInfo::payload_as_str`](https://doc.rust-lang.org/stable/std/panic/struct.PanicHookInfo.html#method.payload_as_str)
+- [`core::iter::chain`](https://doc.rust-lang.org/stable/core/iter/fn.chain.html)
+- [`u{N}::checked_signed_diff`](https://doc.rust-lang.org/stable/std/primitive.u16.html#method.checked_signed_diff)
+- [`core::array::repeat`](https://doc.rust-lang.org/stable/core/array/fn.repeat.html)
+- [`PathBuf::add_extension`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.add_extension)
+- [`PathBuf::with_added_extension`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.with_added_extension)
+- [`Duration::from_mins`](https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_mins)
+- [`Duration::from_hours`](https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_hours)
+- [`impl PartialEq for PathBuf`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#impl-PartialEq%3Cstr%3E-for-PathBuf)
+- [`impl PartialEq for PathBuf`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#impl-PartialEq%3CString%3E-for-PathBuf)
+- [`impl PartialEq for Path`](https://doc.rust-lang.org/stable/std/path/struct.Path.html#impl-PartialEq%3Cstr%3E-for-Path)
+- [`impl PartialEq for Path`](https://doc.rust-lang.org/stable/std/path/struct.Path.html#impl-PartialEq%3CString%3E-for-Path)
+- [`impl PartialEq for String`](https://doc.rust-lang.org/stable/std/string/struct.String.html#impl-PartialEq%3CPathBuf%3E-for-String)
+- [`impl PartialEq for String`](https://doc.rust-lang.org/stable/std/string/struct.String.html#impl-PartialEq%3CPath%3E-for-String)
+- [`impl PartialEq for str`](https://doc.rust-lang.org/stable/std/primitive.str.html#impl-PartialEq%3CPathBuf%3E-for-str)
+- [`impl PartialEq for str`](https://doc.rust-lang.org/stable/std/primitive.str.html#impl-PartialEq%3CPath%3E-for-str)
+- [`Ipv4Addr::from_octets`](https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html#method.from_octets)
+- [`Ipv6Addr::from_octets`](https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.from_octets)
+- [`Ipv6Addr::from_segments`](https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.from_segments)
+- [`impl Default for Pin> where Box: Default, T: ?Sized`](https://doc.rust-lang.org/stable/std/default/trait.Default.html#impl-Default-for-Pin%3CBox%3CT%3E%3E)
+- [`impl Default for Pin> where Rc: Default, T: ?Sized`](https://doc.rust-lang.org/stable/std/default/trait.Default.html#impl-Default-for-Pin%3CRc%3CT%3E%3E)
+- [`impl Default for Pin> where Arc: Default, T: ?Sized`](https://doc.rust-lang.org/stable/std/default/trait.Default.html#impl-Default-for-Pin%3CArc%3CT%3E%3E)
+- [`Cell::as_array_of_cells`](https://doc.rust-lang.org/stable/std/cell/struct.Cell.html#method.as_array_of_cells)
+- [`u{N}::carrying_add`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.carrying_add)
+- [`u{N}::borrowing_sub`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.borrowing_sub)
+- [`u{N}::carrying_mul`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.carrying_mul)
+- [`u{N}::carrying_mul_add`](https://doc.rust-lang.org/stable/std/primitive.u64.html#method.carrying_mul_add)
+- [`BTreeMap::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.BTreeMap.html#method.extract_if)
+- [`BTreeSet::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.BTreeSet.html#method.extract_if)
+- [`impl Debug for windows::ffi::EncodeWide<'_>`](https://doc.rust-lang.org/stable/std/os/windows/ffi/struct.EncodeWide.html#impl-Debug-for-EncodeWide%3C'_%3E)
+- [`str::ceil_char_boundary`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.ceil_char_boundary)
+- [`str::floor_char_boundary`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.floor_char_boundary)
+- [`impl Sum for Saturating`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Sum-for-Saturating%3Cu32%3E)
+- [`impl Sum<&Self> for Saturating`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Sum%3C%26Saturating%3Cu32%3E%3E-for-Saturating%3Cu32%3E)
+- [`impl Product for Saturating`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Product-for-Saturating%3Cu32%3E)
+- [`impl Product<&Self> for Saturating`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html#impl-Product%3C%26Saturating%3Cu32%3E%3E-for-Saturating%3Cu32%3E)
+
+These previously stable APIs are now stable in const contexts:
+
+- [`<[T; N]>::each_ref`](https://doc.rust-lang.org/stable/std/primitive.array.html#method.each_ref)
+- [`<[T; N]>::each_mut`](https://doc.rust-lang.org/stable/std/primitive.array.html#method.each_mut)
+- [`OsString::new`](https://doc.rust-lang.org/stable/std/ffi/struct.OsString.html#method.new)
+- [`PathBuf::new`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.new)
+- [`TypeId::of`](https://doc.rust-lang.org/stable/std/any/struct.TypeId.html#method.of)
+- [`ptr::with_exposed_provenance`](https://doc.rust-lang.org/stable/std/ptr/fn.with_exposed_provenance.html)
+- [`ptr::with_exposed_provenance_mut`](https://doc.rust-lang.org/stable/std/ptr/fn.with_exposed_provenance_mut.html)
+
+
+
+Cargo
+-----
+
+- 🎉 Stabilize `build.build-dir`.
+  This config sets the directory where intermediate build artifacts are stored.
+  These artifacts are produced by Cargo and rustc during the build process.
+  End users usually won't need to interact with them, and the layout inside
+  `build-dir` is an implementation detail that may change without notice.
+  ([config doc](https://doc.rust-lang.org/stable/cargo/reference/config.html#buildbuild-dir))
+  ([build cache doc](https://doc.rust-lang.org/stable/cargo/reference/build-cache.html))
+  [#15833](https://github.com/rust-lang/cargo/pull/15833)
+  [#15840](https://github.com/rust-lang/cargo/pull/15840)
+- The `--target` flag and the `build.target` configuration can now take literal
+  `"host-tuple"` string, which will internally be substituted by the host
+  machine's target triple.
+  [#15838](https://github.com/rust-lang/cargo/pull/15838)
+  [#16003](https://github.com/rust-lang/cargo/pull/16003)
+  [#16032](https://github.com/rust-lang/cargo/pull/16032)
+
+
+
+Rustdoc
+-----
+- [In search results, rank doc aliases lower than non-alias items with the same name](https://github.com/rust-lang/rust/pull/145100)
+- [Raw pointers now work in type-based search like references](https://github.com/rust-lang/rust/pull/145731). This means you can now search for things like `*const u8 ->`, and additionally functions that take or return raw pointers will now display their signature properly in search results.
+
+
+
+Compatibility Notes
+-------------------
+
+- [Always require coroutine captures to be drop-live](https://github.com/rust-lang/rust/pull/144156)
+- [Apple: Always pass SDK root when linking with `cc`, and pass it via `SDKROOT` env var](https://github.com/rust-lang/rust/pull/131477). This should fix linking issues with `rustc` running inside Xcode. Libraries in `/usr/local/lib` may no longer be linked automatically, if you develop or use a crate that relies on this, you should explicitly set `cargo::rustc-link-search=/usr/local/lib` in a `build.rs` script.
+- [Relaxed bounds in associated type bound position like in `TraitRef` are now correctly forbidden](https://github.com/rust-lang/rust/pull/135331)
+- [Add unstable `#[sanitize(xyz = "on|off")]` built-in attribute that shadows procedural macros with the same name](https://github.com/rust-lang/rust/pull/142681)
+- [Fix the drop checker being more permissive for bindings declared with let-else](https://github.com/rust-lang/rust/pull/143028)
+- [Be more strict when parsing attributes, erroring on many invalid attributes](https://github.com/rust-lang/rust/pull/144689)
+    - [Error on invalid `#[should_panic]` attributes](https://github.com/rust-lang/rust/pull/143808)
+    - [Error on invalid `#[link]` attributes](https://github.com/rust-lang/rust/pull/143193)
+- [Mark all deprecation lints in name resolution as deny-by-default and also report in dependencies](https://github.com/rust-lang/rust/pull/143929)
+- The lint `semicolon_in_expressions_from_macros`, for `macro_rules!` macros in expression position that expand to end in a semicolon (`;`), is now deny-by-default. It was already warn-by-default, and a future compatibility warning (FCW) that warned even in dependencies. This lint will become a hard error in the future.
+- [Trait impl modifiers (e.g., `unsafe`, `!`, `default`) in inherent impls are no longer syntactically valid](https://github.com/rust-lang/rust/pull/144386)
+- [Start reporting future breakage for `ill_formed_attribute_input` in dependencies](https://github.com/rust-lang/rust/pull/144544)
+- [Restrict the scope of temporaries created by the macros `pin!`, `format_args!`, `write!`, and `writeln!` in `if let` scrutinees in Rust Edition 2024.](https://github.com/rust-lang/rust/pull/145342) This applies [Rust Edition 2024's `if let` temporary scope rules](https://doc.rust-lang.org/edition-guide/rust-2024/temporary-if-let-scope.html) to these temporaries, which previously could live past the `if` expression regardless of Edition.
+- [Invalid numeric literal suffixes in tuple indexing, tuple struct indexing, and struct field name positions are now correctly rejected](https://github.com/rust-lang/rust/pull/145463)
+- [Closures marked with the keyword `static` are now syntactically invalid](https://github.com/rust-lang/rust/pull/145604)
+- [Shebangs inside `--cfg` and `--check-cfg` arguments are no longer allowed](https://github.com/rust-lang/rust/pull/146211)
+- [Add future incompatibility lint for temporary lifetime shortening in Rust 1.92](https://github.com/rust-lang/rust/pull/147056)
+
+Cargo compatibility notes:
+
+- `cargo publish` no longer keeps `.crate` tarballs as final build artifacts
+  when `build.build-dir` is set. These tarballs were previously included due to
+  an oversight and are now treated as intermediate artifacts.
+  To get `.crate` tarballs as final artifacts, use `cargo package`.
+  In a future version, this change will apply regardless of `build.build-dir`.
+  [#15910](https://github.com/rust-lang/cargo/pull/15910)
+- Adjust Cargo messages to match rustc diagnostic style.
+  This changes some of the terminal colors used by Cargo messages.
+  [#15928](https://github.com/rust-lang/cargo/pull/15928)
+- Tools and projects relying on the
+  [internal details of Cargo's `build-dir`](https://doc.rust-lang.org/cargo/reference/build-cache.html)
+  may not work for users changing their `build-dir` layout.
+  For those doing so, we'd recommend proactively testing these cases
+  particularly as we are considering changing the default location of the `build-dir` in the future
+  ([cargo#16147](https://github.com/rust-lang/cargo/issues/16147)).
+  If you can't migrate off of Cargo's internal details,
+  we'd like to learn more about your use case as we prepare to change the layout of the `build-dir`
+  ([cargo#15010](https://github.com/rust-lang/cargo/issues/15010)).
+
+
+
+Internal Changes
+----------------
+
+These changes do not affect any public interfaces of Rust, but they represent
+significant improvements to the performance or internals of rustc and related
+tools.
+
+- [Update to LLVM 21](https://github.com/rust-lang/rust/pull/143684)
+
+
 Version 1.90.0 (2025-09-18)
 ===========================
 

From 2a03a948b9ff601184320d68456de68ea7cf38ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= 
Date: Sun, 19 Oct 2025 13:46:09 +0200
Subject: [PATCH 121/170] Deduce captures(none) for a return place and
 parameters

Extend attribute deduction to determine whether parameters using
indirect pass mode might have their address captured. Similarly to
the deduction of `readonly` attribute this information facilitates
memcpy optimizations.
---
 compiler/rustc_codegen_llvm/src/abi.rs        |  24 +-
 compiler/rustc_codegen_llvm/src/llvm/ffi.rs   |   1 +
 .../rustc_llvm/llvm-wrapper/RustWrapper.cpp   |   5 +
 .../src/middle/deduced_param_attrs.rs         |  66 +++---
 .../src/deduce_param_attrs.rs                 | 109 ++++++----
 compiler/rustc_target/src/callconv/mod.rs     |  15 +-
 compiler/rustc_ty_utils/src/abi.rs            |  57 +++--
 tests/codegen-llvm/addr-of-mutate.rs          |   2 +-
 tests/codegen-llvm/deduced-param-attrs.rs     | 205 +++++++++++++-----
 tests/codegen-llvm/function-arguments.rs      |   4 +-
 tests/ui/abi/c-zst.powerpc-linux.stderr       |   2 +-
 tests/ui/abi/c-zst.s390x-linux.stderr         |   2 +-
 tests/ui/abi/c-zst.sparc64-linux.stderr       |   2 +-
 .../ui/abi/c-zst.x86_64-pc-windows-gnu.stderr |   2 +-
 tests/ui/abi/debug.generic.stderr             |   6 +-
 tests/ui/abi/debug.loongarch64.stderr         |   6 +-
 tests/ui/abi/debug.riscv64.stderr             |   6 +-
 17 files changed, 333 insertions(+), 181 deletions(-)

diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index 680ad98593e7..e03c2868b0f1 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -39,13 +39,11 @@ trait ArgAttributesExt {
 const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] =
     [(ArgAttribute::InReg, llvm::AttributeKind::InReg)];
 
-const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 6] = [
+const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 4] = [
     (ArgAttribute::NoAlias, llvm::AttributeKind::NoAlias),
-    (ArgAttribute::CapturesAddress, llvm::AttributeKind::CapturesAddress),
     (ArgAttribute::NonNull, llvm::AttributeKind::NonNull),
     (ArgAttribute::ReadOnly, llvm::AttributeKind::ReadOnly),
     (ArgAttribute::NoUndef, llvm::AttributeKind::NoUndef),
-    (ArgAttribute::CapturesReadOnly, llvm::AttributeKind::CapturesReadOnly),
 ];
 
 fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 8]> {
@@ -81,15 +79,23 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'
         }
         for (attr, llattr) in OPTIMIZATION_ATTRIBUTES {
             if regular.contains(attr) {
-                // captures(...) is only available since LLVM 21.
-                if (attr == ArgAttribute::CapturesReadOnly || attr == ArgAttribute::CapturesAddress)
-                    && llvm_util::get_version() < (21, 0, 0)
-                {
-                    continue;
-                }
                 attrs.push(llattr.create_attr(cx.llcx));
             }
         }
+        // captures(...) is only available since LLVM 21.
+        if (21, 0, 0) <= llvm_util::get_version() {
+            const CAPTURES_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 3] = [
+                (ArgAttribute::CapturesNone, llvm::AttributeKind::CapturesNone),
+                (ArgAttribute::CapturesAddress, llvm::AttributeKind::CapturesAddress),
+                (ArgAttribute::CapturesReadOnly, llvm::AttributeKind::CapturesReadOnly),
+            ];
+            for (attr, llattr) in CAPTURES_ATTRIBUTES {
+                if regular.contains(attr) {
+                    attrs.push(llattr.create_attr(cx.llcx));
+                    break;
+                }
+            }
+        }
     } else if cx.tcx.sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::MEMORY) {
         // If we're not optimising, *but* memory sanitizer is on, emit noundef, since it affects
         // memory sanitizer's behavior.
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 2456ed2e46d6..53f0f9ff9d01 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -289,6 +289,7 @@ pub(crate) enum AttributeKind {
     DeadOnUnwind = 43,
     DeadOnReturn = 44,
     CapturesReadOnly = 45,
+    CapturesNone = 46,
 }
 
 /// LLVMIntPredicate
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 6b4f8a6dba79..ad459986826a 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -245,6 +245,7 @@ enum class LLVMRustAttributeKind {
   DeadOnUnwind = 43,
   DeadOnReturn = 44,
   CapturesReadOnly = 45,
+  CapturesNone = 46,
 };
 
 static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
@@ -339,6 +340,7 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
 #endif
   case LLVMRustAttributeKind::CapturesAddress:
   case LLVMRustAttributeKind::CapturesReadOnly:
+  case LLVMRustAttributeKind::CapturesNone:
     report_fatal_error("Should be handled separately");
   }
   report_fatal_error("bad LLVMRustAttributeKind");
@@ -390,6 +392,9 @@ extern "C" void LLVMRustEraseInstFromParent(LLVMValueRef Instr) {
 extern "C" LLVMAttributeRef
 LLVMRustCreateAttrNoValue(LLVMContextRef C, LLVMRustAttributeKind RustAttr) {
 #if LLVM_VERSION_GE(21, 0)
+  if (RustAttr == LLVMRustAttributeKind::CapturesNone) {
+    return wrap(Attribute::getWithCaptureInfo(*unwrap(C), CaptureInfo::none()));
+  }
   if (RustAttr == LLVMRustAttributeKind::CapturesAddress) {
     return wrap(Attribute::getWithCaptureInfo(
         *unwrap(C), CaptureInfo(CaptureComponents::Address)));
diff --git a/compiler/rustc_middle/src/middle/deduced_param_attrs.rs b/compiler/rustc_middle/src/middle/deduced_param_attrs.rs
index 68d1c852f0e2..5e9e625f4c74 100644
--- a/compiler/rustc_middle/src/middle/deduced_param_attrs.rs
+++ b/compiler/rustc_middle/src/middle/deduced_param_attrs.rs
@@ -2,19 +2,21 @@ use rustc_macros::{Decodable, Encodable, HashStable};
 
 use crate::ty::{Ty, TyCtxt, TypingEnv};
 
-/// Flags that dictate how a parameter is mutated. If the flags are empty, the param is
-/// read-only. If non-empty, it is read-only if *all* flags' conditions are met.
+/// Summarizes how a parameter (a return place or an argument) is used inside a MIR body.
 #[derive(Clone, Copy, PartialEq, Debug, Decodable, Encodable, HashStable)]
-pub struct DeducedReadOnlyParam(u8);
+pub struct UsageSummary(u8);
 
 bitflags::bitflags! {
-    impl DeducedReadOnlyParam: u8 {
-        /// This parameter is dropped. It is read-only if `!needs_drop`.
-        const IF_NO_DROP = 1 << 0;
-        /// This parameter is borrowed. It is read-only if `Freeze`.
-        const IF_FREEZE   = 1 << 1;
-        /// This parameter is mutated. It is never read-only.
-        const MUTATED     = 1 << 2;
+    impl UsageSummary: u8 {
+        /// This parameter is dropped when it `needs_drop`.
+        const DROP = 1 << 0;
+        /// There is a shared borrow to this parameter.
+        /// It allows for mutation unless parameter is `Freeze`.
+        const SHARED_BORROW = 1 << 1;
+        /// This parameter is mutated (excluding through a drop or a shared borrow).
+        const MUTATE = 1 << 2;
+        /// This parameter is captured (excluding through a drop).
+        const CAPTURE = 1 << 3;
     }
 }
 
@@ -24,43 +26,53 @@ bitflags::bitflags! {
 /// These can be useful for optimization purposes when a function is directly called. We compute
 /// them and store them into the crate metadata so that downstream crates can make use of them.
 ///
-/// Right now, we only have `read_only`, but `no_capture` and `no_alias` might be useful in the
+/// Right now, we have `readonly` and `captures(none)`, but `no_alias` might be useful in the
 /// future.
 #[derive(Clone, Copy, PartialEq, Debug, Decodable, Encodable, HashStable)]
 pub struct DeducedParamAttrs {
-    /// The parameter is marked immutable in the function.
-    pub read_only: DeducedReadOnlyParam,
-}
-
-// By default, consider the parameters to be mutated.
-impl Default for DeducedParamAttrs {
-    #[inline]
-    fn default() -> DeducedParamAttrs {
-        DeducedParamAttrs { read_only: DeducedReadOnlyParam::MUTATED }
-    }
+    pub usage: UsageSummary,
 }
 
 impl DeducedParamAttrs {
+    /// Returns true if no attributes have been deduced.
     #[inline]
     pub fn is_default(self) -> bool {
-        self.read_only.contains(DeducedReadOnlyParam::MUTATED)
+        self.usage.contains(UsageSummary::MUTATE | UsageSummary::CAPTURE)
     }
 
+    /// For parameters passed indirectly, returns true if pointer is never written through.
     pub fn read_only<'tcx>(
         &self,
         tcx: TyCtxt<'tcx>,
         typing_env: TypingEnv<'tcx>,
         ty: Ty<'tcx>,
     ) -> bool {
-        let read_only = self.read_only;
-        // We have to check *all* set bits; only if all checks pass is this truly read-only.
-        if read_only.contains(DeducedReadOnlyParam::MUTATED) {
+        // Only if all checks pass is this truly read-only.
+        if self.usage.contains(UsageSummary::MUTATE) {
             return false;
         }
-        if read_only.contains(DeducedReadOnlyParam::IF_NO_DROP) && ty.needs_drop(tcx, typing_env) {
+        if self.usage.contains(UsageSummary::DROP) && ty.needs_drop(tcx, typing_env) {
             return false;
         }
-        if read_only.contains(DeducedReadOnlyParam::IF_FREEZE) && !ty.is_freeze(tcx, typing_env) {
+        if self.usage.contains(UsageSummary::SHARED_BORROW) && !ty.is_freeze(tcx, typing_env) {
+            return false;
+        }
+        true
+    }
+
+    /// For parameters passed indirectly, returns true if pointer is not captured, i.e., its
+    /// address is not captured, and pointer is used neither for reads nor writes after function
+    /// returns.
+    pub fn captures_none<'tcx>(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        typing_env: TypingEnv<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> bool {
+        if self.usage.contains(UsageSummary::CAPTURE) {
+            return false;
+        }
+        if self.usage.contains(UsageSummary::DROP) && ty.needs_drop(tcx, typing_env) {
             return false;
         }
         true
diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs
index e421e47bd161..21762c8dd68c 100644
--- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs
+++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs
@@ -11,65 +11,91 @@
 
 use rustc_hir::def_id::LocalDefId;
 use rustc_index::IndexVec;
-use rustc_middle::middle::deduced_param_attrs::{DeducedParamAttrs, DeducedReadOnlyParam};
+use rustc_middle::middle::deduced_param_attrs::{DeducedParamAttrs, UsageSummary};
 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::config::OptLevel;
 
-/// A visitor that determines which arguments have been mutated. We can't use the mutability field
-/// on LocalDecl for this because it has no meaning post-optimization.
-struct DeduceReadOnly {
-    /// Each bit is indexed by argument number, starting at zero (so 0 corresponds to local decl
-    /// 1). The bit is false if the argument may have been mutated or true if we know it hasn't
-    /// been up to the point we're at.
-    read_only: IndexVec,
+/// A visitor that determines how a return place and arguments are used inside MIR body.
+/// To determine whether a local is mutated we can't use the mutability field on LocalDecl
+/// because it has no meaning post-optimization.
+struct DeduceParamAttrs {
+    /// Summarizes how a return place and arguments are used inside MIR body.
+    usage: IndexVec,
 }
 
-impl DeduceReadOnly {
-    /// Returns a new DeduceReadOnly instance.
-    fn new(arg_count: usize) -> Self {
-        Self { read_only: IndexVec::from_elem_n(DeducedReadOnlyParam::empty(), arg_count) }
+impl DeduceParamAttrs {
+    /// Returns a new DeduceParamAttrs instance.
+    fn new(body: &Body<'_>) -> Self {
+        let mut this =
+            Self { usage: IndexVec::from_elem_n(UsageSummary::empty(), body.arg_count + 1) };
+        // Code generation indicates that a return place is writable. To avoid setting both
+        // `readonly` and `writable` attributes, when return place is never written to, mark it as
+        // mutated.
+        this.usage[RETURN_PLACE] |= UsageSummary::MUTATE;
+        this
     }
 
-    /// Returns whether the given local is a parameter and its index.
-    fn as_param(&self, local: Local) -> Option {
-        // Locals and parameters are shifted by `RETURN_PLACE`.
-        let param_index = local.as_usize().checked_sub(1)?;
-        if param_index < self.read_only.len() { Some(param_index) } else { None }
+    /// Returns whether a local is the return place or an argument and returns its index.
+    fn as_param(&self, local: Local) -> Option {
+        if local.index() < self.usage.len() { Some(local) } else { None }
     }
 }
 
-impl<'tcx> Visitor<'tcx> for DeduceReadOnly {
+impl<'tcx> Visitor<'tcx> for DeduceParamAttrs {
     fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
-        // We're only interested in arguments.
-        let Some(param_index) = self.as_param(place.local) else { return };
+        // We're only interested in the return place or an argument.
+        let Some(i) = self.as_param(place.local) else { return };
 
         match context {
-            // Not mutating, so it's fine.
+            // Not actually using the local.
             PlaceContext::NonUse(..) => {}
-            // Dereference is not a mutation.
+            // Neither mutated nor captured.
             _ if place.is_indirect_first_projection() => {}
             // This is a `Drop`. It could disappear at monomorphization, so mark it specially.
             PlaceContext::MutatingUse(MutatingUseContext::Drop)
                 // Projection changes the place's type, so `needs_drop(local.ty)` is not
                 // `needs_drop(place.ty)`.
                 if place.projection.is_empty() => {
-                self.read_only[param_index] |= DeducedReadOnlyParam::IF_NO_DROP;
+                    self.usage[i] |= UsageSummary::DROP;
+            }
+            PlaceContext::MutatingUse(
+                  MutatingUseContext::Call
+                | MutatingUseContext::Yield
+                | MutatingUseContext::Drop
+                | MutatingUseContext::Borrow
+                | MutatingUseContext::RawBorrow) => {
+                self.usage[i] |= UsageSummary::MUTATE;
+                self.usage[i] |= UsageSummary::CAPTURE;
+            }
+            PlaceContext::MutatingUse(
+                  MutatingUseContext::Store
+                | MutatingUseContext::SetDiscriminant
+                | MutatingUseContext::AsmOutput
+                | MutatingUseContext::Projection
+                | MutatingUseContext::Retag) => {
+                self.usage[i] |= UsageSummary::MUTATE;
             }
-            // This is a mutation, so mark it as such.
-            PlaceContext::MutatingUse(..)
-            // Whether mutating though a `&raw const` is allowed is still undecided, so we
-            // disable any sketchy `readonly` optimizations for now.
             | PlaceContext::NonMutatingUse(NonMutatingUseContext::RawBorrow) => {
-                self.read_only[param_index] |= DeducedReadOnlyParam::MUTATED;
+                // Whether mutating though a `&raw const` is allowed is still undecided, so we
+                // disable any sketchy `readonly` optimizations for now.
+                self.usage[i] |= UsageSummary::MUTATE;
+                self.usage[i] |= UsageSummary::CAPTURE;
             }
-            // Not mutating if the parameter is `Freeze`.
             PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) => {
-                self.read_only[param_index] |= DeducedReadOnlyParam::IF_FREEZE;
+                // Not mutating if the parameter is `Freeze`.
+                self.usage[i] |= UsageSummary::SHARED_BORROW;
+                self.usage[i] |= UsageSummary::CAPTURE;
             }
             // Not mutating, so it's fine.
-            PlaceContext::NonMutatingUse(..) => {}
+            PlaceContext::NonMutatingUse(
+                  NonMutatingUseContext::Inspect
+                | NonMutatingUseContext::Copy
+                | NonMutatingUseContext::Move
+                | NonMutatingUseContext::FakeBorrow
+                | NonMutatingUseContext::PlaceMention
+                | NonMutatingUseContext::Projection) => {}
         }
     }
 
@@ -98,11 +124,11 @@ impl<'tcx> Visitor<'tcx> for DeduceReadOnly {
         if let TerminatorKind::Call { ref args, .. } = terminator.kind {
             for arg in args {
                 if let Operand::Move(place) = arg.node
-                    // We're only interested in arguments.
-                    && let Some(param_index) = self.as_param(place.local)
                     && !place.is_indirect_first_projection()
+                    && let Some(i) = self.as_param(place.local)
                 {
-                    self.read_only[param_index] |= DeducedReadOnlyParam::MUTATED;
+                    self.usage[i] |= UsageSummary::MUTATE;
+                    self.usage[i] |= UsageSummary::CAPTURE;
                 }
             }
         };
@@ -154,10 +180,9 @@ pub(super) fn deduced_param_attrs<'tcx>(
     if matches!(fn_ty.kind(), ty::FnDef(..))
         && fn_ty
             .fn_sig(tcx)
-            .inputs()
+            .inputs_and_output()
             .skip_binder()
             .iter()
-            .cloned()
             .all(type_will_always_be_passed_directly)
     {
         return &[];
@@ -170,13 +195,13 @@ pub(super) fn deduced_param_attrs<'tcx>(
 
     // Grab the optimized MIR. Analyze it to determine which arguments have been mutated.
     let body: &Body<'tcx> = tcx.optimized_mir(def_id);
-    let mut deduce_read_only = DeduceReadOnly::new(body.arg_count);
-    deduce_read_only.visit_body(body);
-    tracing::trace!(?deduce_read_only.read_only);
+    let mut deduce = DeduceParamAttrs::new(body);
+    deduce.visit_body(body);
+    tracing::trace!(?deduce.usage);
 
-    let mut deduced_param_attrs: &[_] = tcx.arena.alloc_from_iter(
-        deduce_read_only.read_only.into_iter().map(|read_only| DeducedParamAttrs { read_only }),
-    );
+    let mut deduced_param_attrs: &[_] = tcx
+        .arena
+        .alloc_from_iter(deduce.usage.into_iter().map(|usage| DeducedParamAttrs { usage }));
 
     // Trailing parameters past the size of the `deduced_param_attrs` array are assumed to have the
     // default set of attributes, so we don't have to store them explicitly. Pop them off to save a
diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs
index 3f7382ee0e2b..a33c246c88c6 100644
--- a/compiler/rustc_target/src/callconv/mod.rs
+++ b/compiler/rustc_target/src/callconv/mod.rs
@@ -113,13 +113,14 @@ mod attr_impl {
     pub struct ArgAttribute(u8);
     bitflags::bitflags! {
         impl ArgAttribute: u8 {
-            const NoAlias   = 1 << 1;
-            const CapturesAddress = 1 << 2;
-            const NonNull   = 1 << 3;
-            const ReadOnly  = 1 << 4;
-            const InReg     = 1 << 5;
-            const NoUndef = 1 << 6;
-            const CapturesReadOnly = 1 << 7;
+            const CapturesNone     = 0b111;
+            const CapturesAddress  = 0b110;
+            const CapturesReadOnly = 0b100;
+            const NoAlias  = 1 << 3;
+            const NonNull  = 1 << 4;
+            const ReadOnly = 1 << 5;
+            const InReg    = 1 << 6;
+            const NoUndef  = 1 << 7;
         }
     }
     rustc_data_structures::external_bitflags_debug! { ArgAttribute }
diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs
index ed5289c6850e..0f09e548f0e2 100644
--- a/compiler/rustc_ty_utils/src/abi.rs
+++ b/compiler/rustc_ty_utils/src/abi.rs
@@ -5,6 +5,7 @@ use rustc_abi::{BackendRepr, ExternAbi, PointerKind, Scalar, Size};
 use rustc_hir as hir;
 use rustc_hir::lang_items::LangItem;
 use rustc_middle::bug;
+use rustc_middle::middle::deduced_param_attrs::DeducedParamAttrs;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::layout::{
     FnAbiError, HasTyCtxt, HasTypingEnv, LayoutCx, LayoutOf, TyAndLayout, fn_can_unwind,
@@ -614,37 +615,19 @@ fn fn_abi_adjust_for_abi<'tcx>(
 
     if abi.is_rustic_abi() {
         fn_abi.adjust_for_rust_abi(cx);
-
         // Look up the deduced parameter attributes for this function, if we have its def ID and
         // we're optimizing in non-incremental mode. We'll tag its parameters with those attributes
         // as appropriate.
-        let deduced_param_attrs =
+        let deduced =
             if tcx.sess.opts.optimize != OptLevel::No && tcx.sess.opts.incremental.is_none() {
                 fn_def_id.map(|fn_def_id| tcx.deduced_param_attrs(fn_def_id)).unwrap_or_default()
             } else {
                 &[]
             };
-
-        for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() {
-            if arg.is_ignore() {
-                continue;
-            }
-
-            // If we deduced that this parameter was read-only, add that to the attribute list now.
-            //
-            // The `readonly` parameter only applies to pointers, so we can only do this if the
-            // argument was passed indirectly. (If the argument is passed directly, it's an SSA
-            // value, so it's implicitly immutable.)
-            if let &mut PassMode::Indirect { ref mut attrs, .. } = &mut arg.mode {
-                // The `deduced_param_attrs` list could be empty if this is a type of function
-                // we can't deduce any parameters for, so make sure the argument index is in
-                // bounds.
-                if let Some(deduced_param_attrs) = deduced_param_attrs.get(arg_idx)
-                    && deduced_param_attrs.read_only(tcx, cx.typing_env, arg.layout.ty)
-                {
-                    debug!("added deduced read-only attribute");
-                    attrs.regular.insert(ArgAttribute::ReadOnly);
-                }
+        if !deduced.is_empty() {
+            apply_deduced_attributes(cx, deduced, 0, &mut fn_abi.ret);
+            for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() {
+                apply_deduced_attributes(cx, deduced, arg_idx + 1, arg);
             }
         }
     } else {
@@ -652,6 +635,34 @@ fn fn_abi_adjust_for_abi<'tcx>(
     }
 }
 
+/// Apply deduced optimization attributes to a parameter using an indirect pass mode.
+///
+/// `deduced` is a possibly truncated list of deduced attributes for a return place and arguments.
+/// `idx` the index of the parameter on the list (0 for a return place, and 1.. for arguments).
+fn apply_deduced_attributes<'tcx>(
+    cx: &LayoutCx<'tcx>,
+    deduced: &[DeducedParamAttrs],
+    idx: usize,
+    arg: &mut ArgAbi<'tcx, Ty<'tcx>>,
+) {
+    // Deduction is performed under the assumption of the indirection pass mode.
+    let PassMode::Indirect { ref mut attrs, .. } = arg.mode else {
+        return;
+    };
+    // The default values at the tail of the list are not encoded.
+    let Some(deduced) = deduced.get(idx) else {
+        return;
+    };
+    if deduced.read_only(cx.tcx(), cx.typing_env, arg.layout.ty) {
+        debug!("added deduced ReadOnly attribute");
+        attrs.regular.insert(ArgAttribute::ReadOnly);
+    }
+    if deduced.captures_none(cx.tcx(), cx.typing_env, arg.layout.ty) {
+        debug!("added deduced CapturesNone attribute");
+        attrs.regular.insert(ArgAttribute::CapturesNone);
+    }
+}
+
 #[tracing::instrument(level = "debug", skip(cx))]
 fn make_thin_self_ptr<'tcx>(
     cx: &(impl HasTyCtxt<'tcx> + HasTypingEnv<'tcx>),
diff --git a/tests/codegen-llvm/addr-of-mutate.rs b/tests/codegen-llvm/addr-of-mutate.rs
index 36d6bf555d15..d59d85af62a9 100644
--- a/tests/codegen-llvm/addr-of-mutate.rs
+++ b/tests/codegen-llvm/addr-of-mutate.rs
@@ -24,7 +24,7 @@ pub unsafe fn second(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool {
 }
 
 // If going through a deref (and there are no other mutating accesses), then `readonly` is fine.
-// CHECK: i1 @third(ptr{{( dead_on_return)?}} noalias noundef readonly align {{[0-9]+}}{{( captures\(address\))?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
+// CHECK: i1 @third(ptr{{( dead_on_return)?}} noalias noundef readonly align {{[0-9]+}}{{( captures\(none\))?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
 #[no_mangle]
 pub unsafe fn third(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool {
     let b_bool_ptr = core::ptr::addr_of!((*a_ptr_and_b.0).1).cast_mut();
diff --git a/tests/codegen-llvm/deduced-param-attrs.rs b/tests/codegen-llvm/deduced-param-attrs.rs
index f99615cbe6d4..8062419ed5c6 100644
--- a/tests/codegen-llvm/deduced-param-attrs.rs
+++ b/tests/codegen-llvm/deduced-param-attrs.rs
@@ -1,81 +1,172 @@
 //@ compile-flags: -Copt-level=3 -Cno-prepopulate-passes
-
+//@ compile-flags: -Cpanic=abort -Csymbol-mangling-version=v0
+//@ revisions: LLVM21 LLVM20
+//@ [LLVM21] min-llvm-version: 21
+//@ [LLVM20] max-llvm-major-version: 20
+#![feature(custom_mir, core_intrinsics)]
 #![crate_type = "lib"]
-#![allow(internal_features)]
-#![feature(unsized_fn_params)]
-
+extern crate core;
+use core::intrinsics::mir::*;
 use std::cell::Cell;
-use std::hint;
+use std::hint::black_box;
+use std::mem::ManuallyDrop;
 
-// Check to make sure that we can deduce the `readonly` attribute from function bodies for
-// parameters passed indirectly.
-
-pub struct BigStruct {
-    blah: [i32; 1024],
+pub struct Big {
+    pub blah: [i32; 1024],
 }
 
-pub struct BigCellContainer {
-    blah: [Cell; 1024],
+pub struct BigCell {
+    pub blah: [Cell; 1024],
 }
 
-// The by-value parameter for this big struct can be marked readonly.
-//
-// CHECK: @use_big_struct_immutably({{.*}} readonly {{.*}} %big_struct)
-#[no_mangle]
-pub fn use_big_struct_immutably(big_struct: BigStruct) {
-    hint::black_box(&big_struct);
+pub struct BigDrop {
+    pub blah: [u8; 1024],
 }
 
-// The by-value parameter for this big struct can't be marked readonly, because we mutate it.
-//
-// CHECK: @use_big_struct_mutably(
+impl Drop for BigDrop {
+    #[inline(never)]
+    fn drop(&mut self) {}
+}
+
+// CHECK-LABEL: @mutate(
 // CHECK-NOT: readonly
-// CHECK-SAME: %big_struct)
-#[no_mangle]
-pub fn use_big_struct_mutably(mut big_struct: BigStruct) {
-    big_struct.blah[987] = 654;
-    hint::black_box(&big_struct);
+// CHECK-SAME: %b)
+#[unsafe(no_mangle)]
+pub fn mutate(mut b: Big) {
+    b.blah[987] = 654;
+    black_box(&b);
 }
 
-// The by-value parameter for this big struct can't be marked readonly, because it contains
-// UnsafeCell.
-//
-// CHECK: @use_big_cell_container(
-// CHECK-NOT: readonly
-// CHECK-SAME: %big_cell_container)
-#[no_mangle]
-pub fn use_big_cell_container(big_cell_container: BigCellContainer) {
-    hint::black_box(&big_cell_container);
+// LLVM21-LABEL: @deref_mut({{.*}}readonly {{.*}}captures(none) {{.*}}%c)
+// LLVM20-LABEL: @deref_mut({{.*}}readonly                      {{.*}}%c)
+#[unsafe(no_mangle)]
+pub fn deref_mut(c: (BigCell, &mut usize)) {
+    *c.1 = 42;
 }
 
-// Make sure that we don't mistakenly mark a big struct as `readonly` when passed through a generic
-// type parameter if it contains UnsafeCell.
+// LLVM21-LABEL: @call_copy_arg(ptr {{.*}}readonly {{.*}}captures(none){{.*}})
+// LLVM20-LABEL: @call_copy_arg(ptr {{.*}}readonly                     {{.*}})
+#[unsafe(no_mangle)]
+#[custom_mir(dialect = "runtime", phase = "optimized")]
+pub fn call_copy_arg(a: Big) {
+    mir! {
+        {
+            Call(RET = call_copy_arg(a), ReturnTo(bb1), UnwindUnreachable())
+        }
+        bb1 = {
+            Return()
+        }
+    }
+}
+
+// CHECK-LABEL: @call_move_arg(
+// CHECK-NOT:   readonly
+// LLVM21-SAME: captures(address)
+// CHECK-SAME:  )
+#[unsafe(no_mangle)]
+#[custom_mir(dialect = "runtime", phase = "optimized")]
+pub fn call_move_arg(a: Big) {
+    mir! {
+        {
+            Call(RET = call_move_arg(Move(a)), ReturnTo(bb1), UnwindUnreachable())
+        }
+        bb1 = {
+            Return()
+        }
+    }
+}
+
+fn shared_borrow(a: T) {
+    black_box(&a);
+}
+
+// Freeze parameter cannot be mutated through a shared borrow.
 //
-// CHECK: @use_something(
-// CHECK-NOT: readonly
-// CHECK-SAME: %something)
-#[no_mangle]
+// CHECK-LABEL: ; deduced_param_attrs::shared_borrow::
+// CHECK-NEXT:  ;
+// LLVM21-NEXT: (ptr {{.*}}readonly {{.*}}captures(address) {{.*}}%a)
+// LLVM20-NEXT: (ptr {{.*}}readonly                         {{.*}}%a)
+pub static A0: fn(Big) = shared_borrow;
+
+// !Freeze parameter can be mutated through a shared borrow.
+//
+// CHECK-LABEL: ; deduced_param_attrs::shared_borrow::
+// CHECK-NEXT:  ;
+// CHECK-NOT:   readonly
+// CHECK-NEXT:  %a)
+pub static A1: fn(BigCell) = shared_borrow;
+
+// The parameter can be mutated through a raw const borrow.
+//
+// CHECK-LABEL: ; deduced_param_attrs::raw_const_borrow
+// CHECK-NOT:   readonly
+// CHECK-NEXT : %a)
 #[inline(never)]
-pub fn use_something(something: T) {
-    hint::black_box(&something);
+pub fn raw_const_borrow(a: Big) {
+    black_box(&raw const a);
 }
 
-// Make sure that we still mark a big `Freeze` struct as `readonly` when passed through a generic
-// type parameter.
+fn consume(_: T) {}
+
+// The parameter doesn't need to be dropped.
 //
-// CHECK: @use_something_freeze(
-// CHECK-SAME: readonly
-// CHECK-SAME: %x)
-#[no_mangle]
-#[inline(never)]
-pub fn use_something_freeze(x: T) {}
+// CHECK-LABEL: ; deduced_param_attrs::consume::
+// CHECK-NEXT:  ;
+// LLVM21-NEXT: (ptr {{.*}}readonly {{.*}}captures(none) {{.*}})
+// LLVM20-NEXT: (ptr {{.*}}readonly                      {{.*}})
+pub static B0: fn(BigCell) = consume;
 
-#[no_mangle]
-pub fn forward_big_cell_container(big_cell_container: BigCellContainer) {
-    use_something(big_cell_container)
+// The parameter needs to be dropped.
+//
+// CHECK-LABEL: ; deduced_param_attrs::consume::
+// CHECK-NEXT:  ;
+// LLVM21-NEXT: (ptr {{.*}}captures(address) {{.*}})
+// LLVM20-NEXT: (ptr                         {{.*}})
+pub static B1: fn(BigDrop) = consume;
+
+fn consume_parts(t: (T, T)) {
+    let (_t0, ..) = t;
 }
 
-#[no_mangle]
-pub fn forward_big_container(big_struct: BigStruct) {
-    use_something_freeze(big_struct)
+// In principle it would be possible to deduce readonly here.
+//
+// CHECK-LABEL: ; deduced_param_attrs::consume_parts::<[u8; 40]>
+// CHECK-NEXT:  ;
+// CHECK-NOT:   readonly
+// CHECK-NEXT:  %t)
+pub static C1: fn(([u8; 40], [u8; 40])) = consume_parts;
+
+// The inner field of ManuallyDrop needs to be dropped.
+//
+// CHECK-LABEL: @manually_drop_field(
+// CHECK-NOT:   readonly
+// CHECK-SAME:  %b)
+#[unsafe(no_mangle)]
+pub fn manually_drop_field(a: fn() -> BigDrop, mut b: ManuallyDrop) {
+    // FIXME(tmiasko) replace with custom MIR, instead of expecting MIR optimizations to turn this
+    // into: drop((_2.0: BigDrop))
+    *b = a();
+    unsafe { core::intrinsics::unreachable() }
+}
+
+// `readonly` is omitted from the return place, even when applicable.
+//
+// CHECK-LABEL: @never_returns(
+// CHECK-NOT:   readonly
+// CHECK-SAME:  %_0)
+#[unsafe(no_mangle)]
+pub fn never_returns() -> [u8; 80] {
+    loop {}
+}
+
+// LLVM21-LABEL: @not_captured_return_place(ptr{{.*}} captures(none) {{.*}}%_0)
+#[unsafe(no_mangle)]
+pub fn not_captured_return_place() -> [u8; 80] {
+    [0u8; 80]
+}
+
+// LLVM21-LABEL: @captured_return_place(ptr{{.*}} captures(address) {{.*}}%_0)
+#[unsafe(no_mangle)]
+pub fn captured_return_place() -> [u8; 80] {
+    black_box([0u8; 80])
 }
diff --git a/tests/codegen-llvm/function-arguments.rs b/tests/codegen-llvm/function-arguments.rs
index a0744e44c61e..aaa1d57592a8 100644
--- a/tests/codegen-llvm/function-arguments.rs
+++ b/tests/codegen-llvm/function-arguments.rs
@@ -134,7 +134,7 @@ pub fn mutable_notunpin_borrow(_: &mut NotUnpin) {}
 #[no_mangle]
 pub fn notunpin_borrow(_: &NotUnpin) {}
 
-// CHECK: @indirect_struct(ptr{{( dead_on_return)?}} noalias noundef readonly align 4{{( captures\(address\))?}} dereferenceable(32) %_1)
+// CHECK: @indirect_struct(ptr{{( dead_on_return)?}} noalias noundef readonly align 4{{( captures\(none\))?}} dereferenceable(32) %_1)
 #[no_mangle]
 pub fn indirect_struct(_: S) {}
 
@@ -197,7 +197,7 @@ pub fn notunpin_box(x: Box) -> Box {
     x
 }
 
-// CHECK: @struct_return(ptr{{( dead_on_unwind)?}} noalias noundef{{( writable)?}} sret([32 x i8]) align 4{{( captures\(address\))?}} dereferenceable(32){{( %_0)?}})
+// CHECK: @struct_return(ptr{{( dead_on_unwind)?}} noalias noundef{{( writable)?}} sret([32 x i8]) align 4{{( captures\(none\))?}} dereferenceable(32){{( %_0)?}})
 #[no_mangle]
 pub fn struct_return() -> S {
     S { _field: [0, 0, 0, 0, 0, 0, 0, 0] }
diff --git a/tests/ui/abi/c-zst.powerpc-linux.stderr b/tests/ui/abi/c-zst.powerpc-linux.stderr
index e79b3fcec8b9..816addd79576 100644
--- a/tests/ui/abi/c-zst.powerpc-linux.stderr
+++ b/tests/ui/abi/c-zst.powerpc-linux.stderr
@@ -27,7 +27,7 @@ error: fn_abi_of(pass_zst) = FnAbi {
                    },
                    mode: Indirect {
                        attrs: ArgAttributes {
-                           regular: NoAlias | CapturesAddress | NonNull | NoUndef,
+                           regular: CapturesAddress | NoAlias | NonNull | NoUndef,
                            arg_ext: None,
                            pointee_size: Size(0 bytes),
                            pointee_align: Some(
diff --git a/tests/ui/abi/c-zst.s390x-linux.stderr b/tests/ui/abi/c-zst.s390x-linux.stderr
index e79b3fcec8b9..816addd79576 100644
--- a/tests/ui/abi/c-zst.s390x-linux.stderr
+++ b/tests/ui/abi/c-zst.s390x-linux.stderr
@@ -27,7 +27,7 @@ error: fn_abi_of(pass_zst) = FnAbi {
                    },
                    mode: Indirect {
                        attrs: ArgAttributes {
-                           regular: NoAlias | CapturesAddress | NonNull | NoUndef,
+                           regular: CapturesAddress | NoAlias | NonNull | NoUndef,
                            arg_ext: None,
                            pointee_size: Size(0 bytes),
                            pointee_align: Some(
diff --git a/tests/ui/abi/c-zst.sparc64-linux.stderr b/tests/ui/abi/c-zst.sparc64-linux.stderr
index e79b3fcec8b9..816addd79576 100644
--- a/tests/ui/abi/c-zst.sparc64-linux.stderr
+++ b/tests/ui/abi/c-zst.sparc64-linux.stderr
@@ -27,7 +27,7 @@ error: fn_abi_of(pass_zst) = FnAbi {
                    },
                    mode: Indirect {
                        attrs: ArgAttributes {
-                           regular: NoAlias | CapturesAddress | NonNull | NoUndef,
+                           regular: CapturesAddress | NoAlias | NonNull | NoUndef,
                            arg_ext: None,
                            pointee_size: Size(0 bytes),
                            pointee_align: Some(
diff --git a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr
index e79b3fcec8b9..816addd79576 100644
--- a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr
+++ b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr
@@ -27,7 +27,7 @@ error: fn_abi_of(pass_zst) = FnAbi {
                    },
                    mode: Indirect {
                        attrs: ArgAttributes {
-                           regular: NoAlias | CapturesAddress | NonNull | NoUndef,
+                           regular: CapturesAddress | NoAlias | NonNull | NoUndef,
                            arg_ext: None,
                            pointee_size: Size(0 bytes),
                            pointee_align: Some(
diff --git a/tests/ui/abi/debug.generic.stderr b/tests/ui/abi/debug.generic.stderr
index fd4c43591425..04d6f50872a3 100644
--- a/tests/ui/abi/debug.generic.stderr
+++ b/tests/ui/abi/debug.generic.stderr
@@ -454,7 +454,7 @@ error: ABIs are not compatible
                    },
                    mode: Indirect {
                        attrs: ArgAttributes {
-                           regular: NoAlias | CapturesAddress | NonNull | NoUndef,
+                           regular: CapturesAddress | NoAlias | NonNull | NoUndef,
                            arg_ext: None,
                            pointee_size: Size(32 bytes),
                            pointee_align: Some(
@@ -527,7 +527,7 @@ error: ABIs are not compatible
                    },
                    mode: Indirect {
                        attrs: ArgAttributes {
-                           regular: NoAlias | CapturesAddress | NonNull | NoUndef,
+                           regular: CapturesAddress | NoAlias | NonNull | NoUndef,
                            arg_ext: None,
                            pointee_size: Size(128 bytes),
                            pointee_align: Some(
@@ -939,7 +939,7 @@ error: fn_abi_of(assoc_test) = FnAbi {
                    },
                    mode: Direct(
                        ArgAttributes {
-                           regular: NoAlias | NonNull | ReadOnly | NoUndef | CapturesReadOnly,
+                           regular: CapturesReadOnly | NoAlias | NonNull | ReadOnly | NoUndef,
                            arg_ext: None,
                            pointee_size: Size(2 bytes),
                            pointee_align: Some(
diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr
index 95351b42092a..85c888c4fae0 100644
--- a/tests/ui/abi/debug.loongarch64.stderr
+++ b/tests/ui/abi/debug.loongarch64.stderr
@@ -454,7 +454,7 @@ error: ABIs are not compatible
                    },
                    mode: Indirect {
                        attrs: ArgAttributes {
-                           regular: NoAlias | CapturesAddress | NonNull | NoUndef,
+                           regular: CapturesAddress | NoAlias | NonNull | NoUndef,
                            arg_ext: None,
                            pointee_size: Size(32 bytes),
                            pointee_align: Some(
@@ -527,7 +527,7 @@ error: ABIs are not compatible
                    },
                    mode: Indirect {
                        attrs: ArgAttributes {
-                           regular: NoAlias | CapturesAddress | NonNull | NoUndef,
+                           regular: CapturesAddress | NoAlias | NonNull | NoUndef,
                            arg_ext: None,
                            pointee_size: Size(128 bytes),
                            pointee_align: Some(
@@ -939,7 +939,7 @@ error: fn_abi_of(assoc_test) = FnAbi {
                    },
                    mode: Direct(
                        ArgAttributes {
-                           regular: NoAlias | NonNull | ReadOnly | NoUndef | CapturesReadOnly,
+                           regular: CapturesReadOnly | NoAlias | NonNull | ReadOnly | NoUndef,
                            arg_ext: None,
                            pointee_size: Size(2 bytes),
                            pointee_align: Some(
diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr
index 95351b42092a..85c888c4fae0 100644
--- a/tests/ui/abi/debug.riscv64.stderr
+++ b/tests/ui/abi/debug.riscv64.stderr
@@ -454,7 +454,7 @@ error: ABIs are not compatible
                    },
                    mode: Indirect {
                        attrs: ArgAttributes {
-                           regular: NoAlias | CapturesAddress | NonNull | NoUndef,
+                           regular: CapturesAddress | NoAlias | NonNull | NoUndef,
                            arg_ext: None,
                            pointee_size: Size(32 bytes),
                            pointee_align: Some(
@@ -527,7 +527,7 @@ error: ABIs are not compatible
                    },
                    mode: Indirect {
                        attrs: ArgAttributes {
-                           regular: NoAlias | CapturesAddress | NonNull | NoUndef,
+                           regular: CapturesAddress | NoAlias | NonNull | NoUndef,
                            arg_ext: None,
                            pointee_size: Size(128 bytes),
                            pointee_align: Some(
@@ -939,7 +939,7 @@ error: fn_abi_of(assoc_test) = FnAbi {
                    },
                    mode: Direct(
                        ArgAttributes {
-                           regular: NoAlias | NonNull | ReadOnly | NoUndef | CapturesReadOnly,
+                           regular: CapturesReadOnly | NoAlias | NonNull | ReadOnly | NoUndef,
                            arg_ext: None,
                            pointee_size: Size(2 bytes),
                            pointee_align: Some(

From 775da711c60ab83dc23af428e92b6be19b3d38b2 Mon Sep 17 00:00:00 2001
From: Ben Kimock 
Date: Thu, 23 Oct 2025 12:27:56 -0400
Subject: [PATCH 122/170] Add a fast path for lowering trivial consts

---
 .../src/const_eval/eval_queries.rs            | 70 +++++++++++++----
 compiler/rustc_interface/src/passes.rs        | 12 ++-
 .../src/rmeta/decoder/cstore_impl.rs          |  1 +
 compiler/rustc_metadata/src/rmeta/encoder.rs  | 16 +++-
 compiler/rustc_metadata/src/rmeta/mod.rs      |  2 +
 .../rustc_metadata/src/rmeta/parameterized.rs |  1 +
 compiler/rustc_middle/src/mir/graphviz.rs     |  1 +
 compiler/rustc_middle/src/mir/pretty.rs       | 12 ++-
 compiler/rustc_middle/src/query/erase.rs      |  4 +
 compiler/rustc_middle/src/query/mod.rs        |  6 ++
 compiler/rustc_middle/src/ty/context.rs       |  7 ++
 compiler/rustc_mir_transform/src/lib.rs       | 77 ++++++++++++++++++-
 .../rustc_public_bridge/src/context/impls.rs  |  3 +-
 tests/incremental/issue-54242.rs              |  2 +-
 .../ui/rustc_public-ir-print/operands.stdout  | 14 ----
 .../unsatisfied-const-trait-bound.stderr      | 46 +----------
 16 files changed, 191 insertions(+), 83 deletions(-)

diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 4da2663319c3..d748f7bbcc71 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -7,7 +7,7 @@ use rustc_hir::def::DefKind;
 use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo, ReportedErrorInfo};
 use rustc_middle::mir::{self, ConstAlloc, ConstValue};
 use rustc_middle::query::TyCtxtAt;
-use rustc_middle::ty::layout::HasTypingEnv;
+use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout};
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::{bug, throw_inval};
@@ -24,13 +24,11 @@ use crate::interpret::{
 };
 use crate::{CTRL_C_RECEIVED, errors};
 
-// Returns a pointer to where the result lives
-#[instrument(level = "trace", skip(ecx, body))]
-fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
+fn setup_for_eval<'tcx>(
     ecx: &mut CompileTimeInterpCx<'tcx>,
     cid: GlobalId<'tcx>,
-    body: &'tcx mir::Body<'tcx>,
-) -> InterpResult<'tcx, R> {
+    layout: TyAndLayout<'tcx>,
+) -> InterpResult<'tcx, (InternKind, MPlaceTy<'tcx>)> {
     let tcx = *ecx.tcx;
     assert!(
         cid.promoted.is_some()
@@ -46,7 +44,6 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
         "Unexpected DefKind: {:?}",
         ecx.tcx.def_kind(cid.instance.def_id())
     );
-    let layout = ecx.layout_of(body.bound_return_ty().instantiate(tcx, cid.instance.args))?;
     assert!(layout.is_sized());
 
     let intern_kind = if cid.promoted.is_some() {
@@ -58,12 +55,25 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
         }
     };
 
-    let ret = if let InternKind::Static(_) = intern_kind {
-        create_static_alloc(ecx, cid.instance.def_id().expect_local(), layout)?
+    let return_place = if let InternKind::Static(_) = intern_kind {
+        create_static_alloc(ecx, cid.instance.def_id().expect_local(), layout)
     } else {
-        ecx.allocate(layout, MemoryKind::Stack)?
+        ecx.allocate(layout, MemoryKind::Stack)
     };
 
+    return_place.map(|ret| (intern_kind, ret))
+}
+
+#[instrument(level = "trace", skip(ecx, body))]
+fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
+    ecx: &mut CompileTimeInterpCx<'tcx>,
+    cid: GlobalId<'tcx>,
+    body: &'tcx mir::Body<'tcx>,
+) -> InterpResult<'tcx, R> {
+    let tcx = *ecx.tcx;
+    let layout = ecx.layout_of(body.bound_return_ty().instantiate(tcx, cid.instance.args))?;
+    let (intern_kind, ret) = setup_for_eval(ecx, cid, layout)?;
+
     trace!(
         "eval_body_using_ecx: pushing stack frame for global: {}{}",
         with_no_trimmed_paths!(ecx.tcx.def_path_str(cid.instance.def_id())),
@@ -87,6 +97,31 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
         }
     }
 
+    intern_and_validate(ecx, cid, intern_kind, ret)
+}
+
+#[instrument(level = "trace", skip(ecx))]
+fn eval_trivial_const_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
+    ecx: &mut CompileTimeInterpCx<'tcx>,
+    cid: GlobalId<'tcx>,
+    val: ConstValue,
+    ty: Ty<'tcx>,
+) -> InterpResult<'tcx, R> {
+    let layout = ecx.layout_of(ty)?;
+    let (intern_kind, return_place) = setup_for_eval(ecx, cid, layout)?;
+
+    let opty = ecx.const_val_to_op(val, ty, Some(layout))?;
+    ecx.copy_op(&opty, &return_place)?;
+
+    intern_and_validate(ecx, cid, intern_kind, return_place)
+}
+
+fn intern_and_validate<'tcx, R: InterpretationResult<'tcx>>(
+    ecx: &mut CompileTimeInterpCx<'tcx>,
+    cid: GlobalId<'tcx>,
+    intern_kind: InternKind,
+    ret: MPlaceTy<'tcx>,
+) -> InterpResult<'tcx, R> {
     // Intern the result
     let intern_result = intern_const_alloc_recursive(ecx, intern_kind, &ret);
 
@@ -292,6 +327,9 @@ pub fn eval_to_const_value_raw_provider<'tcx>(
     tcx: TyCtxt<'tcx>,
     key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>,
 ) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> {
+    if let Some((value, _ty)) = tcx.trivial_const(key.value.instance.def_id()) {
+        return Ok(value);
+    }
     tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key))
 }
 
@@ -368,10 +406,14 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
         // so we have to reject reading mutable global memory.
         CompileTimeMachine::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
     );
-    let res = ecx.load_mir(cid.instance.def, cid.promoted);
-    res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body))
-        .report_err()
-        .map_err(|error| report_eval_error(&ecx, cid, error))
+
+    let result = if let Some((value, ty)) = tcx.trivial_const(def) {
+        eval_trivial_const_using_ecx(&mut ecx, cid, value, ty)
+    } else {
+        ecx.load_mir(cid.instance.def, cid.promoted)
+            .and_then(|body| eval_body_using_ecx(&mut ecx, cid, body))
+    };
+    result.report_err().map_err(|error| report_eval_error(&ecx, cid, error))
 }
 
 #[inline(always)]
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index a41d6b858795..03b8b61bbc0a 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -1088,9 +1088,15 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
 
     sess.time("MIR_borrow_checking", || {
         tcx.par_hir_body_owners(|def_id| {
-            if !tcx.is_typeck_child(def_id.to_def_id()) {
+            let not_typeck_child = !tcx.is_typeck_child(def_id.to_def_id());
+            if not_typeck_child {
                 // Child unsafety and borrowck happens together with the parent
                 tcx.ensure_ok().check_unsafety(def_id);
+            }
+            if tcx.is_trivial_const(def_id) {
+                return;
+            }
+            if not_typeck_child {
                 tcx.ensure_ok().mir_borrowck(def_id);
                 tcx.ensure_ok().check_transmutes(def_id);
             }
@@ -1198,7 +1204,9 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) {
     if tcx.sess.opts.unstable_opts.validate_mir {
         sess.time("ensuring_final_MIR_is_computable", || {
             tcx.par_hir_body_owners(|def_id| {
-                tcx.instance_mir(ty::InstanceKind::Item(def_id.into()));
+                if !tcx.is_trivial_const(def_id) {
+                    tcx.instance_mir(ty::InstanceKind::Item(def_id.into()));
+                }
             });
         });
     }
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index df3add316ec2..d20df618b38b 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -240,6 +240,7 @@ provide! { tcx, def_id, other, cdata,
     thir_abstract_const => { table }
     optimized_mir => { table }
     mir_for_ctfe => { table }
+    trivial_const => { table }
     closure_saved_names_of_captured_variables => { table }
     mir_coroutine_witnesses => { table }
     promoted_mir => { table }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index e13ef7e70f44..778007854b77 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1791,8 +1791,15 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                     record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses);
                 }
             }
+            let mut is_trivial = false;
             if encode_const {
-                record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id));
+                if let Some((val, ty)) = tcx.trivial_const(def_id) {
+                    is_trivial = true;
+                    record!(self.tables.trivial_const[def_id.to_def_id()] <- (val, ty));
+                } else {
+                    is_trivial = false;
+                    record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id));
+                }
 
                 // FIXME(generic_const_exprs): this feels wrong to have in `encode_mir`
                 let abstract_const = tcx.thir_abstract_const(def_id);
@@ -1810,7 +1817,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                     }
                 }
             }
-            record!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id));
+            if !is_trivial {
+                record!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id));
+            }
 
             if self.tcx.is_coroutine(def_id.to_def_id())
                 && let Some(witnesses) = tcx.mir_coroutine_witnesses(def_id)
@@ -2234,6 +2243,9 @@ fn prefetch_mir(tcx: TyCtxt<'_>) {
 
     let reachable_set = tcx.reachable_set(());
     par_for_each_in(tcx.mir_keys(()), |&&def_id| {
+        if tcx.is_trivial_const(def_id) {
+            return;
+        }
         let (encode_const, encode_opt) = should_encode_mir(tcx, reachable_set, def_id);
 
         if encode_const {
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 9fbfa0a9f765..8f1c7bbb3968 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -29,6 +29,7 @@ use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
 use rustc_middle::middle::lib_features::FeatureStability;
 use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
 use rustc_middle::mir;
+use rustc_middle::mir::ConstValue;
 use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::{self, Ty, TyCtxt, UnusedGenericParams};
 use rustc_middle::util::Providers;
@@ -426,6 +427,7 @@ define_tables! {
     object_lifetime_default: Table>,
     optimized_mir: Table>>,
     mir_for_ctfe: Table>>,
+    trivial_const: Table)>>,
     closure_saved_names_of_captured_variables: Table>>,
     mir_coroutine_witnesses: Table>>,
     promoted_mir: Table>>>,
diff --git a/compiler/rustc_metadata/src/rmeta/parameterized.rs b/compiler/rustc_metadata/src/rmeta/parameterized.rs
index 733e33f310a6..d8bae5a54e31 100644
--- a/compiler/rustc_metadata/src/rmeta/parameterized.rs
+++ b/compiler/rustc_metadata/src/rmeta/parameterized.rs
@@ -102,6 +102,7 @@ trivially_parameterized_over_tcx! {
     rustc_middle::middle::lib_features::FeatureStability,
     rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault,
     rustc_middle::mir::ConstQualifs,
+    rustc_middle::mir::ConstValue,
     rustc_middle::ty::AnonConstKind,
     rustc_middle::ty::AssocContainer,
     rustc_middle::ty::AsyncDestructor,
diff --git a/compiler/rustc_middle/src/mir/graphviz.rs b/compiler/rustc_middle/src/mir/graphviz.rs
index a64b122fbc9e..cbfeae205177 100644
--- a/compiler/rustc_middle/src/mir/graphviz.rs
+++ b/compiler/rustc_middle/src/mir/graphviz.rs
@@ -16,6 +16,7 @@ where
 
     let mirs = def_ids
         .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)]
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 60c2ef4d563e..04233d8a523c 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -353,8 +353,16 @@ pub fn write_mir_pretty<'tcx>(
             // are shared between mir_for_ctfe and optimized_mir
             writer.write_mir_fn(tcx.mir_for_ctfe(def_id), w)?;
         } else {
-            let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id));
-            render_body(w, instance_mir)?;
+            if let Some((val, ty)) = tcx.trivial_const(def_id) {
+                ty::print::with_forced_impl_filename_line! {
+                    // see notes on #41697 elsewhere
+                    write!(w, "const {}", tcx.def_path_str(def_id))?
+                }
+                writeln!(w, ": {} = const {};", ty, Const::Val(val, ty))?;
+            } else {
+                let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id));
+                render_body(w, instance_mir)?;
+            }
         }
     }
     Ok(())
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index ad82e83ee40b..46100358c7d7 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -160,6 +160,10 @@ impl EraseType for Result {
     type Result = [u8; size_of::>()];
 }
 
+impl EraseType for Option<(mir::ConstValue, Ty<'_>)> {
+    type Result = [u8; size_of::)>>()];
+}
+
 impl EraseType for EvalToValTreeResult<'_> {
     type Result = [u8; size_of::>()];
 }
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 588ff68ba572..30d4cc8b3c8d 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -2719,6 +2719,12 @@ rustc_queries! {
         separate_provide_extern
     }
 
+    query trivial_const(def_id: DefId) -> Option<(mir::ConstValue, Ty<'tcx>)> {
+        desc { |tcx| "checking if `{}` is a trivial const", tcx.def_path_str(def_id) }
+        cache_on_disk_if { def_id.is_local() }
+        separate_provide_extern
+    }
+
     /// Checks for the nearest `#[sanitize(xyz = "off")]` or
     /// `#[sanitize(xyz = "on")]` on this def and any enclosing defs, up to the
     /// crate root.
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 461480eddb87..4e48fd6ec262 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -3546,6 +3546,13 @@ impl<'tcx> TyCtxt<'tcx> {
         self.get_diagnostic_attr(def_id, sym::do_not_recommend).is_some()
     }
 
+    pub fn is_trivial_const

(self, def_id: P) -> bool + where + P: IntoQueryParam, + { + self.trivial_const(def_id).is_some() + } + /// Whether this def is one of the special bin crate entrypoint functions that must have a /// monomorphization and also not be internalized in the bin crate. pub fn is_entrypoint(self, def_id: DefId) -> bool { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 4625b20fd890..129b28659ddc 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -23,11 +23,11 @@ use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::LocalDefId; use rustc_index::IndexVec; use rustc_middle::mir::{ - AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, LocalDecl, - MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, START_BLOCK, - SourceInfo, Statement, StatementKind, TerminatorKind, + AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, ConstValue, + LocalDecl, MirPhase, Operand, Place, ProjectionElem, Promoted, RETURN_PLACE, RuntimePhase, + Rvalue, START_BLOCK, SourceInfo, Statement, StatementKind, TerminatorKind, }; -use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::util::Providers; use rustc_middle::{bug, query, span_bug}; use rustc_mir_build::builder::build_mir; @@ -226,6 +226,7 @@ pub fn provide(providers: &mut Providers) { promoted_mir, deduced_param_attrs: deduce_param_attrs::deduced_param_attrs, coroutine_by_move_body_def_id: coroutine::coroutine_by_move_body_def_id, + trivial_const: trivial_const_provider, ..providers.queries }; } @@ -376,9 +377,71 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs { validator.qualifs_in_return_place() } +fn def_kind_compatible_with_trivial_mir<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> bool { + // Static and InlineConst are the obvious additions, but + // * Statics need additional type-checking to taint `static A: _ = 0;`, currently we'd ICE. + // * The MIR for InlineConst is used by the borrow checker, and not easy to skip over. + matches!(tcx.def_kind(def), DefKind::AssocConst | DefKind::Const | DefKind::AnonConst) +} + +fn trivial_const_provider<'tcx>( + tcx: TyCtxt<'tcx>, + def: LocalDefId, +) -> Option<(ConstValue, Ty<'tcx>)> { + if def_kind_compatible_with_trivial_mir(tcx, def) { + trivial_const(&tcx.mir_built(def).borrow()) + } else { + None + } +} + +fn trivial_const<'tcx>(body: &Body<'tcx>) -> Option<(ConstValue, Ty<'tcx>)> { + if body.has_opaque_types() { + return None; + } + + if body.basic_blocks.len() != 1 { + return None; + } + + let block = &body.basic_blocks[START_BLOCK]; + if block.statements.len() != 1 { + return None; + } + + if block.terminator().kind != TerminatorKind::Return { + return None; + } + + let StatementKind::Assign(box (place, rvalue)) = &block.statements[0].kind else { + return None; + }; + + if *place != Place::from(RETURN_PLACE) { + return None; + } + + if let Rvalue::Use(Operand::Constant(c)) = rvalue { + if let rustc_middle::mir::Const::Val(v, ty) = c.const_ { + return Some((v, ty)); + } + } + + return None; +} + fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { let mut body = build_mir(tcx, def); + // Identifying trivial consts based on their mir_built is easy, but a little wasteful. + // Trying to push this logic earlier in the compiler and never even produce the Body would + // probably improve compile time. + if def_kind_compatible_with_trivial_mir(tcx, def) && trivial_const(&body).is_some() { + let body = tcx.alloc_steal_mir(body); + pass_manager::dump_mir_for_phase_change(tcx, &body.borrow()); + return body; + } + pass_manager::dump_mir_for_phase_change(tcx, &body); pm::run_passes( @@ -409,6 +472,8 @@ fn mir_promoted( tcx: TyCtxt<'_>, def: LocalDefId, ) -> (&Steal>, &Steal>>) { + debug_assert!(!tcx.is_trivial_const(def), "Tried to get mir_promoted of a trivial const"); + // Ensure that we compute the `mir_const_qualif` for constants at // this point, before we steal the mir-const result. // Also this means promotion can rely on all const checks having been done. @@ -436,6 +501,9 @@ fn mir_promoted( tcx.ensure_done().coroutine_by_move_body_def_id(def); } + // the `trivial_const` query uses mir_built, so make sure it is run. + tcx.ensure_done().trivial_const(def); + let mut body = tcx.mir_built(def).steal(); if let Some(error_reported) = const_qualifs.tainted_by_errors { body.tainted_by_errors = Some(error_reported); @@ -463,6 +531,7 @@ fn mir_promoted( /// Compute the MIR that is used during CTFE (and thus has no optimizations run on it) fn mir_for_ctfe(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &Body<'_> { + debug_assert!(!tcx.is_trivial_const(def_id), "Tried to get mir_for_ctfe of a trivial const"); tcx.arena.alloc(inner_mir_for_ctfe(tcx, def_id)) } diff --git a/compiler/rustc_public_bridge/src/context/impls.rs b/compiler/rustc_public_bridge/src/context/impls.rs index d9fa65431f5c..25be9e7b3041 100644 --- a/compiler/rustc_public_bridge/src/context/impls.rs +++ b/compiler/rustc_public_bridge/src/context/impls.rs @@ -92,7 +92,8 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { } else { false }; - !must_override && self.tcx.is_mir_available(def_id) + // FIXME: A good reason to make is_mir_available or mir_keys change behavior + !must_override && self.tcx.is_mir_available(def_id) && !self.tcx.is_trivial_const(def_id) } fn filter_fn_def(&self, def_id: DefId) -> Option { diff --git a/tests/incremental/issue-54242.rs b/tests/incremental/issue-54242.rs index 17bbd619a8fb..1bc456e565b5 100644 --- a/tests/incremental/issue-54242.rs +++ b/tests/incremental/issue-54242.rs @@ -14,7 +14,7 @@ impl Tr for str { type Arr = [u8; 8]; #[cfg(cfail)] type Arr = [u8; Self::C]; - //[cfail]~^ ERROR cycle detected when caching mir + //[cfail]~^ ERROR cycle detected when } fn main() {} diff --git a/tests/ui/rustc_public-ir-print/operands.stdout b/tests/ui/rustc_public-ir-print/operands.stdout index a4b1c07f3a0b..b7775416af85 100644 --- a/tests/ui/rustc_public-ir-print/operands.stdout +++ b/tests/ui/rustc_public-ir-print/operands.stdout @@ -397,13 +397,6 @@ fn operands(_1: u8) -> () { _89 = core::panicking::assert_failed::(move _90, move _91, move _93, move _95) -> unwind unreachable; } } -fn operands::{constant#0}() -> usize { - let mut _0: usize; - bb0: { - _0 = 10_usize; - return; - } -} fn more_operands() -> [Ctors; 3] { let mut _0: [Ctors; 3]; let _1: Dummy; @@ -447,13 +440,6 @@ fn more_operands() -> [Ctors; 3] { return; } } -fn more_operands::{constant#0}() -> usize { - let mut _0: usize; - bb0: { - _0 = 3_usize; - return; - } -} fn closures(_1: bool, _2: bool) -> {closure@$DIR/operands.rs:47:5: 47:19} { let mut _0: {closure@$DIR/operands.rs:47:5: 47:19}; debug x => _1; diff --git a/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr b/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr index 3ed6dc69d0b7..9750d806ce97 100644 --- a/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr +++ b/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr @@ -17,27 +17,7 @@ note: ...which requires const-evaluating + checking `accept0::{constant#0}`... | LL | fn accept0(_: Container<{ T::make() }>) {} | ^^^^^^^^^^^^^ -note: ...which requires caching mir of `accept0::{constant#0}` for CTFE... - --> $DIR/unsatisfied-const-trait-bound.rs:29:35 - | -LL | fn accept0(_: Container<{ T::make() }>) {} - | ^^^^^^^^^^^^^ -note: ...which requires elaborating drops for `accept0::{constant#0}`... - --> $DIR/unsatisfied-const-trait-bound.rs:29:35 - | -LL | fn accept0(_: Container<{ T::make() }>) {} - | ^^^^^^^^^^^^^ -note: ...which requires borrow-checking `accept0::{constant#0}`... - --> $DIR/unsatisfied-const-trait-bound.rs:29:35 - | -LL | fn accept0(_: Container<{ T::make() }>) {} - | ^^^^^^^^^^^^^ -note: ...which requires promoting constants in MIR for `accept0::{constant#0}`... - --> $DIR/unsatisfied-const-trait-bound.rs:29:35 - | -LL | fn accept0(_: Container<{ T::make() }>) {} - | ^^^^^^^^^^^^^ -note: ...which requires const checking `accept0::{constant#0}`... +note: ...which requires checking if `accept0::{constant#0}` is a trivial const... --> $DIR/unsatisfied-const-trait-bound.rs:29:35 | LL | fn accept0(_: Container<{ T::make() }>) {} @@ -70,32 +50,12 @@ LL | fn accept0(_: Container<{ T::make() }>) {} | ^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0391]: cycle detected when caching mir of `accept1::{constant#0}` for CTFE +error[E0391]: cycle detected when checking if `accept1::{constant#0}` is a trivial const --> $DIR/unsatisfied-const-trait-bound.rs:33:49 | LL | const fn accept1(_: Container<{ T::make() }>) {} | ^^^^^^^^^^^^^ | -note: ...which requires elaborating drops for `accept1::{constant#0}`... - --> $DIR/unsatisfied-const-trait-bound.rs:33:49 - | -LL | const fn accept1(_: Container<{ T::make() }>) {} - | ^^^^^^^^^^^^^ -note: ...which requires borrow-checking `accept1::{constant#0}`... - --> $DIR/unsatisfied-const-trait-bound.rs:33:49 - | -LL | const fn accept1(_: Container<{ T::make() }>) {} - | ^^^^^^^^^^^^^ -note: ...which requires promoting constants in MIR for `accept1::{constant#0}`... - --> $DIR/unsatisfied-const-trait-bound.rs:33:49 - | -LL | const fn accept1(_: Container<{ T::make() }>) {} - | ^^^^^^^^^^^^^ -note: ...which requires const checking `accept1::{constant#0}`... - --> $DIR/unsatisfied-const-trait-bound.rs:33:49 - | -LL | const fn accept1(_: Container<{ T::make() }>) {} - | ^^^^^^^^^^^^^ note: ...which requires building MIR for `accept1::{constant#0}`... --> $DIR/unsatisfied-const-trait-bound.rs:33:49 | @@ -126,7 +86,7 @@ note: ...which requires const-evaluating + checking `accept1::{constant#0}`... | LL | const fn accept1(_: Container<{ T::make() }>) {} | ^^^^^^^^^^^^^ - = note: ...which again requires caching mir of `accept1::{constant#0}` for CTFE, completing the cycle + = note: ...which again requires checking if `accept1::{constant#0}` is a trivial const, completing the cycle note: cycle used when const-evaluating + checking `accept1::{constant#0}` --> $DIR/unsatisfied-const-trait-bound.rs:33:49 | From 7af72e79b9a75475150fd9c5ef05d331f3f2a0ba Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Wed, 8 Oct 2025 15:42:16 +0100 Subject: [PATCH 123/170] More intuitive error when using self to instantiate tuple struct with private field --- .../rustc_resolve/src/late/diagnostics.rs | 101 ++++++++++-------- .../private-constructor-self-issue-147343.rs | 16 +++ ...ivate-constructor-self-issue-147343.stderr | 11 ++ 3 files changed, 84 insertions(+), 44 deletions(-) create mode 100644 tests/ui/resolve/private-constructor-self-issue-147343.rs create mode 100644 tests/ui/resolve/private-constructor-self-issue-147343.stderr diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 8c2ddda7f983..5201fef06ddd 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -180,6 +180,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let mut expected = source.descr_expected(); let path_str = Segment::names_to_string(path); let item_str = path.last().unwrap().ident; + if let Some(res) = res { BaseError { msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str), @@ -821,12 +822,18 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { args_snippet = snippet; } - err.span_suggestion( - call_span, - format!("try calling `{ident}` as a method"), - format!("self.{path_str}({args_snippet})"), - Applicability::MachineApplicable, - ); + if let Some(Res::Def(DefKind::Struct, def_id)) = res { + self.update_err_for_private_tuple_struct_fields(err, &source, def_id); + err.note("constructor is not visible here due to private fields"); + } else { + err.span_suggestion( + call_span, + format!("try calling `{ident}` as a method"), + format!("self.{path_str}({args_snippet})"), + Applicability::MachineApplicable, + ); + } + return (true, suggested_candidates, candidates); } } @@ -1611,6 +1618,47 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } + fn update_err_for_private_tuple_struct_fields( + &mut self, + err: &mut Diag<'_>, + source: &PathSource<'_, '_, '_>, + def_id: DefId, + ) -> Option> { + match source { + // e.g. `if let Enum::TupleVariant(field1, field2) = _` + PathSource::TupleStruct(_, pattern_spans) => { + err.primary_message( + "cannot match against a tuple struct which contains private fields", + ); + + // Use spans of the tuple struct pattern. + Some(Vec::from(*pattern_spans)) + } + // e.g. `let _ = Enum::TupleVariant(field1, field2);` + PathSource::Expr(Some(Expr { + kind: ExprKind::Call(path, args), + span: call_span, + .. + })) => { + err.primary_message( + "cannot initialize a tuple struct which contains private fields", + ); + self.suggest_alternative_construction_methods( + def_id, + err, + path.span, + *call_span, + &args[..], + ); + // Use spans of the tuple struct definition. + self.r + .field_idents(def_id) + .map(|fields| fields.iter().map(|f| f.span).collect::>()) + } + _ => None, + } + } + /// Provides context-dependent help for errors reported by the `smart_resolve_path_fragment` /// function. /// Returns `true` if able to provide context-dependent help. @@ -1942,42 +1990,6 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { return true; }; - let update_message = - |this: &mut Self, err: &mut Diag<'_>, source: &PathSource<'_, '_, '_>| { - match source { - // e.g. `if let Enum::TupleVariant(field1, field2) = _` - PathSource::TupleStruct(_, pattern_spans) => { - err.primary_message( - "cannot match against a tuple struct which contains private fields", - ); - - // Use spans of the tuple struct pattern. - Some(Vec::from(*pattern_spans)) - } - // e.g. `let _ = Enum::TupleVariant(field1, field2);` - PathSource::Expr(Some(Expr { - kind: ExprKind::Call(path, args), - span: call_span, - .. - })) => { - err.primary_message( - "cannot initialize a tuple struct which contains private fields", - ); - this.suggest_alternative_construction_methods( - def_id, - err, - path.span, - *call_span, - &args[..], - ); - // Use spans of the tuple struct definition. - this.r - .field_idents(def_id) - .map(|fields| fields.iter().map(|f| f.span).collect::>()) - } - _ => None, - } - }; let is_accessible = self.r.is_accessible_from(ctor_vis, self.parent_scope.module); if let Some(use_span) = self.r.inaccessible_ctor_reexport.get(&span) && is_accessible @@ -2006,13 +2018,14 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { Applicability::MachineApplicable, ); } - update_message(self, err, &source); + self.update_err_for_private_tuple_struct_fields(err, &source, def_id); } if !is_expected(ctor_def) || is_accessible { return true; } - let field_spans = update_message(self, err, &source); + let field_spans = + self.update_err_for_private_tuple_struct_fields(err, &source, def_id); if let Some(spans) = field_spans.filter(|spans| spans.len() > 0 && fields.len() == spans.len()) diff --git a/tests/ui/resolve/private-constructor-self-issue-147343.rs b/tests/ui/resolve/private-constructor-self-issue-147343.rs new file mode 100644 index 000000000000..3c5ac603933c --- /dev/null +++ b/tests/ui/resolve/private-constructor-self-issue-147343.rs @@ -0,0 +1,16 @@ +mod m { + pub struct S(crate::P); +} + +use m::S; + +struct P; + +impl P { + fn foo(self) { + S(self); + //~^ ERROR cannot initialize a tuple struct which contains private fields [E0423] + } +} + +fn main() {} diff --git a/tests/ui/resolve/private-constructor-self-issue-147343.stderr b/tests/ui/resolve/private-constructor-self-issue-147343.stderr new file mode 100644 index 000000000000..f9290d7dcb35 --- /dev/null +++ b/tests/ui/resolve/private-constructor-self-issue-147343.stderr @@ -0,0 +1,11 @@ +error[E0423]: cannot initialize a tuple struct which contains private fields + --> $DIR/private-constructor-self-issue-147343.rs:11:9 + | +LL | S(self); + | ^ + | + = note: constructor is not visible here due to private fields + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0423`. From 7a0d9c8d5e5a0892e0739af12bd0063fd3bda480 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 25 Oct 2025 17:59:22 -0400 Subject: [PATCH 124/170] Improve the ICE message for invalid nullary intrinsic calls --- compiler/rustc_codegen_ssa/src/mir/intrinsic.rs | 11 +++++++++-- library/core/src/intrinsics/mod.rs | 10 ++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index befa00c6861e..cc3316c7f8cc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -120,8 +120,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::atomic_singlethreadfence | sym::caller_location => {} _ => { - span_bug!(span, "nullary intrinsic {name} must either be in a const block or explicitly opted out because it is inherently a runtime intrinsic -"); + span_bug!( + span, + "Nullary intrinsic {name} must be called in a const block. \ + If you are seeing this message from code outside the standard library, the \ + unstable implementation details of the relevant intrinsic may have changed. \ + Consider using stable APIs instead. \ + If you are adding a new nullary intrinsic that is inherently a runtime \ + intrinsic, update this check." + ); } } } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index c397e762d558..c2da8a1a9209 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2728,6 +2728,11 @@ pub unsafe fn vtable_align(ptr: *const ()) -> usize; /// More specifically, this is the offset in bytes between successive /// items of the same type, including alignment padding. /// +/// Note that, unlike most intrinsics, this can only be called at compile-time +/// as backends do not have an implementation for it. The only caller (its +/// stable counterpart) wraps this intrinsic call in a `const` block so that +/// backends only see an evaluated constant. +/// /// The stabilized version of this intrinsic is [`core::mem::size_of`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] @@ -2742,6 +2747,11 @@ pub const fn size_of() -> usize; /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// +/// Note that, unlike most intrinsics, this can only be called at compile-time +/// as backends do not have an implementation for it. The only caller (its +/// stable counterpart) wraps this intrinsic call in a `const` block so that +/// backends only see an evaluated constant. +/// /// The stabilized version of this intrinsic is [`core::mem::align_of`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] From e873f13454946cd289c428976bef51b29bbc3508 Mon Sep 17 00:00:00 2001 From: George Tokmaji Date: Sun, 26 Oct 2025 10:53:13 +0100 Subject: [PATCH 125/170] Add myself as a windows-msvc maintainer --- src/doc/rustc/src/platform-support/windows-msvc.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/rustc/src/platform-support/windows-msvc.md b/src/doc/rustc/src/platform-support/windows-msvc.md index 826c75b79c57..c4b56201e796 100644 --- a/src/doc/rustc/src/platform-support/windows-msvc.md +++ b/src/doc/rustc/src/platform-support/windows-msvc.md @@ -12,6 +12,7 @@ Windows MSVC targets. [@ChrisDenton](https://github.com/ChrisDenton) [@dpaoliello](https://github.com/dpaoliello) +[@Fulgen301](https://github.com/Fulgen301) [@lambdageek](https://github.com/lambdageek) [@sivadeilra](https://github.com/sivadeilra) [@wesleywiser](https://github.com/wesleywiser) From 95e2b8444a700d8a6c88ddb2860f74c2f5176648 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 26 Oct 2025 19:55:09 +0800 Subject: [PATCH 126/170] Fix not applicable on let-chain for replace_is_method_with_if_let_method Example --- ```rust fn main() { let x = Some(1); let cond = true; if cond && x.is_som$0e() {} } ``` **Before this PR** Assist not applicable **After this PR** ```rust fn main() { let x = Some(1); let cond = true; if cond && let Some(${0:x1}) = x {} } ``` --- .../replace_is_method_with_if_let_method.rs | 63 +++++++++++++++++-- .../crates/ide-assists/src/utils.rs | 22 +++++++ 2 files changed, 81 insertions(+), 4 deletions(-) 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 f507cae1bb0d..c57fd4d439dc 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 @@ -1,7 +1,7 @@ use ide_db::syntax_helpers::suggest_name; use syntax::ast::{self, AstNode, syntax_factory::SyntaxFactory}; -use crate::{AssistContext, AssistId, Assists}; +use crate::{AssistContext, AssistId, Assists, utils::cover_let_chain}; // Assist: replace_is_some_with_if_let_some // @@ -27,13 +27,11 @@ pub(crate) fn replace_is_method_with_if_let_method( let if_expr = ctx.find_node_at_offset::()?; let cond = if_expr.condition()?; + let cond = cover_let_chain(cond, ctx.selection_trimmed())?; let call_expr = match cond { ast::Expr::MethodCallExpr(call) => call, _ => return None, }; - if ctx.offset() > if_expr.then_branch()?.stmt_list()?.l_curly_token()?.text_range().end() { - return None; - } let name_ref = call_expr.name_ref()?; match name_ref.text().as_str() { @@ -195,6 +193,63 @@ fn main() { ); } + #[test] + fn replace_is_some_with_if_let_some_in_let_chain() { + check_assist( + replace_is_method_with_if_let_method, + r#" +fn main() { + let x = Some(1); + let cond = true; + if cond && x.is_som$0e() {} +} +"#, + r#" +fn main() { + let x = Some(1); + let cond = true; + if cond && let Some(${0:x1}) = x {} +} +"#, + ); + + check_assist( + replace_is_method_with_if_let_method, + r#" +fn main() { + let x = Some(1); + let cond = true; + if x.is_som$0e() && cond {} +} +"#, + r#" +fn main() { + let x = Some(1); + let cond = true; + if let Some(${0:x1}) = x && cond {} +} +"#, + ); + + check_assist( + replace_is_method_with_if_let_method, + r#" +fn main() { + let x = Some(1); + let cond = true; + if cond && x.is_som$0e() && cond {} +} +"#, + r#" +fn main() { + let x = Some(1); + let cond = true; + if cond && let Some(${0:x1}) = x && cond {} +} +"#, + ); + } + #[test] fn replace_is_some_with_if_let_some_not_applicable_after_l_curly() { check_assist_not_applicable( 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 5a3c5a39dac7..e43516f6b963 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -1133,6 +1133,28 @@ pub(crate) fn tt_from_syntax(node: SyntaxNode) -> Vec Option { + if !expr.syntax().text_range().contains_range(range) { + return None; + } + loop { + let (chain_expr, rest) = if let ast::Expr::BinExpr(bin_expr) = &expr + && bin_expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) + { + (bin_expr.rhs(), bin_expr.lhs()) + } else { + (Some(expr), None) + }; + + if let Some(chain_expr) = chain_expr + && chain_expr.syntax().text_range().contains_range(range) + { + break Some(chain_expr); + } + expr = rest?; + } +} + pub fn is_body_const(sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr) -> bool { let mut is_const = true; preorder_expr(expr, &mut |ev| { From ce94044ff9be11792342f47af98501a35755d884 Mon Sep 17 00:00:00 2001 From: Michael Gruenewald Date: Sun, 26 Oct 2025 13:22:52 +0100 Subject: [PATCH 127/170] Don't add cargo to requiresServerReloadOpts --- src/tools/rust-analyzer/editors/code/src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index c0a1b3f02e36..5dc2c419efa8 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -31,7 +31,7 @@ export class Config { workspaceState: vscode.Memento; private readonly rootSection = "rust-analyzer"; - private readonly requiresServerReloadOpts = ["cargo", "server", "files", "showSyntaxTree"].map( + private readonly requiresServerReloadOpts = ["server", "files", "showSyntaxTree"].map( (opt) => `${this.rootSection}.${opt}`, ); From 85b7d646cd9b2deb6e65b3bd4aa045a71a6415b4 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 27 Oct 2025 02:18:52 +0900 Subject: [PATCH 128/170] Add regression tests for some fixed `A-ty` issues --- .../hir-ty/src/tests/regression/new_solver.rs | 52 +++++++++++++++ .../src/handlers/mismatched_arg_count.rs | 23 +++++++ .../crates/ide/src/goto_definition.rs | 64 +++++++++++++++++++ 3 files changed, 139 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 5983ec764790..f8b73cd50551 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -472,3 +472,55 @@ where "#, ); } + +#[test] +fn regression_16282() { + check_infer( + r#" +//- minicore: coerce_unsized, dispatch_from_dyn +trait MapLookup { + type MapValue; +} + +impl MapLookup for K { + type MapValue = K; +} + +trait Map: MapLookup<::Key> { + type Key; +} + +impl Map for K { + type Key = K; +} + + +fn main() { + let _ = &() + as &dyn Map; +} +"#, + expect![[r#" + 210..272 '{ ...32>; }': () + 220..221 '_': &'? (dyn Map + '?) + 224..227 '&()': &'? () + 224..269 '&() ...e=u32>': &'? (dyn Map + 'static) + 225..227 '()': () + "#]], + ); +} + +#[test] +fn regression_18692() { + check_no_mismatches( + r#" +//- minicore: coerce_unsized, dispatch_from_dyn, send +trait Trait: Send {} + +fn f(_: *const (dyn Trait + Send)) {} +fn g(it: *const (dyn Trait)) { + f(it); +} +"#, + ); +} 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 25c1e633ba3b..4ed71f0d3fb8 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 @@ -485,6 +485,29 @@ fn foo((): (), (): ()) { foo(1); // ^ error: expected 2 arguments, found 1 } +"#, + ); + } + + #[test] + fn regression_17233() { + check_diagnostics( + r#" +pub trait A { + type X: B; +} +pub trait B: A { + fn confused_name(self, _: i32); +} + +pub struct Foo; +impl Foo { + pub fn confused_name(&self) {} +} + +pub fn repro() { + Foo.confused_name(); +} "#, ); } 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 e335989ab2b0..0ee9795af580 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -4015,4 +4015,68 @@ fn bar() { "##, ); } + + #[test] + fn regression_20038() { + check( + r#" +//- minicore: clone, fn +struct Map(Fut, F); + +struct InspectFn(F); + +trait FnOnce1 { + type Output; +} + +trait Future1 { + type Output; +} + +trait FusedFuture1: Future1 { + fn is_terminated(&self) -> bool; + //^^^^^^^^^^^^^ +} + +impl FnOnce1 for T +where + T: FnOnce(A) -> R, +{ + type Output = R; +} + +impl FnOnce1 for InspectFn +where + F: for<'a> FnOnce1<&'a A, Output = ()>, +{ + type Output = A; +} + +impl Future1 for Map +where + Fut: Future1, + F: FnOnce1, +{ + type Output = T; +} + +impl FusedFuture1 for Map +where + Fut: Future1, + F: FnOnce1, +{ + fn is_terminated(&self) -> bool { + false + } +} + +fn overflows(inner: &Map>) +where + Map>: FusedFuture1 +{ + let _x = inner.is_terminated$0(); +} +"#, + ) + } } From 744c670812245dc50291e5121341ca2ca0a51fe8 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sun, 19 Oct 2025 01:06:56 +0000 Subject: [PATCH 129/170] Add built-in `const` impls for `Clone` and `Copy` --- .../src/solve/effect_goals.rs | 28 +++++- .../src/traits/effects.rs | 94 ++++++++++++++++++- compiler/rustc_type_ir/src/predicate.rs | 2 +- .../traits/const-traits/const-traits-alloc.rs | 2 +- .../traits/const-traits/const-traits-core.rs | 15 ++- 5 files changed, 133 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 48bd0963c874..695a29e58a5e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -211,10 +211,32 @@ where } fn consider_builtin_copy_clone_candidate( - _ecx: &mut EvalCtxt<'_, D>, - _goal: Goal, + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, ) -> Result, NoSolution> { - Err(NoSolution) + let cx = ecx.cx(); + + let self_ty = goal.predicate.self_ty(); + let constituent_tys = + structural_traits::instantiate_constituent_tys_for_copy_clone_trait(ecx, self_ty)?; + + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.enter_forall(constituent_tys, |ecx, tys| { + ecx.add_goals( + GoalSource::ImplWhereBound, + tys.into_iter().map(|ty| { + goal.with( + cx, + ty::ClauseKind::HostEffect( + goal.predicate.with_replaced_self_ty(cx, ty), + ), + ) + }), + ); + }); + + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) } fn consider_builtin_fn_ptr_trait_candidate( diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 7f2a9999d646..9ca97fb06d5d 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -8,7 +8,7 @@ use rustc_middle::span_bug; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::elaborate::elaborate; use rustc_middle::ty::fast_reject::DeepRejectCtxt; -use rustc_middle::ty::{self, TypingMode}; +use rustc_middle::ty::{self, Ty, TypingMode}; use thin_vec::{ThinVec, thin_vec}; use super::SelectionContext; @@ -303,6 +303,9 @@ fn evaluate_host_effect_from_builtin_impls<'tcx>( obligation: &HostEffectObligation<'tcx>, ) -> Result>, EvaluationFailure> { match selcx.tcx().as_lang_item(obligation.predicate.def_id()) { + Some(LangItem::Copy | LangItem::Clone) => { + evaluate_host_effect_for_copy_clone_goal(selcx, obligation) + } Some(LangItem::Destruct) => evaluate_host_effect_for_destruct_goal(selcx, obligation), Some(LangItem::Fn | LangItem::FnMut | LangItem::FnOnce) => { evaluate_host_effect_for_fn_goal(selcx, obligation) @@ -311,6 +314,95 @@ fn evaluate_host_effect_from_builtin_impls<'tcx>( } } +fn evaluate_host_effect_for_copy_clone_goal<'tcx>( + selcx: &mut SelectionContext<'_, 'tcx>, + obligation: &HostEffectObligation<'tcx>, +) -> Result>, EvaluationFailure> { + let tcx = selcx.tcx(); + let self_ty = obligation.predicate.self_ty(); + let constituent_tys = match *self_ty.kind() { + // impl Copy/Clone for FnDef, FnPtr + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(ty::Binder::dummy(vec![])), + + // Implementations are provided in core + ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, ty::Mutability::Not) + | ty::Array(..) => Err(EvaluationFailure::NoSolution), + + // Cannot implement in core, as we can't be generic over patterns yet, + // so we'd have to list all patterns and type combinations. + ty::Pat(ty, ..) => Ok(ty::Binder::dummy(vec![ty])), + + ty::Dynamic(..) + | ty::Str + | ty::Slice(_) + | ty::Foreign(..) + | ty::Ref(_, _, ty::Mutability::Mut) + | ty::Adt(_, _) + | ty::Alias(_, _) + | ty::Param(_) + | ty::Placeholder(..) => Err(EvaluationFailure::NoSolution), + + ty::Bound(..) + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + panic!("unexpected type `{self_ty:?}`") + } + + // impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone + ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.to_vec())), + + // impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone + ty::Closure(_, args) => Ok(ty::Binder::dummy(vec![args.as_closure().tupled_upvars_ty()])), + + // impl Copy/Clone for CoroutineClosure where Self::TupledUpvars: Copy/Clone + ty::CoroutineClosure(_, args) => { + Ok(ty::Binder::dummy(vec![args.as_coroutine_closure().tupled_upvars_ty()])) + } + + // only when `coroutine_clone` is enabled and the coroutine is movable + // impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses) + ty::Coroutine(def_id, args) => match tcx.coroutine_movability(def_id) { + ty::Movability::Static => Err(EvaluationFailure::NoSolution), + ty::Movability::Movable => { + if tcx.features().coroutine_clone() { + Ok(ty::Binder::dummy(vec![ + args.as_coroutine().tupled_upvars_ty(), + Ty::new_coroutine_witness_for_coroutine(tcx, def_id, args), + ])) + } else { + Err(EvaluationFailure::NoSolution) + } + } + }, + + ty::UnsafeBinder(_) => Err(EvaluationFailure::NoSolution), + + // impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types + ty::CoroutineWitness(def_id, args) => Ok(tcx + .coroutine_hidden_types(def_id) + .instantiate(tcx, args) + .map_bound(|bound| bound.types.to_vec())), + }?; + + Ok(constituent_tys + .iter() + .map(|ty| { + obligation.with( + tcx, + ty.map_bound(|ty| ty::TraitRef::new(tcx, obligation.predicate.def_id(), [ty])) + .to_host_effect_clause(tcx, obligation.predicate.constness), + ) + }) + .collect()) +} + // NOTE: Keep this in sync with `const_conditions_for_destruct` in the new solver. fn evaluate_host_effect_for_destruct_goal<'tcx>( selcx: &mut SelectionContext<'_, 'tcx>, diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 3e32a7788546..c7dc3fbc9a60 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -121,7 +121,7 @@ impl ty::Binder> { } pub fn to_host_effect_clause(self, cx: I, constness: BoundConstness) -> I::Clause { - self.map_bound(|trait_ref| { + self.map_bound(|trait_ref: TraitRef| { ty::ClauseKind::HostEffect(HostEffectPredicate { trait_ref, constness }) }) .upcast(cx) diff --git a/tests/ui/traits/const-traits/const-traits-alloc.rs b/tests/ui/traits/const-traits/const-traits-alloc.rs index 07725ef02f18..4dfec2f77f1b 100644 --- a/tests/ui/traits/const-traits/const-traits-alloc.rs +++ b/tests/ui/traits/const-traits/const-traits-alloc.rs @@ -1,4 +1,4 @@ -//@ run-pass +//@ check-pass #![feature(const_trait_impl, const_default)] #![allow(dead_code)] // alloc::string diff --git a/tests/ui/traits/const-traits/const-traits-core.rs b/tests/ui/traits/const-traits/const-traits-core.rs index 6df53daae137..2cafde4f5bd0 100644 --- a/tests/ui/traits/const-traits/const-traits-core.rs +++ b/tests/ui/traits/const-traits/const-traits-core.rs @@ -1,6 +1,13 @@ -//@ run-pass +//@ check-pass #![feature( - const_trait_impl, const_default, ptr_alignment_type, ascii_char, f16, f128, sync_unsafe_cell, + const_clone, + const_default, + const_trait_impl, + ptr_alignment_type, + ascii_char, + f16, + f128, + sync_unsafe_cell, )] #![allow(dead_code)] // core::default @@ -43,4 +50,8 @@ const REF_CELL: std::cell::RefCell<()> = Default::default(); const UNSAFE_CELL: std::cell::UnsafeCell<()> = Default::default(); const SYNC_UNSAFE_CELL: std::cell::SyncUnsafeCell<()> = Default::default(); +// `Clone` for tuples +const BUILTIN_CLONE: () = ().clone(); +const BUILTIN_CLONE_2: (u32, i32) = (42, 100).clone(); + fn main() {} From 042018df2d7a98d24a4c5216b122fa2b8610242d Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sun, 26 Oct 2025 19:57:05 +0000 Subject: [PATCH 130/170] respond to review --- .../src/traits/effects.rs | 27 +++++++++++-------- .../src/traits/select/mod.rs | 2 +- compiler/rustc_type_ir/src/predicate.rs | 2 +- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 9ca97fb06d5d..c29e3281f224 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -368,19 +368,24 @@ fn evaluate_host_effect_for_copy_clone_goal<'tcx>( // only when `coroutine_clone` is enabled and the coroutine is movable // impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses) - ty::Coroutine(def_id, args) => match tcx.coroutine_movability(def_id) { - ty::Movability::Static => Err(EvaluationFailure::NoSolution), - ty::Movability::Movable => { - if tcx.features().coroutine_clone() { - Ok(ty::Binder::dummy(vec![ - args.as_coroutine().tupled_upvars_ty(), - Ty::new_coroutine_witness_for_coroutine(tcx, def_id, args), - ])) - } else { - Err(EvaluationFailure::NoSolution) + ty::Coroutine(def_id, args) => { + if selcx.should_stall_coroutine(def_id) { + return Err(EvaluationFailure::Ambiguous); + } + match tcx.coroutine_movability(def_id) { + ty::Movability::Static => Err(EvaluationFailure::NoSolution), + ty::Movability::Movable => { + if tcx.features().coroutine_clone() { + Ok(ty::Binder::dummy(vec![ + args.as_coroutine().tupled_upvars_ty(), + Ty::new_coroutine_witness_for_coroutine(tcx, def_id, args), + ])) + } else { + Err(EvaluationFailure::NoSolution) + } } } - }, + } ty::UnsafeBinder(_) => Err(EvaluationFailure::NoSolution), diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 70a0896ae384..ea903bac9d61 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2877,7 +2877,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { obligations } - fn should_stall_coroutine(&self, def_id: DefId) -> bool { + pub(super) fn should_stall_coroutine(&self, def_id: DefId) -> bool { match self.infcx.typing_mode() { TypingMode::Analysis { defining_opaque_types_and_generators: stalled_generators } => { def_id.as_local().is_some_and(|def_id| stalled_generators.contains(&def_id)) diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index c7dc3fbc9a60..3e32a7788546 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -121,7 +121,7 @@ impl ty::Binder> { } pub fn to_host_effect_clause(self, cx: I, constness: BoundConstness) -> I::Clause { - self.map_bound(|trait_ref: TraitRef| { + self.map_bound(|trait_ref| { ty::ClauseKind::HostEffect(HostEffectPredicate { trait_ref, constness }) }) .upcast(cx) From 2aa314c024cc2561eb284bbe7a20a7645ceec614 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Mon, 27 Oct 2025 00:37:43 +0300 Subject: [PATCH 131/170] Fix duplicate 'the the' typos in comments Signed-off-by: Osama Abdelkader --- compiler/rustc_hir_typeck/src/writeback.rs | 2 +- compiler/rustc_type_ir/src/search_graph/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 697029e55f7c..50b6fe1ad5ec 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -798,7 +798,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } else { let predicate = self.tcx().erase_and_anonymize_regions(predicate); if cause.has_infer() || cause.has_placeholders() { - // We can't use the the obligation cause as it references + // We can't use the obligation cause as it references // information local to this query. cause = self.fcx.misc(cause.span); } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index b2a871a21149..4f3f140af67d 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -924,7 +924,7 @@ enum RebaseReason { /// /// This either happens in the first evaluation step for the cycle head. /// In this case the used provisional result depends on the cycle `PathKind`. - /// We store this path kind to check whether the the provisional cache entry + /// We store this path kind to check whether the provisional cache entry /// we're rebasing relied on the same cycles. /// /// In later iterations cycles always return `stack_entry.provisional_result` From 9503981f32771f28f8ac88d5305261180b58a038 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Wed, 15 Oct 2025 14:43:36 +0200 Subject: [PATCH 132/170] add a mailmap entry I accidentally commited some things with my name/email swapped ^^' --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index fc8e83d6493c..6c168bbbcb55 100644 --- a/.mailmap +++ b/.mailmap @@ -680,6 +680,7 @@ Valerii Lashmanov Vitali Haravy Vitali Haravy Vitaly Shukela Waffle Lapkin +Waffle Lapkin Waffle Lapkin Weihang Lo Weihang Lo From 6f649e4e1a5345fbacc64acc13ea376501e90384 Mon Sep 17 00:00:00 2001 From: ltdk Date: Tue, 14 Oct 2025 20:23:39 -0400 Subject: [PATCH 133/170] const select_unpredictable --- library/core/src/hint.rs | 7 +++++- library/core/src/intrinsics/mod.rs | 8 +++++-- library/core/src/lib.rs | 1 + library/coretests/tests/hint.rs | 34 ++++++++++++++++++------------ library/coretests/tests/lib.rs | 1 + 5 files changed, 35 insertions(+), 16 deletions(-) diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 23cfdf5bfde2..6efe95a9edce 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -4,6 +4,7 @@ //! //! Hints may be compile time or runtime. +use crate::marker::Destruct; use crate::mem::MaybeUninit; use crate::{intrinsics, ub_checks}; @@ -771,7 +772,11 @@ pub const fn cold_path() { /// ``` #[inline(always)] #[stable(feature = "select_unpredictable", since = "1.88.0")] -pub fn select_unpredictable(condition: bool, true_val: T, false_val: T) -> T { +#[rustc_const_unstable(feature = "const_select_unpredictable", issue = "145938")] +pub const fn select_unpredictable(condition: bool, true_val: T, false_val: T) -> T +where + T: [const] Destruct, +{ // FIXME(https://github.com/rust-lang/unsafe-code-guidelines/issues/245): // Change this to use ManuallyDrop instead. let mut true_val = MaybeUninit::new(true_val); diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index c2da8a1a9209..4cee77fda4fb 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -55,7 +55,7 @@ #![allow(missing_docs)] use crate::ffi::va_list::{VaArgSafe, VaListImpl}; -use crate::marker::{ConstParamTy, DiscriminantKind, PointeeSized, Tuple}; +use crate::marker::{ConstParamTy, Destruct, DiscriminantKind, PointeeSized, Tuple}; use crate::ptr; mod bounds; @@ -477,11 +477,15 @@ pub const fn unlikely(b: bool) -> bool { /// However unlike the public form, the intrinsic will not drop the value that /// is not selected. #[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_unstable(feature = "const_select_unpredictable", issue = "145938")] #[rustc_intrinsic] #[rustc_nounwind] #[miri::intrinsic_fallback_is_spec] #[inline] -pub fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { +pub const fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T +where + T: [const] Destruct, +{ if b { true_val } else { false_val } } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index bc428c37a88f..e213e1d91a75 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -106,6 +106,7 @@ #![feature(const_cmp)] #![feature(const_destruct)] #![feature(const_eval_select)] +#![feature(const_select_unpredictable)] #![feature(core_intrinsics)] #![feature(coverage_attribute)] #![feature(disjoint_bitor)] diff --git a/library/coretests/tests/hint.rs b/library/coretests/tests/hint.rs index 24de27b24b80..d15730823eb5 100644 --- a/library/coretests/tests/hint.rs +++ b/library/coretests/tests/hint.rs @@ -1,25 +1,33 @@ #[test] fn select_unpredictable_drop() { use core::cell::Cell; + struct X<'a>(&'a Cell); - impl Drop for X<'_> { + impl const Drop for X<'_> { fn drop(&mut self) { self.0.set(true); } } - let a_dropped = Cell::new(false); - let b_dropped = Cell::new(false); - let a = X(&a_dropped); - let b = X(&b_dropped); - assert!(!a_dropped.get()); - assert!(!b_dropped.get()); - let selected = core::hint::select_unpredictable(core::hint::black_box(true), a, b); - assert!(!a_dropped.get()); - assert!(b_dropped.get()); - drop(selected); - assert!(a_dropped.get()); - assert!(b_dropped.get()); + const fn do_test() { + let a_dropped = Cell::new(false); + let b_dropped = Cell::new(false); + let a = X(&a_dropped); + let b = X(&b_dropped); + assert!(!a_dropped.get()); + assert!(!b_dropped.get()); + let selected = core::hint::select_unpredictable(core::hint::black_box(true), a, b); + assert!(!a_dropped.get()); + assert!(b_dropped.get()); + drop(selected); + assert!(a_dropped.get()); + assert!(b_dropped.get()); + } + + do_test(); + const { + do_test(); + } } #[test] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 5c2522acb136..24e59d7cd73b 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -27,6 +27,7 @@ #![feature(const_option_ops)] #![feature(const_ref_cell)] #![feature(const_result_trait_fn)] +#![feature(const_select_unpredictable)] #![feature(const_trait_impl)] #![feature(control_flow_ok)] #![feature(core_float_math)] From 87c66c93ca71b4d7d326362322584cd2584473bd Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Sun, 26 Oct 2025 14:58:49 +0800 Subject: [PATCH 134/170] ci: loongarch64: use medium code model to avoid relocation overflows The LoongArch C/C++ cross toolchain defaults to the `normal` code model, which can cause relocation overflows when linking LLVM after upgrading to verion 22. This change uses the `medium`code model for `loongarch64-linux-gnu` and `loongarch64-linux-musl` builds to avoid these linking errors. --- src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile | 4 +++- src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile index e3f23149284e..4b86ed32fd55 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile @@ -21,7 +21,9 @@ ENV PATH=$PATH:/x-tools/loongarch64-unknown-linux-gnu/bin ENV CC_loongarch64_unknown_linux_gnu=loongarch64-unknown-linux-gnu-gcc \ AR_loongarch64_unknown_linux_gnu=loongarch64-unknown-linux-gnu-ar \ - CXX_loongarch64_unknown_linux_gnu=loongarch64-unknown-linux-gnu-g++ + CXX_loongarch64_unknown_linux_gnu=loongarch64-unknown-linux-gnu-g++ \ + CFLAGS_loongarch64_unknown_linux_gnu="-mcmodel=medium" \ + CXXFLAGS_loongarch64_unknown_linux_gnu="-mcmodel=medium" # We re-use the Linux toolchain for bare-metal, because upstream bare-metal # target support for LoongArch is only available from GCC 14+. diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile index 2c33b5526eeb..763b29ae1c5e 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile @@ -21,7 +21,9 @@ ENV PATH=$PATH:/x-tools/loongarch64-unknown-linux-musl/bin ENV CC_loongarch64_unknown_linux_musl=loongarch64-unknown-linux-musl-gcc \ AR_loongarch64_unknown_linux_musl=loongarch64-unknown-linux-musl-ar \ - CXX_loongarch64_unknown_linux_musl=loongarch64-unknown-linux-musl-g++ + CXX_loongarch64_unknown_linux_musl=loongarch64-unknown-linux-musl-g++ \ + CFLAGS_loongarch64_unknown_linux_musl="-mcmodel=medium" \ + CXXFLAGS_loongarch64_unknown_linux_musl="-mcmodel=medium" ENV HOSTS=loongarch64-unknown-linux-musl From 6ba56d227fd4f2dfcbfa482f01a9bef3428d9119 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sun, 26 Oct 2025 22:09:33 -0400 Subject: [PATCH 135/170] Move trivial_const to a separate module with a doc comment --- compiler/rustc_mir_transform/src/lib.rs | 67 ++------------ .../rustc_mir_transform/src/trivial_const.rs | 91 +++++++++++++++++++ 2 files changed, 99 insertions(+), 59 deletions(-) create mode 100644 compiler/rustc_mir_transform/src/trivial_const.rs diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 129b28659ddc..18b798c01faa 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -23,11 +23,11 @@ use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::LocalDefId; use rustc_index::IndexVec; use rustc_middle::mir::{ - AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, ConstValue, - LocalDecl, MirPhase, Operand, Place, ProjectionElem, Promoted, RETURN_PLACE, RuntimePhase, - Rvalue, START_BLOCK, SourceInfo, Statement, StatementKind, TerminatorKind, + AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, LocalDecl, + MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, START_BLOCK, + SourceInfo, Statement, StatementKind, TerminatorKind, }; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_middle::util::Providers; use rustc_middle::{bug, query, span_bug}; use rustc_mir_build::builder::build_mir; @@ -55,6 +55,7 @@ mod liveness; mod patch; mod shim; mod ssa; +mod trivial_const; /// We import passes via this macro so that we can have a static list of pass names /// (used to verify CLI arguments). It takes a list of modules, followed by the passes @@ -226,7 +227,7 @@ pub fn provide(providers: &mut Providers) { promoted_mir, deduced_param_attrs: deduce_param_attrs::deduced_param_attrs, coroutine_by_move_body_def_id: coroutine::coroutine_by_move_body_def_id, - trivial_const: trivial_const_provider, + trivial_const: trivial_const::trivial_const_provider, ..providers.queries }; } @@ -377,66 +378,14 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs { validator.qualifs_in_return_place() } -fn def_kind_compatible_with_trivial_mir<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> bool { - // Static and InlineConst are the obvious additions, but - // * Statics need additional type-checking to taint `static A: _ = 0;`, currently we'd ICE. - // * The MIR for InlineConst is used by the borrow checker, and not easy to skip over. - matches!(tcx.def_kind(def), DefKind::AssocConst | DefKind::Const | DefKind::AnonConst) -} - -fn trivial_const_provider<'tcx>( - tcx: TyCtxt<'tcx>, - def: LocalDefId, -) -> Option<(ConstValue, Ty<'tcx>)> { - if def_kind_compatible_with_trivial_mir(tcx, def) { - trivial_const(&tcx.mir_built(def).borrow()) - } else { - None - } -} - -fn trivial_const<'tcx>(body: &Body<'tcx>) -> Option<(ConstValue, Ty<'tcx>)> { - if body.has_opaque_types() { - return None; - } - - if body.basic_blocks.len() != 1 { - return None; - } - - let block = &body.basic_blocks[START_BLOCK]; - if block.statements.len() != 1 { - return None; - } - - if block.terminator().kind != TerminatorKind::Return { - return None; - } - - let StatementKind::Assign(box (place, rvalue)) = &block.statements[0].kind else { - return None; - }; - - if *place != Place::from(RETURN_PLACE) { - return None; - } - - if let Rvalue::Use(Operand::Constant(c)) = rvalue { - if let rustc_middle::mir::Const::Val(v, ty) = c.const_ { - return Some((v, ty)); - } - } - - return None; -} - fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { let mut body = build_mir(tcx, def); // Identifying trivial consts based on their mir_built is easy, but a little wasteful. // Trying to push this logic earlier in the compiler and never even produce the Body would // probably improve compile time. - if def_kind_compatible_with_trivial_mir(tcx, def) && trivial_const(&body).is_some() { + if trivial_const::trivial_const(tcx, def, || &body).is_some() { + // Skip all the passes below for trivial consts. let body = tcx.alloc_steal_mir(body); pass_manager::dump_mir_for_phase_change(tcx, &body.borrow()); return body; diff --git a/compiler/rustc_mir_transform/src/trivial_const.rs b/compiler/rustc_mir_transform/src/trivial_const.rs new file mode 100644 index 000000000000..bc66271e8ea1 --- /dev/null +++ b/compiler/rustc_mir_transform/src/trivial_const.rs @@ -0,0 +1,91 @@ +use std::ops::Deref; + +use rustc_hir::def::DefKind; +use rustc_hir::def_id::LocalDefId; +use rustc_middle::mir::{ + Body, ConstValue, Operand, Place, RETURN_PLACE, Rvalue, START_BLOCK, StatementKind, + TerminatorKind, +}; +use rustc_middle::ty::{Ty, TyCtxt, TypeVisitableExt}; + +/// If the given def is a trivial const, returns the value and type the const evaluates to. +/// +/// A "trivial const" is a const which can be easily proven to evaluate successfully, and the value +/// that it evaluates to can be easily found without going through the usual MIR phases for a const. +/// +/// Currently the only form of trivial const that is supported is this: +/// ``` +/// const A: usize = 0; +/// ``` +/// which has this MIR: +/// ```text +/// const A: usize = { +/// let mut _0: usize; +/// +/// bb0: { +/// _0 = const 0_usize; +/// return; +/// } +/// } +/// ``` +/// Which we recognize by looking for a Body which has a single basic block with a return +/// terminator and a single statement which assigns an `Operand::Constant(Const::Val)` to the +/// return place. +/// This scenario meets the required criteria because: +/// * Control flow cannot panic, we don't have any calls or assert terminators +/// * The value of the const is already computed, so it cannot fail +pub(crate) fn trivial_const<'a, 'tcx: 'a, F, B>( + tcx: TyCtxt<'tcx>, + def: LocalDefId, + body_provider: F, +) -> Option<(ConstValue, Ty<'tcx>)> +where + F: FnOnce() -> B, + B: Deref>, +{ + if !matches!(tcx.def_kind(def), DefKind::AssocConst | DefKind::Const | DefKind::AnonConst) { + return None; + } + + let body = body_provider(); + + if body.has_opaque_types() { + return None; + } + + if body.basic_blocks.len() != 1 { + return None; + } + + let block = &body.basic_blocks[START_BLOCK]; + if block.statements.len() != 1 { + return None; + } + + if block.terminator().kind != TerminatorKind::Return { + return None; + } + + let StatementKind::Assign(box (place, rvalue)) = &block.statements[0].kind else { + return None; + }; + + if *place != Place::from(RETURN_PLACE) { + return None; + } + + if let Rvalue::Use(Operand::Constant(c)) = rvalue { + if let rustc_middle::mir::Const::Val(v, ty) = c.const_ { + return Some((v, ty)); + } + } + + return None; +} + +pub(crate) fn trivial_const_provider<'tcx>( + tcx: TyCtxt<'tcx>, + def: LocalDefId, +) -> Option<(ConstValue, Ty<'tcx>)> { + trivial_const(tcx, def, || tcx.mir_built(def).borrow()) +} From a63035f9bf0514a7120579351bb1189a6ea5957d Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sun, 26 Oct 2025 22:13:12 -0400 Subject: [PATCH 136/170] Explain that the current impl is only silly, not recursive --- compiler/rustc_mir_transform/src/trivial_const.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_mir_transform/src/trivial_const.rs b/compiler/rustc_mir_transform/src/trivial_const.rs index bc66271e8ea1..af096f2b0468 100644 --- a/compiler/rustc_mir_transform/src/trivial_const.rs +++ b/compiler/rustc_mir_transform/src/trivial_const.rs @@ -83,6 +83,10 @@ where return None; } +// The query provider is based on calling the free function trivial_const, which calls mir_built, +// which internally has a fast-path for trivial consts so it too calls trivial_const. This isn't +// recursive, but we are checking if the const is trivial twice. A better design might detect +// trivial consts before getting to MIR, which would hopefully straighten this out. pub(crate) fn trivial_const_provider<'tcx>( tcx: TyCtxt<'tcx>, def: LocalDefId, From 0715745c1cf5b02c3b776ff23c53039e35a52f50 Mon Sep 17 00:00:00 2001 From: Aarav Desai Date: Sun, 26 Oct 2025 19:16:41 -0700 Subject: [PATCH 137/170] Move rustdoc tests to appropriate subdirectories Reorganize rustdoc tests into their correct subdirectories for better categorization: - Move lint-related tests to rustdoc-ui/lints/ - Move intra-doc link tests to rustdoc-ui/intra-doc/ - Move deref-related tests to rustdoc-ui/deref/ - Move doc-cfg test to rustdoc/doc-cfg/ This improves test organization and makes it easier to find tests related to specific rustdoc functionality. --- tests/rustdoc-ui/{ => deref}/deref-generic.rs | 0 tests/rustdoc-ui/{ => deref}/ice-blanket-impl-56701.rs | 0 tests/rustdoc-ui/{ => deref}/recursive-deref-ice.rs | 0 tests/rustdoc-ui/{ => intra-doc}/auxiliary/issue-48414.rs | 0 .../{ => intra-doc}/circular-intra-doc-link-48414.rs | 0 .../{ => intra-doc}/disambiguator-endswith-named-suffix.rs | 0 .../disambiguator-endswith-named-suffix.stderr | 0 .../{ => lints}/custom_code_classes_in_docs-warning3.rs | 0 .../{ => lints}/custom_code_classes_in_docs-warning3.stderr | 0 tests/rustdoc-ui/{ => lints}/diagnostic-width.rs | 0 tests/rustdoc-ui/{ => lints}/diagnostic-width.stderr | 0 tests/rustdoc-ui/{ => lints}/include-str-bare-urls.rs | 2 +- tests/rustdoc-ui/{ => lints}/include-str-bare-urls.stderr | 2 +- .../rustdoc-ui/{ => lints}/redundant-explicit-links-123677.rs | 0 tests/rustdoc-ui/{ => lints}/remap-path-prefix-lint.rs | 0 tests/rustdoc-ui/{ => lints}/remap-path-prefix-lint.stderr | 4 ++-- tests/rustdoc-ui/{ => lints}/unescaped_backticks.rs | 0 tests/rustdoc-ui/{ => lints}/unescaped_backticks.stderr | 0 tests/rustdoc/{ => doc-cfg}/duplicate-cfg.rs | 0 19 files changed, 4 insertions(+), 4 deletions(-) rename tests/rustdoc-ui/{ => deref}/deref-generic.rs (100%) rename tests/rustdoc-ui/{ => deref}/ice-blanket-impl-56701.rs (100%) rename tests/rustdoc-ui/{ => deref}/recursive-deref-ice.rs (100%) rename tests/rustdoc-ui/{ => intra-doc}/auxiliary/issue-48414.rs (100%) rename tests/rustdoc-ui/{ => intra-doc}/circular-intra-doc-link-48414.rs (100%) rename tests/rustdoc-ui/{ => intra-doc}/disambiguator-endswith-named-suffix.rs (100%) rename tests/rustdoc-ui/{ => intra-doc}/disambiguator-endswith-named-suffix.stderr (100%) rename tests/rustdoc-ui/{ => lints}/custom_code_classes_in_docs-warning3.rs (100%) rename tests/rustdoc-ui/{ => lints}/custom_code_classes_in_docs-warning3.stderr (100%) rename tests/rustdoc-ui/{ => lints}/diagnostic-width.rs (100%) rename tests/rustdoc-ui/{ => lints}/diagnostic-width.stderr (100%) rename tests/rustdoc-ui/{ => lints}/include-str-bare-urls.rs (89%) rename tests/rustdoc-ui/{ => lints}/include-str-bare-urls.stderr (91%) rename tests/rustdoc-ui/{ => lints}/redundant-explicit-links-123677.rs (100%) rename tests/rustdoc-ui/{ => lints}/remap-path-prefix-lint.rs (100%) rename tests/rustdoc-ui/{ => lints}/remap-path-prefix-lint.stderr (68%) rename tests/rustdoc-ui/{ => lints}/unescaped_backticks.rs (100%) rename tests/rustdoc-ui/{ => lints}/unescaped_backticks.stderr (100%) rename tests/rustdoc/{ => doc-cfg}/duplicate-cfg.rs (100%) diff --git a/tests/rustdoc-ui/deref-generic.rs b/tests/rustdoc-ui/deref/deref-generic.rs similarity index 100% rename from tests/rustdoc-ui/deref-generic.rs rename to tests/rustdoc-ui/deref/deref-generic.rs diff --git a/tests/rustdoc-ui/ice-blanket-impl-56701.rs b/tests/rustdoc-ui/deref/ice-blanket-impl-56701.rs similarity index 100% rename from tests/rustdoc-ui/ice-blanket-impl-56701.rs rename to tests/rustdoc-ui/deref/ice-blanket-impl-56701.rs diff --git a/tests/rustdoc-ui/recursive-deref-ice.rs b/tests/rustdoc-ui/deref/recursive-deref-ice.rs similarity index 100% rename from tests/rustdoc-ui/recursive-deref-ice.rs rename to tests/rustdoc-ui/deref/recursive-deref-ice.rs diff --git a/tests/rustdoc-ui/auxiliary/issue-48414.rs b/tests/rustdoc-ui/intra-doc/auxiliary/issue-48414.rs similarity index 100% rename from tests/rustdoc-ui/auxiliary/issue-48414.rs rename to tests/rustdoc-ui/intra-doc/auxiliary/issue-48414.rs diff --git a/tests/rustdoc-ui/circular-intra-doc-link-48414.rs b/tests/rustdoc-ui/intra-doc/circular-intra-doc-link-48414.rs similarity index 100% rename from tests/rustdoc-ui/circular-intra-doc-link-48414.rs rename to tests/rustdoc-ui/intra-doc/circular-intra-doc-link-48414.rs diff --git a/tests/rustdoc-ui/disambiguator-endswith-named-suffix.rs b/tests/rustdoc-ui/intra-doc/disambiguator-endswith-named-suffix.rs similarity index 100% rename from tests/rustdoc-ui/disambiguator-endswith-named-suffix.rs rename to tests/rustdoc-ui/intra-doc/disambiguator-endswith-named-suffix.rs diff --git a/tests/rustdoc-ui/disambiguator-endswith-named-suffix.stderr b/tests/rustdoc-ui/intra-doc/disambiguator-endswith-named-suffix.stderr similarity index 100% rename from tests/rustdoc-ui/disambiguator-endswith-named-suffix.stderr rename to tests/rustdoc-ui/intra-doc/disambiguator-endswith-named-suffix.stderr diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs b/tests/rustdoc-ui/lints/custom_code_classes_in_docs-warning3.rs similarity index 100% rename from tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs rename to tests/rustdoc-ui/lints/custom_code_classes_in_docs-warning3.rs diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr b/tests/rustdoc-ui/lints/custom_code_classes_in_docs-warning3.stderr similarity index 100% rename from tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr rename to tests/rustdoc-ui/lints/custom_code_classes_in_docs-warning3.stderr diff --git a/tests/rustdoc-ui/diagnostic-width.rs b/tests/rustdoc-ui/lints/diagnostic-width.rs similarity index 100% rename from tests/rustdoc-ui/diagnostic-width.rs rename to tests/rustdoc-ui/lints/diagnostic-width.rs diff --git a/tests/rustdoc-ui/diagnostic-width.stderr b/tests/rustdoc-ui/lints/diagnostic-width.stderr similarity index 100% rename from tests/rustdoc-ui/diagnostic-width.stderr rename to tests/rustdoc-ui/lints/diagnostic-width.stderr diff --git a/tests/rustdoc-ui/include-str-bare-urls.rs b/tests/rustdoc-ui/lints/include-str-bare-urls.rs similarity index 89% rename from tests/rustdoc-ui/include-str-bare-urls.rs rename to tests/rustdoc-ui/lints/include-str-bare-urls.rs index f80e28e8ca70..83539ef62b3d 100644 --- a/tests/rustdoc-ui/include-str-bare-urls.rs +++ b/tests/rustdoc-ui/lints/include-str-bare-urls.rs @@ -12,6 +12,6 @@ // If the stderr file changes, make sure the warning points at the URL! #![deny(rustdoc::bare_urls)] -#![doc=include_str!("auxiliary/include-str-bare-urls.md")] +#![doc=include_str!("../auxiliary/include-str-bare-urls.md")] //~? ERROR this URL is not a hyperlink diff --git a/tests/rustdoc-ui/include-str-bare-urls.stderr b/tests/rustdoc-ui/lints/include-str-bare-urls.stderr similarity index 91% rename from tests/rustdoc-ui/include-str-bare-urls.stderr rename to tests/rustdoc-ui/lints/include-str-bare-urls.stderr index 53da2411874a..7771a051fe1b 100644 --- a/tests/rustdoc-ui/include-str-bare-urls.stderr +++ b/tests/rustdoc-ui/lints/include-str-bare-urls.stderr @@ -1,5 +1,5 @@ error: this URL is not a hyperlink - --> $DIR/auxiliary/include-str-bare-urls.md:1:11 + --> $DIR/../auxiliary/include-str-bare-urls.md:1:11 | LL | HEADS UP! https://example.com MUST SHOW UP IN THE STDERR FILE! | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/rustdoc-ui/redundant-explicit-links-123677.rs b/tests/rustdoc-ui/lints/redundant-explicit-links-123677.rs similarity index 100% rename from tests/rustdoc-ui/redundant-explicit-links-123677.rs rename to tests/rustdoc-ui/lints/redundant-explicit-links-123677.rs diff --git a/tests/rustdoc-ui/remap-path-prefix-lint.rs b/tests/rustdoc-ui/lints/remap-path-prefix-lint.rs similarity index 100% rename from tests/rustdoc-ui/remap-path-prefix-lint.rs rename to tests/rustdoc-ui/lints/remap-path-prefix-lint.rs diff --git a/tests/rustdoc-ui/remap-path-prefix-lint.stderr b/tests/rustdoc-ui/lints/remap-path-prefix-lint.stderr similarity index 68% rename from tests/rustdoc-ui/remap-path-prefix-lint.stderr rename to tests/rustdoc-ui/lints/remap-path-prefix-lint.stderr index d7c1bb1965d3..e8a88394ffd9 100644 --- a/tests/rustdoc-ui/remap-path-prefix-lint.stderr +++ b/tests/rustdoc-ui/lints/remap-path-prefix-lint.stderr @@ -1,11 +1,11 @@ error: unopened HTML tag `script` - --> remapped_path/remap-path-prefix-lint.rs:9:5 + --> remapped_path/lints/remap-path-prefix-lint.rs:9:5 | LL | /// | ^^^^^^^^^ | note: the lint level is defined here - --> remapped_path/remap-path-prefix-lint.rs:7:9 + --> remapped_path/lints/remap-path-prefix-lint.rs:7:9 | LL | #![deny(rustdoc::invalid_html_tags)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/rustdoc-ui/unescaped_backticks.rs b/tests/rustdoc-ui/lints/unescaped_backticks.rs similarity index 100% rename from tests/rustdoc-ui/unescaped_backticks.rs rename to tests/rustdoc-ui/lints/unescaped_backticks.rs diff --git a/tests/rustdoc-ui/unescaped_backticks.stderr b/tests/rustdoc-ui/lints/unescaped_backticks.stderr similarity index 100% rename from tests/rustdoc-ui/unescaped_backticks.stderr rename to tests/rustdoc-ui/lints/unescaped_backticks.stderr diff --git a/tests/rustdoc/duplicate-cfg.rs b/tests/rustdoc/doc-cfg/duplicate-cfg.rs similarity index 100% rename from tests/rustdoc/duplicate-cfg.rs rename to tests/rustdoc/doc-cfg/duplicate-cfg.rs From b12f71178b101d6423208a28e57816e7220eb831 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 27 Oct 2025 04:14:59 +0000 Subject: [PATCH 138/170] Prepare for merging from rust-lang/rust This updates the rust-version file to b1b464d6f61ec8c4e609c1328106378c066a9729. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index a7367c5c8826..f100e4116686 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -4068bafedd8ba724e332a5221c06a6fa531a30d2 +b1b464d6f61ec8c4e609c1328106378c066a9729 From a918702bcad89205727732ffdbe927ce47f1b6f9 Mon Sep 17 00:00:00 2001 From: Jamesbarford Date: Mon, 27 Oct 2025 10:26:57 +0000 Subject: [PATCH 139/170] Re-enable macro-stepping test for AArch64 --- tests/debuginfo/macro-stepping.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/debuginfo/macro-stepping.rs b/tests/debuginfo/macro-stepping.rs index 35bb6de4fef3..e58975764e53 100644 --- a/tests/debuginfo/macro-stepping.rs +++ b/tests/debuginfo/macro-stepping.rs @@ -1,5 +1,4 @@ //@ ignore-android -//@ ignore-aarch64 //@ min-lldb-version: 1800 //@ min-gdb-version: 13.0 From 349dbecdab77d423118c62046f42aa7cf3c74ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 20 Oct 2025 11:27:43 +0200 Subject: [PATCH 140/170] move old solver coercion code to separate function --- compiler/rustc_hir_typeck/src/coercion.rs | 250 ++++++++++++---------- 1 file changed, 134 insertions(+), 116 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 7766075bade9..a32dd135af76 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -660,127 +660,145 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { return Err(TypeError::Mismatch); } } else { - let mut selcx = traits::SelectionContext::new(self); - // Use a FIFO queue for this custom fulfillment procedure. - // - // A Vec (or SmallVec) is not a natural choice for a queue. However, - // this code path is hot, and this queue usually has a max length of 1 - // and almost never more than 3. By using a SmallVec we avoid an - // allocation, at the (very small) cost of (occasionally) having to - // shift subsequent elements down when removing the front element. - let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![obligation]; - - // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid - // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where - // inference might unify those two inner type variables later. - let traits = [coerce_unsized_did, unsize_did]; - while !queue.is_empty() { - let obligation = queue.remove(0); - let trait_pred = match obligation.predicate.kind().no_bound_vars() { - Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred))) - if traits.contains(&trait_pred.def_id()) => - { - self.resolve_vars_if_possible(trait_pred) - } - // Eagerly process alias-relate obligations in new trait solver, - // since these can be emitted in the process of solving trait goals, - // but we need to constrain vars before processing goals mentioning - // them. - Some(ty::PredicateKind::AliasRelate(..)) => { - let ocx = ObligationCtxt::new(self); - ocx.register_obligation(obligation); - if !ocx.try_evaluate_obligations().is_empty() { - return Err(TypeError::Mismatch); - } - coercion.obligations.extend(ocx.into_pending_obligations()); - continue; - } - _ => { - coercion.obligations.push(obligation); - continue; - } - }; - debug!("coerce_unsized resolve step: {:?}", trait_pred); - match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) { - // Uncertain or unimplemented. - Ok(None) => { - if trait_pred.def_id() == unsize_did { - let self_ty = trait_pred.self_ty(); - let unsize_ty = trait_pred.trait_ref.args[1].expect_ty(); - debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred); - match (self_ty.kind(), unsize_ty.kind()) { - (&ty::Infer(ty::TyVar(v)), ty::Dynamic(..)) - if self.type_var_is_sized(v) => - { - debug!("coerce_unsized: have sized infer {:?}", v); - coercion.obligations.push(obligation); - // `$0: Unsize` where we know that `$0: Sized`, try going - // for unsizing. - } - _ => { - // Some other case for `$0: Unsize`. Note that we - // hit this case even if `Something` is a sized type, so just - // don't do the coercion. - debug!("coerce_unsized: ambiguous unsize"); - return Err(TypeError::Mismatch); - } - } - } else { - debug!("coerce_unsized: early return - ambiguous"); - return Err(TypeError::Mismatch); - } - } - Err(SelectionError::Unimplemented) => { - debug!("coerce_unsized: early return - can't prove obligation"); - return Err(TypeError::Mismatch); - } - - Err(SelectionError::TraitDynIncompatible(_)) => { - // Dyn compatibility errors in coercion will *always* be due to the - // fact that the RHS of the coercion is a non-dyn compatible `dyn Trait` - // writen in source somewhere (otherwise we will never have lowered - // the dyn trait from HIR to middle). - // - // There's no reason to emit yet another dyn compatibility error, - // especially since the span will differ slightly and thus not be - // deduplicated at all! - self.fcx.set_tainted_by_errors(self.fcx.dcx().span_delayed_bug( - self.cause.span, - "dyn compatibility during coercion", - )); - } - Err(err) => { - let guar = self.err_ctxt().report_selection_error( - obligation.clone(), - &obligation, - &err, - ); - self.fcx.set_tainted_by_errors(guar); - // Treat this like an obligation and follow through - // with the unsizing - the lack of a coercion should - // be silent, as it causes a type mismatch later. - } - Ok(Some(ImplSource::UserDefined(impl_source))) => { - queue.extend(impl_source.nested); - // Certain incoherent `CoerceUnsized` implementations may cause ICEs, - // so check the impl's validity. Taint the body so that we don't try - // to evaluate these invalid coercions in CTFE. We only need to do this - // for local impls, since upstream impls should be valid. - if impl_source.impl_def_id.is_local() - && let Err(guar) = - self.tcx.ensure_ok().coerce_unsized_info(impl_source.impl_def_id) - { - self.fcx.set_tainted_by_errors(guar); - } - } - Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), - } - } + self.coerce_unsized_old_solver( + obligation, + &mut coercion, + coerce_unsized_did, + unsize_did, + )?; } Ok(coercion) } + fn coerce_unsized_old_solver( + &self, + obligation: Obligation<'tcx, ty::Predicate<'tcx>>, + coercion: &mut InferOk<'tcx, (Vec>, Ty<'tcx>)>, + coerce_unsized_did: DefId, + unsize_did: DefId, + ) -> Result<(), TypeError<'tcx>> { + let mut selcx = traits::SelectionContext::new(self); + // Use a FIFO queue for this custom fulfillment procedure. + // + // A Vec (or SmallVec) is not a natural choice for a queue. However, + // this code path is hot, and this queue usually has a max length of 1 + // and almost never more than 3. By using a SmallVec we avoid an + // allocation, at the (very small) cost of (occasionally) having to + // shift subsequent elements down when removing the front element. + let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![obligation]; + + // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid + // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where + // inference might unify those two inner type variables later. + let traits = [coerce_unsized_did, unsize_did]; + while !queue.is_empty() { + let obligation = queue.remove(0); + let trait_pred = match obligation.predicate.kind().no_bound_vars() { + Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred))) + if traits.contains(&trait_pred.def_id()) => + { + self.resolve_vars_if_possible(trait_pred) + } + // Eagerly process alias-relate obligations in new trait solver, + // since these can be emitted in the process of solving trait goals, + // but we need to constrain vars before processing goals mentioning + // them. + Some(ty::PredicateKind::AliasRelate(..)) => { + let ocx = ObligationCtxt::new(self); + ocx.register_obligation(obligation); + if !ocx.try_evaluate_obligations().is_empty() { + return Err(TypeError::Mismatch); + } + coercion.obligations.extend(ocx.into_pending_obligations()); + continue; + } + _ => { + coercion.obligations.push(obligation); + continue; + } + }; + debug!("coerce_unsized resolve step: {:?}", trait_pred); + match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) { + // Uncertain or unimplemented. + Ok(None) => { + if trait_pred.def_id() == unsize_did { + let self_ty = trait_pred.self_ty(); + let unsize_ty = trait_pred.trait_ref.args[1].expect_ty(); + debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred); + match (self_ty.kind(), unsize_ty.kind()) { + (&ty::Infer(ty::TyVar(v)), ty::Dynamic(..)) + if self.type_var_is_sized(v) => + { + debug!("coerce_unsized: have sized infer {:?}", v); + coercion.obligations.push(obligation); + // `$0: Unsize` where we know that `$0: Sized`, try going + // for unsizing. + } + _ => { + // Some other case for `$0: Unsize`. Note that we + // hit this case even if `Something` is a sized type, so just + // don't do the coercion. + debug!("coerce_unsized: ambiguous unsize"); + return Err(TypeError::Mismatch); + } + } + } else { + debug!("coerce_unsized: early return - ambiguous"); + return Err(TypeError::Mismatch); + } + } + Err(SelectionError::Unimplemented) => { + debug!("coerce_unsized: early return - can't prove obligation"); + return Err(TypeError::Mismatch); + } + + Err(SelectionError::TraitDynIncompatible(_)) => { + // Dyn compatibility errors in coercion will *always* be due to the + // fact that the RHS of the coercion is a non-dyn compatible `dyn Trait` + // written in source somewhere (otherwise we will never have lowered + // the dyn trait from HIR to middle). + // + // There's no reason to emit yet another dyn compatibility error, + // especially since the span will differ slightly and thus not be + // deduplicated at all! + self.fcx.set_tainted_by_errors( + self.fcx + .dcx() + .span_delayed_bug(self.cause.span, "dyn compatibility during coercion"), + ); + } + Err(err) => { + let guar = self.err_ctxt().report_selection_error( + obligation.clone(), + &obligation, + &err, + ); + self.fcx.set_tainted_by_errors(guar); + // Treat this like an obligation and follow through + // with the unsizing - the lack of a coercion should + // be silent, as it causes a type mismatch later. + } + Ok(Some(ImplSource::UserDefined(impl_source))) => { + queue.extend(impl_source.nested); + // Certain incoherent `CoerceUnsized` implementations may cause ICEs, + // so check the impl's validity. Taint the body so that we don't try + // to evaluate these invalid coercions in CTFE. We only need to do this + // for local impls, since upstream impls should be valid. + if impl_source.impl_def_id.is_local() + && let Err(guar) = + self.tcx.ensure_ok().coerce_unsized_info(impl_source.impl_def_id) + { + self.fcx.set_tainted_by_errors(guar); + } + } + Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), + } + } + + Ok(()) + } + /// Applies reborrowing for `Pin` /// /// We currently only support reborrowing `Pin<&mut T>` as `Pin<&mut T>`. This is accomplished From 3d9cb043dd10a1d189ac9bf35ee869c2fd5e3d48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Thu, 2 Oct 2025 15:29:30 +0200 Subject: [PATCH 141/170] prove both newly added tests are fixed by the changes --- ...t-object-from-unsized-type.current.stderr} | 8 ++-- .../dst-object-from-unsized-type.next.stderr | 41 +++++++++++++++++++ .../unsize-goal-mismatch-2.next.stderr | 13 ++++++ .../next-solver/unsize-goal-mismatch-2.rs | 3 +- .../unsize-goal-mismatch.current.stderr | 17 ++++++++ .../unsize-goal-mismatch.next.stderr | 13 ++++++ .../next-solver/unsize-goal-mismatch.rs | 4 +- .../traits/next-solver/unsize-overflow.stderr | 12 ++++++ 8 files changed, 104 insertions(+), 7 deletions(-) rename tests/ui/dst/{dst-object-from-unsized-type.stderr => dst-object-from-unsized-type.current.stderr} (90%) create mode 100644 tests/ui/dst/dst-object-from-unsized-type.next.stderr create mode 100644 tests/ui/traits/next-solver/unsize-goal-mismatch-2.next.stderr create mode 100644 tests/ui/traits/next-solver/unsize-goal-mismatch.current.stderr create mode 100644 tests/ui/traits/next-solver/unsize-goal-mismatch.next.stderr create mode 100644 tests/ui/traits/next-solver/unsize-overflow.stderr diff --git a/tests/ui/dst/dst-object-from-unsized-type.stderr b/tests/ui/dst/dst-object-from-unsized-type.current.stderr similarity index 90% rename from tests/ui/dst/dst-object-from-unsized-type.stderr rename to tests/ui/dst/dst-object-from-unsized-type.current.stderr index cbb7dc5e9f42..d5f99eca974e 100644 --- a/tests/ui/dst/dst-object-from-unsized-type.stderr +++ b/tests/ui/dst/dst-object-from-unsized-type.current.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/dst-object-from-unsized-type.rs:8:23 + --> $DIR/dst-object-from-unsized-type.rs:11:23 | LL | fn test1(t: &T) { | - this type parameter needs to be `Sized` @@ -14,7 +14,7 @@ LL + fn test1(t: &T) { | error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/dst-object-from-unsized-type.rs:13:23 + --> $DIR/dst-object-from-unsized-type.rs:16:23 | LL | fn test2(t: &T) { | - this type parameter needs to be `Sized` @@ -29,7 +29,7 @@ LL + fn test2(t: &T) { | error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/dst-object-from-unsized-type.rs:18:28 + --> $DIR/dst-object-from-unsized-type.rs:21:28 | LL | let _: &[&dyn Foo] = &["hi"]; | ^^^^ doesn't have a size known at compile-time @@ -38,7 +38,7 @@ LL | let _: &[&dyn Foo] = &["hi"]; = note: required for the cast from `&'static str` to `&dyn Foo` error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/dst-object-from-unsized-type.rs:23:23 + --> $DIR/dst-object-from-unsized-type.rs:26:23 | LL | let _: &dyn Foo = x as &dyn Foo; | ^ doesn't have a size known at compile-time diff --git a/tests/ui/dst/dst-object-from-unsized-type.next.stderr b/tests/ui/dst/dst-object-from-unsized-type.next.stderr new file mode 100644 index 000000000000..6b4adda5154e --- /dev/null +++ b/tests/ui/dst/dst-object-from-unsized-type.next.stderr @@ -0,0 +1,41 @@ +error[E0308]: mismatched types + --> $DIR/dst-object-from-unsized-type.rs:11:23 + | +LL | fn test1(t: &T) { + | - found this type parameter +LL | let u: &dyn Foo = t; + | -------- ^ expected `&dyn Foo`, found `&T` + | | + | expected due to this + | + = note: expected reference `&dyn Foo` + found reference `&T` + = help: type parameters must be constrained to match other types + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters + +error[E0605]: non-primitive cast: `&T` as `&dyn Foo` + --> $DIR/dst-object-from-unsized-type.rs:16:23 + | +LL | let v: &dyn Foo = t as &dyn Foo; + | ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + +error[E0308]: mismatched types + --> $DIR/dst-object-from-unsized-type.rs:21:28 + | +LL | let _: &[&dyn Foo] = &["hi"]; + | ^^^^ expected `&dyn Foo`, found `&str` + | + = note: expected reference `&dyn Foo` + found reference `&'static str` + = help: `str` implements `Foo` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well + +error[E0605]: non-primitive cast: `&[u8]` as `&dyn Foo` + --> $DIR/dst-object-from-unsized-type.rs:26:23 + | +LL | let _: &dyn Foo = x as &dyn Foo; + | ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0308, E0605. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/next-solver/unsize-goal-mismatch-2.next.stderr b/tests/ui/traits/next-solver/unsize-goal-mismatch-2.next.stderr new file mode 100644 index 000000000000..b415db33addb --- /dev/null +++ b/tests/ui/traits/next-solver/unsize-goal-mismatch-2.next.stderr @@ -0,0 +1,13 @@ +error[E0283]: type annotations needed: cannot satisfy `dyn Trait<&()>: Unsize>` + --> $DIR/unsize-goal-mismatch-2.rs:15:5 + | +LL | x + | ^ + | + = note: cannot satisfy `dyn Trait<&()>: Unsize>` + = note: required for `Box>` to implement `CoerceUnsized>>` + = note: required for the cast from `Box<(dyn Trait<&'a ()> + 'static)>` to `Box<(dyn Super<&'a ()> + 'static)>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/unsize-goal-mismatch-2.rs b/tests/ui/traits/next-solver/unsize-goal-mismatch-2.rs index c651309e01b1..2b174eac5c15 100644 --- a/tests/ui/traits/next-solver/unsize-goal-mismatch-2.rs +++ b/tests/ui/traits/next-solver/unsize-goal-mismatch-2.rs @@ -1,7 +1,7 @@ -//@ check-pass //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver +//@[current] check-pass // Test from trait-system-refactor-initiative#241: // Used to ICE in mir typeck because of ambiguity in the new solver. // The wrong (first) trait bound was selected. @@ -13,6 +13,7 @@ trait Trait: Super + for<'hr> Super<&'hr ()> {} fn foo<'a>(x: Box>) -> Box> { x + //[next]~^ ERROR type annotations needed: cannot satisfy `dyn Trait<&()>: Unsize>` } fn main() {} diff --git a/tests/ui/traits/next-solver/unsize-goal-mismatch.current.stderr b/tests/ui/traits/next-solver/unsize-goal-mismatch.current.stderr new file mode 100644 index 000000000000..278a68e15471 --- /dev/null +++ b/tests/ui/traits/next-solver/unsize-goal-mismatch.current.stderr @@ -0,0 +1,17 @@ +error[E0283]: type annotations needed: cannot satisfy `Self: Super<'a>` + --> $DIR/unsize-goal-mismatch.rs:11:18 + | +LL | trait Trait<'a>: Super<'a> + for<'hr> Super<'hr> {} + | ^^^^^^^^^ + | +note: multiple `impl`s or `where` clauses satisfying `Self: Super<'a>` found + --> $DIR/unsize-goal-mismatch.rs:10:1 + | +LL | trait Super<'a> {} + | ^^^^^^^^^^^^^^^ +LL | trait Trait<'a>: Super<'a> + for<'hr> Super<'hr> {} + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/unsize-goal-mismatch.next.stderr b/tests/ui/traits/next-solver/unsize-goal-mismatch.next.stderr new file mode 100644 index 000000000000..cfb978240cbc --- /dev/null +++ b/tests/ui/traits/next-solver/unsize-goal-mismatch.next.stderr @@ -0,0 +1,13 @@ +error[E0283]: type annotations needed: cannot satisfy `dyn Trait<'_>: Unsize>` + --> $DIR/unsize-goal-mismatch.rs:15:5 + | +LL | x + | ^ + | + = note: cannot satisfy `dyn Trait<'_>: Unsize>` + = note: required for `Box>` to implement `CoerceUnsized>>` + = note: required for the cast from `Box<(dyn Trait<'a> + 'static)>` to `Box<(dyn Super<'a> + 'static)>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/unsize-goal-mismatch.rs b/tests/ui/traits/next-solver/unsize-goal-mismatch.rs index c9e201899724..f57cceb4096a 100644 --- a/tests/ui/traits/next-solver/unsize-goal-mismatch.rs +++ b/tests/ui/traits/next-solver/unsize-goal-mismatch.rs @@ -1,4 +1,3 @@ -//@ check-pass //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver @@ -10,10 +9,11 @@ trait Super<'a> {} trait Trait<'a>: Super<'a> + for<'hr> Super<'hr> {} +//[current]~^ ERROR type annotations needed: cannot satisfy `Self: Super<'a>` fn foo<'a>(x: Box>) -> Box> { x - //~^ ERROR type annotations needed: cannot satisfy `dyn Trait<'_>: Unsize> + //[next]~^ ERROR type annotations needed: cannot satisfy `dyn Trait<'_>: Unsize> } fn main() {} diff --git a/tests/ui/traits/next-solver/unsize-overflow.stderr b/tests/ui/traits/next-solver/unsize-overflow.stderr new file mode 100644 index 000000000000..ae0f2957243c --- /dev/null +++ b/tests/ui/traits/next-solver/unsize-overflow.stderr @@ -0,0 +1,12 @@ +error[E0275]: overflow evaluating the requirement `Box<&&&&&&&i32>: CoerceUnsized>` + --> $DIR/unsize-overflow.rs:5:28 + | +LL | let _: Box = Box::new(&&&&&&&1); + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "16"]` attribute to your crate (`unsize_overflow`) + = note: required for the cast from `Box<&&&&&&&i32>` to `Box` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. From 17566221f879ba30d3143a0bc40b824ac794029f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Fri, 3 Oct 2025 11:32:17 +0200 Subject: [PATCH 142/170] find the right error source when we can't unsize --- compiler/rustc_hir_typeck/src/coercion.rs | 19 +++++- ...st-object-from-unsized-type.current.stderr | 6 +- .../dst-object-from-unsized-type.next.stderr | 62 ++++++++++++------- tests/ui/dst/dst-object-from-unsized-type.rs | 12 ++-- .../higher-ranked-upcasting-ub.next.stderr | 13 ++-- .../higher-ranked-upcasting-ub.rs | 4 +- 6 files changed, 77 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index a32dd135af76..857e4f66489a 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -2055,6 +2055,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> { return ControlFlow::Continue(()); }; + // Make sure this predicate is referring to either an `Unsize` or `CoerceUnsized` trait, + // Otherwise there's nothing to do. if !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize) && !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::CoerceUnsized) { @@ -2062,8 +2064,19 @@ impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> { } match goal.result() { + // If we prove the `Unsize` or `CoerceUnsized` goal, continue recursing. Ok(Certainty::Yes) => ControlFlow::Continue(()), - Err(NoSolution) => ControlFlow::Break(()), + Err(NoSolution) => { + // Even if we find no solution, continue recursing if we find a single candidate + // for which we're shallowly certain it holds to get the right error source. + if let [only_candidate] = &goal.candidates()[..] + && only_candidate.shallow_certainty() == Certainty::Yes + { + only_candidate.visit_nested_no_probe(self) + } else { + ControlFlow::Break(()) + } + } Ok(Certainty::Maybe { .. }) => { // FIXME: structurally normalize? if self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize) @@ -2071,6 +2084,10 @@ impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> { && let ty::Infer(ty::TyVar(vid)) = *pred.self_ty().skip_binder().kind() && self.fcx.type_var_is_sized(vid) { + // We get here when trying to unsize a type variable to a `dyn Trait`, + // knowing that that variable is sized. Unsizing definitely has to happen in that case. + // If the variable weren't sized, we may not need an unsizing coercion. + // In general, we don't want to add coercions too eagerly since it makes error messages much worse. ControlFlow::Continue(()) } else if let Some(cand) = goal.unique_applicable_candidate() && cand.shallow_certainty() == Certainty::Yes diff --git a/tests/ui/dst/dst-object-from-unsized-type.current.stderr b/tests/ui/dst/dst-object-from-unsized-type.current.stderr index d5f99eca974e..be9966743d4e 100644 --- a/tests/ui/dst/dst-object-from-unsized-type.current.stderr +++ b/tests/ui/dst/dst-object-from-unsized-type.current.stderr @@ -14,7 +14,7 @@ LL + fn test1(t: &T) { | error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/dst-object-from-unsized-type.rs:16:23 + --> $DIR/dst-object-from-unsized-type.rs:17:23 | LL | fn test2(t: &T) { | - this type parameter needs to be `Sized` @@ -29,7 +29,7 @@ LL + fn test2(t: &T) { | error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/dst-object-from-unsized-type.rs:21:28 + --> $DIR/dst-object-from-unsized-type.rs:23:28 | LL | let _: &[&dyn Foo] = &["hi"]; | ^^^^ doesn't have a size known at compile-time @@ -38,7 +38,7 @@ LL | let _: &[&dyn Foo] = &["hi"]; = note: required for the cast from `&'static str` to `&dyn Foo` error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/dst-object-from-unsized-type.rs:26:23 + --> $DIR/dst-object-from-unsized-type.rs:29:23 | LL | let _: &dyn Foo = x as &dyn Foo; | ^ doesn't have a size known at compile-time diff --git a/tests/ui/dst/dst-object-from-unsized-type.next.stderr b/tests/ui/dst/dst-object-from-unsized-type.next.stderr index 6b4adda5154e..032ba0cb14ae 100644 --- a/tests/ui/dst/dst-object-from-unsized-type.next.stderr +++ b/tests/ui/dst/dst-object-from-unsized-type.next.stderr @@ -1,41 +1,57 @@ -error[E0308]: mismatched types +error[E0277]: the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T` --> $DIR/dst-object-from-unsized-type.rs:11:23 | LL | fn test1(t: &T) { - | - found this type parameter + | - this type parameter needs to be `Sized` LL | let u: &dyn Foo = t; - | -------- ^ expected `&dyn Foo`, found `&T` - | | - | expected due to this + | ^ within `T`, the trait `Sized` is not implemented for `T` + | + = note: required because it appears within the type `T` + = note: required for `&T` to implement `CoerceUnsized<&dyn Foo>` + = note: required for the cast from `&T` to `&dyn Foo` +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - fn test1(t: &T) { +LL + fn test1(t: &T) { | - = note: expected reference `&dyn Foo` - found reference `&T` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters -error[E0605]: non-primitive cast: `&T` as `&dyn Foo` - --> $DIR/dst-object-from-unsized-type.rs:16:23 +error[E0277]: the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T` + --> $DIR/dst-object-from-unsized-type.rs:17:23 | +LL | fn test2(t: &T) { + | - this type parameter needs to be `Sized` LL | let v: &dyn Foo = t as &dyn Foo; - | ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + | ^ within `T`, the trait `Sized` is not implemented for `T` + | + = note: required because it appears within the type `T` + = note: required for `&T` to implement `CoerceUnsized<&dyn Foo>` + = note: required for the cast from `&T` to `&dyn Foo` +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - fn test2(t: &T) { +LL + fn test2(t: &T) { + | -error[E0308]: mismatched types - --> $DIR/dst-object-from-unsized-type.rs:21:28 +error[E0277]: the trait bound `&str: CoerceUnsized<&dyn Foo>` is not satisfied in `str` + --> $DIR/dst-object-from-unsized-type.rs:23:28 | LL | let _: &[&dyn Foo] = &["hi"]; - | ^^^^ expected `&dyn Foo`, found `&str` + | ^^^^ within `str`, the trait `Sized` is not implemented for `str` | - = note: expected reference `&dyn Foo` - found reference `&'static str` - = help: `str` implements `Foo` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well + = note: `str` is considered to contain a `[u8]` slice for auto trait purposes + = note: required for `&str` to implement `CoerceUnsized<&dyn Foo>` + = note: required for the cast from `&'static str` to `&dyn Foo` -error[E0605]: non-primitive cast: `&[u8]` as `&dyn Foo` - --> $DIR/dst-object-from-unsized-type.rs:26:23 +error[E0277]: the trait bound `&[u8]: CoerceUnsized<&dyn Foo>` is not satisfied in `[u8]` + --> $DIR/dst-object-from-unsized-type.rs:29:23 | LL | let _: &dyn Foo = x as &dyn Foo; - | ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + | ^ within `[u8]`, the trait `Sized` is not implemented for `[u8]` + | + = note: required because it appears within the type `[u8]` + = note: required for `&[u8]` to implement `CoerceUnsized<&dyn Foo>` + = note: required for the cast from `&[u8]` to `&dyn Foo` error: aborting due to 4 previous errors -Some errors have detailed explanations: E0308, E0605. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dst/dst-object-from-unsized-type.rs b/tests/ui/dst/dst-object-from-unsized-type.rs index 5ba6c571a39a..1e6113b3fc6f 100644 --- a/tests/ui/dst/dst-object-from-unsized-type.rs +++ b/tests/ui/dst/dst-object-from-unsized-type.rs @@ -9,22 +9,26 @@ impl Foo for [u8] {} fn test1(t: &T) { let u: &dyn Foo = t; - //~^ ERROR the size for values of type + //[current]~^ ERROR the size for values of type + //[next]~^^ ERROR the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T` } fn test2(t: &T) { let v: &dyn Foo = t as &dyn Foo; - //~^ ERROR the size for values of type + //[current]~^ ERROR the size for values of type + //[next]~^^ ERROR the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T` } fn test3() { let _: &[&dyn Foo] = &["hi"]; - //~^ ERROR the size for values of type + //[current]~^ ERROR the size for values of type + //[next]~^^ ERROR the trait bound `&str: CoerceUnsized<&dyn Foo>` is not satisfied in `str` } fn test4(x: &[u8]) { let _: &dyn Foo = x as &dyn Foo; - //~^ ERROR the size for values of type + //[current]~^ ERROR the size for values of type + //[next]~^^ ERROR the trait bound `&[u8]: CoerceUnsized<&dyn Foo>` is not satisfied in `[u8]` } fn main() { } diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr index b82f1eef42b5..392680aa5064 100644 --- a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr @@ -1,14 +1,13 @@ -error[E0308]: mismatched types +error[E0277]: the trait bound `&dyn for<'a> Subtrait<'a, 'a>: CoerceUnsized<&dyn for<'a, 'b> Supertrait<'a, 'b>>` is not satisfied --> $DIR/higher-ranked-upcasting-ub.rs:22:5 | -LL | fn unsound(x: &dyn for<'a> Subtrait<'a, 'a>) -> &dyn for<'a, 'b> Supertrait<'a, 'b> { - | ----------------------------------- expected `&dyn for<'a, 'b> Supertrait<'a, 'b>` because of return type LL | x - | ^ expected trait `Supertrait`, found trait `Subtrait` + | ^ the trait `Unsize Supertrait<'a, 'b>>` is not implemented for `dyn for<'a> Subtrait<'a, 'a>` | - = note: expected reference `&dyn for<'a, 'b> Supertrait<'a, 'b>` - found reference `&dyn for<'a> Subtrait<'a, 'a>` + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = note: required for `&dyn for<'a> Subtrait<'a, 'a>` to implement `CoerceUnsized<&dyn for<'a, 'b> Supertrait<'a, 'b>>` + = note: required for the cast from `&dyn for<'a> Subtrait<'a, 'a>` to `&dyn for<'a, 'b> Supertrait<'a, 'b>` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs index af2594b95f3d..98ca30ca391f 100644 --- a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs @@ -19,8 +19,10 @@ impl<'a> Supertrait<'a, 'a> for () { } impl<'a> Subtrait<'a, 'a> for () {} fn unsound(x: &dyn for<'a> Subtrait<'a, 'a>) -> &dyn for<'a, 'b> Supertrait<'a, 'b> { - x //~ ERROR mismatched types + x //[current]~^ ERROR mismatched types + //[current]~| ERROR mismatched types + //[next]~^^^ ERROR the trait bound `&dyn for<'a> Subtrait<'a, 'a>: CoerceUnsized<&dyn for<'a, 'b> Supertrait<'a, 'b>>` is not satisfied } fn transmute<'a, 'b>(x: &'a str) -> &'b str { From 94c893ee2ed2d0134a9e0ccb808e40149dbbe186 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 26 Oct 2025 14:04:54 +0100 Subject: [PATCH 143/170] Add `coverage` scope for controlling paths in code coverage --- .../src/coverageinfo/mapgen.rs | 4 ++-- compiler/rustc_session/src/config.rs | 8 +++++--- compiler/rustc_session/src/options.rs | 4 ++-- .../src/compiler-flags/remap-path-scope.md | 3 ++- tests/coverage/remap-path-prefix.rs | 17 +++++++++++++++++ ...map-path-prefix.with_coverage_scope.cov-map | 10 ++++++++++ .../remap-path-prefix.with_macro_scope.cov-map | 10 ++++++++++ ...remap-path-prefix.with_macro_scope.coverage | 18 ++++++++++++++++++ ...remap-path-prefix.with_object_scope.cov-map | 10 ++++++++++ .../remap-path-prefix.with_remap.cov-map | 10 ++++++++++ 10 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 tests/coverage/remap-path-prefix.rs create mode 100644 tests/coverage/remap-path-prefix.with_coverage_scope.cov-map create mode 100644 tests/coverage/remap-path-prefix.with_macro_scope.cov-map create mode 100644 tests/coverage/remap-path-prefix.with_macro_scope.coverage create mode 100644 tests/coverage/remap-path-prefix.with_object_scope.cov-map create mode 100644 tests/coverage/remap-path-prefix.with_remap.cov-map diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 7e873347c82b..b3a11f8b12bf 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -128,7 +128,7 @@ impl GlobalFileTable { for file in all_files { raw_file_table.entry(file.stable_id).or_insert_with(|| { file.name - .for_scope(tcx.sess, RemapPathScopeComponents::MACRO) + .for_scope(tcx.sess, RemapPathScopeComponents::COVERAGE) .to_string_lossy() .into_owned() }); @@ -147,7 +147,7 @@ impl GlobalFileTable { .sess .opts .working_dir - .for_scope(tcx.sess, RemapPathScopeComponents::MACRO) + .for_scope(tcx.sess, RemapPathScopeComponents::COVERAGE) .to_string_lossy(); table.push(base_dir.as_ref()); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index d1426ff55fbd..89ae74c902fd 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1377,10 +1377,12 @@ bitflags::bitflags! { const DIAGNOSTICS = 1 << 1; /// Apply remappings to debug information const DEBUGINFO = 1 << 3; + /// Apply remappings to coverage information + const COVERAGE = 1 << 4; - /// An alias for `macro` and `debuginfo`. This ensures all paths in compiled - /// executables or libraries are remapped but not elsewhere. - const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits(); + /// An alias for `macro`, `debuginfo` and `coverage`. This ensures all paths in compiled + /// executables, libraries and objects are remapped but not elsewhere. + const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits() | Self::COVERAGE.bits(); } } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 6dd90546de1b..b89aec7d22a9 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -869,8 +869,7 @@ mod desc { pub(crate) const parse_branch_protection: &str = "a `,` separated combination of `bti`, `gcs`, `pac-ret`, (optionally with `pc`, `b-key`, `leaf` if `pac-ret` is set)"; pub(crate) const parse_proc_macro_execution_strategy: &str = "one of supported execution strategies (`same-thread`, or `cross-thread`)"; - pub(crate) const parse_remap_path_scope: &str = - "comma separated list of scopes: `macro`, `diagnostics`, `debuginfo`, `object`, `all`"; + pub(crate) const parse_remap_path_scope: &str = "comma separated list of scopes: `macro`, `diagnostics`, `debuginfo`, `coverage`, `object`, `all`"; pub(crate) const parse_inlining_threshold: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a non-negative number"; pub(crate) const parse_llvm_module_flag: &str = ":::. Type must currently be `u32`. Behavior should be one of (`error`, `warning`, `require`, `override`, `append`, `appendunique`, `max`, `min`)"; @@ -1705,6 +1704,7 @@ pub mod parse { "macro" => RemapPathScopeComponents::MACRO, "diagnostics" => RemapPathScopeComponents::DIAGNOSTICS, "debuginfo" => RemapPathScopeComponents::DEBUGINFO, + "coverage" => RemapPathScopeComponents::COVERAGE, "object" => RemapPathScopeComponents::OBJECT, "all" => RemapPathScopeComponents::all(), _ => return false, diff --git a/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md b/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md index 65219dc68e97..fb1c7d7a6878 100644 --- a/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md +++ b/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md @@ -10,7 +10,8 @@ This flag accepts a comma-separated list of values and may be specified multiple - `macro` - apply remappings to the expansion of `std::file!()` macro. This is where paths in embedded panic messages come from - `diagnostics` - apply remappings to printed compiler diagnostics -- `debuginfo` - apply remappings to debug informations +- `debuginfo` - apply remappings to debug information +- `coverage` - apply remappings to coverage information - `object` - apply remappings to all paths in compiled executables or libraries, but not elsewhere. Currently an alias for `macro,debuginfo`. - `all` - an alias for all of the above, also equivalent to supplying only `--remap-path-prefix` without `--remap-path-scope`. diff --git a/tests/coverage/remap-path-prefix.rs b/tests/coverage/remap-path-prefix.rs new file mode 100644 index 000000000000..29c5826989c4 --- /dev/null +++ b/tests/coverage/remap-path-prefix.rs @@ -0,0 +1,17 @@ +// This test makes sure that the files used in the coverage are remapped by +// `--remap-path-prefix` and the `coverage` <- `object` scopes. +// +// We also test the `macro` scope to make sure it does not affect coverage. + +// When coverage paths are remapped, the coverage-run mode can't find source files (because +// it doesn't know about the remapping), so it produces an empty coverage report. The empty +// report (i.e. no `.coverage` files) helps to demonstrate that remapping was indeed performed. + +//@ revisions: with_remap with_coverage_scope with_object_scope with_macro_scope +//@ compile-flags: --remap-path-prefix={{src-base}}=remapped +// +//@[with_coverage_scope] compile-flags: -Zremap-path-scope=coverage +//@[with_object_scope] compile-flags: -Zremap-path-scope=object +//@[with_macro_scope] compile-flags: -Zremap-path-scope=macro + +fn main() {} diff --git a/tests/coverage/remap-path-prefix.with_coverage_scope.cov-map b/tests/coverage/remap-path-prefix.with_coverage_scope.cov-map new file mode 100644 index 000000000000..35731d71116f --- /dev/null +++ b/tests/coverage/remap-path-prefix.with_coverage_scope.cov-map @@ -0,0 +1,10 @@ +Function name: remap_path_prefix::main +Raw bytes (14): 0x[01, 01, 00, 02, 01, 11, 01, 00, 0a, 01, 00, 0c, 00, 0d] +Number of files: 1 +- file 0 => remapped/remap-path-prefix.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 17, 1) to (start + 0, 10) +- Code(Counter(0)) at (prev + 0, 12) to (start + 0, 13) +Highest counter ID seen: c0 + diff --git a/tests/coverage/remap-path-prefix.with_macro_scope.cov-map b/tests/coverage/remap-path-prefix.with_macro_scope.cov-map new file mode 100644 index 000000000000..551e28676885 --- /dev/null +++ b/tests/coverage/remap-path-prefix.with_macro_scope.cov-map @@ -0,0 +1,10 @@ +Function name: remap_path_prefix::main +Raw bytes (14): 0x[01, 01, 00, 02, 01, 11, 01, 00, 0a, 01, 00, 0c, 00, 0d] +Number of files: 1 +- file 0 => $DIR/remap-path-prefix.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 17, 1) to (start + 0, 10) +- Code(Counter(0)) at (prev + 0, 12) to (start + 0, 13) +Highest counter ID seen: c0 + diff --git a/tests/coverage/remap-path-prefix.with_macro_scope.coverage b/tests/coverage/remap-path-prefix.with_macro_scope.coverage new file mode 100644 index 000000000000..63979d8fe15a --- /dev/null +++ b/tests/coverage/remap-path-prefix.with_macro_scope.coverage @@ -0,0 +1,18 @@ + LL| |// This test makes sure that the files used in the coverage are remapped by + LL| |// `--remap-path-prefix` and the `coverage` <- `object` scopes. + LL| |// + LL| |// We also test the `macro` scope to make sure it does not affect coverage. + LL| | + LL| |// When coverage paths are remapped, the coverage-run mode can't find source files (because + LL| |// it doesn't know about the remapping), so it produces an empty coverage report. The empty + LL| |// report (i.e. no `.coverage` files) helps to demonstrate that remapping was indeed performed. + LL| | + LL| |//@ revisions: with_remap with_coverage_scope with_object_scope with_macro_scope + LL| |//@ compile-flags: --remap-path-prefix={{src-base}}=remapped + LL| |// + LL| |//@[with_coverage_scope] compile-flags: -Zremap-path-scope=coverage + LL| |//@[with_object_scope] compile-flags: -Zremap-path-scope=object + LL| |//@[with_macro_scope] compile-flags: -Zremap-path-scope=macro + LL| | + LL| 1|fn main() {} + diff --git a/tests/coverage/remap-path-prefix.with_object_scope.cov-map b/tests/coverage/remap-path-prefix.with_object_scope.cov-map new file mode 100644 index 000000000000..35731d71116f --- /dev/null +++ b/tests/coverage/remap-path-prefix.with_object_scope.cov-map @@ -0,0 +1,10 @@ +Function name: remap_path_prefix::main +Raw bytes (14): 0x[01, 01, 00, 02, 01, 11, 01, 00, 0a, 01, 00, 0c, 00, 0d] +Number of files: 1 +- file 0 => remapped/remap-path-prefix.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 17, 1) to (start + 0, 10) +- Code(Counter(0)) at (prev + 0, 12) to (start + 0, 13) +Highest counter ID seen: c0 + diff --git a/tests/coverage/remap-path-prefix.with_remap.cov-map b/tests/coverage/remap-path-prefix.with_remap.cov-map new file mode 100644 index 000000000000..35731d71116f --- /dev/null +++ b/tests/coverage/remap-path-prefix.with_remap.cov-map @@ -0,0 +1,10 @@ +Function name: remap_path_prefix::main +Raw bytes (14): 0x[01, 01, 00, 02, 01, 11, 01, 00, 0a, 01, 00, 0c, 00, 0d] +Number of files: 1 +- file 0 => remapped/remap-path-prefix.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 17, 1) to (start + 0, 10) +- Code(Counter(0)) at (prev + 0, 12) to (start + 0, 13) +Highest counter ID seen: c0 + From 943074fd095f66712eeabb0e53bbc78695be7cd8 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 27 Oct 2025 15:35:18 +0200 Subject: [PATCH 144/170] improve and strengthen wording --- src/doc/rustc-dev-guide/src/contributing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/contributing.md b/src/doc/rustc-dev-guide/src/contributing.md index b927c9a79274..dbd04c51593c 100644 --- a/src/doc/rustc-dev-guide/src/contributing.md +++ b/src/doc/rustc-dev-guide/src/contributing.md @@ -53,8 +53,8 @@ channels: stable, beta, and nightly. - **Stable**: this is the latest stable release for general usage. - **Beta**: this is the next release (will be stable within 6 weeks). - **Nightly**: follows the `master` branch of the repo. - This is the only channel where unstable, incomplete, or experimental features - should be used (with feature gates). + This is the only channel where unstable features are intended to be used, + which happens via opt-in feature gates. See [this chapter on implementing new features](./implementing_new_features.md) for more information. From bf6f3035af62a0391249062e47c4455caa69cf9b Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Mon, 27 Oct 2025 18:01:04 +0300 Subject: [PATCH 145/170] Fix typos: duplicate words in comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix 'the the' → 'the' in rustc_const_eval - Fix 'wether' → 'whether' in compiletest - Fix 'is is' → 'is' in rustc_ast_pretty (2 instances) Signed-off-by: Osama Abdelkader --- compiler/rustc_ast_pretty/src/pp.rs | 4 ++-- compiler/rustc_const_eval/src/interpret/util.rs | 2 +- src/tools/compiletest/src/runtest.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_ast_pretty/src/pp.rs b/compiler/rustc_ast_pretty/src/pp.rs index 8a0dbadf18cd..4108671a3629 100644 --- a/compiler/rustc_ast_pretty/src/pp.rs +++ b/compiler/rustc_ast_pretty/src/pp.rs @@ -298,7 +298,7 @@ impl Printer { } } - // This is is where `BoxMarker`s are produced. + // This is where `BoxMarker`s are produced. fn scan_begin(&mut self, token: BeginToken) -> BoxMarker { if self.scan_stack.is_empty() { self.left_total = 1; @@ -310,7 +310,7 @@ impl Printer { BoxMarker } - // This is is where `BoxMarker`s are consumed. + // This is where `BoxMarker`s are consumed. fn scan_end(&mut self, b: BoxMarker) { if self.scan_stack.is_empty() { self.print_end(); diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index b5cf6c4c3723..1e18a22be81c 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -121,7 +121,7 @@ impl EnteredTraceSpan for tracing::span::EnteredSpan { /// ### `tracing_separate_thread` parameter /// /// This macro was introduced to obtain better traces of Miri without impacting release performance. -/// Miri saves traces using the the `tracing_chrome` `tracing::Layer` so that they can be visualized +/// Miri saves traces using the `tracing_chrome` `tracing::Layer` so that they can be visualized /// in . To instruct `tracing_chrome` to put some spans on a separate trace /// thread/line than other spans when viewed in , you can pass /// `tracing_separate_thread = tracing::field::Empty` to the tracing macros. This is useful to diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 16b604e9df82..b82a533271c1 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -3006,7 +3006,7 @@ impl<'test> TestCx<'test> { self.delete_file(&examined_path); } // If we want them to be the same, but they are different, then error. - // We do this wether we bless or not + // We do this whether we bless or not (_, true, false) => { self.fatal_proc_rec( &format!("`{}` should not have different output from base test!", kind), From 3ce89e257d20c34795d0577ea4ba0e0be194c0db Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 27 Oct 2025 15:54:49 +0000 Subject: [PATCH 146/170] Use the actual StableCrateId for the incr comp session dir Previously only --crate-type would be taken into account, not #![crate_type]. --- compiler/rustc_incremental/src/persist/fs.rs | 20 +++++++------------ .../rustc_incremental/src/persist/load.rs | 11 +++++++--- compiler/rustc_interface/src/passes.rs | 2 +- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 975bf1d18622..f73cc4d43e8c 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -116,8 +116,6 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_data_structures::{base_n, flock}; use rustc_fs_util::{LinkOrCopy, link_or_copy, try_canonicalize}; use rustc_middle::bug; -use rustc_session::config::CrateType; -use rustc_session::output::collect_crate_types; use rustc_session::{Session, StableCrateId}; use rustc_span::Symbol; use tracing::debug; @@ -212,7 +210,11 @@ pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBu /// The garbage collection will take care of it. /// /// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph -pub(crate) fn prepare_session_directory(sess: &Session, crate_name: Symbol) { +pub(crate) fn prepare_session_directory( + sess: &Session, + crate_name: Symbol, + stable_crate_id: StableCrateId, +) { if sess.opts.incremental.is_none() { return; } @@ -222,7 +224,7 @@ pub(crate) fn prepare_session_directory(sess: &Session, crate_name: Symbol) { debug!("prepare_session_directory"); // {incr-comp-dir}/{crate-name-and-disambiguator} - let crate_dir = crate_path(sess, crate_name); + let crate_dir = crate_path(sess, crate_name, stable_crate_id); debug!("crate-dir: {}", crate_dir.display()); create_dir(sess, &crate_dir, "crate"); @@ -595,17 +597,9 @@ fn string_to_timestamp(s: &str) -> Result { Ok(UNIX_EPOCH + duration) } -fn crate_path(sess: &Session, crate_name: Symbol) -> PathBuf { +fn crate_path(sess: &Session, crate_name: Symbol, stable_crate_id: StableCrateId) -> PathBuf { let incr_dir = sess.opts.incremental.as_ref().unwrap().clone(); - let crate_types = collect_crate_types(sess, &[]); - let stable_crate_id = StableCrateId::new( - crate_name, - crate_types.contains(&CrateType::Executable), - sess.opts.cg.metadata.clone(), - sess.cfg_version, - ); - let crate_name = format!("{crate_name}-{}", stable_crate_id.as_u64().to_base_fixed_len(CASE_INSENSITIVE)); incr_dir.join(crate_name) diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 0e646b136c45..1b2a283a1a0d 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -10,8 +10,8 @@ use rustc_middle::dep_graph::{DepGraph, DepsType, SerializedDepGraph, WorkProduc use rustc_middle::query::on_disk_cache::OnDiskCache; use rustc_serialize::Decodable; use rustc_serialize::opaque::MemDecoder; -use rustc_session::Session; use rustc_session::config::IncrementalStateAssertion; +use rustc_session::{Session, StableCrateId}; use rustc_span::Symbol; use tracing::{debug, warn}; @@ -208,9 +208,14 @@ pub fn load_query_result_cache(sess: &Session) -> Option { /// Setups the dependency graph by loading an existing graph from disk and set up streaming of a /// new graph to an incremental session directory. -pub fn setup_dep_graph(sess: &Session, crate_name: Symbol, deps: &DepsType) -> DepGraph { +pub fn setup_dep_graph( + sess: &Session, + crate_name: Symbol, + stable_crate_id: StableCrateId, + deps: &DepsType, +) -> DepGraph { // `load_dep_graph` can only be called after `prepare_session_directory`. - prepare_session_directory(sess, crate_name); + prepare_session_directory(sess, crate_name, stable_crate_id); let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess, deps)); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 03b8b61bbc0a..08659f8511d5 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -936,7 +936,7 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( let outputs = util::build_output_filenames(&pre_configured_attrs, sess); let dep_type = DepsType { dep_names: rustc_query_impl::dep_kind_names() }; - let dep_graph = setup_dep_graph(sess, crate_name, &dep_type); + let dep_graph = setup_dep_graph(sess, crate_name, stable_crate_id, &dep_type); let cstore = FreezeLock::new(Box::new(CStore::new(compiler.codegen_backend.metadata_loader())) as _); From b443a59ba80b92ba6ddd6c1cc5fedf07c53531a3 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 27 Oct 2025 16:14:03 +0000 Subject: [PATCH 147/170] Allow codegen backends to indicate which crate types they support This way cargo will drop the unsupported crate types for crates that specify multiple crate types. --- .../rustc_codegen_ssa/src/traits/backend.rs | 14 +++++++++++++- compiler/rustc_driver_impl/src/lib.rs | 3 ++- compiler/rustc_interface/src/passes.rs | 6 +++++- compiler/rustc_interface/src/util.rs | 17 +++++++++++++++-- compiler/rustc_session/src/output.rs | 13 +++++++++++-- 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index ec53d9f53eb8..85bff4540814 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -10,7 +10,7 @@ use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; -use rustc_session::config::{self, OutputFilenames, PrintRequest}; +use rustc_session::config::{self, CrateType, OutputFilenames, PrintRequest}; use rustc_span::Symbol; use super::CodegenObject; @@ -62,6 +62,18 @@ pub trait CodegenBackend { } } + fn supported_crate_types(&self, _sess: &Session) -> Vec { + vec![ + CrateType::Executable, + CrateType::Dylib, + CrateType::Rlib, + CrateType::Staticlib, + CrateType::Cdylib, + CrateType::ProcMacro, + CrateType::Sdylib, + ] + } + fn print_passes(&self) {} fn print_version(&self) {} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 9529ef2b99ad..c926a7c742a0 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -686,7 +686,8 @@ fn print_crate_info( }; let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess); let crate_name = passes::get_crate_name(sess, attrs); - let crate_types = collect_crate_types(sess, attrs); + let crate_types = + collect_crate_types(sess, &codegen_backend.supported_crate_types(sess), attrs); for &style in &crate_types { let fname = rustc_session::output::filename_for_input( sess, style, crate_name, &t_outputs, diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 08659f8511d5..6842b61f629e 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -925,7 +925,11 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( let pre_configured_attrs = rustc_expand::config::pre_configure_attrs(sess, &krate.attrs); let crate_name = get_crate_name(sess, &pre_configured_attrs); - let crate_types = collect_crate_types(sess, &pre_configured_attrs); + let crate_types = collect_crate_types( + sess, + &compiler.codegen_backend.supported_crate_types(sess), + &pre_configured_attrs, + ); let stable_crate_id = StableCrateId::new( crate_name, crate_types.contains(&CrateType::Executable), diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index be2fd0787b98..801c144c811a 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -354,6 +354,14 @@ impl CodegenBackend for DummyCodegenBackend { "dummy" } + fn supported_crate_types(&self, _sess: &Session) -> Vec { + // This includes bin despite failing on the link step to ensure that you + // can still get the frontend handling for binaries. For all library + // like crate types cargo will fallback to rlib unless you specifically + // say that only a different crate type must be used. + vec![CrateType::Rlib, CrateType::Executable] + } + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { Box::new(CodegenResults { modules: vec![], @@ -380,12 +388,17 @@ impl CodegenBackend for DummyCodegenBackend { ) { // JUSTIFICATION: TyCtxt no longer available here #[allow(rustc::bad_opt_access)] - if sess.opts.crate_types.iter().any(|&crate_type| crate_type != CrateType::Rlib) { + if codegen_results + .crate_info + .crate_types + .iter() + .any(|&crate_type| crate_type != CrateType::Rlib) + { #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] sess.dcx().fatal(format!( "crate type {} not supported by the dummy codegen backend", - sess.opts.crate_types[0], + codegen_results.crate_info.crate_types[0], )); } diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index cba70b5bd5d1..a48a4f649da1 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -174,7 +174,11 @@ pub fn categorize_crate_type(s: Symbol) -> Option { Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1) } -pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { +pub fn collect_crate_types( + session: &Session, + backend_crate_types: &[CrateType], + attrs: &[ast::Attribute], +) -> Vec { // If we're generating a test executable, then ignore all other output // styles at all other locations if session.opts.test { @@ -219,7 +223,12 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec Date: Mon, 27 Oct 2025 09:45:28 -0700 Subject: [PATCH 148/170] Link to `i32` for `strict_div`/`rem` methods Co-authored-by: Kevin Reid --- RELEASES.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 992d0ffbd380..74b0d4424c16 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -65,10 +65,10 @@ Stabilized APIs - [`{integer}::strict_add`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_add) - [`{integer}::strict_sub`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_sub) - [`{integer}::strict_mul`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_mul) -- [`{integer}::strict_div`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_div) -- [`{integer}::strict_div_euclid`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_div_euclid) -- [`{integer}::strict_rem`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_rem) -- [`{integer}::strict_rem_euclid`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_rem_euclid) +- [`{integer}::strict_div`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_div) +- [`{integer}::strict_div_euclid`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_div_euclid) +- [`{integer}::strict_rem`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_rem) +- [`{integer}::strict_rem_euclid`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.strict_rem_euclid) - [`{integer}::strict_neg`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_neg) - [`{integer}::strict_shl`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_shl) - [`{integer}::strict_shr`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.strict_shr) From 826c925128c71dcf8ed6a4d4c7673e748fc1d3d8 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Mon, 27 Oct 2025 10:10:48 -0700 Subject: [PATCH 149/170] Update cc-rs to 1.2.39 --- Cargo.lock | 5 +++-- compiler/rustc_llvm/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c290b392d59..378b7f8e9fcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -464,10 +464,11 @@ version = "0.1.0" [[package]] name = "cc" -version = "1.2.16" +version = "1.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index ad93c7453813..209ee93a9fef 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -11,7 +11,7 @@ libc = "0.2.73" [build-dependencies] # tidy-alphabetical-start # `cc` updates often break things, so we pin it here. -cc = "=1.2.16" +cc = "=1.2.39" # tidy-alphabetical-end [features] From 9cbfbb164e0131470620aad7594004399b0066fb Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Mon, 27 Oct 2025 13:32:50 -0400 Subject: [PATCH 150/170] Accept trivial consts based on trivial consts --- .../rustc_middle/src/mir/interpret/queries.rs | 1 + .../rustc_mir_transform/src/trivial_const.rs | 31 +++++++++++++----- ...9-assoc-const-static-recursion-impl.stderr | 28 +++++++--------- tests/ui/issues/issue-17252.stderr | 32 ++++++++++++------- .../cycle_crash-issue-135870.rs | 2 +- .../cycle_crash-issue-135870.stderr | 8 +---- tests/ui/recursion/issue-23302-3.stderr | 24 ++++++++------ 7 files changed, 71 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index ecf35d9dd6d7..89034981cbd6 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -134,6 +134,7 @@ impl<'tcx> TyCtxt<'tcx> { // If we don't *only* FCW anon consts we can wind up incorrectly FCW'ing uses of assoc // consts in pattern positions. #140447 && self.def_kind(cid.instance.def_id()) == DefKind::AnonConst + && !self.is_trivial_const(cid.instance.def_id()) { let mir_body = self.mir_for_ctfe(cid.instance.def_id()); if mir_body.is_polymorphic { diff --git a/compiler/rustc_mir_transform/src/trivial_const.rs b/compiler/rustc_mir_transform/src/trivial_const.rs index af096f2b0468..3b80ae30be42 100644 --- a/compiler/rustc_mir_transform/src/trivial_const.rs +++ b/compiler/rustc_mir_transform/src/trivial_const.rs @@ -3,8 +3,8 @@ use std::ops::Deref; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::mir::{ - Body, ConstValue, Operand, Place, RETURN_PLACE, Rvalue, START_BLOCK, StatementKind, - TerminatorKind, + Body, Const, ConstValue, Operand, Place, RETURN_PLACE, Rvalue, START_BLOCK, StatementKind, + TerminatorKind, UnevaluatedConst, }; use rustc_middle::ty::{Ty, TyCtxt, TypeVisitableExt}; @@ -13,7 +13,9 @@ use rustc_middle::ty::{Ty, TyCtxt, TypeVisitableExt}; /// A "trivial const" is a const which can be easily proven to evaluate successfully, and the value /// that it evaluates to can be easily found without going through the usual MIR phases for a const. /// -/// Currently the only form of trivial const that is supported is this: +/// Currently, we support two forms of trivial const. +/// +/// The base case is this: /// ``` /// const A: usize = 0; /// ``` @@ -34,6 +36,13 @@ use rustc_middle::ty::{Ty, TyCtxt, TypeVisitableExt}; /// This scenario meets the required criteria because: /// * Control flow cannot panic, we don't have any calls or assert terminators /// * The value of the const is already computed, so it cannot fail +/// +/// In addition to assignment of literals, assignments of trivial consts are also considered +/// trivial consts. In this case, both `A` and `B` are trivial: +/// ``` +/// const A: usize = 0; +/// const B: usize = A; +/// ``` pub(crate) fn trivial_const<'a, 'tcx: 'a, F, B>( tcx: TyCtxt<'tcx>, def: LocalDefId, @@ -74,13 +83,19 @@ where return None; } - if let Rvalue::Use(Operand::Constant(c)) = rvalue { - if let rustc_middle::mir::Const::Val(v, ty) = c.const_ { - return Some((v, ty)); + let Rvalue::Use(Operand::Constant(c)) = rvalue else { + return None; + }; + match c.const_ { + Const::Ty(..) => None, + Const::Unevaluated(UnevaluatedConst { def, args, .. }, _ty) => { + if !args.is_empty() { + return None; + } + tcx.trivial_const(def) } + Const::Val(v, ty) => Some((v, ty)), } - - return None; } // The query provider is based on calling the free function trivial_const, which calls mir_built, diff --git a/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr b/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr index bf37f537a497..718854c59f4f 100644 --- a/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr +++ b/tests/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr @@ -1,36 +1,30 @@ -error[E0391]: cycle detected when simplifying constant for the type system `IMPL_REF_BAR` +error[E0391]: cycle detected when checking if `IMPL_REF_BAR` is a trivial const --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1 | LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; | ^^^^^^^^^^^^^^^^^^^^^^^ | -note: ...which requires const-evaluating + checking `IMPL_REF_BAR`... - --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:27 +note: ...which requires building MIR for `IMPL_REF_BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1 | LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; - | ^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `::BAR`... + | ^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires checking if `::BAR` is a trivial const... --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5 | LL | const BAR: u32 = IMPL_REF_BAR; | ^^^^^^^^^^^^^^ -note: ...which requires const-evaluating + checking `::BAR`... +note: ...which requires building MIR for `::BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5 | LL | const BAR: u32 = IMPL_REF_BAR; | ^^^^^^^^^^^^^^ -note: ...which requires caching mir of `::BAR` for CTFE... - --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5 + = note: ...which again requires checking if `IMPL_REF_BAR` is a trivial const, completing the cycle +note: cycle used when simplifying constant for the type system `IMPL_REF_BAR` + --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1 | -LL | const BAR: u32 = IMPL_REF_BAR; - | ^^^^^^^^^^^^^^ -note: ...which requires elaborating drops for `::BAR`... - --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:22 - | -LL | const BAR: u32 = IMPL_REF_BAR; - | ^^^^^^^^^^^^ - = note: ...which again requires simplifying constant for the type system `IMPL_REF_BAR`, completing the cycle - = note: cycle used when running analysis passes on this crate +LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-17252.stderr b/tests/ui/issues/issue-17252.stderr index 56bc32b19ab7..b0d9d94e07dd 100644 --- a/tests/ui/issues/issue-17252.stderr +++ b/tests/ui/issues/issue-17252.stderr @@ -1,31 +1,39 @@ -error[E0391]: cycle detected when simplifying constant for the type system `FOO` +error[E0391]: cycle detected when checking if `FOO` is a trivial const --> $DIR/issue-17252.rs:1:1 | LL | const FOO: usize = FOO; | ^^^^^^^^^^^^^^^^ | -note: ...which requires const-evaluating + checking `FOO`... - --> $DIR/issue-17252.rs:1:20 +note: ...which requires building MIR for `FOO`... + --> $DIR/issue-17252.rs:1:1 | LL | const FOO: usize = FOO; - | ^^^ - = note: ...which again requires simplifying constant for the type system `FOO`, completing the cycle - = note: cycle used when running analysis passes on this crate + | ^^^^^^^^^^^^^^^^ + = note: ...which again requires checking if `FOO` is a trivial const, completing the cycle +note: cycle used when simplifying constant for the type system `FOO` + --> $DIR/issue-17252.rs:1:1 + | +LL | const FOO: usize = FOO; + | ^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0391]: cycle detected when simplifying constant for the type system `main::BAR` +error[E0391]: cycle detected when checking if `main::BAR` is a trivial const --> $DIR/issue-17252.rs:6:9 | LL | const BAR: usize = BAR; | ^^^^^^^^^^^^^^^^ | -note: ...which requires const-evaluating + checking `main::BAR`... - --> $DIR/issue-17252.rs:6:28 +note: ...which requires building MIR for `main::BAR`... + --> $DIR/issue-17252.rs:6:9 | LL | const BAR: usize = BAR; - | ^^^ - = note: ...which again requires simplifying constant for the type system `main::BAR`, completing the cycle - = note: cycle used when running analysis passes on this crate + | ^^^^^^^^^^^^^^^^ + = note: ...which again requires checking if `main::BAR` is a trivial const, completing the cycle +note: cycle used when simplifying constant for the type system `main::BAR` + --> $DIR/issue-17252.rs:6:9 + | +LL | const BAR: usize = BAR; + | ^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 2 previous errors diff --git a/tests/ui/parallel-rustc/cycle_crash-issue-135870.rs b/tests/ui/parallel-rustc/cycle_crash-issue-135870.rs index 4407e3aca802..d199bc14842a 100644 --- a/tests/ui/parallel-rustc/cycle_crash-issue-135870.rs +++ b/tests/ui/parallel-rustc/cycle_crash-issue-135870.rs @@ -3,6 +3,6 @@ //@ compile-flags: -Z threads=2 //@ compare-output-by-lines -const FOO: usize = FOO; //~ ERROR cycle detected when simplifying constant for the type system `FOO` +const FOO: usize = FOO; //~ ERROR cycle detected fn main() {} diff --git a/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr b/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr index 6e588d1f8946..b80d0f92fcfa 100644 --- a/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr +++ b/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr @@ -1,18 +1,12 @@ -error[E0391]: cycle detected when simplifying constant for the type system `FOO` --> $DIR/cycle_crash-issue-135870.rs:6:1 | LL | const FOO: usize = FOO; | ^^^^^^^^^^^^^^^^ | -note: ...which requires const-evaluating + checking `FOO`... - --> $DIR/cycle_crash-issue-135870.rs:6:20 | LL | const FOO: usize = FOO; - | ^^^ - = note: ...which again requires simplifying constant for the type system `FOO`, completing the cycle - = note: cycle used when running analysis passes on this crate = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0391`. +For more information about this error, try `rustc --explain E0391`. \ No newline at end of file diff --git a/tests/ui/recursion/issue-23302-3.stderr b/tests/ui/recursion/issue-23302-3.stderr index 8a152f589663..ddca746e165b 100644 --- a/tests/ui/recursion/issue-23302-3.stderr +++ b/tests/ui/recursion/issue-23302-3.stderr @@ -1,26 +1,30 @@ -error[E0391]: cycle detected when simplifying constant for the type system `A` +error[E0391]: cycle detected when checking if `A` is a trivial const --> $DIR/issue-23302-3.rs:1:1 | LL | const A: i32 = B; | ^^^^^^^^^^^^ | -note: ...which requires const-evaluating + checking `A`... - --> $DIR/issue-23302-3.rs:1:16 +note: ...which requires building MIR for `A`... + --> $DIR/issue-23302-3.rs:1:1 | LL | const A: i32 = B; - | ^ -note: ...which requires simplifying constant for the type system `B`... + | ^^^^^^^^^^^^ +note: ...which requires checking if `B` is a trivial const... --> $DIR/issue-23302-3.rs:3:1 | LL | const B: i32 = A; | ^^^^^^^^^^^^ -note: ...which requires const-evaluating + checking `B`... - --> $DIR/issue-23302-3.rs:3:16 +note: ...which requires building MIR for `B`... + --> $DIR/issue-23302-3.rs:3:1 | LL | const B: i32 = A; - | ^ - = note: ...which again requires simplifying constant for the type system `A`, completing the cycle - = note: cycle used when running analysis passes on this crate + | ^^^^^^^^^^^^ + = note: ...which again requires checking if `A` is a trivial const, completing the cycle +note: cycle used when simplifying constant for the type system `A` + --> $DIR/issue-23302-3.rs:1:1 + | +LL | const A: i32 = B; + | ^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error From b9b29c4379c81e3b744ce1275684d3556c08d982 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 17 Oct 2025 15:02:46 +0200 Subject: [PATCH 151/170] repr(transparent): do not consider repr(C) types to be 1-ZST --- .../rustc_hir_analysis/src/check/check.rs | 115 ++++--- compiler/rustc_lint/src/lib.rs | 4 + compiler/rustc_lint_defs/src/builtin.rs | 37 +- compiler/rustc_lint_defs/src/lib.rs | 2 +- tests/ui/lint/improper-ctypes/lint-ctypes.rs | 1 + .../lint/improper-ctypes/lint-ctypes.stderr | 55 +-- tests/ui/lint/improper-ctypes/lint-fn.rs | 1 + tests/ui/lint/improper-ctypes/lint-fn.stderr | 47 ++- ...ent-non-exhaustive-transparent-in-prose.rs | 2 +- .../repr/repr-transparent-non-exhaustive.rs | 10 +- .../repr-transparent-non-exhaustive.stderr | 318 ++++++++++++++++-- tests/ui/repr/repr-transparent-repr-c.rs | 32 ++ tests/ui/repr/repr-transparent-repr-c.stderr | 85 +++++ 13 files changed, 584 insertions(+), 125 deletions(-) create mode 100644 tests/ui/repr/repr-transparent-repr-c.rs create mode 100644 tests/ui/repr/repr-transparent-repr-c.stderr diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 4c910d25c30a..943f494b7773 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -12,9 +12,7 @@ use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::{LangItem, Node, attrs, find_attr, intravisit}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, ObligationCauseCode, WellFormedLoc}; -use rustc_lint_defs::builtin::{ - REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, UNSUPPORTED_CALLING_CONVENTIONS, -}; +use rustc_lint_defs::builtin::{REPR_TRANSPARENT_NON_ZST_FIELDS, UNSUPPORTED_CALLING_CONVENTIONS}; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; @@ -1511,8 +1509,25 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) } let typing_env = ty::TypingEnv::non_body_analysis(tcx, adt.did()); - // For each field, figure out if it's known to have "trivial" layout (i.e., is a 1-ZST), with - // "known" respecting #[non_exhaustive] attributes. + // For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST). + // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private + // fields or `repr(C)`. We call those fields "unsuited". + struct FieldInfo<'tcx> { + span: Span, + trivial: bool, + unsuited: Option>, + } + struct UnsuitedInfo<'tcx> { + /// The source of the problem, a type that is found somewhere within the field type. + ty: Ty<'tcx>, + reason: UnsuitedReason, + } + enum UnsuitedReason { + NonExhaustive, + PrivateField, + ReprC, + } + let field_infos = adt.all_fields().map(|field| { let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did)); let layout = tcx.layout_of(typing_env.as_query_input(ty)); @@ -1520,22 +1535,20 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) let span = tcx.hir_span_if_local(field.did).unwrap(); let trivial = layout.is_ok_and(|layout| layout.is_1zst()); if !trivial { - return (span, trivial, None); + // No need to even compute `unsuited`. + return FieldInfo { span, trivial, unsuited: None }; } - // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`. - fn check_non_exhaustive<'tcx>( + fn check_unsuited<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - t: Ty<'tcx>, - ) -> ControlFlow<(&'static str, DefId, GenericArgsRef<'tcx>, bool)> { + ty: Ty<'tcx>, + ) -> ControlFlow> { // We can encounter projections during traversal, so ensure the type is normalized. - let t = tcx.try_normalize_erasing_regions(typing_env, t).unwrap_or(t); - match t.kind() { - ty::Tuple(list) => { - list.iter().try_for_each(|t| check_non_exhaustive(tcx, typing_env, t)) - } - ty::Array(ty, _) => check_non_exhaustive(tcx, typing_env, *ty), + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + match ty.kind() { + ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited(tcx, typing_env, t)), + ty::Array(ty, _) => check_unsuited(tcx, typing_env, *ty), ty::Adt(def, args) => { if !def.did().is_local() && !find_attr!( @@ -1550,28 +1563,36 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) .any(ty::VariantDef::is_field_list_non_exhaustive); let has_priv = def.all_fields().any(|f| !f.vis.is_public()); if non_exhaustive || has_priv { - return ControlFlow::Break(( - def.descr(), - def.did(), - args, - non_exhaustive, - )); + return ControlFlow::Break(UnsuitedInfo { + ty, + reason: if non_exhaustive { + UnsuitedReason::NonExhaustive + } else { + UnsuitedReason::PrivateField + }, + }); } } + if def.repr().c() { + return ControlFlow::Break(UnsuitedInfo { + ty, + reason: UnsuitedReason::ReprC, + }); + } def.all_fields() .map(|field| field.ty(tcx, args)) - .try_for_each(|t| check_non_exhaustive(tcx, typing_env, t)) + .try_for_each(|t| check_unsuited(tcx, typing_env, t)) } _ => ControlFlow::Continue(()), } } - (span, trivial, check_non_exhaustive(tcx, typing_env, ty).break_value()) + FieldInfo { span, trivial, unsuited: check_unsuited(tcx, typing_env, ty).break_value() } }); let non_trivial_fields = field_infos .clone() - .filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None }); + .filter_map(|field| if !field.trivial { Some(field.span) } else { None }); let non_trivial_count = non_trivial_fields.clone().count(); if non_trivial_count >= 2 { bad_non_zero_sized_fields( @@ -1583,36 +1604,40 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) ); return; } - let mut prev_non_exhaustive_1zst = false; - for (span, _trivial, non_exhaustive_1zst) in field_infos { - if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive_1zst { + + let mut prev_unsuited_1zst = false; + for field in field_infos { + if let Some(unsuited) = field.unsuited { + assert!(field.trivial); // If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts. // Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst. - if non_trivial_count > 0 || prev_non_exhaustive_1zst { + if non_trivial_count > 0 || prev_unsuited_1zst { tcx.node_span_lint( - REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, + REPR_TRANSPARENT_NON_ZST_FIELDS, tcx.local_def_id_to_hir_id(adt.did().expect_local()), - span, + field.span, |lint| { - lint.primary_message( - "zero-sized fields in `repr(transparent)` cannot \ - contain external non-exhaustive types", - ); - let note = if non_exhaustive { - "is marked with `#[non_exhaustive]`" - } else { - "contains private fields" + let title = match unsuited.reason { + UnsuitedReason::NonExhaustive => "external non-exhaustive types", + UnsuitedReason::PrivateField => "external types with private fields", + UnsuitedReason::ReprC => "`repr(C)` types", + }; + lint.primary_message( + format!("zero-sized fields in `repr(transparent)` cannot contain {title}"), + ); + let note = match unsuited.reason { + UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.", + UnsuitedReason::PrivateField => "contains private fields, so it could become non-zero-sized in the future.", + UnsuitedReason::ReprC => "is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets.", }; - let field_ty = tcx.def_path_str_with_args(def_id, args); lint.note(format!( - "this {descr} contains `{field_ty}`, which {note}, \ - and makes it not a breaking change to become \ - non-zero-sized in the future." + "this field contains `{field_ty}`, which {note}", + field_ty = unsuited.ty, )); }, - ) + ); } else { - prev_non_exhaustive_1zst = true; + prev_unsuited_1zst = true; } } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 9bb53fea54a1..003bf0c3db3c 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -362,6 +362,10 @@ fn register_builtins(store: &mut LintStore) { store.register_renamed("static_mut_ref", "static_mut_refs"); store.register_renamed("temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"); store.register_renamed("elided_named_lifetimes", "mismatched_lifetime_syntaxes"); + store.register_renamed( + "repr_transparent_external_private_fields", + "repr_transparent_non_zst_fields", + ); // These were moved to tool lints, but rustc still sees them when compiling normally, before // tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 8b7b13a4c362..91b82ecfa43d 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -86,7 +86,7 @@ declare_lint_pass! { REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE, RENAMED_AND_REMOVED_LINTS, - REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, + REPR_TRANSPARENT_NON_ZST_FIELDS, RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, RUST_2021_INCOMPATIBLE_OR_PATTERNS, RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, @@ -3011,10 +3011,9 @@ declare_lint! { } declare_lint! { - /// The `repr_transparent_external_private_fields` lint + /// The `repr_transparent_non_zst_fields` lint /// detects types marked `#[repr(transparent)]` that (transitively) - /// contain an external ZST type marked `#[non_exhaustive]` or containing - /// private fields + /// contain a type that is not guaranteed to remain a ZST type under all configurations. /// /// ### Example /// @@ -3022,8 +3021,13 @@ declare_lint! { /// #![deny(repr_transparent_external_private_fields)] /// use foo::NonExhaustiveZst; /// + /// #[repr(C)] + /// struct CZst([u8; 0]); + /// /// #[repr(transparent)] /// struct Bar(u32, ([u32; 0], NonExhaustiveZst)); + /// #[repr(transparent)] + /// struct Baz(u32, CZst); /// ``` /// /// This will produce: @@ -3042,26 +3046,39 @@ declare_lint! { /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! /// = note: for more information, see issue #78586 - /// = note: this struct contains `NonExhaustiveZst`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + /// = note: this field contains `NonExhaustiveZst`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. + /// + /// error: zero-sized fields in repr(transparent) cannot contain `#[repr(C)]` types + /// --> src/main.rs:5:28 + /// | + /// 5 | struct Baz(u32, CZst); + /// | ^^^^ + /// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + /// = note: for more information, see issue #78586 + /// = note: this field contains `CZst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. /// ``` /// /// ### Explanation /// - /// Previous, Rust accepted fields that contain external private zero-sized types, - /// even though it should not be a breaking change to add a non-zero-sized field to - /// that private type. + /// Previous, Rust accepted fields that contain external private zero-sized types, even though + /// those types could gain a non-zero-sized field in a future, semver-compatible update. + /// + /// Rust also accepted fields that contain `repr(C)` zero-sized types, even though those types + /// are not guaranteed to be zero-sized on all targets, and even though those types can + /// make a difference for the ABI (and therefore cannot be ignored by `repr(transparent)`). /// /// This is a [future-incompatible] lint to transition this /// to a hard error in the future. See [issue #78586] for more details. /// /// [issue #78586]: https://github.com/rust-lang/rust/issues/78586 /// [future-incompatible]: ../index.md#future-incompatible-lints - pub REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, - Warn, + pub REPR_TRANSPARENT_NON_ZST_FIELDS, + Deny, "transparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::FutureReleaseError, reference: "issue #78586 ", + report_in_deps: true, }; } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 40a818a3c9dc..ec26c35ffdc9 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -403,7 +403,7 @@ pub enum FutureIncompatibilityReason { /// /// After a lint has been in this state for a while and you feel like it is ready to graduate /// to warning everyone, consider setting [`FutureIncompatibleInfo::report_in_deps`] to true. - /// (see it's documentation for more guidance) + /// (see its documentation for more guidance) /// /// After some period of time, lints with this variant can be turned into /// hard errors (and the lint removed). Preferably when there is some diff --git a/tests/ui/lint/improper-ctypes/lint-ctypes.rs b/tests/ui/lint/improper-ctypes/lint-ctypes.rs index 7dc06079fa32..5d90e22e4b49 100644 --- a/tests/ui/lint/improper-ctypes/lint-ctypes.rs +++ b/tests/ui/lint/improper-ctypes/lint-ctypes.rs @@ -38,6 +38,7 @@ pub struct TransparentLifetime<'a>(*const u8, PhantomData<&'a ()>); #[repr(transparent)] pub struct TransparentUnit(f32, PhantomData); #[repr(transparent)] +#[allow(repr_transparent_non_zst_fields)] pub struct TransparentCustomZst(i32, ZeroSize); #[repr(C)] diff --git a/tests/ui/lint/improper-ctypes/lint-ctypes.stderr b/tests/ui/lint/improper-ctypes/lint-ctypes.stderr index 6f8b951c53d7..ef23f3ca6c91 100644 --- a/tests/ui/lint/improper-ctypes/lint-ctypes.stderr +++ b/tests/ui/lint/improper-ctypes/lint-ctypes.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:47:28 + --> $DIR/lint-ctypes.rs:48:28 | LL | pub fn ptr_type1(size: *const Foo); | ^^^^^^^^^^ not FFI-safe @@ -18,7 +18,7 @@ LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:48:28 + --> $DIR/lint-ctypes.rs:49:28 | LL | pub fn ptr_type2(size: *const Foo); | ^^^^^^^^^^ not FFI-safe @@ -32,7 +32,7 @@ LL | pub struct Foo; | ^^^^^^^^^^^^^^ error: `extern` block uses type `((),)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:50:25 + --> $DIR/lint-ctypes.rs:51:25 | LL | pub fn ptr_tuple(p: *const ((),)); | ^^^^^^^^^^^^ not FFI-safe @@ -41,7 +41,7 @@ LL | pub fn ptr_tuple(p: *const ((),)); = note: tuples have unspecified layout error: `extern` block uses type `[u32]`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:51:26 + --> $DIR/lint-ctypes.rs:52:26 | LL | pub fn slice_type(p: &[u32]); | ^^^^^^ not FFI-safe @@ -50,7 +50,7 @@ LL | pub fn slice_type(p: &[u32]); = note: slices have no C equivalent error: `extern` block uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:52:24 + --> $DIR/lint-ctypes.rs:53:24 | LL | pub fn str_type(p: &str); | ^^^^ not FFI-safe @@ -59,7 +59,7 @@ LL | pub fn str_type(p: &str); = note: string slices have no C equivalent error: `extern` block uses type `Box`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:53:24 + --> $DIR/lint-ctypes.rs:54:24 | LL | pub fn box_type(p: Box); | ^^^^^^^^ not FFI-safe @@ -68,7 +68,7 @@ LL | pub fn box_type(p: Box); = note: this struct has unspecified layout error: `extern` block uses type `char`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:55:25 + --> $DIR/lint-ctypes.rs:56:25 | LL | pub fn char_type(p: char); | ^^^^ not FFI-safe @@ -77,7 +77,7 @@ LL | pub fn char_type(p: char); = note: the `char` type has no C equivalent error: `extern` block uses type `dyn Bar`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:56:26 + --> $DIR/lint-ctypes.rs:57:26 | LL | pub fn trait_type(p: &dyn Bar); | ^^^^^^^^ not FFI-safe @@ -85,7 +85,7 @@ LL | pub fn trait_type(p: &dyn Bar); = note: trait objects have no C equivalent error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:57:26 + --> $DIR/lint-ctypes.rs:58:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -94,7 +94,7 @@ LL | pub fn tuple_type(p: (i32, i32)); = note: tuples have unspecified layout error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:58:27 + --> $DIR/lint-ctypes.rs:59:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -103,7 +103,7 @@ LL | pub fn tuple_type2(p: I32Pair); = note: tuples have unspecified layout error: `extern` block uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:59:25 + --> $DIR/lint-ctypes.rs:60:25 | LL | pub fn zero_size(p: ZeroSize); | ^^^^^^^^ not FFI-safe @@ -117,20 +117,20 @@ LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:60:33 + --> $DIR/lint-ctypes.rs:61:33 | LL | pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | = note: composed only of `PhantomData` note: the type is defined here - --> $DIR/lint-ctypes.rs:44:1 + --> $DIR/lint-ctypes.rs:45:1 | LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:63:12 + --> $DIR/lint-ctypes.rs:64:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -138,7 +138,7 @@ LL | -> ::std::marker::PhantomData; = note: composed only of `PhantomData` error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:64:23 + --> $DIR/lint-ctypes.rs:65:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -147,7 +147,7 @@ LL | pub fn fn_type(p: RustFn); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:65:24 + --> $DIR/lint-ctypes.rs:66:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -156,7 +156,7 @@ LL | pub fn fn_type2(p: fn()); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `Box`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:66:28 + --> $DIR/lint-ctypes.rs:67:28 | LL | pub fn fn_contained(p: RustBadRet); | ^^^^^^^^^^ not FFI-safe @@ -165,7 +165,7 @@ LL | pub fn fn_contained(p: RustBadRet); = note: this struct has unspecified layout error: `extern` block uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:67:31 + --> $DIR/lint-ctypes.rs:68:31 | LL | pub fn transparent_str(p: TransparentStr); | ^^^^^^^^^^^^^^ not FFI-safe @@ -174,7 +174,7 @@ LL | pub fn transparent_str(p: TransparentStr); = note: string slices have no C equivalent error: `extern` block uses type `Box`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:68:30 + --> $DIR/lint-ctypes.rs:69:30 | LL | pub fn transparent_fn(p: TransparentBadFn); | ^^^^^^^^^^^^^^^^ not FFI-safe @@ -183,7 +183,7 @@ LL | pub fn transparent_fn(p: TransparentBadFn); = note: this struct has unspecified layout error: `extern` block uses type `[u8; 8]`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:69:27 + --> $DIR/lint-ctypes.rs:70:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -192,7 +192,7 @@ LL | pub fn raw_array(arr: [u8; 8]); = note: passing raw arrays by value is not FFI-safe error: `extern` block uses type `Option>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:71:26 + --> $DIR/lint-ctypes.rs:72:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -201,7 +201,7 @@ LL | pub fn no_niche_a(a: Option>); = note: enum has no representation hint error: `extern` block uses type `Option>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:73:26 + --> $DIR/lint-ctypes.rs:74:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -211,3 +211,14 @@ LL | pub fn no_niche_b(b: Option>); error: aborting due to 21 previous errors +Future incompatibility report: Future breakage diagnostic: +warning: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types + --> $DIR/lint-ctypes.rs:42:38 + | +LL | pub struct TransparentCustomZst(i32, ZeroSize); + | ^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `ZeroSize`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. + diff --git a/tests/ui/lint/improper-ctypes/lint-fn.rs b/tests/ui/lint/improper-ctypes/lint-fn.rs index 0b84098e3906..e71febb493dc 100644 --- a/tests/ui/lint/improper-ctypes/lint-fn.rs +++ b/tests/ui/lint/improper-ctypes/lint-fn.rs @@ -54,6 +54,7 @@ pub struct TransparentLifetime<'a>(*const u8, PhantomData<&'a ()>); pub struct TransparentUnit(f32, PhantomData); #[repr(transparent)] +#[allow(repr_transparent_non_zst_fields)] pub struct TransparentCustomZst(i32, ZeroSize); #[repr(C)] diff --git a/tests/ui/lint/improper-ctypes/lint-fn.stderr b/tests/ui/lint/improper-ctypes/lint-fn.stderr index 34e3bd021b92..a0d1ab232c6a 100644 --- a/tests/ui/lint/improper-ctypes/lint-fn.stderr +++ b/tests/ui/lint/improper-ctypes/lint-fn.stderr @@ -1,5 +1,5 @@ error: `extern` fn uses type `[u32]`, which is not FFI-safe - --> $DIR/lint-fn.rs:70:33 + --> $DIR/lint-fn.rs:71:33 | LL | pub extern "C" fn slice_type(p: &[u32]) { } | ^^^^^^ not FFI-safe @@ -13,7 +13,7 @@ LL | #![deny(improper_ctypes_definitions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `str`, which is not FFI-safe - --> $DIR/lint-fn.rs:73:31 + --> $DIR/lint-fn.rs:74:31 | LL | pub extern "C" fn str_type(p: &str) { } | ^^^^ not FFI-safe @@ -22,7 +22,7 @@ LL | pub extern "C" fn str_type(p: &str) { } = note: string slices have no C equivalent error: `extern` fn uses type `Box<[u8]>`, which is not FFI-safe - --> $DIR/lint-fn.rs:80:34 + --> $DIR/lint-fn.rs:81:34 | LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } | ^^^^^^^^^ not FFI-safe @@ -30,7 +30,7 @@ LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } = note: box cannot be represented as a single pointer error: `extern` fn uses type `Box`, which is not FFI-safe - --> $DIR/lint-fn.rs:83:35 + --> $DIR/lint-fn.rs:84:35 | LL | pub extern "C" fn boxed_string(p: Box) { } | ^^^^^^^^ not FFI-safe @@ -38,7 +38,7 @@ LL | pub extern "C" fn boxed_string(p: Box) { } = note: box cannot be represented as a single pointer error: `extern` fn uses type `Box`, which is not FFI-safe - --> $DIR/lint-fn.rs:86:34 + --> $DIR/lint-fn.rs:87:34 | LL | pub extern "C" fn boxed_trait(p: Box) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -46,7 +46,7 @@ LL | pub extern "C" fn boxed_trait(p: Box) { } = note: box cannot be represented as a single pointer error: `extern` fn uses type `char`, which is not FFI-safe - --> $DIR/lint-fn.rs:89:32 + --> $DIR/lint-fn.rs:90:32 | LL | pub extern "C" fn char_type(p: char) { } | ^^^^ not FFI-safe @@ -55,7 +55,7 @@ LL | pub extern "C" fn char_type(p: char) { } = note: the `char` type has no C equivalent error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-fn.rs:92:33 + --> $DIR/lint-fn.rs:93:33 | LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } | ^^^^^^^^^^ not FFI-safe @@ -64,7 +64,7 @@ LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } = note: tuples have unspecified layout error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-fn.rs:95:34 + --> $DIR/lint-fn.rs:96:34 | LL | pub extern "C" fn tuple_type2(p: I32Pair) { } | ^^^^^^^ not FFI-safe @@ -73,7 +73,7 @@ LL | pub extern "C" fn tuple_type2(p: I32Pair) { } = note: tuples have unspecified layout error: `extern` fn uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-fn.rs:98:32 + --> $DIR/lint-fn.rs:99:32 | LL | pub extern "C" fn zero_size(p: ZeroSize) { } | ^^^^^^^^ not FFI-safe @@ -87,20 +87,20 @@ LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-fn.rs:101:40 + --> $DIR/lint-fn.rs:102:40 | LL | pub extern "C" fn zero_size_phantom(p: ZeroSizeWithPhantomData) { } | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | = note: composed only of `PhantomData` note: the type is defined here - --> $DIR/lint-fn.rs:60:1 + --> $DIR/lint-fn.rs:61:1 | LL | pub struct ZeroSizeWithPhantomData(PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-fn.rs:104:51 + --> $DIR/lint-fn.rs:105:51 | LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -108,7 +108,7 @@ LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { = note: composed only of `PhantomData` error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-fn.rs:109:30 + --> $DIR/lint-fn.rs:110:30 | LL | pub extern "C" fn fn_type(p: RustFn) { } | ^^^^^^ not FFI-safe @@ -117,7 +117,7 @@ LL | pub extern "C" fn fn_type(p: RustFn) { } = note: this function pointer has Rust-specific calling convention error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-fn.rs:112:31 + --> $DIR/lint-fn.rs:113:31 | LL | pub extern "C" fn fn_type2(p: fn()) { } | ^^^^ not FFI-safe @@ -126,7 +126,7 @@ LL | pub extern "C" fn fn_type2(p: fn()) { } = note: this function pointer has Rust-specific calling convention error: `extern` fn uses type `str`, which is not FFI-safe - --> $DIR/lint-fn.rs:117:38 + --> $DIR/lint-fn.rs:118:38 | LL | pub extern "C" fn transparent_str(p: TransparentStr) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -135,7 +135,7 @@ LL | pub extern "C" fn transparent_str(p: TransparentStr) { } = note: string slices have no C equivalent error: `extern` fn uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-fn.rs:169:43 + --> $DIR/lint-fn.rs:170:43 | LL | pub extern "C" fn unused_generic2() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -143,7 +143,7 @@ LL | pub extern "C" fn unused_generic2() -> PhantomData { = note: composed only of `PhantomData` error: `extern` fn uses type `Vec`, which is not FFI-safe - --> $DIR/lint-fn.rs:182:39 + --> $DIR/lint-fn.rs:183:39 | LL | pub extern "C" fn used_generic4(x: Vec) { } | ^^^^^^ not FFI-safe @@ -152,7 +152,7 @@ LL | pub extern "C" fn used_generic4(x: Vec) { } = note: this struct has unspecified layout error: `extern` fn uses type `Vec`, which is not FFI-safe - --> $DIR/lint-fn.rs:185:41 + --> $DIR/lint-fn.rs:186:41 | LL | pub extern "C" fn used_generic5() -> Vec { | ^^^^^^ not FFI-safe @@ -162,3 +162,14 @@ LL | pub extern "C" fn used_generic5() -> Vec { error: aborting due to 17 previous errors +Future incompatibility report: Future breakage diagnostic: +warning: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types + --> $DIR/lint-fn.rs:58:38 + | +LL | pub struct TransparentCustomZst(i32, ZeroSize); + | ^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `ZeroSize`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. + diff --git a/tests/ui/repr/repr-transparent-non-exhaustive-transparent-in-prose.rs b/tests/ui/repr/repr-transparent-non-exhaustive-transparent-in-prose.rs index 6ab34719f066..8de25ab26629 100644 --- a/tests/ui/repr/repr-transparent-non-exhaustive-transparent-in-prose.rs +++ b/tests/ui/repr/repr-transparent-non-exhaustive-transparent-in-prose.rs @@ -2,7 +2,7 @@ #![feature(sync_unsafe_cell)] #![allow(unused)] -#![deny(repr_transparent_external_private_fields)] +#![deny(repr_transparent_non_zst_fields)] // https://github.com/rust-lang/rust/issues/129470 diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.rs b/tests/ui/repr/repr-transparent-non-exhaustive.rs index 38bab0163b4c..9188d6df3446 100644 --- a/tests/ui/repr/repr-transparent-non-exhaustive.rs +++ b/tests/ui/repr/repr-transparent-non-exhaustive.rs @@ -1,4 +1,4 @@ -#![deny(repr_transparent_external_private_fields)] +#![deny(repr_transparent_non_zst_fields)] //@ aux-build: repr-transparent-non-exhaustive.rs extern crate repr_transparent_non_exhaustive; @@ -42,7 +42,7 @@ pub struct T4(Sized, ExternalIndirection<(InternalPrivate, InternalNonExhaustive #[repr(transparent)] pub struct T5(Sized, Private); -//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types +//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external types with private fields //~| WARN this was previously accepted by the compiler #[repr(transparent)] @@ -67,7 +67,7 @@ pub struct T8(Sized, NonExhaustiveVariant); #[repr(transparent)] pub struct T9(Sized, InternalIndirection); -//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types +//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external types with private fields //~| WARN this was previously accepted by the compiler #[repr(transparent)] @@ -87,7 +87,7 @@ pub struct T12(Sized, InternalIndirection); #[repr(transparent)] pub struct T13(Sized, ExternalIndirection); -//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types +//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external types with private fields //~| WARN this was previously accepted by the compiler #[repr(transparent)] @@ -117,7 +117,7 @@ pub struct T18(NonExhaustive, NonExhaustive); #[repr(transparent)] pub struct T19(NonExhaustive, Private); -//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types +//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain external types with private fields //~| WARN this was previously accepted by the compiler #[repr(transparent)] diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.stderr b/tests/ui/repr/repr-transparent-non-exhaustive.stderr index 27ba6e82a539..ac5493bf7e59 100644 --- a/tests/ui/repr/repr-transparent-non-exhaustive.stderr +++ b/tests/ui/repr/repr-transparent-non-exhaustive.stderr @@ -1,4 +1,4 @@ -error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types +error: zero-sized fields in `repr(transparent)` cannot contain external types with private fields --> $DIR/repr-transparent-non-exhaustive.rs:44:22 | LL | pub struct T5(Sized, Private); @@ -6,12 +6,12 @@ LL | pub struct T5(Sized, Private); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | -LL | #![deny(repr_transparent_external_private_fields)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:49:22 @@ -21,7 +21,7 @@ LL | pub struct T6(Sized, NonExhaustive); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:54:23 @@ -31,7 +31,7 @@ LL | pub struct T6a(Sized, ::Assoc); // normalizes to `NonExhausti | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:59:22 @@ -41,7 +41,7 @@ LL | pub struct T7(Sized, NonExhaustiveEnum); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this enum contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:64:22 @@ -51,9 +51,9 @@ LL | pub struct T8(Sized, NonExhaustiveVariant); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. -error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types +error: zero-sized fields in `repr(transparent)` cannot contain external types with private fields --> $DIR/repr-transparent-non-exhaustive.rs:69:22 | LL | pub struct T9(Sized, InternalIndirection); @@ -61,7 +61,7 @@ LL | pub struct T9(Sized, InternalIndirection); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:74:23 @@ -71,7 +71,7 @@ LL | pub struct T10(Sized, InternalIndirection); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:79:23 @@ -81,7 +81,7 @@ LL | pub struct T11(Sized, InternalIndirection); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this enum contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:84:23 @@ -91,9 +91,9 @@ LL | pub struct T12(Sized, InternalIndirection); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. -error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types +error: zero-sized fields in `repr(transparent)` cannot contain external types with private fields --> $DIR/repr-transparent-non-exhaustive.rs:89:23 | LL | pub struct T13(Sized, ExternalIndirection); @@ -101,7 +101,7 @@ LL | pub struct T13(Sized, ExternalIndirection); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:94:23 @@ -111,7 +111,7 @@ LL | pub struct T14(Sized, ExternalIndirection); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:99:23 @@ -121,7 +121,7 @@ LL | pub struct T15(Sized, ExternalIndirection); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this enum contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:104:23 @@ -131,7 +131,7 @@ LL | pub struct T16(Sized, ExternalIndirection); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:109:16 @@ -141,7 +141,7 @@ LL | pub struct T17(NonExhaustive, Sized); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:114:31 @@ -151,9 +151,9 @@ LL | pub struct T18(NonExhaustive, NonExhaustive); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. -error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types +error: zero-sized fields in `repr(transparent)` cannot contain external types with private fields --> $DIR/repr-transparent-non-exhaustive.rs:119:31 | LL | pub struct T19(NonExhaustive, Private); @@ -161,7 +161,7 @@ LL | pub struct T19(NonExhaustive, Private); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:124:32 @@ -171,7 +171,279 @@ LL | pub struct T19Flipped(Private, NonExhaustive); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: aborting due to 17 previous errors +Future incompatibility report: Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external types with private fields + --> $DIR/repr-transparent-non-exhaustive.rs:44:22 + | +LL | pub struct T5(Sized, Private); + | ^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-non-exhaustive.rs:49:22 + | +LL | pub struct T6(Sized, NonExhaustive); + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-non-exhaustive.rs:54:23 + | +LL | pub struct T6a(Sized, ::Assoc); // normalizes to `NonExhaustive` + | ^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-non-exhaustive.rs:59:22 + | +LL | pub struct T7(Sized, NonExhaustiveEnum); + | ^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-non-exhaustive.rs:64:22 + | +LL | pub struct T8(Sized, NonExhaustiveVariant); + | ^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external types with private fields + --> $DIR/repr-transparent-non-exhaustive.rs:69:22 + | +LL | pub struct T9(Sized, InternalIndirection); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-non-exhaustive.rs:74:23 + | +LL | pub struct T10(Sized, InternalIndirection); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-non-exhaustive.rs:79:23 + | +LL | pub struct T11(Sized, InternalIndirection); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-non-exhaustive.rs:84:23 + | +LL | pub struct T12(Sized, InternalIndirection); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external types with private fields + --> $DIR/repr-transparent-non-exhaustive.rs:89:23 + | +LL | pub struct T13(Sized, ExternalIndirection); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-non-exhaustive.rs:94:23 + | +LL | pub struct T14(Sized, ExternalIndirection); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-non-exhaustive.rs:99:23 + | +LL | pub struct T15(Sized, ExternalIndirection); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-non-exhaustive.rs:104:23 + | +LL | pub struct T16(Sized, ExternalIndirection); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-non-exhaustive.rs:109:16 + | +LL | pub struct T17(NonExhaustive, Sized); + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-non-exhaustive.rs:114:31 + | +LL | pub struct T18(NonExhaustive, NonExhaustive); + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external types with private fields + --> $DIR/repr-transparent-non-exhaustive.rs:119:31 + | +LL | pub struct T19(NonExhaustive, Private); + | ^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types + --> $DIR/repr-transparent-non-exhaustive.rs:124:32 + | +LL | pub struct T19Flipped(Private, NonExhaustive); + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. +note: the lint level is defined here + --> $DIR/repr-transparent-non-exhaustive.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/ui/repr/repr-transparent-repr-c.rs b/tests/ui/repr/repr-transparent-repr-c.rs new file mode 100644 index 000000000000..c887c443f3fc --- /dev/null +++ b/tests/ui/repr/repr-transparent-repr-c.rs @@ -0,0 +1,32 @@ +#![deny(repr_transparent_non_zst_fields)] + +#[repr(C)] +pub struct ReprC1Zst { + pub _f: (), +} + +pub type Sized = i32; + +#[repr(transparent)] +pub struct T1(ReprC1Zst); +#[repr(transparent)] +pub struct T2((), ReprC1Zst); +#[repr(transparent)] +pub struct T3(ReprC1Zst, ()); + +#[repr(transparent)] +pub struct T5(Sized, ReprC1Zst); +//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types +//~| WARN this was previously accepted by the compiler + +#[repr(transparent)] +pub struct T6(ReprC1Zst, Sized); +//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types +//~| WARN this was previously accepted by the compiler + +#[repr(transparent)] +pub struct T7(T1, Sized); // still wrong, even when the repr(C) is hidden inside another type +//~^ ERROR zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types +//~| WARN this was previously accepted by the compiler + +fn main() {} diff --git a/tests/ui/repr/repr-transparent-repr-c.stderr b/tests/ui/repr/repr-transparent-repr-c.stderr new file mode 100644 index 000000000000..5724845afdc1 --- /dev/null +++ b/tests/ui/repr/repr-transparent-repr-c.stderr @@ -0,0 +1,85 @@ +error: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types + --> $DIR/repr-transparent-repr-c.rs:18:22 + | +LL | pub struct T5(Sized, ReprC1Zst); + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. +note: the lint level is defined here + --> $DIR/repr-transparent-repr-c.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types + --> $DIR/repr-transparent-repr-c.rs:23:15 + | +LL | pub struct T6(ReprC1Zst, Sized); + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. + +error: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types + --> $DIR/repr-transparent-repr-c.rs:28:15 + | +LL | pub struct T7(T1, Sized); // still wrong, even when the repr(C) is hidden inside another type + | ^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. + +error: aborting due to 3 previous errors + +Future incompatibility report: Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types + --> $DIR/repr-transparent-repr-c.rs:18:22 + | +LL | pub struct T5(Sized, ReprC1Zst); + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. +note: the lint level is defined here + --> $DIR/repr-transparent-repr-c.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types + --> $DIR/repr-transparent-repr-c.rs:23:15 + | +LL | pub struct T6(ReprC1Zst, Sized); + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. +note: the lint level is defined here + --> $DIR/repr-transparent-repr-c.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types + --> $DIR/repr-transparent-repr-c.rs:28:15 + | +LL | pub struct T7(T1, Sized); // still wrong, even when the repr(C) is hidden inside another type + | ^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #78586 + = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. +note: the lint level is defined here + --> $DIR/repr-transparent-repr-c.rs:1:9 + | +LL | #![deny(repr_transparent_non_zst_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + From 978fd435ae9f9af7849292f829cd0fa7614cdf86 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 27 Oct 2025 11:57:32 -0700 Subject: [PATCH 152/170] rustdoc-search: add an integration test for CCI Part of https://github.com/rust-lang/rust/issues/130676 --- .../src/rustdoc-internals/search.md | 23 ++++++++++++++++++- src/tools/compiletest/src/runtest/js_doc.rs | 4 +++- src/tools/rustdoc-js/tester.js | 10 ++++---- tests/rustdoc-js/auxiliary/merged-dep.rs | 6 +++++ tests/rustdoc-js/merged-doc.js | 15 ++++++++++++ tests/rustdoc-js/merged-doc.rs | 10 ++++++++ 6 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 tests/rustdoc-js/auxiliary/merged-dep.rs create mode 100644 tests/rustdoc-js/merged-doc.js create mode 100644 tests/rustdoc-js/merged-doc.rs diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md index beff0a94c1ec..9cf59ea0c381 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md @@ -521,4 +521,25 @@ const EXPECTED = [ returned: [], }, ] -``` \ No newline at end of file +``` + +If the [`//@ revisions`] directive is used, the JS file will +have access to a variable called `REVISION`. + +```js +const EXPECTED = [ + // This first test targets name-based search. + { + query: "constructor", + others: REVISION === "has_constructor" ? + [ + { path: "constructor_search", name: "constructor" }, + ] : + [], + in_args: [], + returned: [], + }, +]; +``` + +[`//@ revisions`]: ../tests/compiletest.md#revisions diff --git a/src/tools/compiletest/src/runtest/js_doc.rs b/src/tools/compiletest/src/runtest/js_doc.rs index 93b05617e6f8..f7c2e6f01180 100644 --- a/src/tools/compiletest/src/runtest/js_doc.rs +++ b/src/tools/compiletest/src/runtest/js_doc.rs @@ -18,7 +18,9 @@ impl TestCx<'_> { .arg("--crate-name") .arg(file_stem.replace("-", "_")) .arg("--test-file") - .arg(self.testpaths.file.with_extension("js")), + .arg(self.testpaths.file.with_extension("js")) + .arg("--revision") + .arg(self.revision.unwrap_or_default()), ); if !res.status.success() { self.fatal_proc_rec("rustdoc-js test failed!", &res); diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index ff809d58e907..124839efdd02 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -364,10 +364,10 @@ function hasCheck(content, checkName) { return content.startsWith(`const ${checkName}`) || content.includes(`\nconst ${checkName}`); } -async function runChecks(testFile, doSearch, parseQuery) { +async function runChecks(testFile, doSearch, parseQuery, revision) { let checkExpected = false; let checkParsed = false; - let testFileContent = readFile(testFile); + let testFileContent = `const REVISION = "${revision}";\n${readFile(testFile)}`; if (testFileContent.indexOf("FILTER_CRATE") !== -1) { testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;"; @@ -548,6 +548,7 @@ function parseOptions(args) { "doc_folder": "", "test_folder": "", "test_file": [], + "revision": "", }; const correspondences = { "--resource-suffix": "resource_suffix", @@ -555,6 +556,7 @@ function parseOptions(args) { "--test-folder": "test_folder", "--test-file": "test_file", "--crate-name": "crate_name", + "--revision": "revision", }; for (let i = 0; i < args.length; ++i) { @@ -611,7 +613,7 @@ async function main(argv) { if (opts["test_file"].length !== 0) { for (const file of opts["test_file"]) { process.stdout.write(`Testing ${file} ... `); - errors += await runChecks(file, doSearch, parseAndSearch.parseQuery); + errors += await runChecks(file, doSearch, parseAndSearch.parseQuery, opts.revision); } } else if (opts["test_folder"].length !== 0) { for (const file of fs.readdirSync(opts["test_folder"])) { @@ -619,7 +621,7 @@ async function main(argv) { continue; } process.stdout.write(`Testing ${file} ... `); - errors += await runChecks(path.join(opts["test_folder"], file), doSearch, + errors += await runChecks(path.join(opts["test_folder"], file, ""), doSearch, parseAndSearch.parseQuery); } } diff --git a/tests/rustdoc-js/auxiliary/merged-dep.rs b/tests/rustdoc-js/auxiliary/merged-dep.rs new file mode 100644 index 000000000000..fadeb6c65270 --- /dev/null +++ b/tests/rustdoc-js/auxiliary/merged-dep.rs @@ -0,0 +1,6 @@ +//@ unique-doc-out-dir +//@ doc-flags:--merge=none +//@ doc-flags:--parts-out-dir=info/doc.parts/merged-dep +//@ doc-flags:-Zunstable-options + +pub struct Dep; diff --git a/tests/rustdoc-js/merged-doc.js b/tests/rustdoc-js/merged-doc.js new file mode 100644 index 000000000000..4380abb5912e --- /dev/null +++ b/tests/rustdoc-js/merged-doc.js @@ -0,0 +1,15 @@ +const EXPECTED = [ + { + 'query': 'merged_doc::Doc', + 'others': [ + { 'path': 'merged_doc', 'name': 'Doc' }, + ], + }, + { + 'query': 'merged_dep::Dep', + 'others': REVISION === "nomerge" ? [] : + [ + { 'path': 'merged_dep', 'name': 'Dep' }, + ], + }, +]; diff --git a/tests/rustdoc-js/merged-doc.rs b/tests/rustdoc-js/merged-doc.rs new file mode 100644 index 000000000000..ef7ce4b1fd36 --- /dev/null +++ b/tests/rustdoc-js/merged-doc.rs @@ -0,0 +1,10 @@ +//@ revisions: merge nomerge +//@ aux-build:merged-dep.rs +//@ build-aux-docs +//@[merge] doc-flags:--merge=finalize +//@[merge] doc-flags:--include-parts-dir=info/doc.parts/merged-dep +//@[merge] doc-flags:-Zunstable-options + +extern crate merged_dep; + +pub struct Doc; From 610b82ef0c4ba81a5accccbf80067d21c9933976 Mon Sep 17 00:00:00 2001 From: pirama-arumuga-nainar Date: Mon, 20 Oct 2025 09:57:25 -0700 Subject: [PATCH 153/170] Update target maintainers android.md --- src/doc/rustc/src/platform-support/android.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc/src/platform-support/android.md b/src/doc/rustc/src/platform-support/android.md index a54288f8f050..dcf7659130a3 100644 --- a/src/doc/rustc/src/platform-support/android.md +++ b/src/doc/rustc/src/platform-support/android.md @@ -9,8 +9,9 @@ ## Target maintainers [@chriswailes](https://github.com/chriswailes) +[@jfgoog](https://github.com/jfgoog) [@maurer](https://github.com/maurer) -[@mgeisler](https://github.com/mgeisler) +[@pirama-arumuga-nainar](https://github.com/pirama-arumuga-nainar) ## Requirements From fe3490c562e24c7aca64df1e9277291da405d74e Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 27 Oct 2025 10:04:27 -0700 Subject: [PATCH 154/170] rustdoc: remove `--emit=unversioned-shared-resources` This option hasn't done anything for a long time, and can be removed. I've kept a shim in place to avoid breaking docs.rs, but the option no longer does anything. Using git-blame, I tracked this option down to f77ebd4ffaea7fc5af49425cafefe141e7458cc3, the commit that introduced EmitType in the first place. It was used with SharedResource::Unversioned, which no longer exists since f9e1f6ffdf03ec33cb29e20c88fc7bcc938c7f42 removed them. CC https://github.com/rust-lang/rust/pull/146220 Part of https://github.com/rust-lang/rust/issues/83784 --- src/librustdoc/config.rs | 2 -- src/librustdoc/lib.rs | 2 +- tests/run-make/emit-shared-files/rmake.rs | 2 +- tests/run-make/rustdoc-default-output/output-default.stdout | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index cf0858810f55..630b2a6e5c18 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -311,7 +311,6 @@ pub(crate) enum ModuleSorting { #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum EmitType { - Unversioned, Toolchain, InvocationSpecific, DepInfo(Option), @@ -322,7 +321,6 @@ impl FromStr for EmitType { fn from_str(s: &str) -> Result { match s { - "unversioned-shared-resources" => Ok(Self::Unversioned), "toolchain-shared-resources" => Ok(Self::Toolchain), "invocation-specific" => Ok(Self::InvocationSpecific), "dep-info" => Ok(Self::DepInfo(None)), diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 820b2392e07c..dd6378b25def 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -567,7 +567,7 @@ fn opts() -> Vec { "", "emit", "Comma separated list of types of output for rustdoc to emit", - "[unversioned-shared-resources,toolchain-shared-resources,invocation-specific,dep-info]", + "[toolchain-shared-resources,invocation-specific,dep-info]", ), opt(Unstable, FlagMulti, "", "no-run", "Compile doctests without running them", ""), opt( diff --git a/tests/run-make/emit-shared-files/rmake.rs b/tests/run-make/emit-shared-files/rmake.rs index 911ceb3adca4..bd868d4fd19e 100644 --- a/tests/run-make/emit-shared-files/rmake.rs +++ b/tests/run-make/emit-shared-files/rmake.rs @@ -68,7 +68,7 @@ fn main() { rustdoc() .arg("-Zunstable-options") - .arg("--emit=toolchain-shared-resources,unversioned-shared-resources") + .arg("--emit=toolchain-shared-resources") .out_dir("all-shared") .arg("--resource-suffix=-xxx") .args(&["--extend-css", "z.css"]) diff --git a/tests/run-make/rustdoc-default-output/output-default.stdout b/tests/run-make/rustdoc-default-output/output-default.stdout index badbc0b6d15b..3afe0ca3ca8f 100644 --- a/tests/run-make/rustdoc-default-output/output-default.stdout +++ b/tests/run-make/rustdoc-default-output/output-default.stdout @@ -150,7 +150,7 @@ Options: --generate-redirect-map Generate JSON file at the top level instead of generating HTML redirection files - --emit [unversioned-shared-resources,toolchain-shared-resources,invocation-specific,dep-info] + --emit [toolchain-shared-resources,invocation-specific,dep-info] Comma separated list of types of output for rustdoc to emit --no-run Compile doctests without running them From eb113533a3903bd2073755c92893f9a32e9f4131 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 27 Oct 2025 13:50:51 +0000 Subject: [PATCH 155/170] Add test --- .../next-solver/opaques/stranded_opaque.rs | 50 ++++++++ .../opaques/stranded_opaque.stderr | 116 ++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 tests/ui/traits/next-solver/opaques/stranded_opaque.rs create mode 100644 tests/ui/traits/next-solver/opaques/stranded_opaque.stderr diff --git a/tests/ui/traits/next-solver/opaques/stranded_opaque.rs b/tests/ui/traits/next-solver/opaques/stranded_opaque.rs new file mode 100644 index 000000000000..f600a1496b99 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/stranded_opaque.rs @@ -0,0 +1,50 @@ +//@ compile-flags: -Znext-solver +#![feature(type_alias_impl_trait)] +use std::future::Future; + +// Test for https://github.com/rust-lang/trait-system-refactor-initiative/issues/235 + +// These are cases where an opaque types become "stranded" due to +// some errors. Make sure we don't ICE in either case. + +// Case 1: `impl Send` is stranded +fn foo() -> impl ?Future { + //~^ ERROR bound modifier `?` can only be applied to `Sized` + //~| ERROR bound modifier `?` can only be applied to `Sized` + () +} + +// Case 2: `Assoc = impl Trait` is stranded +trait Trait {} +impl Trait for i32 {} + +fn produce() -> impl Trait { + //~^ ERROR associated type `Assoc` not found for `Trait` + //~| ERROR associated type `Assoc` not found for `Trait` + 16 +} + +// Case 3: `impl Trait` is stranded +fn ill_formed_string() -> String { + //~^ ERROR struct takes 0 generic arguments but 1 generic argument was supplied + String::from("a string") +} + +// Case 4: TAIT variant of Case 1 to 3 +type Foo = impl ?Future; +//~^ ERROR unconstrained opaque type +//~| ERROR unconstrained opaque type +//~| ERROR bound modifier `?` can only be applied to `Sized` +//~| ERROR bound modifier `?` can only be applied to `Sized` + +type Produce = impl Trait; +//~^ ERROR unconstrained opaque type +//~| ERROR unconstrained opaque type +//~| ERROR associated type `Assoc` not found for `Trait` +//~| ERROR associated type `Assoc` not found for `Trait` + +type IllFormedString = String; +//~^ ERROR unconstrained opaque type +//~| ERROR struct takes 0 generic arguments but 1 generic argument was supplied + +fn main() {} diff --git a/tests/ui/traits/next-solver/opaques/stranded_opaque.stderr b/tests/ui/traits/next-solver/opaques/stranded_opaque.stderr new file mode 100644 index 000000000000..bcb357eb6953 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/stranded_opaque.stderr @@ -0,0 +1,116 @@ +error: bound modifier `?` can only be applied to `Sized` + --> $DIR/stranded_opaque.rs:11:18 + | +LL | fn foo() -> impl ?Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0220]: associated type `Assoc` not found for `Trait` + --> $DIR/stranded_opaque.rs:21:28 + | +LL | fn produce() -> impl Trait { + | ^^^^^ associated type `Assoc` not found + +error[E0107]: struct takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/stranded_opaque.rs:28:27 + | +LL | fn ill_formed_string() -> String { + | ^^^^^^------------ help: remove the unnecessary generics + | | + | expected 0 generic arguments + +error[E0107]: struct takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/stranded_opaque.rs:46:25 + | +LL | type IllFormedString = String; + | ^^^^^^------------ help: remove the unnecessary generics + | | + | expected 0 generic arguments + +error: bound modifier `?` can only be applied to `Sized` + --> $DIR/stranded_opaque.rs:11:18 + | +LL | fn foo() -> impl ?Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0220]: associated type `Assoc` not found for `Trait` + --> $DIR/stranded_opaque.rs:21:28 + | +LL | fn produce() -> impl Trait { + | ^^^^^ associated type `Assoc` not found + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: unconstrained opaque type + --> $DIR/stranded_opaque.rs:34:12 + | +LL | type Foo = impl ?Future; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `Foo` must be used in combination with a concrete type within the same crate + +error: bound modifier `?` can only be applied to `Sized` + --> $DIR/stranded_opaque.rs:34:17 + | +LL | type Foo = impl ?Future; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: bound modifier `?` can only be applied to `Sized` + --> $DIR/stranded_opaque.rs:34:17 + | +LL | type Foo = impl ?Future; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: unconstrained opaque type + --> $DIR/stranded_opaque.rs:34:34 + | +LL | type Foo = impl ?Future; + | ^^^^^^^^^ + | + = note: `Foo` must be used in combination with a concrete type within the same crate + +error: unconstrained opaque type + --> $DIR/stranded_opaque.rs:40:17 + | +LL | type Produce = impl Trait; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `Produce` must be used in combination with a concrete type within the same crate + +error[E0220]: associated type `Assoc` not found for `Trait` + --> $DIR/stranded_opaque.rs:40:28 + | +LL | type Produce = impl Trait; + | ^^^^^ associated type `Assoc` not found + +error[E0220]: associated type `Assoc` not found for `Trait` + --> $DIR/stranded_opaque.rs:40:28 + | +LL | type Produce = impl Trait; + | ^^^^^ associated type `Assoc` not found + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: unconstrained opaque type + --> $DIR/stranded_opaque.rs:40:36 + | +LL | type Produce = impl Trait; + | ^^^^^^^^^^ + | + = note: `Produce` must be used in combination with a concrete type within the same crate + +error: unconstrained opaque type + --> $DIR/stranded_opaque.rs:46:32 + | +LL | type IllFormedString = String; + | ^^^^^^^^^^ + | + = note: `IllFormedString` must be used in combination with a concrete type within the same crate + +error: aborting due to 15 previous errors + +Some errors have detailed explanations: E0107, E0220. +For more information about an error, try `rustc --explain E0107`. From c797724ed5a9f548aa40e6c10bd5557a94a04c48 Mon Sep 17 00:00:00 2001 From: tiif Date: Wed, 22 Oct 2025 14:10:42 +0000 Subject: [PATCH 156/170] Add delayed bug for stranded opaque --- .../rustc_hir_analysis/src/collect/type_of/opaque.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index a02990fe4aba..870a7b656386 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -242,6 +242,16 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>( owner_def_id: LocalDefId, opaque_types_from: DefiningScopeKind, ) -> Ty<'tcx> { + // When an opaque type is stranded, its hidden type cannot be inferred + // so we should not continue. + if !tcx.opaque_types_defined_by(owner_def_id).contains(&def_id) { + let opaque_type_span = tcx.def_span(def_id); + let guar = tcx + .dcx() + .span_delayed_bug(opaque_type_span, "cannot infer type for stranded opaque type"); + return Ty::new_error(tcx, guar); + } + match opaque_types_from { DefiningScopeKind::HirTypeck => { let tables = tcx.typeck(owner_def_id); From 9fc1378916ed3e2c45ff3476b5b169157979017e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 28 Oct 2025 14:15:19 +0000 Subject: [PATCH 157/170] Report correct unsupported crate type for the dummy codegen backend --- compiler/rustc_interface/src/util.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 801c144c811a..5e9cb4236504 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -388,17 +388,16 @@ impl CodegenBackend for DummyCodegenBackend { ) { // JUSTIFICATION: TyCtxt no longer available here #[allow(rustc::bad_opt_access)] - if codegen_results + if let Some(&crate_type) = codegen_results .crate_info .crate_types .iter() - .any(|&crate_type| crate_type != CrateType::Rlib) + .find(|&&crate_type| crate_type != CrateType::Rlib) { #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] sess.dcx().fatal(format!( - "crate type {} not supported by the dummy codegen backend", - codegen_results.crate_info.crate_types[0], + "crate type {crate_type} not supported by the dummy codegen backend" )); } From a45c6dd2c08e82713af0c0d60b2b68cabb2fe2b3 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 28 Oct 2025 08:16:43 -0500 Subject: [PATCH 158/170] Remove AssignDesugar span --- compiler/rustc_ast_lowering/src/expr.rs | 9 ++------- compiler/rustc_hir/src/hir.rs | 5 ++--- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 3 +-- compiler/rustc_lint/src/let_underscore.rs | 2 +- compiler/rustc_mir_build/src/builder/matches/mod.rs | 3 +-- src/tools/clippy/clippy_lints/src/shadow.rs | 2 +- .../clippy_lints/src/undocumented_unsafe_blocks.rs | 2 +- tests/ui/stats/input-stats.stderr | 4 ++-- 8 files changed, 11 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 9467f86a2252..7e1130d9f233 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1242,13 +1242,8 @@ impl<'hir> LoweringContext<'_, 'hir> { let rhs = self.lower_expr(rhs); // Introduce a `let` for destructuring: `let (lhs1, lhs2) = t`. - let destructure_let = self.stmt_let_pat( - None, - whole_span, - Some(rhs), - pat, - hir::LocalSource::AssignDesugar(self.lower_span(eq_sign_span)), - ); + let destructure_let = + self.stmt_let_pat(None, whole_span, Some(rhs), pat, hir::LocalSource::AssignDesugar); // `a = lhs1; b = lhs2;`. let stmts = self.arena.alloc_from_iter(std::iter::once(destructure_let).chain(assignments)); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 38cb12cf24bc..8b55a0d73299 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2975,8 +2975,7 @@ pub enum LocalSource { /// A desugared `.await`. AwaitDesugar, /// A desugared `expr = expr`, where the LHS is a tuple, struct, array or underscore expression. - /// The span is that of the `=` sign. - AssignDesugar(Span), + AssignDesugar, /// A contract `#[ensures(..)]` attribute injects a let binding for the check that runs at point of return. Contract, } @@ -5013,7 +5012,7 @@ mod size_asserts { static_assert_size!(ImplItemKind<'_>, 40); static_assert_size!(Item<'_>, 88); static_assert_size!(ItemKind<'_>, 64); - static_assert_size!(LetStmt<'_>, 72); + static_assert_size!(LetStmt<'_>, 64); static_assert_size!(Param<'_>, 32); static_assert_size!(Pat<'_>, 80); static_assert_size!(PatKind<'_>, 56); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 4d1c7be39197..abdb01bcefb2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1112,8 +1112,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Stmt { kind: hir::StmtKind::Let(hir::LetStmt { - source: - hir::LocalSource::AssignDesugar(_), + source: hir::LocalSource::AssignDesugar, .. }), .. diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index 3e2c1af97f43..d061a56c9346 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -152,7 +152,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { // We can't suggest `drop()` when we're on the top level. drop_fn_start_end: can_use_init .map(|init| (local.span.until(init.span), init.span.shrink_to_hi())), - is_assign_desugar: matches!(local.source, rustc_hir::LocalSource::AssignDesugar(_)), + is_assign_desugar: matches!(local.source, rustc_hir::LocalSource::AssignDesugar), }; if is_sync_lock { let span = MultiSpan::from_span(pat.span); diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 926a3eb018ff..82f12a969bb1 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2897,8 +2897,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for (_, node) in tcx.hir_parent_iter(var_id.0) { // FIXME(khuey) at what point is it safe to bail on the iterator? // Can we stop at the first non-Pat node? - if matches!(node, Node::LetStmt(&LetStmt { source: LocalSource::AssignDesugar(_), .. })) - { + if matches!(node, Node::LetStmt(&LetStmt { source: LocalSource::AssignDesugar, .. })) { return false; } } diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index 7fdea6bec510..f6083394fea5 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -133,7 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { .tcx .hir_parent_iter(pat.hir_id) .find(|(_, node)| !matches!(node, Node::Pat(_) | Node::PatField(_))) - && let LocalSource::AssignDesugar(_) = let_stmt.source + && let LocalSource::AssignDesugar = let_stmt.source { return; } diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 9afa9d65c261..9935dc309611 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -374,7 +374,7 @@ fn expr_has_unnecessary_safety_comment<'tcx>( hir::Stmt { kind: hir::StmtKind::Let(hir::LetStmt { - source: hir::LocalSource::AssignDesugar(_), + source: hir::LocalSource::AssignDesugar, .. }), .. diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr index 4a73a4747ad0..18862eeef06e 100644 --- a/tests/ui/stats/input-stats.stderr +++ b/tests/ui/stats/input-stats.stderr @@ -105,10 +105,10 @@ hir-stats - Semi 32 (NN.N%) 1 hir-stats Arm 80 (NN.N%) 2 40 hir-stats WherePredicate 72 (NN.N%) 3 24 hir-stats - BoundPredicate 72 (NN.N%) 3 -hir-stats Local 72 (NN.N%) 1 72 hir-stats InlineAsm 72 (NN.N%) 1 72 hir-stats Body 72 (NN.N%) 3 24 hir-stats Param 64 (NN.N%) 2 32 +hir-stats Local 64 (NN.N%) 1 64 hir-stats GenericArg 64 (NN.N%) 4 16 hir-stats - Type 16 (NN.N%) 1 hir-stats - Lifetime 48 (NN.N%) 3 @@ -119,5 +119,5 @@ hir-stats TraitItemId 8 (NN.N%) 2 4 hir-stats ImplItemId 8 (NN.N%) 2 4 hir-stats ForeignItemId 4 (NN.N%) 1 4 hir-stats ---------------------------------------------------------------- -hir-stats Total 8_624 173 +hir-stats Total 8_616 173 hir-stats ================================================================ From c49278f77a35e73941190dbfbdeedf8d1b9a610d Mon Sep 17 00:00:00 2001 From: apiraino Date: Tue, 28 Oct 2025 17:24:26 +0100 Subject: [PATCH 159/170] Update T-compiler/ops Zulip messages Slightly reword the issue prioritization and beta backport Zulip messages --- triagebot.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index a0d0b1892e4f..7a4e0bb5f43b 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -644,7 +644,7 @@ trigger_files = [ zulip_stream = 245100 # #t-compiler/prioritization/alerts topic = "#{number} {title}" message_on_add = """\ -@*WG-prioritization/alerts* issue #{number} has been requested for prioritization. +Issue #{number} has been requested for prioritization. # [Procedure](https://forge.rust-lang.org/compiler/prioritization.html) - Priority? @@ -738,12 +738,12 @@ message_on_reopen = "PR #{number} has been reopened. Pinging @*T-rustdoc*." [notify-zulip."beta-nominated".compiler] required_labels = ["T-compiler"] zulip_stream = 474880 # #t-compiler/backports -topic = "#{number}: beta-backport nomination" +topic = "#{number}: beta-nominated" message_on_add = [ """\ -PR #{number} "{title}" fixes a regression. -{recipients}, please evaluate nominating this PR for backport. -The following poll is a vibe-check and not binding. +PR #{number} "{title}" fixes a regression and has been nominated for backport. +{recipients}, what do you think about it? +This topic will help T-compiler getting context about it. """, """\ /poll Should #{number} be beta backported? @@ -757,7 +757,7 @@ message_on_remove = "PR #{number}'s beta-nomination has been removed." [notify-zulip."beta-accepted".compiler] required_labels = ["T-compiler"] zulip_stream = 474880 # #t-compiler/backports -# Put it in the same thread as beta-nominated. +# Put it in the same Zulip topic as beta-nominated. topic = "#{number}: beta-nominated" message_on_add = "PR #{number} has been **accepted** for **beta** backport." From 706d600232f783c41ece4a0f368ed806cbc57582 Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Tue, 28 Oct 2025 13:04:02 -0400 Subject: [PATCH 160/170] bootstrap: `ensure(doc::Std)` no longer opens a browser In general, the rationale for `--open` is to only open HTML files if they were "explicitly" invoked from the CLI (e.g. `x doc --open library/core`). The existing logic did not do that. Instead it opened the docs unconditionally when a subset of the crates was requested. This is unfortunate for other Steps in bootstrap, which may wish to `ensure()` the standard library docs without opening them. Change `Std` to check if it was explicitly invoked, rather than assuming it's the case. --- src/bootstrap/src/core/build_steps/doc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 70a9bcf38c0f..6622aae069d5 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -709,12 +709,12 @@ impl Step for Std { if builder.paths.iter().any(|path| path.ends_with("library")) { // For `x.py doc library --open`, open `std` by default. let index = out.join("std").join("index.html"); - builder.open_in_browser(index); + builder.maybe_open_in_browser::(index); } else { for requested_crate in crates { if STD_PUBLIC_CRATES.iter().any(|&k| k == requested_crate) { let index = out.join(requested_crate).join("index.html"); - builder.open_in_browser(index); + builder.maybe_open_in_browser::(index); break; } } From f25ca45fd1aa58e5313823c941d331c6aab35899 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 27 Oct 2025 10:27:05 -0700 Subject: [PATCH 161/170] Update CURRENT_RUSTC_VERSION post-bump (cherry picked from commit 813072186c1c305ea62c7270f1514dfab5166af2) --- compiler/rustc_feature/src/removed.rs | 4 ++-- library/alloc/src/boxed.rs | 4 ++-- library/alloc/src/collections/btree/map/entry.rs | 4 ++-- library/alloc/src/rc.rs | 4 ++-- library/alloc/src/sync.rs | 4 ++-- library/core/src/num/nonzero.rs | 7 ++----- library/core/src/panic/location.rs | 4 ++-- library/core/src/slice/mod.rs | 4 ++-- library/core/src/wtf8.rs | 2 +- library/proc_macro/src/lib.rs | 2 +- library/std/src/sync/poison/rwlock.rs | 2 +- 11 files changed, 19 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 539d67e0b6bc..a7584df8927e 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -102,9 +102,9 @@ declare_features! ( /// Allows deriving traits as per `SmartPointer` specification (removed, derive_smart_pointer, "1.84.0", Some(123430), Some("replaced by `CoercePointee`"), 131284), /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. - (removed, doc_auto_cfg, "CURRENT_RUSTC_VERSION", Some(43781), Some("merged into `doc_cfg`"), 138907), + (removed, doc_auto_cfg, "1.92.0", Some(43781), Some("merged into `doc_cfg`"), 138907), /// Allows `#[doc(cfg_hide(...))]`. - (removed, doc_cfg_hide, "CURRENT_RUSTC_VERSION", Some(43781), Some("merged into `doc_cfg`"), 138907), + (removed, doc_cfg_hide, "1.92.0", Some(43781), Some("merged into `doc_cfg`"), 138907), /// Allows using `#[doc(keyword = "...")]`. (removed, doc_keyword, "1.58.0", Some(51315), Some("merged into `#![feature(rustdoc_internals)]`"), 90420), diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index ae43fbfe1d69..7d8077f231dd 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -300,7 +300,7 @@ impl Box { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] #[must_use] pub fn new_zeroed() -> Box> { Self::new_zeroed_in(Global) @@ -692,7 +692,7 @@ impl Box<[T]> { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Box<[mem::MaybeUninit]> { unsafe { RawVec::with_capacity_zeroed(len).into_box(len) } diff --git a/library/alloc/src/collections/btree/map/entry.rs b/library/alloc/src/collections/btree/map/entry.rs index df51be3de54b..add8782a9499 100644 --- a/library/alloc/src/collections/btree/map/entry.rs +++ b/library/alloc/src/collections/btree/map/entry.rs @@ -284,7 +284,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> Entry<'a, K, V, A> { /// assert_eq!(entry.key(), &"poneyland"); /// ``` #[inline] - #[stable(feature = "btree_entry_insert", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "btree_entry_insert", since = "1.92.0")] pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, A> { match self { Occupied(mut entry) => { @@ -394,7 +394,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> VacantEntry<'a, K, V, A> { /// } /// assert_eq!(map["poneyland"], 37); /// ``` - #[stable(feature = "btree_entry_insert", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "btree_entry_insert", since = "1.92.0")] pub fn insert_entry(mut self, value: V) -> OccupiedEntry<'a, K, V, A> { let handle = match self.handle { None => { diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 2b62b92d4388..0baae0b314eb 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -529,7 +529,7 @@ impl Rc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] #[must_use] pub fn new_zeroed() -> Rc> { unsafe { @@ -1057,7 +1057,7 @@ impl Rc<[T]> { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Rc<[mem::MaybeUninit]> { unsafe { diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index c78f2c8a47e0..c6b85ca5b30b 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -536,7 +536,7 @@ impl Arc { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] #[must_use] pub fn new_zeroed() -> Arc> { unsafe { @@ -1205,7 +1205,7 @@ impl Arc<[T]> { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Arc<[mem::MaybeUninit]> { unsafe { diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index fcdb65bd45c9..efb0665b7f46 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1382,11 +1382,8 @@ macro_rules! nonzero_integer_signedness_dependent_impls { #[doc = concat!("let three = NonZero::new(3", stringify!($Int), ").unwrap();")] /// assert_eq!(three.div_ceil(two), two); /// ``` - #[stable(feature = "unsigned_nonzero_div_ceil", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable( - feature = "unsigned_nonzero_div_ceil", - since = "CURRENT_RUSTC_VERSION" - )] + #[stable(feature = "unsigned_nonzero_div_ceil", since = "1.92.0")] + #[rustc_const_stable(feature = "unsigned_nonzero_div_ceil", since = "1.92.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 593584934447..8176af03d13a 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -194,8 +194,8 @@ impl<'a> Location<'a> { /// `std::source_location::file_name`, both of which return a nul-terminated `const char*`. #[must_use] #[inline] - #[stable(feature = "file_with_nul", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "file_with_nul", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "file_with_nul", since = "1.92.0")] + #[rustc_const_stable(feature = "file_with_nul", since = "1.92.0")] pub const fn file_as_c_str(&self) -> &'a CStr { let filename = self.filename.as_ptr(); diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 61eb78294f68..024868873395 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3629,7 +3629,7 @@ impl [T] { /// assert_eq!(a, ['a', 'c', 'd', 'e', 'b', 'f']); /// ``` #[stable(feature = "slice_rotate", since = "1.26.0")] - #[rustc_const_stable(feature = "const_slice_rotate", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_slice_rotate", since = "1.92.0")] pub const fn rotate_left(&mut self, mid: usize) { assert!(mid <= self.len()); let k = self.len() - mid; @@ -3675,7 +3675,7 @@ impl [T] { /// assert_eq!(a, ['a', 'e', 'b', 'c', 'd', 'f']); /// ``` #[stable(feature = "slice_rotate", since = "1.26.0")] - #[rustc_const_stable(feature = "const_slice_rotate", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_slice_rotate", since = "1.92.0")] pub const fn rotate_right(&mut self, k: usize) { assert!(k <= self.len()); let mid = self.len() - k; diff --git a/library/core/src/wtf8.rs b/library/core/src/wtf8.rs index 0c03496c5e36..11cd2b8776f2 100644 --- a/library/core/src/wtf8.rs +++ b/library/core/src/wtf8.rs @@ -565,7 +565,7 @@ impl Iterator for EncodeWide<'_> { #[stable(feature = "encode_wide_fused_iterator", since = "1.62.0")] impl FusedIterator for EncodeWide<'_> {} -#[stable(feature = "encode_wide_debug", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "encode_wide_debug", since = "1.92.0")] impl fmt::Debug for EncodeWide<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { struct CodeUnit(u16); diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 1aa6064633c3..4efdfcad924b 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -379,7 +379,7 @@ impl Extend for TokenStream { macro_rules! extend_items { ($($item:ident)*) => { $( - #[stable(feature = "token_stream_extend_tt_items", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "token_stream_extend_tt_items", since = "1.92.0")] impl Extend<$item> for TokenStream { fn extend>(&mut self, iter: T) { self.extend(iter.into_iter().map(TokenTree::$item)); diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index fe51d8975e42..10e45bc8c11a 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -859,7 +859,7 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { /// # let final_check = rw.read().unwrap(); /// # assert_eq!(*final_check, 3); /// ``` - #[stable(feature = "rwlock_downgrade", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "rwlock_downgrade", since = "1.92.0")] pub fn downgrade(s: Self) -> RwLockReadGuard<'rwlock, T> { let lock = s.lock; From 25c7bd9c3d9665c454e4a45341232dc20948cc44 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 28 Oct 2025 13:21:49 -0700 Subject: [PATCH 162/170] Bump stage0 to 1.92.0-beta.1 --- src/bootstrap/src/core/sanity.rs | 1 - src/stage0 | 1044 +++++++++++++++--------------- 2 files changed, 522 insertions(+), 523 deletions(-) diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index eaa9e3a6a3e9..d0405270f663 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -34,7 +34,6 @@ pub struct Finder { // Targets can be removed from this list once they are present in the stage0 compiler (usually by updating the beta compiler of the bootstrap). const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined - "x86_64-unknown-motor", ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM diff --git a/src/stage0 b/src/stage0 index 556ced41d3da..43e1a635a42c 100644 --- a/src/stage0 +++ b/src/stage0 @@ -13,528 +13,528 @@ nightly_branch=master # All changes below this comment will be overridden the next time the # tool is executed. -compiler_channel_manifest_hash=55f3014ea3a3b17cfa5060d9188fd13d13b065456661a3d670dd061719713f32 -compiler_git_commit_hash=bb624dcb4c8ab987e10c0808d92d76f3b84dd117 -compiler_date=2025-09-21 +compiler_channel_manifest_hash=d8adf7e1722d2dc8ebdb327ee661e58a58145464bfcd9bb3cab66d652cc7bb90 +compiler_git_commit_hash=3b4dd9bf1410f8da6329baa36ce5e37673cbbd1f +compiler_date=2025-10-28 compiler_version=beta -rustfmt_channel_manifest_hash=2f19b9b74a17d0283264a227ed552d7df6729929fcb73b2d5bb321feed8a5c85 -rustfmt_git_commit_hash=54a8a1db604e4caff93e26e167ad4a6fde9f0681 -rustfmt_date=2025-09-27 +rustfmt_channel_manifest_hash=108599f303463cabaef3b53ab5e0c0e560a6a5a97f606d75a802efb595141baf +rustfmt_git_commit_hash=adaa838976ff99a4f0661136322f64cb466b58a0 +rustfmt_date=2025-10-28 rustfmt_version=nightly -dist/2025-09-21/rustc-beta-aarch64-apple-darwin.tar.gz=08f8aee2085d8da9041fa9f4c7c6d79b5b1c06c544a3e2309f353844e1250bd0 -dist/2025-09-21/rustc-beta-aarch64-apple-darwin.tar.xz=a36bed31d0f600ca8e8efc19322fe05a88e31bc218078e79c8ca0e7c3d582b20 -dist/2025-09-21/rustc-beta-aarch64-pc-windows-gnullvm.tar.gz=2b6b8f275d1b03ed7bc05e631378c0b462d274b7f1f038f2feec752b29993b10 -dist/2025-09-21/rustc-beta-aarch64-pc-windows-gnullvm.tar.xz=13adf0b39c176761adcf754671911d5309cf04348ef9f93fcf8c09afa6b70da0 -dist/2025-09-21/rustc-beta-aarch64-pc-windows-msvc.tar.gz=568566c82dd296babbd5588d0c69f23c5b5bfd32b3b25e493e6d45f15d645db7 -dist/2025-09-21/rustc-beta-aarch64-pc-windows-msvc.tar.xz=8357fb4ec176279416cabc0edbb2f7c3d4c812975867c8dd490fd2ee30ed1d1f -dist/2025-09-21/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=a885d01f6f5043bacb3bf4820777e29ab45aac4dbdfed75ee71a3de01b056e05 -dist/2025-09-21/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=3a2bed859214bbea2cdd13675eaf480e62c01646efed26067ba7078e6dd8591f -dist/2025-09-21/rustc-beta-aarch64-unknown-linux-musl.tar.gz=4b66e79a48d172eb674ba7e6b4eea91ebda2351d6d253deef90010ffc48d4801 -dist/2025-09-21/rustc-beta-aarch64-unknown-linux-musl.tar.xz=131f270aee35b36ae02959abe032c77e1de0c75f23f7c61bbca1e2c18a23f4f9 -dist/2025-09-21/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=d8c38594dfd1ef17c9ceb2ea614be730f4647fa5e75e80b5bc12d235216ecbf4 -dist/2025-09-21/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=1643434757b590b1586e9074e82be3cc9e50e5551212d5f2040fdd8feba8f1e2 -dist/2025-09-21/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=1619461791fa6c2b8750043a41acd285bdf1f32d376af675343be3449bb7e5b8 -dist/2025-09-21/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=cd2ed3ae30cf77b5530a2ebee13daeb1419ceec2ab18f754d07b081dd6a607c1 -dist/2025-09-21/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=b0703e79530bd836df864facbfb5065c3e5e8b3a457e4ef55b4f7a4d362b9ba8 -dist/2025-09-21/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=a647780abbe8d36049edd90857b3baab556ac9b61caaef1d98307fe92fc20453 -dist/2025-09-21/rustc-beta-i686-pc-windows-gnu.tar.gz=c7ec1f853b96edbf1a914b8345b025c87641225e0c49507bbffd88f2da05b8f4 -dist/2025-09-21/rustc-beta-i686-pc-windows-gnu.tar.xz=53803baae3061eb164f34900f5867cfdf3bf50733ca0a6bda674b491bc0250b8 -dist/2025-09-21/rustc-beta-i686-pc-windows-msvc.tar.gz=a0f9192059b9989db0c4dba57b5eae9cace1b8e6f8bb2362b028c7f36e34d44c -dist/2025-09-21/rustc-beta-i686-pc-windows-msvc.tar.xz=f8c237af5f0e2fe293671ddfe7fcf90f6e2b161a52314b2eb622f2a1b23ba3fc -dist/2025-09-21/rustc-beta-i686-unknown-linux-gnu.tar.gz=da2a42e5a76e95460a348ba70cdf1c5c6ade82eb6ad3796935158bbf5859b602 -dist/2025-09-21/rustc-beta-i686-unknown-linux-gnu.tar.xz=c98df2f0156c3065179f50d55dafda8c5db1f2eae99ecb3f568a8861299be289 -dist/2025-09-21/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=1149494d96b4ce949308620a360a365c4304b8ee8f8c9512a35f08048aa13c78 -dist/2025-09-21/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=c906f519bc65a3a7514a62f8920d334bc10623a76dd2d3464af88065b79cb334 -dist/2025-09-21/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=e3a9dd7ae0179ebb7659024532b9f3cca9ac4cdf62c0ae411b00d8f8768aaa34 -dist/2025-09-21/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=d9f3428d80a7b72b15c62bd3306628820f73b64f48de37ea079699b733bda048 -dist/2025-09-21/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=4b52241a8b65a9a42db2a75e057176a99e3b434907f498c4b6b9da104e138c72 -dist/2025-09-21/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=40f3a5fc7478b40153ab20e3f14a6d838c80dda529481735e898de989a31f963 -dist/2025-09-21/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=f5aa411dfe614ed288351fa4b17d1e2935501c804c0ad51f22e3db71617b17ea -dist/2025-09-21/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=b9efeb2e185e43775d309d083d8c4f45e30e18b7af71b9c45e397af6bc723fcf -dist/2025-09-21/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=38b6a69885e4dac48f7c3128ff1e88d0bf2d0ce1fbd6a5baa9dda62bca0b2b08 -dist/2025-09-21/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=8576ae6d787b0f8b127bb2dbeee213cc6093ba92dc7d5ff08f463d56e2e6ce90 -dist/2025-09-21/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=5e91ce627e900da2606782ae60598843a6ba17593a7eb0dcc8f5f9a1cc20676d -dist/2025-09-21/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=fe24fad781f829838592e6670655dcff52002ae720f987868fd4b17eb9ed631e -dist/2025-09-21/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=170ccaaa94eb7c813bcedf2afb37cb0c696c5f48ca9d93238ee8cf26efc5bafa -dist/2025-09-21/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=db5a862a260fe8688e3b1da1161948eed5361723a296bb27dcc62758eaa2b873 -dist/2025-09-21/rustc-beta-s390x-unknown-linux-gnu.tar.gz=c764aa7b70dacf7ac3ef5d2184d021304b633309b771f24d62375fa64791614f -dist/2025-09-21/rustc-beta-s390x-unknown-linux-gnu.tar.xz=69eedf71d20924d9729684e27a3a8cebc60efa7c0e7a3f8b6fe664f469a36788 -dist/2025-09-21/rustc-beta-sparcv9-sun-solaris.tar.gz=efb9d78cc395774e05353615c029ed674c1ba55204ae9be3d022abda9f5c6d9c -dist/2025-09-21/rustc-beta-sparcv9-sun-solaris.tar.xz=e8de37de871888886bb58d915f3a27bfd8c30a912ea3f3af4abf52f66708268f -dist/2025-09-21/rustc-beta-x86_64-apple-darwin.tar.gz=b945ef94a4efdc0fdd4a66c3708cb95a97592c922652af06d8d1b6bbaaf71660 -dist/2025-09-21/rustc-beta-x86_64-apple-darwin.tar.xz=876a6080177df194c14d0984f112692295a21186b05bd03a77d0a304dec5ad51 -dist/2025-09-21/rustc-beta-x86_64-pc-solaris.tar.gz=d7c0b4fb19c785ed3a0c16d903cef266438031c3a43b1721d19864a1923d3cb4 -dist/2025-09-21/rustc-beta-x86_64-pc-solaris.tar.xz=b4a5494890bd92b85f66054523b26e9aae5e74b3177c9eae64740ed7fa1d4da4 -dist/2025-09-21/rustc-beta-x86_64-pc-windows-gnu.tar.gz=ea55aab0bd57f0cd6568a6e78c9d0a3727fb7cfaf8ada9379084091244b3221b -dist/2025-09-21/rustc-beta-x86_64-pc-windows-gnu.tar.xz=426009c68fa78001b34f8b329cac7634dd8921c569061f45972058b770206f9f -dist/2025-09-21/rustc-beta-x86_64-pc-windows-gnullvm.tar.gz=c79a925f6f2b406db97732e22e1b245ef2c7d1291a574b0d55a861d3c7e5d766 -dist/2025-09-21/rustc-beta-x86_64-pc-windows-gnullvm.tar.xz=0f46e118b67656fe16c484589ee0213654cd6edfbe5a29d641aa810bddcc8c62 -dist/2025-09-21/rustc-beta-x86_64-pc-windows-msvc.tar.gz=d7b5fb269841039af498a6d0be2cbd70cb7b65f6a9918a2b6c022cadfdb30fcd -dist/2025-09-21/rustc-beta-x86_64-pc-windows-msvc.tar.xz=dfef15ca4fcf06622953296ebec960e446402ce542e2f264c12c0c03b9a476ce -dist/2025-09-21/rustc-beta-x86_64-unknown-freebsd.tar.gz=09994a33e03f50212cabee85294dfb684fafcef95e1de5b082540d01d92df1ce -dist/2025-09-21/rustc-beta-x86_64-unknown-freebsd.tar.xz=a0e3409ec6f6b02517c8f9d0e00a0627434f6b06a5360da286c46ceab9d12ab1 -dist/2025-09-21/rustc-beta-x86_64-unknown-illumos.tar.gz=01daeea1f1f10d84444b56e8e74e3a451c54e65832234b2caa2ce0a63c0a9ea1 -dist/2025-09-21/rustc-beta-x86_64-unknown-illumos.tar.xz=6299fa81533b9362d1cdbf7984506fcbc26f7d9e857a942f081f5325c87cc4c4 -dist/2025-09-21/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=cafaecb5158fcf39b676baf2d0060fb8b31558be0e442389755ae33a3e7bb42f -dist/2025-09-21/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=5d3c1fceba5285bc54f5a327841ecb8893e719ba874e483c904b903f4dc293ed -dist/2025-09-21/rustc-beta-x86_64-unknown-linux-musl.tar.gz=290a07f32c769f74d21c57a856b3aa39da89e2f086e53c42f3333d1f9ffb5673 -dist/2025-09-21/rustc-beta-x86_64-unknown-linux-musl.tar.xz=8bb354230d3da85bb0fc280c3e95ceadd9f7522d231fe787be8c82aa072ad506 -dist/2025-09-21/rustc-beta-x86_64-unknown-netbsd.tar.gz=af3c98fb99b419cfe50433f5f4a046065066183b248708ec8800253867c678df -dist/2025-09-21/rustc-beta-x86_64-unknown-netbsd.tar.xz=0ea09297621d3b735b2931f6472f95e1587d38f6fb0df7fdf5e427fa3baec4a1 -dist/2025-09-21/rust-std-beta-aarch64-apple-darwin.tar.gz=8c08542fe69e9fd5b2256d17d84427ac206c9e79e265fddbcdf12d1af4e5d913 -dist/2025-09-21/rust-std-beta-aarch64-apple-darwin.tar.xz=7ddd43d1e32a829ffa9a7a798e1339d0569e773d841d8b7ad33701664373b8ae -dist/2025-09-21/rust-std-beta-aarch64-apple-ios.tar.gz=bf3df6d2eb7e5515ae86bb970b5c145140f8e9d503e636fcfc435d47797b650a -dist/2025-09-21/rust-std-beta-aarch64-apple-ios.tar.xz=fbb88375a8c0c5e41d35e4838ecbd31e4ad1b96e22eb689ae37dd50322545d39 -dist/2025-09-21/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=26e9fb3607dfeab90500bac3e9eaa23170f7f22a4738ae6b58e2e89e0c87f72a -dist/2025-09-21/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=6c9622771203bf342d59673bb1f53fe715b4255f0860feb6b19be57de2ef9f92 -dist/2025-09-21/rust-std-beta-aarch64-apple-ios-sim.tar.gz=a9c15e05b58adede5d08d7628de75d47d5c38ca60fad87dca8b8c9801050ee1a -dist/2025-09-21/rust-std-beta-aarch64-apple-ios-sim.tar.xz=bd847e7952c02312e36900766a71a5284c221b177ddef0b9cb071c5f6186a70b -dist/2025-09-21/rust-std-beta-aarch64-linux-android.tar.gz=2dbfa47893553b2868c375bedda6c4e08c33bbfa9cc96ff6e89ccf0525f8b14b -dist/2025-09-21/rust-std-beta-aarch64-linux-android.tar.xz=6b83da57cd768bad91a820d698bd18ae60586b0920950ea14105d6f23b4b1db8 -dist/2025-09-21/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=a71a57f26812c2e1529c10e2e02b8d9b466564065a8a10ceb69f22cb76e83814 -dist/2025-09-21/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=29f8789707c545e45680acd30a329fa28613dd247ce7c91d65b13a10c0c21202 -dist/2025-09-21/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=07067546648ac6e784f9d5f6455534b62b9eb5cd86805c327b98505a0caeb2d8 -dist/2025-09-21/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=f224f6b979ceef56ef8a10eaf063a6ea072d405a52ef123b720d44925b530d36 -dist/2025-09-21/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=756534ef3e62c80481e4886348bc80632ca68ec9c49045d71838dc7ef0751373 -dist/2025-09-21/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=c6eee692a79106d7733c1cbc525b241b6d88a53ee08242e494d286eb0ad3024a -dist/2025-09-21/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=4843b5a6d3e8b89a54ab91e44768bb023178c8cc7cd745639d9a616f2331d9a2 -dist/2025-09-21/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=89a808aade0a91043bef23f3e3f38d193b4f02b6556f01cbaf103c43ce428478 -dist/2025-09-21/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=6c09b424925e89957fb1921887b7034c3d3adf012571415b9930e1d0119bed41 -dist/2025-09-21/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=3c34d71b74adb145302772db2a5c565235576a14c42eca0a270b0e9e67ac9032 -dist/2025-09-21/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=70225984fa7ad361126efd9cbd40fe9dcf4756866b23079e4dbde5ec51f3a10d -dist/2025-09-21/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=e696cf9ac5b4b0e16e3947377424515c761697541351d0924b61a37557394828 -dist/2025-09-21/rust-std-beta-aarch64-unknown-none.tar.gz=7b754105300a911c24f7483b7eb482d076f5f98e16f685215d06df3dab1fdcba -dist/2025-09-21/rust-std-beta-aarch64-unknown-none.tar.xz=8168916ec457eaeb32ad1d274ce67666d6ff8fdf662f32bc6e17656ef4ff7c81 -dist/2025-09-21/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=1ff511547185b42b801ce51f84985767d580c08f183b15033e8ae05274b7f90c -dist/2025-09-21/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=263e10d4226c5328cb41299466b82e154fa1c94c67fc3a185a0bb9a6639bebad -dist/2025-09-21/rust-std-beta-aarch64-unknown-uefi.tar.gz=eb3710b6ccbde9a265217a4e64f2c62bb22bcc79dd58a48381cc81c5e95073d4 -dist/2025-09-21/rust-std-beta-aarch64-unknown-uefi.tar.xz=b1c231790f55fd9a3dfcb9e6c946f34afb3bf96c57bb9d8615894d81aed44f95 -dist/2025-09-21/rust-std-beta-arm-linux-androideabi.tar.gz=0d12ed3a2980fce0d71d728d6c2840eac36a3e3ae93f2dfc3cb391d56c20cbf1 -dist/2025-09-21/rust-std-beta-arm-linux-androideabi.tar.xz=2219966a5e884a94c324b74872a2a4a12378d595cae10b53958190acc05ccd45 -dist/2025-09-21/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=286c12975e333cdf9c7ad0b21217e3b83682047c37c1dba766cff4a201bab40b -dist/2025-09-21/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=9e56db5ac01ae18077551117348fe56050426f521e41f217ac3d71c23eff4d88 -dist/2025-09-21/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=f52e699638e352d9f816a21965a5696acc5fd77325ff074f93c27587292bca19 -dist/2025-09-21/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=d0a4abaa489bfc6f2f7d2305e8edbd502b12f247357ba4cda541786377648108 -dist/2025-09-21/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=7ee043969e94b14ea74e083fd3edb9e0639d68c2bdd7ebdb188e98b36c854fcb -dist/2025-09-21/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=8ce6bafaae0e8217bdb138d104955ce7f0a747ef915aebb181cf3047beb4456f -dist/2025-09-21/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=8e5c901df3f932bac7dad635bc1197ffbee4884c0a101632d51a32f23c875cdb -dist/2025-09-21/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=166a1d949b6847e9bf102a89f9a417a30d8ac25a35fe3f732d6a513a7674af85 -dist/2025-09-21/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=8660de6d0d97fdffe3bbcfd5ffc7e11bbfe9c43f80067ba17845414958778845 -dist/2025-09-21/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=8b563eb032535c58c487d4ad992a12a106208861e45ea78c342e940b9356ebf1 -dist/2025-09-21/rust-std-beta-armebv7r-none-eabi.tar.gz=cd761b3f761198cc6ac64809eaa28ac299b67ba48d964298db3f5b4ea52f3623 -dist/2025-09-21/rust-std-beta-armebv7r-none-eabi.tar.xz=071864464c52c37bd102fad26b5d28f31aa9e06d34ee868a33ead297c3e20a4d -dist/2025-09-21/rust-std-beta-armebv7r-none-eabihf.tar.gz=402c044cdaad16d2b60365b6894250aa43424902c0b3f0526e849d7d0d452315 -dist/2025-09-21/rust-std-beta-armebv7r-none-eabihf.tar.xz=0028e17d0164bbb8166ddaad815874123fcc326dffef4740389ff2cb069a3e2b -dist/2025-09-21/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=0b9be6bfa168756361aa5feb7802133c4cbebd3fd20d75d32a4385b716417a9f -dist/2025-09-21/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=1893fcd9b16f0c334257cbc78dc416cc048d7c1603ba632ed7300bbf6c0bffb0 -dist/2025-09-21/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=5aa03609f57d6c959673963b633adf194ea240b3604ba6635a6ef5fbe5c519d3 -dist/2025-09-21/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=8f075eb3b1ed0bdfde8af08ee69778af2d2e896d1cdf17035657d1a84c85e856 -dist/2025-09-21/rust-std-beta-armv7-linux-androideabi.tar.gz=1499f0b4c3a4838dbd7b0df7303fbe7e157dfaec396b5ee1a6ae6a727ea3122a -dist/2025-09-21/rust-std-beta-armv7-linux-androideabi.tar.xz=f0ad36dd56abf6c03395bfc246209bce90aba1887dee81a2841f7e1013f93850 -dist/2025-09-21/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=4a430fa04ef3d23dcf1d5e1f69c37127a5fb58e883ac52c8885125e0f578cde9 -dist/2025-09-21/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=6fab12ecab36252aa1a4f6aa523ae1546f534548394e2585e96a869b87656806 -dist/2025-09-21/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=4081b51910cb95fbacafc9518ee5891e1131af79a8348635c13765706c18c3ea -dist/2025-09-21/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=ed9a56e0e8b4e0f639afd9c6c75c4adfeddc7ce0aaa9132591f774754f412b6e -dist/2025-09-21/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=571a08bf8eaa522017b0aa67fb78b344590fde57ab3425575c01ceb3b258c557 -dist/2025-09-21/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=e50728c440ae8fc9d154a3893914126d4486ca7dd197891b78531f7d5d081211 -dist/2025-09-21/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=1f8570de307fbc59e962ef6b419009d57fb05b2e1d5fc9ade9d425e3c9977cfe -dist/2025-09-21/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=35db980cea1ba70003374a738f20af63d54796e9181b8cf0e9d0626e0935a9a2 -dist/2025-09-21/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=ba5a8487dbb60851fc927dc24ee58186aa6e74d42dbf5202df7981a456b5f8f7 -dist/2025-09-21/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=f763ae3e33f4785ff505eb068ed6515aff8ffcdb9895595d23c2cea519e26355 -dist/2025-09-21/rust-std-beta-armv7a-none-eabi.tar.gz=3cd90f1a7c14a732a951026458a976fd5e833f212c6f6433f8de348b7b742b9c -dist/2025-09-21/rust-std-beta-armv7a-none-eabi.tar.xz=237f44f2b9984743f71ef0cab8d693092748fd2da25cabd3468e3e45ca20e2bc -dist/2025-09-21/rust-std-beta-armv7r-none-eabi.tar.gz=ec38043fd7877d45331414d059d0198d055ab724e84c077ca75a2902afbb2d6b -dist/2025-09-21/rust-std-beta-armv7r-none-eabi.tar.xz=f511909286f3e1cb52f0e698722bec1a3cfb750e19bb2fa781bfff225620ca8c -dist/2025-09-21/rust-std-beta-armv7r-none-eabihf.tar.gz=556365cb3ed473222e1b135be77086214f3f94f863817de4a87ee7d75456b824 -dist/2025-09-21/rust-std-beta-armv7r-none-eabihf.tar.xz=8483d2550782e253cdace51fe24249fbd6bd0b10a850c75e62dc60f803be17b0 -dist/2025-09-21/rust-std-beta-i586-unknown-linux-gnu.tar.gz=0ed5faa9e8e73f4e7b9e75741d000954558bafeaf776a6e61a4e44ac120b91e9 -dist/2025-09-21/rust-std-beta-i586-unknown-linux-gnu.tar.xz=665d5a0debd829f3682572e4c3578d41bec58b01df10cc8c71ca66d326a3579f -dist/2025-09-21/rust-std-beta-i586-unknown-linux-musl.tar.gz=c59dcbce2435a5826161d4319dcf84e128f9fa4c0bf075fab2a26c2bfb5d9887 -dist/2025-09-21/rust-std-beta-i586-unknown-linux-musl.tar.xz=13a2292936e289941be4a02903051eadb076bb44368494d530cf66832978f46f -dist/2025-09-21/rust-std-beta-i686-linux-android.tar.gz=5d23e660218e04a7dc4aaf940959619ec9aa14bf5574a554c0d8f377910ed017 -dist/2025-09-21/rust-std-beta-i686-linux-android.tar.xz=cffd08cc85df3cc661d8a572e940316da06754b61893efcd9ad3b7db09a0e6ee -dist/2025-09-21/rust-std-beta-i686-pc-windows-gnu.tar.gz=91743434207475f4404707cf7a203b46f032a041184a729ddcaeca280b2fac05 -dist/2025-09-21/rust-std-beta-i686-pc-windows-gnu.tar.xz=1f0240a71bf5a3bd74e1ae960c1aae440c3b3e32e6c62835287f78cc777f0d7f -dist/2025-09-21/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=ec5907cfb6faafcc20f3d7cdb22fd7836c9c2d7cb4871c48e64732bb7f5dcba5 -dist/2025-09-21/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=e7e8453b9dafc3c8c222455f5327fc8cde127f8dc877991688afd3c2f23675f5 -dist/2025-09-21/rust-std-beta-i686-pc-windows-msvc.tar.gz=50dd40b16e8ea85fd7ca67583d5cae80910d36531a7babe13a94cb638015a1d3 -dist/2025-09-21/rust-std-beta-i686-pc-windows-msvc.tar.xz=dafeb013333acc8a3a4181358584851b47c5f21138d8164ccfd6863b171309ba -dist/2025-09-21/rust-std-beta-i686-unknown-freebsd.tar.gz=91d741bfd158f22f4dea8bf768c5fb60ca05f5dc64cd5a848428b8dfe8beccbf -dist/2025-09-21/rust-std-beta-i686-unknown-freebsd.tar.xz=12ddbbb201a973148979a99ccbac3c65690010dd2f6984fa390fe5e63a28dbda -dist/2025-09-21/rust-std-beta-i686-unknown-linux-gnu.tar.gz=975e30f37f03afb47777a38edcd535df6729311cc0acb587d417ebff694df796 -dist/2025-09-21/rust-std-beta-i686-unknown-linux-gnu.tar.xz=bc95dd6129e90c9275e0340962993de7a0842040bdfcde9aa419f227d79dbf31 -dist/2025-09-21/rust-std-beta-i686-unknown-linux-musl.tar.gz=1c937cce8b40567851578790512fe079c0aa828374a3bb76423d685357388576 -dist/2025-09-21/rust-std-beta-i686-unknown-linux-musl.tar.xz=b42d227d63f0b3352d4d66f1198294c2f4df574c48fff794ac3483cef869c2bf -dist/2025-09-21/rust-std-beta-i686-unknown-uefi.tar.gz=0e8c239ce3b8701c4a26b46aca9a700083667ffc3228d796ba0ba6d0728c6826 -dist/2025-09-21/rust-std-beta-i686-unknown-uefi.tar.xz=54cba2405dfa2a23164bb8e7de5e0d6a6a6523f36b0763f077d2bfec1f303576 -dist/2025-09-21/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=72db70ab9289bce8ace5da246432d2a00552b4cd9ebef7930b563e04d1cdebf1 -dist/2025-09-21/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=5b578548a62dfd3902920719acd17550f45d0e9106049cbdc1f36c8907a8291f -dist/2025-09-21/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=993fdc6d1894f823b3782fe180ac40a3ad7baba110f2eff68d9e38e8f79a95a4 -dist/2025-09-21/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=b5161bd0064bfb312cf156ec4689a3f5922c7df24468660e1046798c8984938c -dist/2025-09-21/rust-std-beta-loongarch64-unknown-none.tar.gz=670ef40754ac30a2edb65384de65f028a4f8e96dca49fd0bb5eb2d9d6020e906 -dist/2025-09-21/rust-std-beta-loongarch64-unknown-none.tar.xz=b9aa6311d6a3e428f151fc6720147ea8759092545b05cad3f16b6e563d523813 -dist/2025-09-21/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=5c4c2e6bdc6d3c79578a3fd581064ba6aeb21fd9311a39a62bf58b36e7ea00cc -dist/2025-09-21/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=8e272682e98ff8b139da621d7273cf71efa407538ea176d873a1ea7e22246ebd -dist/2025-09-21/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=0781275b356176417c21c1bd1a4068fe2a42dc6de9b34695c937d5ba94b98ad6 -dist/2025-09-21/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=8747e3e686fbf41389a8ad958596577670f0626a610d380b0a775e704bc6c6be -dist/2025-09-21/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=95e8443355571edf645d75d31a33277f0d6f7161f8592ec213a407bc4839819c -dist/2025-09-21/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=4abcda8b23d17f6e6681329b54215f37cca5fc1f3383e58dd60c38220f5528de -dist/2025-09-21/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=a24d3b7db647c114c95f7da40ca2001085d935ebffcc17e269af5be636ec1f2a -dist/2025-09-21/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=7579f587df01fb55211e6a0a61ed96f14955b7f56990e679715157b06b49fe79 -dist/2025-09-21/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=ea071da90747334a786f1e4784e39c11058d7f7719e498a8b6ae29672a999abb -dist/2025-09-21/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=d612ed01011c1e7f24b08318029523f6b7ffb12ec38b1f41ebcedf463f924430 -dist/2025-09-21/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=722b93d6c5e1f9a326461bb920fafef62cc8257ff67732c3f65ecc540782a504 -dist/2025-09-21/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=5b5010f550b317facbd599fa363d633e0540836145717f35ffa0bff14ec80558 -dist/2025-09-21/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=7dea77dc10830754d5aa9a6e5ae3272e4955cab8df1e20f0784901ca6a60c49d -dist/2025-09-21/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=f212b4afaaa954809af920d3fb3de76a611d387910e6162b902fad8f38f36c49 -dist/2025-09-21/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=3f81a7134f44a87b7724a510e4cd4209ab52fb03fee3dc051c26bc0612e4b1af -dist/2025-09-21/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=cd9db852c6f7e454e94161379c032e3ccabfcdaeddd74e8f612870ef39eb230f -dist/2025-09-21/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=6da7d7c9cdc05fc3b86930d98fe9828ecef02b5b3cead51252fe9f131ab5f9e2 -dist/2025-09-21/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=97ad71f7f63f2c3b01ba822164df457d88331880bd21837a18354fffd1b38918 -dist/2025-09-21/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=9a5d94e1a77159a4bbf4fe7490019fff763daeb24dc2b8c732442275619f9ffd -dist/2025-09-21/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=dc5826fef922a6987650b491957b17693c49d1ab26b618efacbb1bb0b5a9b1bc -dist/2025-09-21/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=1b9c1cc0648fc86fdaaf23e6793fa826f3639bab9d42e1bbe2c70f19cecc11a8 -dist/2025-09-21/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=90cb9c376894a122f3872a77a653e3decf95f1eef54ba7980846165e6f34377f -dist/2025-09-21/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=22069b14b3eab5f3bd24a0f10185a5484022ac60fb7b2b5cb0019281bee79a4d -dist/2025-09-21/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=395f2975bc86a63736ba7661985db038efa5f5982459add18201c97e4b1a9200 -dist/2025-09-21/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=7932c8dbc9727a85dbf2ad28066cef1da46cf0ced358aea0e78a254fc1e423f9 -dist/2025-09-21/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=69b8ce57a0c459ca57353cd8302deba6791a19dcf54e16b8d07f76b44e3c65fa -dist/2025-09-21/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=27717f0b8b51f90c7e1579a2e3fa781f2a19064872133a951e60200c05db1df8 -dist/2025-09-21/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=d6cccdb4ca0856ce1d314c03779c082ee0dff153aa6bf9ea050ca3d0a395dc1c -dist/2025-09-21/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=9fd58b7c529d530e8b894a24e7f3a33d291d7305357c7cf52bbe708cde28c381 -dist/2025-09-21/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=f62de7e74f558a27bc2ef04897ad2f4fdfc162a17f21fde8efb2ba15435d80f2 -dist/2025-09-21/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=3e97b06bc72aa51aafd2d2f65b4c4d9ab08599c2616729b79dbd9c51886ab6f4 -dist/2025-09-21/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=8f149e17d654de210a71ace9db03f23bd1a80d0e2c17f8336da2b1ec2315c8a0 -dist/2025-09-21/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=ae6f3a738f1793fb9659e7613811b2ac151e91e3d8e470166b6ae615e5a285b2 -dist/2025-09-21/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=92af1bc3beaf80a763aac682a15957d82771fc619d446fb4327f4e8be229438d -dist/2025-09-21/rust-std-beta-sparcv9-sun-solaris.tar.gz=b1f5ef77e28e9ed25050b130299a1c431a851df8b11bd457393fd464a7a9c35a -dist/2025-09-21/rust-std-beta-sparcv9-sun-solaris.tar.xz=e5d744447649c57f8238c7d020f405559185d644b9739cae53c6963cdb380ea1 -dist/2025-09-21/rust-std-beta-thumbv6m-none-eabi.tar.gz=964c824519d8f352ea049766c428e6409549f7a4921c50f91dc548f2ec7f65f0 -dist/2025-09-21/rust-std-beta-thumbv6m-none-eabi.tar.xz=2f3b6d4781b21902e5f6986b75f3a0617198bad4741d4a9b957ab5ae2beab05d -dist/2025-09-21/rust-std-beta-thumbv7em-none-eabi.tar.gz=7a95faa851ac8dff7f57cfa42169018b29683fbe06dbcf29e2cb311a0c880e84 -dist/2025-09-21/rust-std-beta-thumbv7em-none-eabi.tar.xz=deb1eafb4cdad0391bad8dd0657577d4c0960fb7fad7b7552ef1e662c4c1f12a -dist/2025-09-21/rust-std-beta-thumbv7em-none-eabihf.tar.gz=1b35c7e25986065e63dfe8e8a1824bf13b62daa5777f32b140f03d0f4fe5cd1e -dist/2025-09-21/rust-std-beta-thumbv7em-none-eabihf.tar.xz=335b28756025f8454ae7c6ef6760a512f0b59385cdeacc7dca0ea1bfe5e9a703 -dist/2025-09-21/rust-std-beta-thumbv7m-none-eabi.tar.gz=2291b4c7f27fa0408f394c48390cfd6c7144db5cc4e51c8891c3bb24300b8421 -dist/2025-09-21/rust-std-beta-thumbv7m-none-eabi.tar.xz=16b6104ae79bc0f3fa6d862c72cb3f35a9f67bbea7f9aee7b2a3b0c810225c6b -dist/2025-09-21/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=6a7f167dd4c457d6185ee47dc206d19d6ca93e3e0418b21c0745d84c53995e64 -dist/2025-09-21/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=4fb366719cdadec26e88d64154b2b1b459affe5b894b426a0509681d173cf823 -dist/2025-09-21/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=3ff578be0c8b1171c5c2d0aaa3f4fc20f3a252f5adf050bd5856b201cc22841f -dist/2025-09-21/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=16c518c3daf87722a5e2556e92e97d429a06b2ed2c79380989db04ffa4791279 -dist/2025-09-21/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=b1d67e62ac198fcff25c29e731f2dca9eba3fbb09adb29db68d823b0ad63e85b -dist/2025-09-21/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=ac586b0a3cd91eb2928861ded895b96a85880851df2f3e63c2391cb38d98d140 -dist/2025-09-21/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=93cfb0ceb07879366ecb4e00caf5b4459574852943363b0d6fd3293c4a0c27eb -dist/2025-09-21/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=879724fc40ca55193760b3739387dc237587e91c30e334709d5453e07840d4d0 -dist/2025-09-21/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=7eb1217837173f0974f7a0fc69b0e9fea484f2d457f3b193ca3b2c04ed83bcd9 -dist/2025-09-21/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=c38af1e6560589be7a0733508b800e68bb5b57f2ec3c5452fb14000cf9ef2fa0 -dist/2025-09-21/rust-std-beta-wasm32-unknown-emscripten.tar.gz=9475cb292c64491c545a02df4deae77d4174d77db18a84c8db635ae6de691b8e -dist/2025-09-21/rust-std-beta-wasm32-unknown-emscripten.tar.xz=6e477e9ea0e5bac0b567deacfba3c236ceda28fab4d63c011d6bc54ac22c8570 -dist/2025-09-21/rust-std-beta-wasm32-unknown-unknown.tar.gz=eb7bf16e819eabe3f685bb8dd09bfff31d35d87cf03535195c411ec1738b6647 -dist/2025-09-21/rust-std-beta-wasm32-unknown-unknown.tar.xz=05a2bc1539b02ef314b268fc2860836c111705b872d5d56ba6ea511cb47e7169 -dist/2025-09-21/rust-std-beta-wasm32-wasip1.tar.gz=aa34f89676c72a3ce5df82cd819466631ed91896dd7a1b64fb4ca9a97595e254 -dist/2025-09-21/rust-std-beta-wasm32-wasip1.tar.xz=ad5756f4ce3e0309d04746609abdee2152fae66383b2b13d338c900b8f787060 -dist/2025-09-21/rust-std-beta-wasm32-wasip1-threads.tar.gz=36c42b952305d381718c36f34c4d5c1705aec71f946eee56c685eae56f9c40d1 -dist/2025-09-21/rust-std-beta-wasm32-wasip1-threads.tar.xz=c0285e26be272e3e832a74f22960899ac0f350dc6764701df748541ddbf69377 -dist/2025-09-21/rust-std-beta-wasm32-wasip2.tar.gz=198d4cb5195fa1e992cec8bf84716eed1ade0e9a8cc3981f3fb3cb9971e2796d -dist/2025-09-21/rust-std-beta-wasm32-wasip2.tar.xz=0fd2cd8923741931aa17d1571a5f8c20c9b0e96d74dc75ab47cd9245586bfa03 -dist/2025-09-21/rust-std-beta-wasm32v1-none.tar.gz=cef69dbdfbd0352bf781c1e59129c29c17a6c1367aa00184be309c56f8f29dfe -dist/2025-09-21/rust-std-beta-wasm32v1-none.tar.xz=a412840ff9550e447a2608a9c26ec02e969b2579bfe5c635a3af0cccd011922f -dist/2025-09-21/rust-std-beta-x86_64-apple-darwin.tar.gz=290fefcf45ff24a79459c44523bfbbeeaf9eb9bf3e7e64fcab64368fe21ed2d7 -dist/2025-09-21/rust-std-beta-x86_64-apple-darwin.tar.xz=176634d6797df21873c317b93cecfc32f415b3248139a32bfdbee83607e734c1 -dist/2025-09-21/rust-std-beta-x86_64-apple-ios.tar.gz=639916204bcc229bd5d5fd1ccb455d9a962a11d05388252c1e5e310d424f1ef6 -dist/2025-09-21/rust-std-beta-x86_64-apple-ios.tar.xz=c0c597f428fdc8f2f89e26c0e5d9debef45ec449b869ea0a738102a8727e8da4 -dist/2025-09-21/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=debfb6dfe448e345cc934e5a0d09715ca899ed9593c26eab07c58c41683113f4 -dist/2025-09-21/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=a9b6b2de9e26182f5a37a8ff56487916379809b2afe9e14d34ee55f98d526267 -dist/2025-09-21/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=2517ded939281e8722e5ca6d2cdff6a78a4fa39b5828a3048d9f25a3ec40bbea -dist/2025-09-21/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=7e8efa9cb373f580c46fa348b1f76acb46456c71fb6afea2b22d5a16b90ce28a -dist/2025-09-21/rust-std-beta-x86_64-linux-android.tar.gz=2bb9470fe62c5c1e1d31f63544b2bedb833c07c5305448e46283b48d8a575d65 -dist/2025-09-21/rust-std-beta-x86_64-linux-android.tar.xz=66a024fd9bda49ff4db5d70a2dc094708ef73c027ad0aa7dcbd7cea8449b151f -dist/2025-09-21/rust-std-beta-x86_64-pc-solaris.tar.gz=104b17a08a01593195921a56153a2b54782640f9dbf9e59c7da9f29afe3fe4aa -dist/2025-09-21/rust-std-beta-x86_64-pc-solaris.tar.xz=c3950a3a8bdd1326ab7d0ac08dc2a4f5c354e9ef6447324145cbe9fdef54f026 -dist/2025-09-21/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=d1d998991c9c8107f919c851d327d730beb6d4f4937a9f8dd2de2fbade1c1dd6 -dist/2025-09-21/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=654322ad813f9414e7ba2c5c5cb141db234d73b9ad237595d844dad564917a98 -dist/2025-09-21/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=6b9323f0bc1055dbf3e5fb4ec5fa09f28b7a0cd04ee8bb40e727d85d1a5225b5 -dist/2025-09-21/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=3ea958ef88fc3334e98556fd3bcc00264d9dd75cccf6f19f6f5514ec447d0557 -dist/2025-09-21/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=ddfbb760544eb8a7562cc8fab7cf313d45f490dacde3575329f627546971db0b -dist/2025-09-21/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=25f8e1279fc8647e117c6f3dbf3f4059e7ddc058cf6e02b43f499a72bee6ebbe -dist/2025-09-21/rust-std-beta-x86_64-unknown-freebsd.tar.gz=2941d17a2370ecab1e839236ba092c065cfa1b94e448a77a5851dab9ec2f1a59 -dist/2025-09-21/rust-std-beta-x86_64-unknown-freebsd.tar.xz=ff2aae7c2e37e48f500df5876c3a26d3dd10affd04e888ce54a4635a5345efa6 -dist/2025-09-21/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=1cdbbbdf1aa9c6e764493576adbd962e004ff029b064089be35910768f409579 -dist/2025-09-21/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=8196d32e28630a3ccc1dc96d9abb3efb5f2090b7bdce9963b2579995f575435c -dist/2025-09-21/rust-std-beta-x86_64-unknown-illumos.tar.gz=bbe4419e2d9f5bee75f6c1f7b0cf272100e3a37aebc28bc626820c886fabec47 -dist/2025-09-21/rust-std-beta-x86_64-unknown-illumos.tar.xz=2fc8f8ccd022152a87a447079169340218d7541b3513eed36cf7af20d5f565ce -dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=222198fa6b782010beac1710693ee1aeac1ad7eb9ac183625128de788a1a4bfd -dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=b60da22feb82c21128a151013c690cdef1c291de33e1b6ada5dcc95d3bff3899 -dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=31ab3940e428fe58ac584c33072be16d31edb0c16df379d9847cb904947126cc -dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=8304e2e4440e0a91b05bfe58bd44e7087c28c2682a1a5f5b659e2aba708463fb -dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=c15ecaa46a814cfd5fa27b29aed9e0e578a652b8f6392b916341d30172da7ede -dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=08a84716ed6bc70a58841c5d61216a781b8a947bbb5fb5ebde757e537a2e5dd3 -dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=5f1a8ed2093099b18cc83eddb304234f201f8ab137ae950c73329156570ba975 -dist/2025-09-21/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=ebcd581394fd243eac3d683e334d73ef3d3bbaf7de28bd4082329683e2c770c1 -dist/2025-09-21/rust-std-beta-x86_64-unknown-netbsd.tar.gz=430f4b7f7eceb5e633bccafa9acf08095c1aa4b3dfaa94734fcd331b3d69ca44 -dist/2025-09-21/rust-std-beta-x86_64-unknown-netbsd.tar.xz=2e403587de5c02ba9c5f9f2515d4c9fdffde59cec28c8dcafdfe40d03e4f3152 -dist/2025-09-21/rust-std-beta-x86_64-unknown-none.tar.gz=84d695e6f19706fdd7c01dbfc4607f310e8495f57c29bad2476e00c7bb269646 -dist/2025-09-21/rust-std-beta-x86_64-unknown-none.tar.xz=6e12698afd8a6743a9a6a011ad67ab16d5a40b6dbf1d09104b8294ea95fc2636 -dist/2025-09-21/rust-std-beta-x86_64-unknown-redox.tar.gz=cadafa58684734fc43417742d9151aea36b62f82aa3cd7b858140ce31e9a6ce6 -dist/2025-09-21/rust-std-beta-x86_64-unknown-redox.tar.xz=f1089cab004cb67134bbac6d8acb09b4dd5e02010e069790e13970b004ca4ab5 -dist/2025-09-21/rust-std-beta-x86_64-unknown-uefi.tar.gz=2dbc6eec98b7d730fe2ba982d78f7331346e9018146597200340256d28c0aaf2 -dist/2025-09-21/rust-std-beta-x86_64-unknown-uefi.tar.xz=8a5896f3301a6238984114cf52f7f234bdcb712cb6d914093159ecc82904ba7e -dist/2025-09-21/cargo-beta-aarch64-apple-darwin.tar.gz=4c6172e8523576deaa6c83274dbd993338d545a794e42aca3c074450d7e7cea0 -dist/2025-09-21/cargo-beta-aarch64-apple-darwin.tar.xz=5e8978daaaed1304e94c071ab5414ce90eb9c7bd1c4f1c8c5f4ff515f6558851 -dist/2025-09-21/cargo-beta-aarch64-pc-windows-gnullvm.tar.gz=49cba73291916ddf2e4912d4ea02add165f2786ad7f7b8885628d92579cbebd8 -dist/2025-09-21/cargo-beta-aarch64-pc-windows-gnullvm.tar.xz=d60a0a176b7d15606f6ee31b67d4a5ac6735e5a0b012022e9212fe723bddec48 -dist/2025-09-21/cargo-beta-aarch64-pc-windows-msvc.tar.gz=dfd4aa83d38a6236789676ef02c81382f0741671ed9a973cd74d37c65b3f111a -dist/2025-09-21/cargo-beta-aarch64-pc-windows-msvc.tar.xz=8ab6cd565993b58c6e2169bfb468c441dd385c5336081c45f6a60608522ce549 -dist/2025-09-21/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=eb361e2d12c90c9380112ef48b81db1b41f04b4ae08cd061fe1caa46cca9ce6b -dist/2025-09-21/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=e9f4c66995b8e955e86a67c44fd8d7f6e7349645393bfa605c6c1bb0afc7b930 -dist/2025-09-21/cargo-beta-aarch64-unknown-linux-musl.tar.gz=dc88806e5ac4004a9a3cb24f0c850fde2c22b0e38e6ad84bd570069043485bfc -dist/2025-09-21/cargo-beta-aarch64-unknown-linux-musl.tar.xz=e103f1d074ab105d03a88066363d2b103508ec95c18cbf8b1f92d0f473ddbf40 -dist/2025-09-21/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=e35bcf36bd7578cdbccb60d554feb19f8376fd41850e4e8046e0b2f931040c01 -dist/2025-09-21/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=ff1b46781284948aaf8c8f582203877ffda5a78d86c266bf724fbb08503a6e80 -dist/2025-09-21/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=dd657b5eb50264c90fafbd967b20768d9e4df14ef179902420b3f9a3e2145271 -dist/2025-09-21/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=3f821c437963aec534cdbd686f719eb86bfe41cf254ed5395730f7827d45a68a -dist/2025-09-21/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=7874b945f3d77e2a8ca308e5400a2411ab4f615f45a036bd9fab8a74434c309d -dist/2025-09-21/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=39523f09c76473d10b91ee946523976e01dc337d2af067f08168f1d9cb44226a -dist/2025-09-21/cargo-beta-i686-pc-windows-gnu.tar.gz=43b095acb25cf5c0dbfffc6fbc864c2b2415251931b149b282d5e70844fc2c50 -dist/2025-09-21/cargo-beta-i686-pc-windows-gnu.tar.xz=6cac1a1a6d74765f4233908920d295761570ddcd8cf3638bbc8f8eb427084b92 -dist/2025-09-21/cargo-beta-i686-pc-windows-msvc.tar.gz=7f4314596e6ea01a35b9e2e250227a74b5d4bd772ac8d33d12bd44f8c11b37e5 -dist/2025-09-21/cargo-beta-i686-pc-windows-msvc.tar.xz=eeaca23bf3cafbd01cdcef890d02ecd622d3ccfd6d9830f1e599d29acfa371bb -dist/2025-09-21/cargo-beta-i686-unknown-linux-gnu.tar.gz=e79e3a25bb790c5f6ed9e81a0559a55750a1a3e35250f0fc5fd92c195625aa28 -dist/2025-09-21/cargo-beta-i686-unknown-linux-gnu.tar.xz=1fe31a0e463736a9ae90ef11c1e3c7b7972eb82779ecdf5b6bff1f643684a014 -dist/2025-09-21/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=917db09ef343b6702c1410c2c68070c4bcfd90f6951591490a6a237290a4aed3 -dist/2025-09-21/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=222010f8428a3d165801d95a820b639ac930747af3cb4a42a25548330585f73e -dist/2025-09-21/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=7e949bc169a58e450e58088fd716aac9a05f5fca0790d94dd211ce823c2c5d36 -dist/2025-09-21/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=dfeb99ac76e18160aee7ff1c878b44b9fdb725f7be28609e637bd372aab448a3 -dist/2025-09-21/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=523103d950944aed52578002dd372207b3bb38e4130b4b11097b03f7d55345c9 -dist/2025-09-21/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=db5c4ce1a1a4d87cca4fb64a6c533cc5ab1c94e25e69b26e13808b0fa5e853e9 -dist/2025-09-21/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=68479082544f7f68a2fe073ed3d35e1895643f8ab9abe9d0e968efa9f342de36 -dist/2025-09-21/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=8878cf473faf120efb80bac0564b193f3baa14a9027fb4c060574e6fc921edcc -dist/2025-09-21/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=eb37177cdbc9e2b7f9b74856b351bb764e5c2603366fd92a5c863cbad26e6940 -dist/2025-09-21/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=92734c444e0156e16c8d8998a8432b24d9d01b82da15123508d0002eb008b9bb -dist/2025-09-21/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=502d5d2ec61d9fcd5b92caa0b4f0aaa11f27fccb7ec4736e05beca313f306585 -dist/2025-09-21/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=e00bc0ef1784b2f7b1fdbb757cd50342cacc49f7f5d2d3f7b36f9f4eca23882c -dist/2025-09-21/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=3e00c4c0d64977ddd2fcece9407a01f92ec9b44ea37d72ebbdb77cf0c532163c -dist/2025-09-21/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=3be5932af030c758a84bc09cbb1c2bc5abecc4e7d8a82de58c2a069ad36e737e -dist/2025-09-21/cargo-beta-s390x-unknown-linux-gnu.tar.gz=aefcadb257bbcf93dda58526390962316b4e579707c043c45e52bfd4d7a097dc -dist/2025-09-21/cargo-beta-s390x-unknown-linux-gnu.tar.xz=3d163f9fdc2b8b0f5cbbf846caf1dccaee07984d8783250e8988ef447e53663d -dist/2025-09-21/cargo-beta-sparcv9-sun-solaris.tar.gz=bc1692d8d75654012a823adb40b87d3b5721b19beb49a30b404a6c78f431c944 -dist/2025-09-21/cargo-beta-sparcv9-sun-solaris.tar.xz=ff7f36d7832b094b9ca2132df4851cf0ca50c9fc2de3d55bb6c75b46dd028f10 -dist/2025-09-21/cargo-beta-x86_64-apple-darwin.tar.gz=1a67e618eeadf362e868bf2cb35c1a312db83d1a59cee38f61794e45cba3ba4e -dist/2025-09-21/cargo-beta-x86_64-apple-darwin.tar.xz=28c0ae4f78f37abe27a3db5e5fb8c78c51a98b71cd0c4c69f9256b5d4064e78d -dist/2025-09-21/cargo-beta-x86_64-pc-solaris.tar.gz=c2da94328a164d889ebbbcd5f403068126e8f28ebc0c4ff7bf5cde1e8cc380b4 -dist/2025-09-21/cargo-beta-x86_64-pc-solaris.tar.xz=4fb30f600f8a10f43bfbf4361fbc7e906217007d46d65731b1bae0007eaca783 -dist/2025-09-21/cargo-beta-x86_64-pc-windows-gnu.tar.gz=86e23a551906f961a8a05b50185185de683f824a69bc739c3786e4f2004d83f8 -dist/2025-09-21/cargo-beta-x86_64-pc-windows-gnu.tar.xz=860562d5c50c60233d088886dd22b23c0c40504107b04cdfb51506c631d948ba -dist/2025-09-21/cargo-beta-x86_64-pc-windows-gnullvm.tar.gz=b568148f13e609e6cbb7e2b424c13a8b85126c8ef84f3b884043aab204352615 -dist/2025-09-21/cargo-beta-x86_64-pc-windows-gnullvm.tar.xz=7983768e6c77334eedd563cea4cd51cbf85d5234d1952801783016f07f1d6ce7 -dist/2025-09-21/cargo-beta-x86_64-pc-windows-msvc.tar.gz=839f7866c75750a5fdc0e7b9fdf37e0b60e71be916b496a9be3ecedc87473c2c -dist/2025-09-21/cargo-beta-x86_64-pc-windows-msvc.tar.xz=5d0e3c8e9082a00be80cc3924e12b7d9d067f9ecfbe14dd1e1bfadff55d2bccd -dist/2025-09-21/cargo-beta-x86_64-unknown-freebsd.tar.gz=8c22ee4fb01955f20d04dba271b44e69718266d70610fbd979565d95df316e6b -dist/2025-09-21/cargo-beta-x86_64-unknown-freebsd.tar.xz=6356f4d133c3820736f82c4eb2857548b5255af4ead57f1f8e66ebc6aaa628ed -dist/2025-09-21/cargo-beta-x86_64-unknown-illumos.tar.gz=43523fa8da79aca1e5a618c10ea031404250cdf1a41b0da369ed6efd05c4190e -dist/2025-09-21/cargo-beta-x86_64-unknown-illumos.tar.xz=c9a1b43c762b3658b0fac5145c6314a1c9e416d025ac22958fc0809fbb24d1e0 -dist/2025-09-21/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=ba780983f067e7dbcce49dd4d39a0d3c0002dbe7dba73eb2a98d7eae17f70931 -dist/2025-09-21/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=07aa13a5411a49238b31191a0797d63b74120a1fa9b5658a67f6c1065271c30c -dist/2025-09-21/cargo-beta-x86_64-unknown-linux-musl.tar.gz=96e6367138d6ff9ae2ca4343f3d5277b5fce39fe6909cfccdd57f1867eb9b021 -dist/2025-09-21/cargo-beta-x86_64-unknown-linux-musl.tar.xz=83e6fb5196805c9bdfca4e80e76e185a885da0820108e98e1fc7ef4aeea7f1e5 -dist/2025-09-21/cargo-beta-x86_64-unknown-netbsd.tar.gz=422fdb2cc97767f235d6abb29dbb0e802b320e11c743f794f8ad13160e4c7c7c -dist/2025-09-21/cargo-beta-x86_64-unknown-netbsd.tar.xz=75c4aee9a720fa55ac5e80c58a890efbf88c57fbd2c57043b9f29bdbd6ae0e3b -dist/2025-09-21/clippy-beta-aarch64-apple-darwin.tar.gz=de39b5014bffa7e20ae1f981616664703828428b6b1a74a6fee80fbab446e74e -dist/2025-09-21/clippy-beta-aarch64-apple-darwin.tar.xz=d5ad3181f6978604f725db8607daf39ee20cbbb6ade35bb50ae7b032b0b62e9f -dist/2025-09-21/clippy-beta-aarch64-pc-windows-gnullvm.tar.gz=a4f5538776b2f1f31bef81f37615d9bc3495080174fe83be0c549508923c9e9b -dist/2025-09-21/clippy-beta-aarch64-pc-windows-gnullvm.tar.xz=a78a56cf381483703f120c596d6921b04aface91847310e20da53aa887a2e603 -dist/2025-09-21/clippy-beta-aarch64-pc-windows-msvc.tar.gz=06a6ee3aa204812322d0b9946ea31dbc5045e59253891fea7e079d4c7e1de894 -dist/2025-09-21/clippy-beta-aarch64-pc-windows-msvc.tar.xz=a4f0add69dad90f0dd7c47966b12f6cb7a4c6e34cc1b44e4a816d359659ae012 -dist/2025-09-21/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=9d88fade821957052581f65765ae84286eee07e0985504d5a7324f615649a506 -dist/2025-09-21/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=0c278a9aaa1ae41bd9bd96f52fed50b1a11a65822024fd01a9eacfa3aa8f1de9 -dist/2025-09-21/clippy-beta-aarch64-unknown-linux-musl.tar.gz=7d04aeb77402ca2ad964b6430ad75d0ec08a68efb505573f5e134664a5aae044 -dist/2025-09-21/clippy-beta-aarch64-unknown-linux-musl.tar.xz=d28207a804219edccb110160ffdf1c1525248ac225df89f4d11e3538a5dd0dcb -dist/2025-09-21/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=2433be238da05b6dbf44a74537e48a1dcd96fc03a8059ab78e553833546f1b97 -dist/2025-09-21/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=74d5920785504fbc0c1e0237a4ee4e8355ffeba2c4bd9471c38d44e3ae52ef4d -dist/2025-09-21/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=f83867145c740302ad81912f8e39433aac19fa5312f14d35aee2b59638660299 -dist/2025-09-21/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=420c7b7b6cf54eb27fc3446223ab03a3f90628b47d6b4ae66e432380b57661ad -dist/2025-09-21/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=14eefaa624591a49d6d2a3af9663ea4f3aca804d3563f668c734d9e18cc0b39b -dist/2025-09-21/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=14e8371566643fdf146117d06b9fa77aa886360d3696d9e43482f288338822b6 -dist/2025-09-21/clippy-beta-i686-pc-windows-gnu.tar.gz=b7ceb33faebadc67294e1df3f08d8d9760a6a17ca1ad30f26da3c586487a14c6 -dist/2025-09-21/clippy-beta-i686-pc-windows-gnu.tar.xz=f0a3f41a65d90119a4c66c6a2007d1f1a75a24e86d9a572837c4410b02af426b -dist/2025-09-21/clippy-beta-i686-pc-windows-msvc.tar.gz=dbdf0cae38daed8bee11eb63d7c3f1c5d019777c238495149baa5ccb10af0f37 -dist/2025-09-21/clippy-beta-i686-pc-windows-msvc.tar.xz=adc49c09b72ff46d3d03f31c8c641675af389ba99c4c517149a10ae471c37c25 -dist/2025-09-21/clippy-beta-i686-unknown-linux-gnu.tar.gz=793ace0c8927a48caf443b794de097895f9e503299da07da13238a56ea8ac07e -dist/2025-09-21/clippy-beta-i686-unknown-linux-gnu.tar.xz=22b9b2b27d0b6b1fd88d67b18d34a4a91207e6b64ba8d47dbfd0c58763d429b3 -dist/2025-09-21/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=101437e0f1bdc8ca07455d92c87bc32914a5047f6c9d7b7ab9e34799c5d4a5a3 -dist/2025-09-21/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=afd8c55fa82482a852b564511c4fdddf12abbffc0bbee1b0b4155fd1d6c04105 -dist/2025-09-21/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=df33c329856ed057d069a479181b4fa97fd4a11d109abfa32d6b46c36215e6f3 -dist/2025-09-21/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=ca8451dfcb5b919c1a6510616c8e93dfb15914e689cb30f7debf4c1a4aef58fe -dist/2025-09-21/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=1b738f58256186f0b530375ea2da804aa1834a908412e56767c9a44b134cfd68 -dist/2025-09-21/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=93333f47a041d4ddea4fd9ad3fb3ab43c40fcee4fabe6405190fa26d6bfe3e2a -dist/2025-09-21/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=2f0da38cf8efcda85634249df5398bb99f3b34982fb4509a0a3171437d809ab0 -dist/2025-09-21/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=fb3d68e09e40cbf7d6c330c3866c37c759ed728c1d8cbeb6e8e834f6a1fce1c9 -dist/2025-09-21/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=7cb5fdfebbc0565e2d883da09815dfb626104afe39c01b169a919a82f62df607 -dist/2025-09-21/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=d6705b1e6722e3faf5b863fb319cd811fcb27f4a564e633f164f02f8699c9255 -dist/2025-09-21/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=ea33f22a67f7c8354e7421129bfcbfb4bce7d909fcfa6a64a3107d82be69d213 -dist/2025-09-21/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=081e303cf123ddc162633d4d1e3adef4e6fd39598f60ac9dd75c76230df39ddb -dist/2025-09-21/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=76ac5dc8b8284437e5fe81cb4978460a6aa5c4a857c4f14246dfabf1831998f4 -dist/2025-09-21/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=2ed67a738a07e9d07c1db7789cc5ebe7af033334670fcb1ce84441b9e474ec0c -dist/2025-09-21/clippy-beta-s390x-unknown-linux-gnu.tar.gz=d5a7e6cfdd099ed18e54d88dc1d740b90d1f7d2f22d1fe6ca7960b7319b0783a -dist/2025-09-21/clippy-beta-s390x-unknown-linux-gnu.tar.xz=78231b81d8e612a4c41828428ba2e9af926f2119308b291c8ce81a5233c3c6a6 -dist/2025-09-21/clippy-beta-sparcv9-sun-solaris.tar.gz=a1b0086259586a26f6ca65b02adea83b953989a508385d58fa56c7eafb770227 -dist/2025-09-21/clippy-beta-sparcv9-sun-solaris.tar.xz=b58151b098d58b19bc900f72813138799e2e568a5ad3038528045e5ac562606e -dist/2025-09-21/clippy-beta-x86_64-apple-darwin.tar.gz=62ecc253fa747ec67ae11c7a1672661cbac7d78c1001654e17ca5c0e3bd72d91 -dist/2025-09-21/clippy-beta-x86_64-apple-darwin.tar.xz=b7e9785d3ab00163a0070b7772a4354e9503cdb8456d1a2b0708920658aac614 -dist/2025-09-21/clippy-beta-x86_64-pc-solaris.tar.gz=783d47012b943cd4497c2e0e854cd7727b0957518178165cc1cbc4dc5e6509ff -dist/2025-09-21/clippy-beta-x86_64-pc-solaris.tar.xz=94efccbbe73b2f15f5f86c90324b3adbd1b58bbdb81ea9c32d7efaf067bc6795 -dist/2025-09-21/clippy-beta-x86_64-pc-windows-gnu.tar.gz=8b12cc5e7b9b7e0b234a29886c81455878e806067c025cf3d26eef4a52e08bc5 -dist/2025-09-21/clippy-beta-x86_64-pc-windows-gnu.tar.xz=35fc298fd25949b491c54bfa2f40c963d7ca530b65ac8e52031edf17624b3d05 -dist/2025-09-21/clippy-beta-x86_64-pc-windows-gnullvm.tar.gz=824e1590e12bcad69b43912068e27585466fcc5cf7a2f92f41f727aa39cbcaad -dist/2025-09-21/clippy-beta-x86_64-pc-windows-gnullvm.tar.xz=6fad67e180d0eb0d551b2101dc27bf6846ae2840c63d1ef05588691d055e3806 -dist/2025-09-21/clippy-beta-x86_64-pc-windows-msvc.tar.gz=1353d8c3310d53576d94aa744fe0844b5527d8b54fe43a692042be78b0fca6f5 -dist/2025-09-21/clippy-beta-x86_64-pc-windows-msvc.tar.xz=a7ca6fecd77dc44f3102abad7fbe1fa3846d9ff6ea98a25d4c3bd703800894d2 -dist/2025-09-21/clippy-beta-x86_64-unknown-freebsd.tar.gz=33b5f8dd6a0ef045ad19df4327259a468ece00b250d9fbfe1be7c0f293f874ce -dist/2025-09-21/clippy-beta-x86_64-unknown-freebsd.tar.xz=1bd56197e30fc325c7482aa7a42006a7ad9a0ffad9f3d74d209e98582d2897e4 -dist/2025-09-21/clippy-beta-x86_64-unknown-illumos.tar.gz=0ba3c497472c34de44bda2485d3b964cdab83e3700b44ffd8b41037ccf59a932 -dist/2025-09-21/clippy-beta-x86_64-unknown-illumos.tar.xz=040302a04decb3cfcd599b329db3f17e5f96b7aa4b8174d6f2b17ba19c991853 -dist/2025-09-21/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=21fde20675c5f786b5da4f1be39785d1106f748d88a6609fd4976bfe372e6817 -dist/2025-09-21/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=217255d6ea157f7b06aa66d033dca6239bbc296bc14ff3f0017d5c68bb4d1022 -dist/2025-09-21/clippy-beta-x86_64-unknown-linux-musl.tar.gz=7be4202a658df30aeba451e6dd4f740068dbcc769fe0eaa9a7eb8cb2c2e264ff -dist/2025-09-21/clippy-beta-x86_64-unknown-linux-musl.tar.xz=d2cf3cfa0c5c67d57867586709c274e320c1a76418ffe7dcf65b271448d4de06 -dist/2025-09-21/clippy-beta-x86_64-unknown-netbsd.tar.gz=91f510466f2a8606efc746a5be209a1f0ffe1e20b803f9c54ee91786053cabbc -dist/2025-09-21/clippy-beta-x86_64-unknown-netbsd.tar.xz=2c17d3a00885495f81cb8606ceb78674f63396b3c2a0b3415bb2e62ab39f9d87 -dist/2025-09-21/rust-beta-aarch64-pc-windows-msvc.msi=d5e39b0a1deaaeaf956e57da755e16255b265e80722428625783e7be0835cbb8 -dist/2025-09-21/rust-beta-i686-pc-windows-gnu.msi=edcb39b92d1e84c7d6b0d2559e37be673795a14e807e77e40b32dcaac8b9d415 -dist/2025-09-21/rust-beta-i686-pc-windows-msvc.msi=dac7d64336aa8fcc77761910392efc845aa2137fff8be8df980b02d48809bbd4 -dist/2025-09-21/rust-beta-x86_64-pc-windows-gnu.msi=44e1e8298714b11bc7cc44184f2b20aa39fbadc23f8b2b86005e74879b8430f8 -dist/2025-09-21/rust-beta-x86_64-pc-windows-msvc.msi=4c673f514c7f0f9bf780c2448fa4a4bbe4e4db618d6a9931bd092a6116d432fa -dist/2025-09-21/rust-beta-aarch64-apple-darwin.pkg=4a23353da7a58deac032341011c7bdb78f069ff4bda97d837c67e54454e6e1af -dist/2025-09-21/rust-beta-x86_64-apple-darwin.pkg=5e02da3f6ab8791426060ca40ac7c719451f6f5acba06ec27c273e6f2590cad6 -dist/2025-09-21/rustc-beta-src.tar.gz=22b0288ca9f949cac41260370afd4e6e487c1e3430f6aef23340b50ec4e4ea9b -dist/2025-09-21/rustc-beta-src.tar.xz=31f4b8b4b3471e7063da5038fe5072e44293705ec65b2c272f8d4cdd37875ff1 -dist/2025-09-27/rustfmt-nightly-aarch64-apple-darwin.tar.gz=78627de068d788f65482cdb2763b27fb7570a197b97056ad16f9f6117fccff8a -dist/2025-09-27/rustfmt-nightly-aarch64-apple-darwin.tar.xz=d6c4252e895d303337ce1c8edf2fcfd02078b81007e785ff7a15f773a1789e3e -dist/2025-09-27/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.gz=d65ef7c1348a74dc1b042c30281ec57c2619a25bdfd8151223415f9d6e067fc5 -dist/2025-09-27/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.xz=dcd986e9560c45eae6f1d0ee0bce9ad2365d101f4c9b792062557cb26a26152e -dist/2025-09-27/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=8b5a164ee78ee9bf76c1ac9d95f63743cc0b05cff9823d42b88d596ee34c9b52 -dist/2025-09-27/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=da675f08931285b2d59be0b8cda46f7489855ec9cc07a608d17e4c0f1e6de486 -dist/2025-09-27/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=7a1b11c66f3832e0ccd390441a921cd50a25ae87e641bb856966fd81cd3d5d59 -dist/2025-09-27/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=5f6aa12529624b66f1de643afe6805cf5484c57e3a7c791f85023d28b590dac2 -dist/2025-09-27/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=0cc213fabdad76e6ff699f2f0462c8b3dfe5bdc6b14131fc2c87d915a8fdabbb -dist/2025-09-27/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=ca84ce0de6d11b69ddc691f4edca1474e66b513f695fab738374942d57ab8b83 -dist/2025-09-27/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=dc0f391a0ac09a8ae2271443584dc8f1338bc0b89b50ee82d47599912fb74c52 -dist/2025-09-27/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=d4bebefbc157ecde2fbf7f7ef6a6d8c703d264f56e2ca8a80b7c241b8e14f862 -dist/2025-09-27/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=672fd91b880195a0fb2eb294129c0ec465aa3be217451fd4b835b2c3294d4c1b -dist/2025-09-27/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=4aa45c993b82f9d9f6b8bf79db2d04acb83cd70147c9ecb1804a3c8258a6c022 -dist/2025-09-27/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=3a8fef72bf471ea1c575c3a6d3a0ffb957fd862f55afb0d40b39c85ff7fc1f13 -dist/2025-09-27/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=08854b212790685caa928e37aa7fe50009590050873c390d2999d6b814bcd2bc -dist/2025-09-27/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=e99f0d4b314c59b7564e85be580477e751e46acf30752b970c36aa9719e10995 -dist/2025-09-27/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=ba3b9a0e0c44c6edc1396915034efe9e7f59e0724271fd6c1fd4805382e95677 -dist/2025-09-27/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=40f42081d1d2eec00bf49f62c12d75e5e5c345e2a4d8da4fa0741239aea72218 -dist/2025-09-27/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=1b9ef88c7ea98880835d8c298625e2bdd219af46eabb18b8c18c92882d81d054 -dist/2025-09-27/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=2ff88b8231c70044e9b35c3855515d143aac1b3d7a82bfc84833f76f45539c97 -dist/2025-09-27/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=e07bab9116c10576b7ab01e26af72bdc97bd34a56aa2468e188e58864b030c33 -dist/2025-09-27/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=766a69e69be097f710a7c175dbfa39b20970135a6fe420457191e095de5fab1e -dist/2025-09-27/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=ad4a38853cb9e6bb6029dbb2ffedf4b49dfc7cb696edbcb561b204bfa89fd8d8 -dist/2025-09-27/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=f08d5f5ac31fda285b81069709a74eb382450543c4d22289980a9ef94a473fac -dist/2025-09-27/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=40bb1e41db10d4c6b22e46c0f8b5fa1a6ad06cd5f3102c189705380383444323 -dist/2025-09-27/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=d3bf8c8c186c94a0190ae73386839e53dd6ea76cd81e9132438fb7f245d955c5 -dist/2025-09-27/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=665dce6b1a464e1969e3901d7bd293d35a85d5a50ad976600566dcc2a9c46b58 -dist/2025-09-27/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=a28c82ea8a7e2bbd61043e89994cf2be71ead745b3fa782d0653a99fd81bfa64 -dist/2025-09-27/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=3c5cc78a7e311f73c39030f42b8f1d3dd0e54e09f4d636be6a581a829f15483d -dist/2025-09-27/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=372c476dc902ffb7ebb8ab8934a89d1bbddf9df9c810bc6d90d3afab984b8205 -dist/2025-09-27/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=cf2e738c44ea95d71090bc3526d8c7c70e4554667449f4705614c93444e817a9 -dist/2025-09-27/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=47f215d0c639f0a4bb67423c65c5b87a06cbecd47ea53484b57c9b7d87c6791b -dist/2025-09-27/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=9085b66b2e8e3460f0993896ca3d684395001ab4ed37a16947ce1d15d5aa224b -dist/2025-09-27/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=0f07ac40b25eeef46a4f4a0d34cf50c9336407f2d7f23c05c47fe35f3a7a1d49 -dist/2025-09-27/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=ce08e9b33e75eb504f28ba23e1cc3003c0aa503fbdceb04271bd533613713160 -dist/2025-09-27/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=4e64fc0ec680a294308f897131f8ab185872dc68cd1312fbe1a306ed6e53ba26 -dist/2025-09-27/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=b46d423db54a90944276cee172b8cf0ea70562c01537c37c65f3ea17c13a47fe -dist/2025-09-27/rustfmt-nightly-sparcv9-sun-solaris.tar.gz=3a84501e05cc7430f91903dbb0de0946621d05c095459c47dde3cf7e662e771f -dist/2025-09-27/rustfmt-nightly-sparcv9-sun-solaris.tar.xz=0107d3c129e1a18a814d5c213b6445aa7ecb7dd95507641d2cb3d0c39293818c -dist/2025-09-27/rustfmt-nightly-x86_64-apple-darwin.tar.gz=c58e0a2db9b3933539a20614b143e6575f6aa1459ee35af4d67210dd572e6af0 -dist/2025-09-27/rustfmt-nightly-x86_64-apple-darwin.tar.xz=0cd4d7a8cfedc2787bacebbb2fa481d5efe3d56ba476ef8799c34325c40283e1 -dist/2025-09-27/rustfmt-nightly-x86_64-pc-solaris.tar.gz=ad3fdf81b7b00ee670b05ed2bdc05f79a9c066104d797dc7eaa4d767dfe2eeae -dist/2025-09-27/rustfmt-nightly-x86_64-pc-solaris.tar.xz=df79594ece4b8753d8215672004e9071a8c10c8ece8c86d1d3608c8d7c3f0486 -dist/2025-09-27/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=cb800c92a8f899d148adc96283818aa81c115b73555c047e07a67d738e9cd2c9 -dist/2025-09-27/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=09f6d95c49725c36bace10c8e119d6850dabee1dcdebac264074e296f9e8ab48 -dist/2025-09-27/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.gz=e061a3925b95a99dffb17d34c85803bbcac4604f95da2674872f0725d82cdda4 -dist/2025-09-27/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.xz=1090793fe09cd2ec4c54063600c1999f5e53a9ddc5c5d74e4f5e85dc6f2ef98f -dist/2025-09-27/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=f3978135d4a9bf2537625e38866fca74ca1f0655fc9fae736bf87d257d6cd0d5 -dist/2025-09-27/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=03814722fe9798b503ab7d8284c67e84cf18a9a2f179fe227e3313d0ae3e2cff -dist/2025-09-27/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=d5a477ce3f220016f097f8949fc2eb1c700c612e97105804156e82264e7ba787 -dist/2025-09-27/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=0fe90bad20ee599e4e57c46d4bf700c5775c484f0a8bfb2ce4957d2aa2df90cb -dist/2025-09-27/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=6d48ed9c944fb01655d4025c4aa3b719813cfef040fecff1f59b8b51a0b9510d -dist/2025-09-27/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=34d5a066b9f5bcef81b38badcc96f295150c2b2a96c35621235bdcc54ce92158 -dist/2025-09-27/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=61a6a8feaf0490e3db169e86e85989538bff994fb76481a81a1ae02222c7ba59 -dist/2025-09-27/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=cd94e20b4985964442b080454c2b628bcb435898e50bc2de55799cc51cd75f16 -dist/2025-09-27/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=c2912263d24904ee5b1014a98d5b349754a6fa1bd66498f607cc62ebcf903cc3 -dist/2025-09-27/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=8ad5b1284c91798a01fd25b3b690f88b55026e109471e759d4cecdefd1f83a39 -dist/2025-09-27/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=e0f52a6511c36c2ece175bc993861cffe0cc72a2e1b56b1def246e09f70d3a75 -dist/2025-09-27/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=229b92a2c0ef8ab1ac588858bb371ea0ed3449dec82a11ca7386df6efb2f65b7 -dist/2025-09-27/rustc-nightly-aarch64-apple-darwin.tar.gz=de7af74b8c91fb87b20df2d65b536fe6f49cc632b1f0c52a1e65a215fd5e4a06 -dist/2025-09-27/rustc-nightly-aarch64-apple-darwin.tar.xz=7a3e8c68f0bf4d393393628bd85d22242eee59605e3d56e0e94d06163ee2d4e9 -dist/2025-09-27/rustc-nightly-aarch64-pc-windows-gnullvm.tar.gz=2826132a82eb5adaabe2fdadc76ddc21460834365085ff2a113d934c11870a41 -dist/2025-09-27/rustc-nightly-aarch64-pc-windows-gnullvm.tar.xz=47980ea13cb887d85f8e501ca2b5d6e4b77ba8f229b2cfb9a1f28426c60d87a9 -dist/2025-09-27/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=10dc98065c0b19d737ea93506df1ac399c33190edb3f6bbc51d6c1697e910f8a -dist/2025-09-27/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=d791bf9c54ccdb02da34e408aa93e0680f19a3bfbed1e5dbd61b57f1e1f38fdd -dist/2025-09-27/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=ba6e33f6efa2f5a97790e29bb72c89bd460d758244dc9dfa4684e01bc75b6656 -dist/2025-09-27/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=4e38e862770ed0215720445e56fb027570e4f3c09d63a7f68cdacbff482b4cec -dist/2025-09-27/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=b126fbef234c0b67df42fb0568580b3d95ce98b7346095c3762214fcdece14a5 -dist/2025-09-27/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=07af694d0ab0b55b18bd437ec9edb965f451f1bbb8334e1667f87d1d8e8354b2 -dist/2025-09-27/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=6901b57b0fe7182a45b1934e1d7a006ba353daf114ea7601563caade4de1b2c2 -dist/2025-09-27/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=c1af7bcb75c1ce5975e606aacb2d3decaf7a8470cd347d4caf75f11f84d3122f -dist/2025-09-27/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=8f29e493bd15175ed2a72d50857cbcc07992194c0b38d2b0a4660217b04b8276 -dist/2025-09-27/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=fe478ade162b6b5d33808f4872de54e0b9dedd84e9e420480a370a2555d28cbc -dist/2025-09-27/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=f03a3368f41969d061a9ec2e87af512c346f9e82b6286eea65dbce33de90391e -dist/2025-09-27/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=c771ab34e5bab9344be3dab9315225296e41b3fa70cfe59fd3e0b287c4985fc2 -dist/2025-09-27/rustc-nightly-i686-pc-windows-gnu.tar.gz=9f5555633f1f1462285c0eb39aa42d0beb45cdb3b45c524483e4e4c6b76b6551 -dist/2025-09-27/rustc-nightly-i686-pc-windows-gnu.tar.xz=ae8c171fa20a49d7323bb5e6a36b262947caae260adb942204206aada00bcfaf -dist/2025-09-27/rustc-nightly-i686-pc-windows-msvc.tar.gz=2b50b6d9027d5b480dcd2693a551bf80db7d3dae802bfd9a825b68a50ab022a6 -dist/2025-09-27/rustc-nightly-i686-pc-windows-msvc.tar.xz=d4b396eb0256cd62718751f3a52498dba992ba063ed77e5d675da8dc06a6751e -dist/2025-09-27/rustc-nightly-i686-unknown-linux-gnu.tar.gz=f74acd9ecd35d10040e388d5224a9c88e66348dca09930d89068e87a0371a7d8 -dist/2025-09-27/rustc-nightly-i686-unknown-linux-gnu.tar.xz=92bb07e968cbbbfcf1bc7d0ecdd1a088b8c2975691bbf6ed846bc69708e34f13 -dist/2025-09-27/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=7b756904c495de2d37993d71fe1e70e182c232aa408296c6ba05f71a94423406 -dist/2025-09-27/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=522114675fb36949b953491d9a5fa0db39d22118f015f8ce92a120eab39244b0 -dist/2025-09-27/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=2309e49988ec8c35ef17f7293d6b2a787589eb38bba217a8f9429446713cc2a4 -dist/2025-09-27/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=820950e1cbfe6d973e1835532f9e201fe215d149bc415ac7ea011b16bf6b7bc8 -dist/2025-09-27/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=6fe053425d6b840c72352a88021c3b2b6deb389986575cb5e7b8c5991e86d039 -dist/2025-09-27/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=d62cad3e6a7dbab7cbefa493e78a0b7d7e8f724dcd766ae03b6715c325594fe5 -dist/2025-09-27/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=8bc6b3d521f5117bd3f9321d9d086e928fecf548be58edc71b257269e68ad21c -dist/2025-09-27/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=d08a0ed4adb7fdf451d39c1dd56171d6ce345b10cf905515c07ac5eb66f7d030 -dist/2025-09-27/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=02ac6a9c23c1dfaf12e26b466bb33057787c28f2bfe8503b998a5d5aa55a4370 -dist/2025-09-27/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=77709b47a9d99657e03c77f32183b2127e75488df59cd000ed20cad5868afd5d -dist/2025-09-27/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=b811bfae5380ffe89e2f48f6c0e6f293e8db33461a1fda94a85759d3464100c4 -dist/2025-09-27/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=bc41de8c0c65d912b5d6be06f3b12b3e4be1c20c1dc6ce1b7f5226e7d3ab3ae2 -dist/2025-09-27/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=110b42065218c2607b01edb83d41425176d7f065fac52c5836bed0d2215fc5b3 -dist/2025-09-27/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=97e3a41b0718ea14be4b7320aa4efc7f19b3feeabc7aa9079ce4ea487cad8064 -dist/2025-09-27/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=4e1fd4ed9df5ae921380e3396159053c87623a9ee1c7bcc1f897674c9165714d -dist/2025-09-27/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=1e2833f165f7b255731fb1d26bd6026f5b6152ed91ac70d8dceb4f692ea9a66f -dist/2025-09-27/rustc-nightly-sparcv9-sun-solaris.tar.gz=8113fa75d9ad92c411c71b6769f2af4450ed3ae285be1ebf10afe022abe52661 -dist/2025-09-27/rustc-nightly-sparcv9-sun-solaris.tar.xz=150128e8dde149bfbb2071cc933844ff87931cb856939db922eab98230ab7bb1 -dist/2025-09-27/rustc-nightly-x86_64-apple-darwin.tar.gz=727f7ae1f1e5fe51a3722105211cef3eb92f792cd054857ffef7bf858d0963cd -dist/2025-09-27/rustc-nightly-x86_64-apple-darwin.tar.xz=295672b0d6afb6e80f25dfd6d1643414f976eab6da00a5babf377ecede580e56 -dist/2025-09-27/rustc-nightly-x86_64-pc-solaris.tar.gz=3505cebc0659388e110d1e55a5eca94ac945d75b3320f16ed9ded08629a91638 -dist/2025-09-27/rustc-nightly-x86_64-pc-solaris.tar.xz=515d5a5046dd2c4b3ac2b21a6dd4bc834eba20d08b902ed396e0b62101978210 -dist/2025-09-27/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=838ce3f625b6dfb87f0271770515988d3b3f1535d75353b8f0f4a69074c1ceac -dist/2025-09-27/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=186eae6c01ecfc67facc96ac75d8518c31de1bf8897d82bc587941c3f686f4c3 -dist/2025-09-27/rustc-nightly-x86_64-pc-windows-gnullvm.tar.gz=76e5d092c78b663c2d75ee9d95f6c60d1ecb509b440312f4a8ad333d58de54b8 -dist/2025-09-27/rustc-nightly-x86_64-pc-windows-gnullvm.tar.xz=69ffcda8f985b3c5b78b18f0eea037890f2efc205f0b7cc4b788f1b35a3b7eb1 -dist/2025-09-27/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=2b08c563daa21d817bdac9c8dd81021a80967e7e671a312c2990575e3622b928 -dist/2025-09-27/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=2e54f6a6b6e096be1f717361d2e474b2ca94957ee006d5fa62910ff3d85cf05b -dist/2025-09-27/rustc-nightly-x86_64-unknown-freebsd.tar.gz=6e00949c5d3a2f0ba86f1d89f54278f09e58f043cfd00d1f5df984835228d28d -dist/2025-09-27/rustc-nightly-x86_64-unknown-freebsd.tar.xz=46d9945d5361b758448454c4778a42ce01b4cff7370b9988d5e7b2c7d889d24f -dist/2025-09-27/rustc-nightly-x86_64-unknown-illumos.tar.gz=83f3d4d069729a72da4b96067400b812367e0a81284bfe3cd73b1939fb81db9c -dist/2025-09-27/rustc-nightly-x86_64-unknown-illumos.tar.xz=110ca4f2630368f1c94084332d825964f3852bc9e70db8ec738de2cd4f450f2a -dist/2025-09-27/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=e1ad313cbe777997222bbdd4b26a5b4c21da50b6378e434501c58219137dad77 -dist/2025-09-27/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=2ab176057835fabd55e6e2372b036c245be44c0705198557ef2a16d187ea9457 -dist/2025-09-27/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=3e643ce549e9db3768c478b37f088afbf9b2f63dc0275bfdf7c2cbb48ac4fef8 -dist/2025-09-27/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=597c47ff84de68f317b0672d5564a3121edd88cbf5dd3d27192d133ca4ac05a8 -dist/2025-09-27/rustc-nightly-x86_64-unknown-netbsd.tar.gz=5f948f48d64420119f1bd1a90952a04cec074ca45bda8d46f020163cb2809016 -dist/2025-09-27/rustc-nightly-x86_64-unknown-netbsd.tar.xz=977612fd1ed20d57b95e577dd9b3632209fb1f376f46c66467e2a2ccdd7d29f0 -dist/2025-09-27/rust-nightly-aarch64-pc-windows-msvc.msi=b39edbdc83f4329be0e194be1b7e002e764153628bb107bdc77e6f8c2331abe1 -dist/2025-09-27/rust-nightly-i686-pc-windows-gnu.msi=d023b88f94d2d25b4a29c03d4e1243484fd7a20d67753fd3e9a10e6f39069df8 -dist/2025-09-27/rust-nightly-i686-pc-windows-msvc.msi=8441a5f6e8650613b5b9c0c2778bc88bcf259fd4f3acd226a6ec52f1b4a960cb -dist/2025-09-27/rust-nightly-x86_64-pc-windows-gnu.msi=15a83b7056623d30dc1d47560151ec79e2cb7db1d229069085e73b782347e8e7 -dist/2025-09-27/rust-nightly-x86_64-pc-windows-msvc.msi=607a9219272d8b41fd6bedf884515d3584471c75be19f9353c1c67826c115aea -dist/2025-09-27/rust-nightly-aarch64-apple-darwin.pkg=55cc7129e581244dcbc567eb905183ff3e45edc8847fc58cb350394e6df55e96 -dist/2025-09-27/rust-nightly-x86_64-apple-darwin.pkg=a6add14a01bfd77634e67425db47cf63144dbc0b618beeaa4f37be2d7103146c -dist/2025-09-27/rustc-nightly-src.tar.gz=419d5aea9252c3a9377fcfefe0a0e91b7be1354b9c33e36e346c547c4a9ec3eb -dist/2025-09-27/rustc-nightly-src.tar.xz=fd454f13408f045e3ba1d4618699a3c6e42fcc66902c37972aa3729bb681d951 +dist/2025-10-28/rustc-beta-aarch64-apple-darwin.tar.gz=5d971d3a1c127d314f94f41440f0717b51692176630fef7f05a70d9bcd3bae44 +dist/2025-10-28/rustc-beta-aarch64-apple-darwin.tar.xz=2cedfc3a96acc9ebe82c5cf5bcf5355902a5726e1c28e7d50b15cfa2ee343174 +dist/2025-10-28/rustc-beta-aarch64-pc-windows-gnullvm.tar.gz=1875833b43088879444902023cc76682fa58f39520a1135e438afaacf3605f88 +dist/2025-10-28/rustc-beta-aarch64-pc-windows-gnullvm.tar.xz=a925fb02b296b8ce753ff7b1fae583a27fb2ef6de582a80f1428678a2f61a466 +dist/2025-10-28/rustc-beta-aarch64-pc-windows-msvc.tar.gz=28f423bb7db80232b887bd0d47243341a50611eca913c17c305dd55fa6bd3f12 +dist/2025-10-28/rustc-beta-aarch64-pc-windows-msvc.tar.xz=3be7d39a956afd591ba4b22785b4851fd6c0c7afd23d045c54d6b603cc14bc2d +dist/2025-10-28/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=8b98b7e04ef03c3b2bc95e0d6e3a92ad7849bad8d5a8969d6c9eefe169673066 +dist/2025-10-28/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=1b35688d58c10fdd1f1d02325022ae9eb36ee139f76ae753e16a0f4524d0f6d4 +dist/2025-10-28/rustc-beta-aarch64-unknown-linux-musl.tar.gz=c1de08dbaff419f08f82aef49972965d87e02054822e83e6e7f73714afbc4155 +dist/2025-10-28/rustc-beta-aarch64-unknown-linux-musl.tar.xz=2755a71cee31227e4638499dc882347ed9ecf0698cc64a177ff8a94c6ff3be8c +dist/2025-10-28/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=dac50bed8e2bb5d65b5f6ea52963d2b268829c9f79800304a225e058152be0bd +dist/2025-10-28/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=f818783e566cebc2ac392f923efd0ae8f51ae77ce6ade284514a57c21d288b7e +dist/2025-10-28/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=922d9d3aba6c5ac4da95ab9310d0f33ec7da4830a7d424b83d6f0181af96bccb +dist/2025-10-28/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=746a200dab88eec922dc08ea244ae818c27a0ca91360278e578ab9fc433bd3be +dist/2025-10-28/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=ac45fe7547dc1064286de6704a9343e95fa760163247ac02ff88ebf2bf4d7d8c +dist/2025-10-28/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=35ab727f335242d588742f79f29fb94590ac0c3bef29d012538f2e925cb9f96f +dist/2025-10-28/rustc-beta-i686-pc-windows-gnu.tar.gz=42eab38b498e0b8efd7fae51facd7caaf253e1bc341f915f978b484065e4c812 +dist/2025-10-28/rustc-beta-i686-pc-windows-gnu.tar.xz=d663982b6293285fa1ffcc5987ded64569216c0133476243b5525768cd6123bb +dist/2025-10-28/rustc-beta-i686-pc-windows-msvc.tar.gz=571eeccc9c1f25c4b9c2c6f45d15dbd9730bf6c0ac0f75a5b49652b6d4e98f68 +dist/2025-10-28/rustc-beta-i686-pc-windows-msvc.tar.xz=595a8b6b74910fa53a947ddde81f5519152f8c773a41a2f5b0d663540bc913fa +dist/2025-10-28/rustc-beta-i686-unknown-linux-gnu.tar.gz=3f2261279e5c16b0a4b572e799e2d3079aa33192e7713803214011e14c5c6707 +dist/2025-10-28/rustc-beta-i686-unknown-linux-gnu.tar.xz=c49c0e84b4b43aa6b72dc355173b4b5dc41563f3f76e882c405a8625c00befbc +dist/2025-10-28/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=92cea4e2708fbd86b35bc798b25a69ab10b62f28e972ff0624709e6e8268db60 +dist/2025-10-28/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=224f6e9896d19e00e369da70bc0e0dbd545d04033ff6ecff12b3f2d2bfe73fc8 +dist/2025-10-28/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=74901d397d5f6695df197520d16851986df2a5c5d432b8f24da1a5c3a5c0474b +dist/2025-10-28/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=02d8962da7234bfd365147b4177497a99c8f1f12f6841160abede61332edd35f +dist/2025-10-28/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=b8d810c76102a325cdecb98e7d7fab480a8d349328ce8a275366018a627fa4bf +dist/2025-10-28/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=f09c4e8d4a4ae4cf490e55bcd29ab00b6f7c1aed09c93b63f10fc3f812f415e7 +dist/2025-10-28/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=6c71850444d597cdc07a7169bd5d2564cbd99d1a34ec806ac8b818b39c9fd529 +dist/2025-10-28/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=01f3537787f41c2fd2ed1f955520ff0852bb27d899141cf44de3ae126abf8808 +dist/2025-10-28/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=833dcfab6dc87d375154d593c8e53cd5c74b3029f8b94c753928de718185283c +dist/2025-10-28/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=cf99a56b49b3db107d33f5c6e668310317aaeb2b2ca3c80a0ea2b30a09b453dd +dist/2025-10-28/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=55b1db794a4bb0f535e46a2c4a85eb7ea0016482b7086b6deae033746a42a69d +dist/2025-10-28/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=a741b18a92f5fa35cab2ed93d90782175b70e3ef5a127e21fde7acabb6228b59 +dist/2025-10-28/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=97335caa2ace3f33cd959dc35f01ec8a3d2de5699518b93fd191766eda3fe318 +dist/2025-10-28/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=2127853b7b4e2b3cf906ef292e9c7e370ec3f3760ce5576b447ed7902048794f +dist/2025-10-28/rustc-beta-s390x-unknown-linux-gnu.tar.gz=eb3df84c1a840cc69799ef0649972e5312afe59144700ba3652bd6083f48074a +dist/2025-10-28/rustc-beta-s390x-unknown-linux-gnu.tar.xz=16942680ea11ead1ab4d9d46d31389a14f237637a6877b278cd30a608446b787 +dist/2025-10-28/rustc-beta-sparcv9-sun-solaris.tar.gz=5c7848ab2b2cb181bd7f64cdbdc3b5b659b61850c14604a7977c35b9807daa48 +dist/2025-10-28/rustc-beta-sparcv9-sun-solaris.tar.xz=0f12cb118e253206ea74e7b1dabdda6e95bb0b10d47657aa4c560fa9ca28cf83 +dist/2025-10-28/rustc-beta-x86_64-apple-darwin.tar.gz=3c34e03deee0445864c7f5ebc89883db051a3c69b874a2daff658fb5dd364aba +dist/2025-10-28/rustc-beta-x86_64-apple-darwin.tar.xz=292d40e5ea57295ecd921a07fa11796771953d38793edcea9841a95e79d94ac2 +dist/2025-10-28/rustc-beta-x86_64-pc-solaris.tar.gz=6ac79775ff166ba76e663e8760ddf9625e06a28e5033d4d8cc570dcca73ca725 +dist/2025-10-28/rustc-beta-x86_64-pc-solaris.tar.xz=46c214af2c11ccb17529a991201624bad044d59c171c6f872468acb8523b6f96 +dist/2025-10-28/rustc-beta-x86_64-pc-windows-gnu.tar.gz=29067fc4a5e0f123c34a045a1b0d55f88170bc9f658584d50e32219cd5945854 +dist/2025-10-28/rustc-beta-x86_64-pc-windows-gnu.tar.xz=30c20794d0c244bd04dbd4de304ef3aea5ba11e26882f2a004680281f21e05b0 +dist/2025-10-28/rustc-beta-x86_64-pc-windows-gnullvm.tar.gz=6c6c7614b538d9bad498b3642321adf0b2b6cdd3b24706903a8eb2125c0c122a +dist/2025-10-28/rustc-beta-x86_64-pc-windows-gnullvm.tar.xz=09822c7d5bbf218d5113e902968b37719949997fd788523bf80400274f428986 +dist/2025-10-28/rustc-beta-x86_64-pc-windows-msvc.tar.gz=fcf05b8150cf7199e100f560854f6acb6874985a5cfbb00ead36914dc3fb66e9 +dist/2025-10-28/rustc-beta-x86_64-pc-windows-msvc.tar.xz=f5259f5b521ea10f093c522abd5bcec4e9dfb94ce3e384e3b53865a223e700d0 +dist/2025-10-28/rustc-beta-x86_64-unknown-freebsd.tar.gz=c3719366a9687d243483431231b86140e268bf2ce99e1ea1759a611524ea8ade +dist/2025-10-28/rustc-beta-x86_64-unknown-freebsd.tar.xz=015e02fb99098c31d502738e9e525d48ac6ca386e16f3ecb55235c7268d4abe7 +dist/2025-10-28/rustc-beta-x86_64-unknown-illumos.tar.gz=508acec5f2b092d09d55590e7fe921f7415aacc13c535532997776c4ebb86483 +dist/2025-10-28/rustc-beta-x86_64-unknown-illumos.tar.xz=f7437c9b0e747b0eccd7755a3ef551fa81194f23304ddb40ae44bca5782dda4b +dist/2025-10-28/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=cd985cf21af999dbb76e25cb6c9b5ac5723e8cda8625cf511e7df2c98e7e735a +dist/2025-10-28/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=d30acafe9f3ddfd25148abdd3078deaaafb6bb62f7979252e9e7880abc260b23 +dist/2025-10-28/rustc-beta-x86_64-unknown-linux-musl.tar.gz=1778c649d790e52b8462d5ea377339443e607b2434fa9b579236b465f63748c7 +dist/2025-10-28/rustc-beta-x86_64-unknown-linux-musl.tar.xz=909808e6e3a10cd3fc2c0927f47dbe4ac8de4653b4e771c9c44b8e58cbe5fe2d +dist/2025-10-28/rustc-beta-x86_64-unknown-netbsd.tar.gz=8584d5ad71c7cedcf6e64b0f83ed2cfa82346e88a1000acac677f464dbb0e6f6 +dist/2025-10-28/rustc-beta-x86_64-unknown-netbsd.tar.xz=7b85928ce5f6908ad82895097ba07fad70990b7810aa23c63725d7ecbbf54d54 +dist/2025-10-28/rust-std-beta-aarch64-apple-darwin.tar.gz=312c1121edf001f03ea169e8fff9668efd1925a45962b282b32d5f126bd6b010 +dist/2025-10-28/rust-std-beta-aarch64-apple-darwin.tar.xz=11e0fd642939358a892e66ebeea7f1737a009ad23692da486c7427cdce60c37d +dist/2025-10-28/rust-std-beta-aarch64-apple-ios.tar.gz=d7086698f3414ebf1410f79f8167ef2e4cf8e9143dc7e3704200d67e64252080 +dist/2025-10-28/rust-std-beta-aarch64-apple-ios.tar.xz=a71fd039b0bfa2fa7a1abd13c622c89371f7af273fc44b3c210b1ead6a1f9156 +dist/2025-10-28/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=52795cad4c58b32c3f36fb7b596c25a8e3277c85c165e161445d1fc81e0c4200 +dist/2025-10-28/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=7db4e584f7890bd05d7cf8a991f539be249e0f04526e30743575129dada977c2 +dist/2025-10-28/rust-std-beta-aarch64-apple-ios-sim.tar.gz=575b6d5185a71a4f4d29dc881f5dea5fe492d7b21a01c0bd9987cc212bb8ca25 +dist/2025-10-28/rust-std-beta-aarch64-apple-ios-sim.tar.xz=ea1c7896e21d0a89da399405611a25ec447ff71495a4b5d48501c9f8129b2a20 +dist/2025-10-28/rust-std-beta-aarch64-linux-android.tar.gz=f7a85a478dd9d55491a2395dda9539ab7898385782ef5be479362ba35e8be837 +dist/2025-10-28/rust-std-beta-aarch64-linux-android.tar.xz=4fc53a2be557540332410446f6f43f5e6339d10d8010c153d99d707df64a5ef1 +dist/2025-10-28/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=99b3f95696883f18e3734d02c75a5dc91edac003b6d9627c5a32baac41f3c69c +dist/2025-10-28/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=375db4717baaac6a784d8f53f84035f99d5f76f9a3f7df7dd385ee2d93027368 +dist/2025-10-28/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=b650ca7708ae5b05fc82c7aa483f9aaa42be83d6bcc6c524690da745f32f6f60 +dist/2025-10-28/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=1683d5a33387dd546ff641222b501e90049241df0f52c2f1a5dc568b35594135 +dist/2025-10-28/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=3e2d64dd8d2fdedb30b25f899f8ed8deff653c4d3f3f7e16e534ab7e1eec8a66 +dist/2025-10-28/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=e83d63666e0372f27d404a4ddf48de7abc5b646d9f087ebf8856bcbfe6f86362 +dist/2025-10-28/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=8a99f5fbaf869f68bced6435acf18127ac8880193fb23e44cb5791546bc795d7 +dist/2025-10-28/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=53be5ecbcb4d47e6a1fb811a6e8b4ecec7fd6ae2b6efac7d9934dee4cfb19dce +dist/2025-10-28/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=f61af59bc40a53dddf0a85b5205bec90d287a41ee3ac32345a97d21861ae6546 +dist/2025-10-28/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=9ed2894300f6ac917ac33d0724133ba2fada2ab2e42a0b21a9bd994d70c942f4 +dist/2025-10-28/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=70ad473c7d4934bab123afb8cff98773993fdbb73b4941f6022a25ef2942a223 +dist/2025-10-28/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=d0a06230a38c725ceed103c89e8031b541b7bc15a3646a1a3662d8147901b2fd +dist/2025-10-28/rust-std-beta-aarch64-unknown-none.tar.gz=90d4484a1e390e0d3edf25331d41f9b419730bd2e318e8927c4a891b2c035652 +dist/2025-10-28/rust-std-beta-aarch64-unknown-none.tar.xz=5b795b7378b6753fb2a8ea0ecef760009ffa05c9a4778815d2d2b4085baffe14 +dist/2025-10-28/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=52d0e13f1dc2083787d3294d5c3569b11227724ab4ced4ba9905ac0a5f25bde0 +dist/2025-10-28/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=981dca064b73d6617039bb35a32a17111445bc1ed9ba957525d227d3be9e8f6d +dist/2025-10-28/rust-std-beta-aarch64-unknown-uefi.tar.gz=fbb7066da0824513e4441bb59d4c6a6d4c8bc34f67ed79f67da3b8ec69ff3a4c +dist/2025-10-28/rust-std-beta-aarch64-unknown-uefi.tar.xz=c0bb51d8c513c0b5d597cbc00260bf2764595b12244906606c20ae6f98e0fc66 +dist/2025-10-28/rust-std-beta-arm-linux-androideabi.tar.gz=7cd5763f202c5679dc1bdc0e6551dc8093ee95a9b2d405ad8cbd31fcf232332c +dist/2025-10-28/rust-std-beta-arm-linux-androideabi.tar.xz=126b3a04432a3f6f5f1aef6f5e22deec98629da4a0809b0c7739911ae5e4b6cc +dist/2025-10-28/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=01bc31ad9812edeb11dc4b6c15d4bf80a325e286390829babd85176b91c38cfa +dist/2025-10-28/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=0b1c799856c8314afddfaa5109535535c36df31bb9188a319c1e45f4968a7079 +dist/2025-10-28/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=8792d0115ba1d9b0e3bfefbd7a9ddf7859f7bbf8431ef32530b05ec7cf9c68b8 +dist/2025-10-28/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=fc470db282fb0fab537e4b93a9498d4b4fc65cf014536905654cc2c14f82d245 +dist/2025-10-28/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=fe82493d2a7f4a9566916beefcd7fb26301bd13b8b838b5251398ecac0c867ef +dist/2025-10-28/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=b7cd0514db083e115d8aacceb3f7998da11f26e9f2be44137e8af2e2f9ac786e +dist/2025-10-28/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=0adc4e2e0f5bee4c28c81ba2cfd9796268e830d23b23b80c71121c30f08e3326 +dist/2025-10-28/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=8c98799e7b4c454cad130d0eb87e8ed59e0e7e64bb0513af8beb6d1d5f8e3ac7 +dist/2025-10-28/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=bb1da273578b508b9f397074438ecb2527a729e404d2820bd34d67dc8824106a +dist/2025-10-28/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=e004d91a9732284851d613a3c488909bc7d457b2053a1af45710aa4bbe3169e9 +dist/2025-10-28/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=7e6b5d4266306001366003cdc4fd4b60d700e86ec5dce01b58b0e73bd6a50c42 +dist/2025-10-28/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=170b32ea83d21436e744d11a56ea8e298278f8b9aa58fd82d6d788ff1e3258f3 +dist/2025-10-28/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=8ee71b71a8e90b21d580f9e64cc115f182db8582cd6238f946e24fc32de27e65 +dist/2025-10-28/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=9319475d974dce729182c3d99696bd5cf6a36257b1b80186f261d2885610ddb9 +dist/2025-10-28/rust-std-beta-armv7-linux-androideabi.tar.gz=71db2b02df33e229899f0dafc675572f2b4041400954f9145796edee7cd923d4 +dist/2025-10-28/rust-std-beta-armv7-linux-androideabi.tar.xz=0f331806b4523d61204f38f2d7ddd07e6f3dcda8a15806a9f01cb1747a587e8f +dist/2025-10-28/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=52f331579373b749fb1c7a6302a770a4be33008cdd9db4a5640208ba1ba7b540 +dist/2025-10-28/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=1e8c5dcc4cb672c4e8ba2862549cd281ea6802fb73701c885eaf5423673ab233 +dist/2025-10-28/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=2d0dded898f567cbfa5605dcc09b1e44970e345a030ef91c806eafcac4470258 +dist/2025-10-28/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=3a63913c874f30c8a68d5cbb62dc654ec9c3bf2f4488e1098034f60c51824bb7 +dist/2025-10-28/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=24c341427a9b5a4d883e7f322a9c7760446896e8c2eeb1f8654b80cef00db6ea +dist/2025-10-28/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=53c5607351479ad76c3450bcc81353633f2587aafda473487bb11e42f80d8523 +dist/2025-10-28/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=beda2ede28453b81588d639c18b36fa6e935e586723159e39ab9b714291c680a +dist/2025-10-28/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=f175a55fc81159e9a40407b1d97ea658239014cf0f5191a3533318e53ee1ff7f +dist/2025-10-28/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=db1f0cfd65d795d23a60d29e44f24e6174a0a516b998e65bd1964230c9c6c03c +dist/2025-10-28/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=264efedfd340c095dfe08fc95a35c36392eda0d1c2c179e56c75b27685b90d7e +dist/2025-10-28/rust-std-beta-armv7a-none-eabi.tar.gz=578fe97f24afa73c155290bf7386594e8b18a4652f54477d7a4e3f6f3ada0b5a +dist/2025-10-28/rust-std-beta-armv7a-none-eabi.tar.xz=a373109143f701d266ba53b3fff32206d88f5dfabdd888ac988606fade0b4928 +dist/2025-10-28/rust-std-beta-armv7a-none-eabihf.tar.gz=b3d6ec33c14a034c621aaf7de6ec81f0a916e18f5acc933713cc479fc21af755 +dist/2025-10-28/rust-std-beta-armv7a-none-eabihf.tar.xz=6550b2a97ce84884aa0da9133b81703e08b6e57b65c2e93f310fad2559ea4a08 +dist/2025-10-28/rust-std-beta-armv7r-none-eabi.tar.gz=b50984f65ee8a9e8c1b62158d8e91cb976f7c36f6ebc3c70ccfcab2a3da26533 +dist/2025-10-28/rust-std-beta-armv7r-none-eabi.tar.xz=0227fe58cacca86a04d85860a1de852a29ab9ac1bfea7bc50c2f560b73951d8b +dist/2025-10-28/rust-std-beta-armv7r-none-eabihf.tar.gz=da5e26a7cb5b91d3b8ec4f1a02c81b0f538dbb416e10e9453f3fd9d5cfc08129 +dist/2025-10-28/rust-std-beta-armv7r-none-eabihf.tar.xz=9132e8a69bf5dbd144def8bc647c659304fa785612a0f68f2e1a2df5dc195e33 +dist/2025-10-28/rust-std-beta-armv8r-none-eabihf.tar.gz=a312253ad16ccffacd0d840cba85d290fb5d2580865de99887990eff96f58b45 +dist/2025-10-28/rust-std-beta-armv8r-none-eabihf.tar.xz=474813a75bd586e562cf76705bdabf37a915cda6edb45afe029cbcbe54be3cb5 +dist/2025-10-28/rust-std-beta-i586-unknown-linux-gnu.tar.gz=4be0898bb9da3e8d75cb9b4851d5119efdd3136eb10cf31a42109f188781b6ea +dist/2025-10-28/rust-std-beta-i586-unknown-linux-gnu.tar.xz=5a51fb1f2da0dd645c1eb6ce19f37f16f356042ba176ab109f8bcd4ad3514ad3 +dist/2025-10-28/rust-std-beta-i586-unknown-linux-musl.tar.gz=96f621043d426d00f6baec72f66db5d91940215ba3e6985bb823c7b02acbd7ff +dist/2025-10-28/rust-std-beta-i586-unknown-linux-musl.tar.xz=2e62b9a670e3ca1d892ab18da1cd4b2bd946395e35c3b7829dd4b03726a73e4c +dist/2025-10-28/rust-std-beta-i686-linux-android.tar.gz=6c3b0e8796cb87e1a0369b55a91e49d5129af929deb4b63a623cf68219679543 +dist/2025-10-28/rust-std-beta-i686-linux-android.tar.xz=682747e9ab2706e90b5071ce13e8bff21160987d42ed0565f8e0012acded5f40 +dist/2025-10-28/rust-std-beta-i686-pc-windows-gnu.tar.gz=e7afe1e6ccb84fcf03114a6a3fd7232b40d7b2005f31a00a02d9c8e092af8b5d +dist/2025-10-28/rust-std-beta-i686-pc-windows-gnu.tar.xz=99d64d295d7bc0befe84a1da5d888de3ee0daa67a027c8628f78c36ab53eb7b4 +dist/2025-10-28/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=1b36e3979b73348b46a01961a2515bc6ae19a54924e63272cb1bca709a07bcf0 +dist/2025-10-28/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=f579f144f9f653df8be318e4f5ba363116a538b840030d3cd5305f87e03b9e23 +dist/2025-10-28/rust-std-beta-i686-pc-windows-msvc.tar.gz=79de79f2ed569a93dcd57aba35c7995582dde8b633bafbf0fc43ab6a8fb9d152 +dist/2025-10-28/rust-std-beta-i686-pc-windows-msvc.tar.xz=8c3fdbeb815085aaa9d484f1b22d0adbcc829a88511df5930bb518b139d20801 +dist/2025-10-28/rust-std-beta-i686-unknown-freebsd.tar.gz=d25fbb684148778444743e9d651a6d36dc4527d42367bfa67eebc43200e6cc0f +dist/2025-10-28/rust-std-beta-i686-unknown-freebsd.tar.xz=32c6d84e419a72cb862294a110fc79a88b123c4180a3a64bd53c38b200c60f9a +dist/2025-10-28/rust-std-beta-i686-unknown-linux-gnu.tar.gz=577cec0f5fb83cd22a9256dcf7c74529d95572976f102a8a63eb1cb9550f0a6c +dist/2025-10-28/rust-std-beta-i686-unknown-linux-gnu.tar.xz=dc537d7c09efaeb054d2954689842c813b5e9d646fcad912005f4b10e137181d +dist/2025-10-28/rust-std-beta-i686-unknown-linux-musl.tar.gz=5713f1784a424f509f47f70e0bce2bf8ae82e1a16efab8cd771677988b37516b +dist/2025-10-28/rust-std-beta-i686-unknown-linux-musl.tar.xz=1a3dc46f4cb4e4c8e4f96e9835b1188242addcb7a5a8ebbe3ef614c3aef862c4 +dist/2025-10-28/rust-std-beta-i686-unknown-uefi.tar.gz=4c32dcb28c1fbaeb910c9c7c60ea7dfe50e6556663eb3f002b66ec3ed42bd7ed +dist/2025-10-28/rust-std-beta-i686-unknown-uefi.tar.xz=1683c1c2838a2693b2f08a9daf0626675623d6bccbc4c996fcda8489eac853a1 +dist/2025-10-28/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=401945e68f2246d1f502d4ac63666d5443ca7fa2f81ce38e76fbb0647fed4643 +dist/2025-10-28/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=9b8d7d2f8a4ebf1369718d8bd3ae6973c977cdcb7030ccea0542f9e28970c281 +dist/2025-10-28/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=ae351e8f921f0c3b8bcccafa0c84402e15ced7591debf82287e63bac0965801b +dist/2025-10-28/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=7d64ee15508ebb610abe61e1aa03607140302fdbe41200e82070be85312d4a07 +dist/2025-10-28/rust-std-beta-loongarch64-unknown-none.tar.gz=9f951f51a32a4f037f3548935a07208e14f3e67d29100954ae6fbf61a8e56c06 +dist/2025-10-28/rust-std-beta-loongarch64-unknown-none.tar.xz=80494d9d9e02d7a6f56a638d93253d087a4edb83d4662b4771bd1d5c2cba97b3 +dist/2025-10-28/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=f23fd6331ba600a3a4350565ad11f51129ad13a248f35c37b9d485fab725b4ac +dist/2025-10-28/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=03d765256191ab9fac9f37102fa4252f7efb05261c8d2464745826232351dcd7 +dist/2025-10-28/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=509bcbeb245d69fcb828bf9f3fa4413d1d4d59a0e85ed28c4f412929c734aacf +dist/2025-10-28/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=543aa41bd2ae544c0114cca0e5a50c15eb7ed78d3d132bf5b48f93f2f92b502d +dist/2025-10-28/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=d67670d0b9385f4433635a473548031d083fe14e1a407766fb1ae5c4762da93e +dist/2025-10-28/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=e2f403936d191b38347bbad4e9b9bd66270b23e9df238ba9b5d52f81aaebfdb8 +dist/2025-10-28/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=15ef51a8fc10b2298db444d68130ef350414722133097d670b8106e8de68609b +dist/2025-10-28/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=515151364a93fc458ab5f11831797d2d708b22d9a71c78f717cf89e4e593417a +dist/2025-10-28/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=8ea0204c3ac93828c5a72f671c12167d0b5120cde99bb797c8d839caeb3ba3d8 +dist/2025-10-28/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=138969d2a43b6c4d736a950f8ba5bc0ed5fc7868c7044623d389d8278727e759 +dist/2025-10-28/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=a3423e7917b59232faaf855ad9b9cd3b23e5d2188aeb8636e2cef324d405beb4 +dist/2025-10-28/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=36914d3ce6a2e5a56a2a394d2c5a461a7c8a40fc235b3552a658ab1ed7d786f6 +dist/2025-10-28/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=dbc0b69f7eba79726bc08a0a6b357f9d818623d3cbe153dff5f2fcb31aeea61b +dist/2025-10-28/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=39def1e42a80080159347b5e85bc666830a3a346c9b0e83eea8207e94659ed94 +dist/2025-10-28/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=7aebf847c08b0eee42c77da8defc6b30439f4b6d29e4122802f02d029518a31e +dist/2025-10-28/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=35c49b2df4b3e18e0ec8ac20ec8b9a4a81b69f026921d85a666e0a97a671045b +dist/2025-10-28/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=b10e9e0a0457db1441a9bfeb3c4b9a8b2af4d33518bc58a8ee1d3fa7546c0bb5 +dist/2025-10-28/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=8e210b59b95b3c8db109d978ea73a540478315fb49d4515ccf248aafa7517617 +dist/2025-10-28/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=e2bfdac9e858f144db5e9ae79b494f1e09b2a012b18f0eaac96ce684a6a43e25 +dist/2025-10-28/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=6d2f5e60af9f6824fe2078a58a6cb804e61116e4848e1502289e08b0b9a5196c +dist/2025-10-28/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=382f448ea95627ee9fbb3d42c32ab9ecffe21ea12c00810e4eb8e94553d463a9 +dist/2025-10-28/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=ad7f534565c1b97495bf29054c202d8fada0a0ddfa0cca110c14a8221c654cb9 +dist/2025-10-28/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=7c8cf6afaa4edb589259dcc61ec5e1e667a5deeace8e277f752a433f20bcbf8d +dist/2025-10-28/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=8a396e9db63f1352bfde7a0d51446dabe6873c9a0a46e3c74f59ee775092e2af +dist/2025-10-28/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=551d398055ee522ec4a67a9173daca7a1f0f537ab006383ed2ada6b00cc81cbc +dist/2025-10-28/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=cf08dcb74f961bff041ad52efcae0c8e239e6439ecdcb32f869eb57b1a3aea1d +dist/2025-10-28/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=fd4e49a23ea0d512ed32bbaaefc1e5ea32c837e9a371513b94643966afabc521 +dist/2025-10-28/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=10700fa42e00a3b58ddcd664c0179016b837b43a31476fe4c56e8124619e4a22 +dist/2025-10-28/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=ea2f2873255dcc73185490b2c396abb4bcd5f1623f7749f77885426f753ce29f +dist/2025-10-28/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=7c02315d003cf65ac1246e3ac21b6da39cb1d99e61078ba9941647ae48bf9aa5 +dist/2025-10-28/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=008321f0ccd9b96e77829b709c2683c0053acbb19ae86f2fccef49c2cb964531 +dist/2025-10-28/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=047db35903bec7fee6c1d9deee5b4dc21db546a06e11c30d8372ffaf09814c59 +dist/2025-10-28/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=19677dc6495a139eac6c5827cb765e54e3b099d64f83e92273190f09ead7dab7 +dist/2025-10-28/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=d5815d1eb4906a442da0a0b56fe61cf94767dda29479da447d94a23803f9cf0e +dist/2025-10-28/rust-std-beta-sparcv9-sun-solaris.tar.gz=184424d0d18c2c49ce1c819e49804d7667edf2bce34fafedd4eeb6d4f427ae5e +dist/2025-10-28/rust-std-beta-sparcv9-sun-solaris.tar.xz=30ad3f9e3eaf48af0eb700d528b330118ad39f221a7beaab1a42e52b1ffa8531 +dist/2025-10-28/rust-std-beta-thumbv6m-none-eabi.tar.gz=c9a72ea7a13651f1b9bc6e509d9d6d32e56a4659d541e20f571ef9f6dd858480 +dist/2025-10-28/rust-std-beta-thumbv6m-none-eabi.tar.xz=6dd3a25ee5060cdf4631d8b0b7f16aa569bf611ef392d93826e88846053779e5 +dist/2025-10-28/rust-std-beta-thumbv7em-none-eabi.tar.gz=ccf8d09ba0bed60fecc775689649f2de31bab68cff273d796378d213c9d1e44a +dist/2025-10-28/rust-std-beta-thumbv7em-none-eabi.tar.xz=b7a972a9b972a2e0665387cd825491bf0ee9581ff26a586806c986014f0ae742 +dist/2025-10-28/rust-std-beta-thumbv7em-none-eabihf.tar.gz=26baa00580268fb5b7aba63a069c187dcca43dcf29161a0c1b849ef3765bbbda +dist/2025-10-28/rust-std-beta-thumbv7em-none-eabihf.tar.xz=dcd67a3ad1f2c553fb90009c630a8edcd8f571e28e5fcef0ade63c624fa11037 +dist/2025-10-28/rust-std-beta-thumbv7m-none-eabi.tar.gz=cef72caa89fcd3d48c070b1aae2fe85f29547c9b615446af2d51767ef42b5045 +dist/2025-10-28/rust-std-beta-thumbv7m-none-eabi.tar.xz=70ad20d0016c6c81b4526ccf89bbd96f4bc0a97b904b2c06487fd0a0368d7a17 +dist/2025-10-28/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=f22aeb9cdd40a4a465525fca7e6520aaa74b5fbfb41f45777e1376742f711c59 +dist/2025-10-28/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=48410a5a2f05c80f3d76b9f0b3e0eb89521495b273a854b40386048f27b553c2 +dist/2025-10-28/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=a791f5442467819c154f943c3126de1dd8f5fdd6b62527aadd88260ecb8c380b +dist/2025-10-28/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=cf43ec766a353cad48150d47d108d8df4b1561101b2c95241b909d4a5ec47bce +dist/2025-10-28/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=3699e5b727abc016ab9fa78f40655ef7d88ff157e46759297cd80e5c169136f8 +dist/2025-10-28/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=a6942c04a569f68cf1c5126e520400c51dfccd9e18a1d3941e28d7a8e63ae648 +dist/2025-10-28/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=eb27f46d3ee51e4f71316a75087d4ca2493f55ed5e0b720f0dfa3564c8f19a67 +dist/2025-10-28/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=f50b7fa2830844fd27041ced4c96eaa68ae6595b370eef1c958e3542c718d5aa +dist/2025-10-28/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=398556eca8a50c1d905ef7e2bf3f25d75f8dcf1d9b679af87b565f5f58af5c2b +dist/2025-10-28/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=8dc4a5036cfda5725120a8d7e7d0cd05151230ed967ff77fa028c4b73dde6885 +dist/2025-10-28/rust-std-beta-wasm32-unknown-emscripten.tar.gz=212f761263b9a4c72a53aba0b76986f70b3d624557f34d36af2e7d935702359d +dist/2025-10-28/rust-std-beta-wasm32-unknown-emscripten.tar.xz=89c0dae14955afab89b8fa2892944325ff154987c863fd36f93089f29ef993b6 +dist/2025-10-28/rust-std-beta-wasm32-unknown-unknown.tar.gz=92159d52588bb1ca6346025f894dce5520b42d89cb54a78a3ff42ee119c1e90b +dist/2025-10-28/rust-std-beta-wasm32-unknown-unknown.tar.xz=a187e36833f587ad5f117b012a5d9ddacb3122b39b902bc2a9d9d03b5aba27a2 +dist/2025-10-28/rust-std-beta-wasm32-wasip1.tar.gz=fa8971fcd7c22f5d382f4239fa8742238ec967f70fa40180ac9d8a39bffc28b3 +dist/2025-10-28/rust-std-beta-wasm32-wasip1.tar.xz=0603acb7e6fa5540fb458cf363fe473889b0abba91ec0c124ce380ffb1869cbf +dist/2025-10-28/rust-std-beta-wasm32-wasip1-threads.tar.gz=7651d409d4a89dab576cabf51cdd2947f877b77f09f668c6f9b8826dccd5c322 +dist/2025-10-28/rust-std-beta-wasm32-wasip1-threads.tar.xz=a6c3bc7f7f2e6a4cee6db2aa16f74af80cdd17ca92dcfcb1ab1e0c06bbaddd79 +dist/2025-10-28/rust-std-beta-wasm32-wasip2.tar.gz=77537c7a4518e62cb592e5e9d41df02d1ec8a3038b87cdfe082c98ae578973d5 +dist/2025-10-28/rust-std-beta-wasm32-wasip2.tar.xz=794a3c2e08edd74b7a004489017172c30a3242f7f0c5538c3b033937787da332 +dist/2025-10-28/rust-std-beta-wasm32v1-none.tar.gz=746ac6eb726ea2d2996b68be881d5c5fbeeba2bec1fbe57e6c315d4953cccba1 +dist/2025-10-28/rust-std-beta-wasm32v1-none.tar.xz=8bdbcf0cc2b598cbb2098f113128e6f94085dfdaea3ed3d5e15cfd0a82262b56 +dist/2025-10-28/rust-std-beta-x86_64-apple-darwin.tar.gz=2082bcdfd0e837163aec012cba1ae674825f52f5891c8cf3d4d00c619d5bd27b +dist/2025-10-28/rust-std-beta-x86_64-apple-darwin.tar.xz=b2267e0df76f21bcfbb55ab323c58015501d3a3060c4127a005e29fa0e628a7b +dist/2025-10-28/rust-std-beta-x86_64-apple-ios.tar.gz=6a48b60d0aa883ffad612a657d9de232ad1df92f937a5166532a7ee4e655fcc7 +dist/2025-10-28/rust-std-beta-x86_64-apple-ios.tar.xz=81ee2cdad7fd25b56be24d919268d0dc61c1d44d50bccb4c1811e30e85b97869 +dist/2025-10-28/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=01d5952f0970508064fa24196e126cb09aca5085ad6258c53397c3b23b006217 +dist/2025-10-28/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=3c10717be0087060ffb0e5b83c86ce69bff8b0dee9dee95b1be6a44f4238b5c5 +dist/2025-10-28/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=ce87a282ea73eb83015a321b208abfa9d4afdf4a797c799d9efeb1a246e4a64d +dist/2025-10-28/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=5c8866ec1cb4ec7826a94510c00f5cbc7f31822dd7e01ec2fe6b781b25e2afa8 +dist/2025-10-28/rust-std-beta-x86_64-linux-android.tar.gz=19538e0dd74d638b3ac1ac9ed679b8ebc6c058f661d7f167f957eb0866f7cb65 +dist/2025-10-28/rust-std-beta-x86_64-linux-android.tar.xz=15554ba8cc8d1d1c4c46e0438503170aff5a5519eb8bedef07a32d187ca32915 +dist/2025-10-28/rust-std-beta-x86_64-pc-solaris.tar.gz=41a6db659dff188414e33595448dc9c2b99103dbbdedab53d74c6aeccbb76458 +dist/2025-10-28/rust-std-beta-x86_64-pc-solaris.tar.xz=2bbcb13b8271c39a48ac2774e7444fdce883a283622400085623c161f8be405d +dist/2025-10-28/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=e2390bf5da8a91f056df7133020d053d5d90c84c81763738efe8a862864318ae +dist/2025-10-28/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=5d6fb692f2a1d2bad7b550b79e1288223ec598a46975848d07f2a5105b36cb4d +dist/2025-10-28/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=30c66f64c7343388f3205e172e64e51dc2930214fae46eb3b21648285092c944 +dist/2025-10-28/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=25788901f6fc13a4963bba4e985d619e0739b1239a5679406229a2bd5eaafbaa +dist/2025-10-28/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=9fab4a6cc38c2f06113ae9f0620e40ff5e9134f83562c32576c1e82048ad3bc8 +dist/2025-10-28/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=1b7007f9bc3619cfba09bfa92c3b1e5460316501fee4ae7c8492703a79517f4a +dist/2025-10-28/rust-std-beta-x86_64-unknown-freebsd.tar.gz=461036ed77e332851e88fb6f0f5c64068aef0b177b27662f326d157c06c7115a +dist/2025-10-28/rust-std-beta-x86_64-unknown-freebsd.tar.xz=fd7c7b75a50833650f131a6a676df90dcd57c4784654aee12ce594846da1c219 +dist/2025-10-28/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=882fe895b376384d9b1b1d9c01d9b641ab56b1405d63d7a83266f228b4b759d7 +dist/2025-10-28/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=e9d1fdbd0ba327b6d4e618622c0336eb6895e0e188005423710d514e145baac7 +dist/2025-10-28/rust-std-beta-x86_64-unknown-illumos.tar.gz=aade9f95b362cf13ee48e1d7608eb8487229cf86cc72082afe21fef533e45f5d +dist/2025-10-28/rust-std-beta-x86_64-unknown-illumos.tar.xz=de92c9cf00a2413d5481e96e1db5157c5cb3814851951076e4881b9269651cb9 +dist/2025-10-28/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=04f6feb37de8305d17e7aacc6174395bd55447340963303767c5a66a63200149 +dist/2025-10-28/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=2838910a0ef0084245a69fefdc67845ea579f49629cd24fa051b4db0bdc76d98 +dist/2025-10-28/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=d2f88d608fdccd0aecbcd83cc184d157d93159e29ad525583188013f327572d1 +dist/2025-10-28/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=b5e7ae18377727f93fece51d9ee5704ffd5a9ccd00c540b38ebbf3b9155cf20a +dist/2025-10-28/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=40a52cf3527090a12095b0ed4ad8d35fa5e3cef39146a9a8bff9009cf5772dc1 +dist/2025-10-28/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=28be865d06f9bd744aaad2e82ab8bdb87045e6e2d7ee6fceef2024fa620ba920 +dist/2025-10-28/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=e0afdd5422adac97fe8c6943470a8561847a614d3e367cb983f28250c0e2e34c +dist/2025-10-28/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=3007d13a0640d915eca622260c6b8b6a8936788a1228011b90df817b6888155c +dist/2025-10-28/rust-std-beta-x86_64-unknown-netbsd.tar.gz=1744c2485854792601518dcf7855d4223db63e9f3693e2d06c1ac285b92ba33b +dist/2025-10-28/rust-std-beta-x86_64-unknown-netbsd.tar.xz=757803d3cfb8db12dd7c9528852c1e28fb3a9ba97e4230d1e29dbf1834cf3f84 +dist/2025-10-28/rust-std-beta-x86_64-unknown-none.tar.gz=21d5de489cc22d849f246225909278f123483a446e1a3c68b14b8b992c104082 +dist/2025-10-28/rust-std-beta-x86_64-unknown-none.tar.xz=62755ecf24c0a2423b4d05c09ec8e8d38737e6762312d1ba993e1a9417083ba9 +dist/2025-10-28/rust-std-beta-x86_64-unknown-redox.tar.gz=0339d37d857f7f5ecb1607e8c53c499623f0c3563e3b68f2b939ee960d31b270 +dist/2025-10-28/rust-std-beta-x86_64-unknown-redox.tar.xz=613b5ca94e5c33c46d9f29cb557d1e512abd1104c5d81f1897915f6153175cc5 +dist/2025-10-28/rust-std-beta-x86_64-unknown-uefi.tar.gz=1da6167f0a2652b1622cae5ac815d55e307dbd639e4901005a3b225150dcafa7 +dist/2025-10-28/rust-std-beta-x86_64-unknown-uefi.tar.xz=6fca79a15c1ef9f8a3974f84af805106ab453099bdb6f652f584abcd7c8174be +dist/2025-10-28/cargo-beta-aarch64-apple-darwin.tar.gz=e66d076303fe8010c27fdbf8a9e31f47223508a280cd2ffcfea698aede848986 +dist/2025-10-28/cargo-beta-aarch64-apple-darwin.tar.xz=f4e87741a4070fe5bc4ad59ecfd324785cd9a477c539543d6a4fc1675afa8111 +dist/2025-10-28/cargo-beta-aarch64-pc-windows-gnullvm.tar.gz=5e9213968960bab07ad28408563b46d01c1d881c26ccd07651bdb527440461cb +dist/2025-10-28/cargo-beta-aarch64-pc-windows-gnullvm.tar.xz=ec01099da59fab595f8ed8a2f6fb308fbaaabf563cf50c103d47fad4595d72ec +dist/2025-10-28/cargo-beta-aarch64-pc-windows-msvc.tar.gz=ed3cd99db73648bb87c5a25556da30993346f1acc665f9dc906094639370282a +dist/2025-10-28/cargo-beta-aarch64-pc-windows-msvc.tar.xz=c5f89cae65188a7d1b5b5a93f8645774f878db6a14032abb8fd960fa9f4cdb06 +dist/2025-10-28/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=2c85f1c5b1a8bb9532194a16ceb8b04f1586d0ade14442f45e740f3e85022924 +dist/2025-10-28/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=a675601cf6552783625b79c33c1d42715c3918a08078e6eed01236910bafc4b4 +dist/2025-10-28/cargo-beta-aarch64-unknown-linux-musl.tar.gz=e5f2cc9f31b927e2e02117c38a5822c39993242459a057c48e7e11695dabe5a5 +dist/2025-10-28/cargo-beta-aarch64-unknown-linux-musl.tar.xz=1830b8b7e43fc03f0348503ae036b30d0e906cb024bc2257e717a58df95ac222 +dist/2025-10-28/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=eef6f553184a8177525a1ee932d6e293f42eaed6986c1c83f7d76d579f02b867 +dist/2025-10-28/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=13c81e5874532e490c987dbc46b19cf3a7eac14fefdffc30d0c5493a84f8a257 +dist/2025-10-28/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=56d7f1b41af4d72091c8a99b9a583f24d5aa3912f6dfcbfeeae4eb47bfbf158e +dist/2025-10-28/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=010292117dca5b10b71b7aabe38d1e8c2f0180ab54e9bf072a9ca8bab3f40ced +dist/2025-10-28/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=91671bdd6438df48e4c529e5897afe57b91e8575c12d40c3cb3280493599c0d9 +dist/2025-10-28/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=0da8474db35ef547b28f123e8c98ec84dcc4a4a5c4a487a222ac006e809ceb80 +dist/2025-10-28/cargo-beta-i686-pc-windows-gnu.tar.gz=5d9ce7ba82e8d694e53ac877214c0873b0dd78a60c8eabcfe9128433d199d12e +dist/2025-10-28/cargo-beta-i686-pc-windows-gnu.tar.xz=209100fad49e274457d8299360a4c2e83803411a69fec69898a03d9a2bdee955 +dist/2025-10-28/cargo-beta-i686-pc-windows-msvc.tar.gz=edca4ca03b055b40d0c4778e325b93e3759123f207a6d32d7ff42cfa0f323604 +dist/2025-10-28/cargo-beta-i686-pc-windows-msvc.tar.xz=5146440d030e04450de61bd6022cd7ad6364fd06f9cec28bbb7889bad171e579 +dist/2025-10-28/cargo-beta-i686-unknown-linux-gnu.tar.gz=d1045ab266d914060e24224b0e74ea865f4bf4cd527d9a09c5631441c40f48a3 +dist/2025-10-28/cargo-beta-i686-unknown-linux-gnu.tar.xz=caed7a0786cc9f476d1b9fb5367c67a02397ccb12ed18a357b5ed04fe77fb12f +dist/2025-10-28/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=017335d6ca97cbe4bf93e2ffb4def850b05a8efa0367b008de4cc8b55f1af3cf +dist/2025-10-28/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=f5671ec0dee9841d4a482183f56ccb986f358a80d043d1ec4b05bc8a356f8ad7 +dist/2025-10-28/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=0a280ebc1b5aab8452f1445fe858ad4808fd7035dd498510951d8b7caa13edc8 +dist/2025-10-28/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=8ab41df2264470d6ed0a6d82b665992f76f9065046a153bc8ee1eda9ca81eaae +dist/2025-10-28/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=7368d0d297fa06108271b03138a9dda0cfbdbe5463a614ebf6fb5b573dde08d7 +dist/2025-10-28/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=1ad0b7d4e3173150768db7105b1a691e8c85db3e9c6a391390e2f5df7844d420 +dist/2025-10-28/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=75740d0b1847f73777894f484e085f70f24d981b0ef69d00cb5b0c29d276c5b5 +dist/2025-10-28/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=1ca71e60a6fa9752dd27bfe4f354a1b732b67e55c9e645021e6041a2450b7994 +dist/2025-10-28/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=ab37e6d39ccb7fe33c55bf62836c2174f1a4197f02cbe9d88d118b81ec6d49e2 +dist/2025-10-28/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=77cc38c5b89fc65fb7453dcc3c3212e0a512923497420b7782ffbfd88f00b39f +dist/2025-10-28/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=29a2dd23fe30b2d1741e1d4032d4250b4f7f0e2af5ae718b309f2d271e13ef41 +dist/2025-10-28/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=bac42c5d4af02abbb4db1a9dd4664340d686e4c23f923f372d3b08e090bd105c +dist/2025-10-28/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=3b396bf0ddede9f4964bc9fb27b52e628db1d9ccc38dbc5d5e06c316ba8bef87 +dist/2025-10-28/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=e706521e4496c79bb677d7d06d79fc1ae2770cdba4a81d8c28ede81ab4ee7909 +dist/2025-10-28/cargo-beta-s390x-unknown-linux-gnu.tar.gz=2fa6ecf04d1cb20dde5f700a0e425ea186d628c5704885e84e197e4494af101f +dist/2025-10-28/cargo-beta-s390x-unknown-linux-gnu.tar.xz=e8810699e679fd0306d4e6c95608970ec5a1d1ccffcdd707fe4a4508518a0e6d +dist/2025-10-28/cargo-beta-sparcv9-sun-solaris.tar.gz=2fcae7eb2f538d84545944c744bbac39e28b3bd58e8d2bdc862d5f66df7068e7 +dist/2025-10-28/cargo-beta-sparcv9-sun-solaris.tar.xz=2fe42f27469fdc23a9e45ec025c60a09f00be07a239cd8c8df74ce52e94b4cab +dist/2025-10-28/cargo-beta-x86_64-apple-darwin.tar.gz=c92c1a580eb85d96f973df4dd9cf4440eb661f8fab9c632b5f0621cf0839badc +dist/2025-10-28/cargo-beta-x86_64-apple-darwin.tar.xz=6c2b13253e1066e49fc52f3cb1654776d5d38853a15e8022652b4a2cb6845daa +dist/2025-10-28/cargo-beta-x86_64-pc-solaris.tar.gz=ca98af5dcaae734ccc02fde2807dca8d95947918eb753d407c12dd05ae937568 +dist/2025-10-28/cargo-beta-x86_64-pc-solaris.tar.xz=864ed5842890359e9f767b4050b8a27ec9c27c88a8cef20993b8f775c213d394 +dist/2025-10-28/cargo-beta-x86_64-pc-windows-gnu.tar.gz=ec8a9e8436f83388736f16f7ad9962a1d9b72673c3da56b8233a6b39dde54534 +dist/2025-10-28/cargo-beta-x86_64-pc-windows-gnu.tar.xz=1f059aae8e6faedcc56663d833a07417e07eededdc2776b2762822987a010131 +dist/2025-10-28/cargo-beta-x86_64-pc-windows-gnullvm.tar.gz=f364bf2f20f35c7ff0381cbd34c88a256849a1b9a27267436e14bc01b7bacfbd +dist/2025-10-28/cargo-beta-x86_64-pc-windows-gnullvm.tar.xz=451f18d049f1c47bfa0c010481e499afa1baaffb889de11534cf451578c62f76 +dist/2025-10-28/cargo-beta-x86_64-pc-windows-msvc.tar.gz=96ca48b12b27b8e933cbb426e7123873a1120bd884a62d150aceb6a26bfc7694 +dist/2025-10-28/cargo-beta-x86_64-pc-windows-msvc.tar.xz=dfe14d019f5c1744a312da8d926059147974de0b5bcdcd869526b5257a1112b3 +dist/2025-10-28/cargo-beta-x86_64-unknown-freebsd.tar.gz=7ef3d3edb6b536382f5063a5de5aaf036d6ba09bf7bd1ff770b87d8942110c2a +dist/2025-10-28/cargo-beta-x86_64-unknown-freebsd.tar.xz=beb5587235e8ff471a8eb9fd84ae5cf4380e7ec9b00ab03dc0c549be0e661dd0 +dist/2025-10-28/cargo-beta-x86_64-unknown-illumos.tar.gz=b97f511486ad044dae0c8b50df8430e489887ac36b770155f163d8e4216cd9c9 +dist/2025-10-28/cargo-beta-x86_64-unknown-illumos.tar.xz=21fc84a42fa988632fb9b0d9dccdf6ab2fbbd06a6780076282fc9bea56567559 +dist/2025-10-28/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=d5b05dc398716909df77415b4a68233b68a5d578855e0e91dcf423f2985fc1cd +dist/2025-10-28/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=21e79bba772e93a6fd910e9d6ee833b55a53b80088f8280525717a4cd800c21b +dist/2025-10-28/cargo-beta-x86_64-unknown-linux-musl.tar.gz=4d126f2591652ddea26884973c1ae1f1e25246cc5f60baeb3a52d6ff45a1aa15 +dist/2025-10-28/cargo-beta-x86_64-unknown-linux-musl.tar.xz=62113a7383f7ff8a9bee099155ca55c3c69d16bf135fc06046f672b2102deafa +dist/2025-10-28/cargo-beta-x86_64-unknown-netbsd.tar.gz=e9a07f035f308d4b9cf5f1b0ab4ef969cad1c41528adea2fca81d8dfe45ebff0 +dist/2025-10-28/cargo-beta-x86_64-unknown-netbsd.tar.xz=3f3d87269072d8e219e7213ca878a7fd441aaf74e4d052ca44ccc7ad75e83eee +dist/2025-10-28/clippy-beta-aarch64-apple-darwin.tar.gz=a44daddb30309be19856b9572679569ffc2ab2c30318706ad59aa2dc14887587 +dist/2025-10-28/clippy-beta-aarch64-apple-darwin.tar.xz=00576ad0f30db5be96e6434e22a2fcdd167067d65ab85199df06a6d11d1ef6c0 +dist/2025-10-28/clippy-beta-aarch64-pc-windows-gnullvm.tar.gz=24fb3dc6680e845c84e535f9c14be5e3d4052ef3ccc800e0fd81815edccb4836 +dist/2025-10-28/clippy-beta-aarch64-pc-windows-gnullvm.tar.xz=20d2ebdb6e2a902eb74ad502a14a2a66e6a9316d5f94513ec9781d146c323d9d +dist/2025-10-28/clippy-beta-aarch64-pc-windows-msvc.tar.gz=652e5bdffbb89ba281cbd5efe3a2cf103adea6af7ba56a21cb6bb538a2ea6536 +dist/2025-10-28/clippy-beta-aarch64-pc-windows-msvc.tar.xz=59d0fb72db77c484571ebab8c992303659225735d3f820ed7048122d8f96d764 +dist/2025-10-28/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=6bf6fb7279ecf5df6edca6014db5f8ba0427fd8cd32f073630b6b02e2e69feaf +dist/2025-10-28/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=0a8d19bca62dc38e64cf527b239106901fa2e0642ca5c2ef1cde070faaa901b0 +dist/2025-10-28/clippy-beta-aarch64-unknown-linux-musl.tar.gz=663581dbd93b22b3b20d281486c8dc0580e43ceca42d9455d78f2d2c341cc758 +dist/2025-10-28/clippy-beta-aarch64-unknown-linux-musl.tar.xz=49548d8ee0561ab96629e354ad055ed5f0c6f3f60c1559ca2a27352b441ff848 +dist/2025-10-28/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=b3d44866b1f909576e0e586aa34b480f1170aa3eba28209ac5bcb929f544d07c +dist/2025-10-28/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=717397166acd760e391c251b1c308657f45deadd928035d2860c772c8b377b91 +dist/2025-10-28/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=8911fa3347ca26a2afed6140bc4cafa629830740b8e4ff5e648c517f6ea43c6b +dist/2025-10-28/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=e7360c728068520ec7bd712d57357ce30bbfbb629ec69cfdcf6596002751d93b +dist/2025-10-28/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=6d94df05e43804f1054d17542db72cfd3f2df27eedc275c810b35362f9d581c1 +dist/2025-10-28/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=25c33744d9722aad4f9fc80674233d16eb653adb17d0deadcd3be8f16a79ecb7 +dist/2025-10-28/clippy-beta-i686-pc-windows-gnu.tar.gz=dfb9ba0cc842b7c92f1dda8bb88bd1db9beeac746c3ca3d144dd4d66d91ff6ff +dist/2025-10-28/clippy-beta-i686-pc-windows-gnu.tar.xz=20f3cf3fe6b52b8f54391be7edd11331a376313c37b674f4987589f50eac68d0 +dist/2025-10-28/clippy-beta-i686-pc-windows-msvc.tar.gz=bcabe1f7679e1ae416c7ed56afd74ad224ff47cd9b5413dececb0e6f503ce393 +dist/2025-10-28/clippy-beta-i686-pc-windows-msvc.tar.xz=f8ebfef39e7119b45a4f714e91b47964aeb6653fbdaf9b01786ef629cbaf3d31 +dist/2025-10-28/clippy-beta-i686-unknown-linux-gnu.tar.gz=f0fc318e90de4f45f6ca7d0d6ca3ed5d044db75f97254d792cdea9a7b196042e +dist/2025-10-28/clippy-beta-i686-unknown-linux-gnu.tar.xz=27c9adb19a7f84edb19ff4c8ce34dd836fb8d10e8786999591e41ec9c60e4602 +dist/2025-10-28/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=04673347c7e13eee9e301ffc1640ce7a5ed733915c29d90750706829d6b02884 +dist/2025-10-28/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=2484f24346fbbd2d48f69d86ab7710ef65a5952bb5ea0141f3388f616c7d6d0c +dist/2025-10-28/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=656eb1b94dd2324958e6ba0b1c7a37281abe24dac7d87b0e406fc10931ea0d29 +dist/2025-10-28/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=3349952da5d2693554d2e1ae60bb7ff9cd64451719cadf4227095b73377228c1 +dist/2025-10-28/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=40eeba3b2807a1d807ed25c3bc0482d5b91135a769d2281a894ec8cb8bb52a35 +dist/2025-10-28/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=0a6e79cea6655d6b3e1ff1218651c627886d8f06a5d36e9c3bd3c9c45b375f57 +dist/2025-10-28/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=b11f919fce34d70816a6dfa0884e37939c38336bad6261fccf5d67d25935852c +dist/2025-10-28/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=ead91ce4260d07d4e4f950092e94ae224f067ae3c711efe2ccedc9250b2f3dd7 +dist/2025-10-28/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=481a2e3b45751e2d86c30d4fb3897f0a7a18e9eadde2c9a3ad1a8b0259fa2066 +dist/2025-10-28/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=d5d6529cfc89c29443d9e87041a5f93b746e5bc5791016242e2f271be5bcb557 +dist/2025-10-28/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=a70d30948f5e95cdd454fa2b3284b97f29134f59e98e130fc2877781f3b62e89 +dist/2025-10-28/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=8dddfc0b1b2ca8e8866a11a1c95b8601c50150f1e52adbd3803cca2fabb386e9 +dist/2025-10-28/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=bd1b28dd79a52de657515ba5c25063b0b6015eb30ee3b0ee05511567b429c992 +dist/2025-10-28/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=5262011db6be068ef70905a28939b1838a2fd2b719d38d59e80754695049269b +dist/2025-10-28/clippy-beta-s390x-unknown-linux-gnu.tar.gz=6d3d83d03c46e1de2cc6b6f204e701acc9dddb6d4baef80fada9d868b7eb86cc +dist/2025-10-28/clippy-beta-s390x-unknown-linux-gnu.tar.xz=e57ac819f962d9b431b699b40a047d3e2530f76f2670c5a04747b7de11c3d5e2 +dist/2025-10-28/clippy-beta-sparcv9-sun-solaris.tar.gz=7a492d27b49facb9554248935728cba483eb7ecf3de317751a7445c3932932e6 +dist/2025-10-28/clippy-beta-sparcv9-sun-solaris.tar.xz=cc02f4f971b9e5eb63ca65ff6947cf3490101bfb40ea9fb2b310283bb6d10b27 +dist/2025-10-28/clippy-beta-x86_64-apple-darwin.tar.gz=56f11b8ca012467ef6aa8dc3458a5bf45950f1de062a616a01f6c0aae412d0fd +dist/2025-10-28/clippy-beta-x86_64-apple-darwin.tar.xz=01fe53ec63d71a8427fa3220d037313fc31f8d0fa33a64305b01109344aac21a +dist/2025-10-28/clippy-beta-x86_64-pc-solaris.tar.gz=d222cfda8613bba09b0ac1d02fad99c0d081d10733f0f9253d809221ffdda506 +dist/2025-10-28/clippy-beta-x86_64-pc-solaris.tar.xz=918706ee8ed38bf398cbf0dad85333e3082dc461fff2fb2399b96e840dc24766 +dist/2025-10-28/clippy-beta-x86_64-pc-windows-gnu.tar.gz=7764df051100b08b4b4e5c434d5ced1aaa84d9665f6a4401c9ed6350db983b70 +dist/2025-10-28/clippy-beta-x86_64-pc-windows-gnu.tar.xz=4a43dee6ce0279de929566f844fa4987d10fa5ca856bb2a2cbea4327eef3308e +dist/2025-10-28/clippy-beta-x86_64-pc-windows-gnullvm.tar.gz=add2532fe4e8afb8b53aee8a729fde0c0b36f83a8f6a859c39ae25f3d01e8dd6 +dist/2025-10-28/clippy-beta-x86_64-pc-windows-gnullvm.tar.xz=7117f0afc57ac09c277ba99be4a7de80d62ffaebe4174706910d5ab6501ec9f7 +dist/2025-10-28/clippy-beta-x86_64-pc-windows-msvc.tar.gz=e71d45b8bdb5346d89f769b4698b404b293884878a7f697680318c72829c1d6f +dist/2025-10-28/clippy-beta-x86_64-pc-windows-msvc.tar.xz=67be8cf6580a4a6e36eff94bfe19fdad290eae5db598d2a016dfcc7516178e14 +dist/2025-10-28/clippy-beta-x86_64-unknown-freebsd.tar.gz=03b78bad90efe98cab6416ecc641b7da0c9a2be5950edbb4c520c0758cdb90a8 +dist/2025-10-28/clippy-beta-x86_64-unknown-freebsd.tar.xz=056aecae34c8ae10a41516834809d983bd6126bd0374a7bf0857710492f0d0a5 +dist/2025-10-28/clippy-beta-x86_64-unknown-illumos.tar.gz=a67147c647da8a9b68a507087b66610ed19e65b94538b08326b000e66c170260 +dist/2025-10-28/clippy-beta-x86_64-unknown-illumos.tar.xz=09bd43034d7bc1e9ad1873610237f98543751809ebe63d8255813eadf4a00e70 +dist/2025-10-28/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=9397e5c3b1f45ce949b8e3420233e8b86f95a887667c917b8e7615218989820f +dist/2025-10-28/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=5b67e9eb803c180d101e799f6d7c2a6ea7ec07a93a6395b4f688bf2592bc30d5 +dist/2025-10-28/clippy-beta-x86_64-unknown-linux-musl.tar.gz=b255c33a89b0b1bd666ffb584c20554daa3a7c48123de75eae5e1b4cbc53e1aa +dist/2025-10-28/clippy-beta-x86_64-unknown-linux-musl.tar.xz=bc7f3f7d59f9c52370849092541a9368632f390ed9bc88f2566f1e3700f4da80 +dist/2025-10-28/clippy-beta-x86_64-unknown-netbsd.tar.gz=a8f366918520cc5a82c9bcfbb419570e6792712455ec5c04655e6557a657e949 +dist/2025-10-28/clippy-beta-x86_64-unknown-netbsd.tar.xz=dad3555be56e7c8d042f65dfced7915856f9f23cec13a7a633a4aad38c634247 +dist/2025-10-28/rust-beta-aarch64-pc-windows-msvc.msi=943683af0d464ecd36678e1c87b1207a67105820cb3d1516eeebaae450a2928b +dist/2025-10-28/rust-beta-i686-pc-windows-gnu.msi=8532743b3406fbf2926e5c6643300194eaace9de492e7f2ad0caa6f7d040f8db +dist/2025-10-28/rust-beta-i686-pc-windows-msvc.msi=1b3b15aa90c3dca73e9d72d53d214eb3c02b97386c83d8755a03f571cc09de4e +dist/2025-10-28/rust-beta-x86_64-pc-windows-gnu.msi=6249ad7cfcfe78b541e4935ed18dc4cfb10130252885668383a4c8f5046ecc2d +dist/2025-10-28/rust-beta-x86_64-pc-windows-msvc.msi=700f63a1baf0de2f0d1972454486f890af15b3700481698ffb0daf403778390a +dist/2025-10-28/rust-beta-aarch64-apple-darwin.pkg=1f2862fd5dbf8a9c7a765e75391cfffc5a3cd6837066b29f95778b5a346f0f59 +dist/2025-10-28/rust-beta-x86_64-apple-darwin.pkg=ffa2c6275b401c73575a5158911bc53edfece2edc03f6b3f6a140823cf870ee4 +dist/2025-10-28/rustc-beta-src.tar.gz=dbbae9e1c0b5e914b588ba5b659b7fb6af3d5b422831aba028d64b00c7161417 +dist/2025-10-28/rustc-beta-src.tar.xz=aa771d2b1ea8c933444c962c03c384bf2511bdeddf90edcaac8f45bd49f5232c +dist/2025-10-28/rustfmt-nightly-aarch64-apple-darwin.tar.gz=5a5e525f45010fd3e30503f408f21eef78cafa54e42adf9709cff50f6cb29720 +dist/2025-10-28/rustfmt-nightly-aarch64-apple-darwin.tar.xz=b6f92173abde0be1f4f3378c682e42016390e6e5e20df7e84cbd7804a6161114 +dist/2025-10-28/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.gz=430775c6856f4aea6a2b83e245904d5527f6ace134a9375dc7172f77ecd328f6 +dist/2025-10-28/rustfmt-nightly-aarch64-pc-windows-gnullvm.tar.xz=3d14ccf194d04ad5767a5fc3df6f9c1427cd0e282671887d2264b8314586e168 +dist/2025-10-28/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=251f7fa0b78fdad3be1977ffd6a2f563d1524b6423d2bab73d1741aec97b4717 +dist/2025-10-28/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=534cacc40024fec78c02916e7e2d46cde8b8153d303efc1b805af8789f25fb1e +dist/2025-10-28/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=5663e413cd9ae9f403a4b5c5e9a9862c00d57bcd68bde9161c91e852115e45cd +dist/2025-10-28/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=3625b287a83796cb71741b9a4b002eb4517950d386f97a91470c0acd5aed835c +dist/2025-10-28/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=657b0774a95a925eb38d039ef11bc23d15c4733d0b322f172f0b8236bc7410f6 +dist/2025-10-28/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=4e058f11346ec86801bc9cdb830c6dec2620b81fe2cd4b439b275a5145859dfa +dist/2025-10-28/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=820ece81e455d11f65ddf09093b6bf97359cc803959d951bd199c70a79ae117e +dist/2025-10-28/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=8a72a3e715ab2427fc120f8af20d67f722881ee9e13bf482e45ef07f5fb2bf86 +dist/2025-10-28/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=fb583c846e1a11663a1fc525f02c0146dc70e7b4da12c264c8b00cf7927ad9c9 +dist/2025-10-28/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=8a15430000d48b702142cc4569a5eef3aba59c07f33f69cfb6966250fae18178 +dist/2025-10-28/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=c470dd9f35b1b701e8475716e61cfadee5475fa29a2994f407d532a794c0ef82 +dist/2025-10-28/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=774898dd43893b99e4e11d004c52244d78d2fb08aaa6fa242cf1d7070d9ff8ac +dist/2025-10-28/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=31ef71bea5f5ce3668b903fabb1582dc256fe6e2f97e5c23ca468e05f3deb7c9 +dist/2025-10-28/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=232351f0a578f9973b536eb587b2e90cb01ca5f76ad376c09d7dd225c09810b0 +dist/2025-10-28/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=eb469e8475af339d36e1381474f931e326ef1f095a6f56f44730019c5f6422fb +dist/2025-10-28/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=42c5f2e88f81cdcc375d26c4ab6d9ec03b387b8de632af8a3fa75bcbfcbf56da +dist/2025-10-28/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=79e06c14b8f18927b1c24cc4ea97e55a805355137a9bdbe3ba616e334878d85a +dist/2025-10-28/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=3883c297de03da522a20b893f24970305fadaf726dd9388f89196e1f284115e9 +dist/2025-10-28/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=b3ddf283d7964a4c261668df7685bc47b469c7294325f975436d465cb7d44163 +dist/2025-10-28/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=9e63e1bd8b4c52943092e91942737be9b3ee9c128dfbeafdc01d3dc732058165 +dist/2025-10-28/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=83845c5809a9b525655565b3e3b4889e2a07562587c6f046280a44dcbb809094 +dist/2025-10-28/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=533113ead2985a36cf685cfbd6645d43f1a7e2e66b6eaadbde2ff5a72d10c5dd +dist/2025-10-28/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=82c6aa57adb0f03b96982f51edc65aebfac3811cd2845d280b7bf5fb81cbe92a +dist/2025-10-28/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=8f4b3c91072d56ba80ba4702e611dbc6fe66163666e6ee35d74602ef36fe9839 +dist/2025-10-28/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=4fe0b85296e2249b36a0b021d6b82e7c31c40a5f717a5f447b6bf9b9524069a2 +dist/2025-10-28/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=9257bb295a58a7df3fbbda809bafc74a8601618afd0fd41467439a8972278cae +dist/2025-10-28/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=d3a5851060a66a0e027ba142dbdb6e827f4f5f5116b5fdb0b8e86ee56616d682 +dist/2025-10-28/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=fe1e9e4e3a234ba8cb656b08b7bbf5349f5068906cb60a4c5c53f7fe3e214487 +dist/2025-10-28/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=2187f1ccbd4e638efaf9f05566998b579b32729fd9ee4332964a3c54556c3a20 +dist/2025-10-28/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=88526ca6af21bdbc6c46130012cb5fdde25fd44e6372187e66d273067efbc052 +dist/2025-10-28/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=9299c8d4dfc320cf331dc4005c36a0108961a6d5ad750a5b0a6be8c7cda77953 +dist/2025-10-28/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=7da58822a9ba4d65258ca0cd514b9abcc85070baa195ce65d40ed9d9de049049 +dist/2025-10-28/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=e07cc8f1166bb39aada202559c1e2ee9190cf85fc11b4d4abb921cab352748f8 +dist/2025-10-28/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=ebcb49b4ee1e8dded5e6569c7edea1578a369e92aac9030a51d11e95bd8bb8b1 +dist/2025-10-28/rustfmt-nightly-sparcv9-sun-solaris.tar.gz=247ce6a1fb10911b8f38bc8f88617d9a7dd2147e1f1896710b0b610f8bb4b86e +dist/2025-10-28/rustfmt-nightly-sparcv9-sun-solaris.tar.xz=020d64391c730a61f4c61d802b432a463d4909d0e4d5fe4756db4683df6e000c +dist/2025-10-28/rustfmt-nightly-x86_64-apple-darwin.tar.gz=547d7711acee55435e271fca1cebead82045375b0fb136404be7df379b0d404b +dist/2025-10-28/rustfmt-nightly-x86_64-apple-darwin.tar.xz=e8ef4fa56be91b642fd3a8c29afb9dfb3fb7601c8fbd24393be4f3dea1802163 +dist/2025-10-28/rustfmt-nightly-x86_64-pc-solaris.tar.gz=0e41caa314927c9a8656d16b6623c95463a64f07a23941d03597a17582c224d6 +dist/2025-10-28/rustfmt-nightly-x86_64-pc-solaris.tar.xz=d73dcd42d9110befbdf64c46b9e4e8bad07839302cec49e2d8cd99ab436d4bbf +dist/2025-10-28/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=8e821c45dc91d5df1b9f8487554cadc4deb86a9ef3db25f4e21b38eab3cc8ed2 +dist/2025-10-28/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=f98ccfcc37b7412788a546cbafe5f09b90c26f823e3cad17e71432df4ba910ef +dist/2025-10-28/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.gz=762e15406bac23f0f41dae2b1b3370458b92e8be771868b4f8f536f53cb26fc3 +dist/2025-10-28/rustfmt-nightly-x86_64-pc-windows-gnullvm.tar.xz=cd87b4a4c2af3edea8ba09ce9bb8704449d8f51c5dee8ba10161e0ce2ec2aa19 +dist/2025-10-28/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=7de2ee69be5384656024a6d7c3481315fe90abc9ba9a26726f1eab09aae2c818 +dist/2025-10-28/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=16377ae5474e30b6c11ba886579ef13db4039d0b9f43240db00aacf7366b37d7 +dist/2025-10-28/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=d98fd17080ed5a4fda444719011540a80320001a0b0dc36148f2e3f78e5f5c33 +dist/2025-10-28/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=c825c2b46f43fe71f4452494fb422cd9565ec6454857835a6c77081553081523 +dist/2025-10-28/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=8844743cc05351b1f6ba8fb50ac1d86161a285775eb4c0e474308aec71b631c7 +dist/2025-10-28/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=172dc660ceb00e7003e855a09f46d9f83096773495ed6d7b8b1ed6b9e770912f +dist/2025-10-28/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=2710450c26834b07a5eb80bf990dc67a6db89f87e26d6b613ed96116c28958d4 +dist/2025-10-28/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=bbb8eac454e0ba3b23c9803d8201fa341d0f0af509aa9e8b530c9b98a1d9886c +dist/2025-10-28/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=e6bb6a67878c9cbbbbc96f1a1858500403ecc3b8c951366e18ace0f2601f41cb +dist/2025-10-28/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=f6509af9551e73ac16c425c53e667382bf7b6b48be65f4779485ee7843e68030 +dist/2025-10-28/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=3915f1907939fbfcd82012efa4293e7ddf22bd5bf1484efffacb31c805e6cf68 +dist/2025-10-28/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=cca6182ce9f2906a8b16971741a8435dbcf60d9ef6020cc7d6f7909525a73dc2 +dist/2025-10-28/rustc-nightly-aarch64-apple-darwin.tar.gz=876004fa16a6154ea8ea46cd1bb60a06fc14b24e72e26e20e42fde4e32882b46 +dist/2025-10-28/rustc-nightly-aarch64-apple-darwin.tar.xz=0a98f29bb6a2a1555f6ceaa161ecdd2e599ffe879b98bc98c6066e257fc12362 +dist/2025-10-28/rustc-nightly-aarch64-pc-windows-gnullvm.tar.gz=b07b2f7975b463970cbb4a8c92e51788ac63c3a46d3ae6c8cb54c7024161d899 +dist/2025-10-28/rustc-nightly-aarch64-pc-windows-gnullvm.tar.xz=5445b2e30804733fe2a7d07f66a77c2e3ec5e7c71bb55403541b8d365cf91870 +dist/2025-10-28/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=d2cff6b8cb52172f4bb394f8ca891897c774ece1b87f9d30729aa236dfa24900 +dist/2025-10-28/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=a6d0c00367b49654c91d4b71b33819854f628d51b7193c2475b9f737258a575a +dist/2025-10-28/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=c30649c2360aef41d97bdbf76d55bbfe674a1225161bc125a8215d3954656cda +dist/2025-10-28/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=3b204fb1461f47b4e2a79a67923abcf8610ab7ab006ee3f9a70cdc87458f81c5 +dist/2025-10-28/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=949f55697b50db7d70a6465b100ae4458d2f676b818ca7227014ddc9c5f707e5 +dist/2025-10-28/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=ff500cd8f318f0316936af462264254df2aa1bd5a2b368ad3c5758e9079eaf71 +dist/2025-10-28/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=231c4cfa37df03a0e0517843bd115a0f0b13ec259397d6e4dfb4014d3a0d4d9f +dist/2025-10-28/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=50f4d7060d02833949ffc970b88c5ad2183fabd3c498334bf63d87d0c9602720 +dist/2025-10-28/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=f89586ea5bac722a06c225a50d7d603b4895aea89297509f248a1cb51a5cf0ed +dist/2025-10-28/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=0de0552ae68cb9b61f2c5031cdbefae5ccde39c4003441e93c2e4e704d3751aa +dist/2025-10-28/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=019bbb267d1c0520a715287e0a1a680b58dd5e9ece2071f3ebd6f2b1d8eb2228 +dist/2025-10-28/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=069bcd46adfb5e14786fd8aced5ceb74af24cc05dfa2bd41ebd81ac146122598 +dist/2025-10-28/rustc-nightly-i686-pc-windows-gnu.tar.gz=911eb977b10aa4bb1f25124f08f3397fb3ac65f36666865be40bf44259b5d77a +dist/2025-10-28/rustc-nightly-i686-pc-windows-gnu.tar.xz=14ed0abc8708a90e8bbaf0c0de2aec1a4ed60c63b4db00fd4a732f3c808c125b +dist/2025-10-28/rustc-nightly-i686-pc-windows-msvc.tar.gz=8917e6dd4786f0f413908e9db4c92e060bce525f91b9cc0d43b453d26057435d +dist/2025-10-28/rustc-nightly-i686-pc-windows-msvc.tar.xz=86a8580a180de9224c176beb1ada2c69acc37fc73ae0e7c7780bbbded842365c +dist/2025-10-28/rustc-nightly-i686-unknown-linux-gnu.tar.gz=0d29e999ccbc46ff6b0c50a5648e0c5c8f767daa9a0d1fc86dff30c6e0078dd5 +dist/2025-10-28/rustc-nightly-i686-unknown-linux-gnu.tar.xz=bcafb54ca3f587d3cc9302c057da2fbdb4531e2279ef462fc8682e82e1046561 +dist/2025-10-28/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=0a5bfd49a7a2fb98fe06815a2a50d9dc859722da3cc107928e36596c7b8c7f8e +dist/2025-10-28/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=b8ec7898e087577b78ef1b716f8935c7023d740b389234f38075a09b2c7b494a +dist/2025-10-28/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=5cab6709c372feed484b5148ec642cf9767e7fe051146023459467012f2e0b64 +dist/2025-10-28/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=e42a3c007bdf7e8179c597daffc61d7a6bd8eac782b8d81d0b883267bc226f86 +dist/2025-10-28/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=a7cb220770ebd482ff9737d787d04e2e1274c46b7da0809041d06712e27f69ae +dist/2025-10-28/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=088b1d0898bc5b5d83d0fe20edfdadc2d1c0865c39108d11a5d078074aa37a1f +dist/2025-10-28/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=90a3b28ca667187fb18eee8b87f5aebdb2c2e457acbe5b7b088d9b9a39314a1a +dist/2025-10-28/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=4eb8f1d20b2eeb85a2b78d3e28a1d665e1171e1271540bdd8453833818d53c30 +dist/2025-10-28/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=e4fc106d7d4e098912b4dc0ac0211769ecc1f01731e118bf6c0bca33c7ceb9f5 +dist/2025-10-28/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=74bf23136731240e8dac7497d8b1a244fc35cd529d12f77fff308099354f3aef +dist/2025-10-28/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=85ada50366246530a0dcbd462aef31a6edac600d22074d97bfc07321569eafdf +dist/2025-10-28/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=f0cb51df2f6639fc65091608d569b9ac0730f5a8d88e27079d87bc8a8834b1e4 +dist/2025-10-28/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=c37432d8d8db7aaad9132e1301025dd5bf3671bad0ac1fea0f04e9aae238cae3 +dist/2025-10-28/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=57f72896f0309149a4b376a21d56ea28e65316db08065bfe32cdbd750bbc9657 +dist/2025-10-28/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=170776eefa09f01859af0b3694c013ebfab04e9fcf8fb44233593ef4a763bd38 +dist/2025-10-28/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=29335ffe7d88b758ec1a7a067056e35fe8f24430e8d899df3256b4e2d19ac16e +dist/2025-10-28/rustc-nightly-sparcv9-sun-solaris.tar.gz=14b5cfe710c92e8691aaad99de891695ef025638eca065587107c075a31215bf +dist/2025-10-28/rustc-nightly-sparcv9-sun-solaris.tar.xz=d21bb41b4abdaededefd199192bc257424295ec74d482693d032ca67f02dee15 +dist/2025-10-28/rustc-nightly-x86_64-apple-darwin.tar.gz=09c7d0777ea308ad552ea1e958f5c65fa3380c9cb93efa1e6edec3a29fc30b5c +dist/2025-10-28/rustc-nightly-x86_64-apple-darwin.tar.xz=923ef47f5bd9608d277b6636b8c798c5b2760460264fa6f0c0836f317746a89c +dist/2025-10-28/rustc-nightly-x86_64-pc-solaris.tar.gz=805643ab958df449ffd8dd5557e43c5e7060a10a3deab2238096b721ab451f08 +dist/2025-10-28/rustc-nightly-x86_64-pc-solaris.tar.xz=f2fb0c0643808bd9f483bf9bf66ead4444285f2e50bc1264b4861deddfaa9c62 +dist/2025-10-28/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=05b7ee4041b2cdcee1739da7a22e4083acc24c043d11cb3f61d0c22237a9f42a +dist/2025-10-28/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=2f3b04d56dd929ab5997711bc52395fa700247ff38b9362cda375b685c740337 +dist/2025-10-28/rustc-nightly-x86_64-pc-windows-gnullvm.tar.gz=52575038868d91349b40434ec8faf9836a0aa0a4f77c2decc634a9ba02796c07 +dist/2025-10-28/rustc-nightly-x86_64-pc-windows-gnullvm.tar.xz=d2d1f2a0d26cc3dc86d88ae46a9dcaa5f0ce694ebac3f46b7466fb058a1a76e5 +dist/2025-10-28/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=5469701cbe03939a82ce5ec50dc166342ab968d2f96cb372dee8f29dfd6e4362 +dist/2025-10-28/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=e6d18f7b7fa3a3613f1aa129f4a14820d4fa1977550689cd99b7dad12054edab +dist/2025-10-28/rustc-nightly-x86_64-unknown-freebsd.tar.gz=e2a5ff9df2b07f8c939fdb885da717faf19a5b3e63ab780962dc288f832cba6f +dist/2025-10-28/rustc-nightly-x86_64-unknown-freebsd.tar.xz=b577558a8f1b42d6bbbc80804e3b72835a85979c98659696db32832595283d04 +dist/2025-10-28/rustc-nightly-x86_64-unknown-illumos.tar.gz=5535a693d5b8edf3fae7e1da97c20596a1f60135fefe22835109ed3648765a16 +dist/2025-10-28/rustc-nightly-x86_64-unknown-illumos.tar.xz=bfc15f0678099ea110135d4e7bcb1b9a545f041e5810f6528d49e0b5451a29bb +dist/2025-10-28/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=877bf56730551f851e2ce5279c105f231906959bcf904aa2fc29acaba5160cc5 +dist/2025-10-28/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=911bf7891d7687675f9200d3bd51946e21df3762b5320b7d31bd49d58526a8a7 +dist/2025-10-28/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=c0c26704d16d6612d0d99ad078bbcb2ac0ed86419a0404a9e08739990acc5ad9 +dist/2025-10-28/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=677f5c6126262b64126b5fc7c3c527cbc9e8ba62aaf4376c924149b6d05e16db +dist/2025-10-28/rustc-nightly-x86_64-unknown-netbsd.tar.gz=84c82523969ff1b987a09d5f7401e9a773b3110f51f00d708a4791ef6615272b +dist/2025-10-28/rustc-nightly-x86_64-unknown-netbsd.tar.xz=680eb70467d5695e986b013b3d16dbd0c0b0b7bc85cf8df4a3392c100f97eff9 +dist/2025-10-28/rust-nightly-aarch64-pc-windows-msvc.msi=a8f7ef71113d5ca8094d9433f23d5bab8ae910803e0cc23110d4fd0e04816bf3 +dist/2025-10-28/rust-nightly-i686-pc-windows-gnu.msi=6c4c49c2c2303f94a39d5c598c4f75b9d2c894b1c30aa6b49c8ab7ca674c8ca9 +dist/2025-10-28/rust-nightly-i686-pc-windows-msvc.msi=f7c80d8c614f5b53a596bfd8a263fc0e680b554ec47630e36e23354afb7fd426 +dist/2025-10-28/rust-nightly-x86_64-pc-windows-gnu.msi=2f830bf3391a7da78de14ba4ffd82de8a49652262fcc7a3ae377a3703fe7ab93 +dist/2025-10-28/rust-nightly-x86_64-pc-windows-msvc.msi=c9b61b50bace832fc504853a5ea5dacd869909946c04d1d9c95b15385e98fd47 +dist/2025-10-28/rust-nightly-aarch64-apple-darwin.pkg=8d7cb75018220eac497ffa180c94b210e2e53e8e8c36ab8a39d6c4c6234fe72b +dist/2025-10-28/rust-nightly-x86_64-apple-darwin.pkg=82e9979fe0cdbac0e008f46cd2d7da70e9fa2caa0d4f1df56654cf57114c993a +dist/2025-10-28/rustc-nightly-src.tar.gz=f6da3a086dd47e5ace7eec9f03879d4c3082503912fe631e54e38168dac60f22 +dist/2025-10-28/rustc-nightly-src.tar.xz=523289ae9e6a87f076decc1beb412897184e728302826882bf53dfde4df4ce15 From 8fd2dc0034a3d95626504cba6f4b7daf4d7599d2 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 28 Oct 2025 13:34:47 -0700 Subject: [PATCH 163/170] Update `#[cfg(bootstrap)]` --- compiler/rustc_index/src/idx.rs | 2 +- compiler/rustc_index/src/lib.rs | 1 - src/tools/miri/src/lib.rs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_index/src/idx.rs b/compiler/rustc_index/src/idx.rs index 2fb2008f9a3d..60c4dd90d8f6 100644 --- a/compiler/rustc_index/src/idx.rs +++ b/compiler/rustc_index/src/idx.rs @@ -135,7 +135,7 @@ impl IntoSliceIdx for core::range::RangeInclusive { } } -#[cfg(all(feature = "nightly", not(bootstrap)))] +#[cfg(feature = "nightly")] impl IntoSliceIdx for core::range::RangeToInclusive { type Output = core::range::RangeToInclusive; #[inline] diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index f61b5cf52798..42d284938468 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -1,6 +1,5 @@ // tidy-alphabetical-start #![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))] -#![cfg_attr(bootstrap, feature(new_zeroed_alloc))] #![cfg_attr(feature = "nightly", allow(internal_features))] #![cfg_attr(feature = "nightly", feature(extend_one, step_trait, test))] #![cfg_attr(feature = "nightly", feature(new_range_api))] diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 1206859d1cdc..b756fbb901bc 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -17,7 +17,7 @@ #![feature(derive_coerce_pointee)] #![feature(arbitrary_self_types)] #![feature(iter_advance_by)] -#![cfg_attr(not(bootstrap), feature(duration_from_nanos_u128))] +#![feature(duration_from_nanos_u128)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, From 8e0fe66920502044464265aa7960573a59c07f84 Mon Sep 17 00:00:00 2001 From: ltdk Date: Tue, 28 Oct 2025 16:41:32 -0400 Subject: [PATCH 164/170] Run regression test for Cow inference on next solver too --- tests/ui/traits/generic-cow-inference-regression.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ui/traits/generic-cow-inference-regression.rs b/tests/ui/traits/generic-cow-inference-regression.rs index 6fd4715f85bb..e9dd76d1aea4 100644 --- a/tests/ui/traits/generic-cow-inference-regression.rs +++ b/tests/ui/traits/generic-cow-inference-regression.rs @@ -1,3 +1,5 @@ +//@[new] compile-flags: -Znext-solver +//@ revisions: old new //@ run-pass // regression test for #147964: From e9252a42f531c14230ed3465e618032dedfdbce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 26 Oct 2025 09:18:41 +0100 Subject: [PATCH 165/170] Skip parameter attribute deduction for MIR with `spread_arg` When a MIR argument is spread at ABI level, deduced attributes are potentially misapplied, since a spread argument can correspond to zero or more arguments at ABI level. Disable deduction for MIR using spread argument for the time being. --- compiler/rustc_mir_transform/src/deduce_param_attrs.rs | 5 +++++ tests/codegen-llvm/deduced-param-attrs.rs | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index 21762c8dd68c..1b3e01f3a380 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -195,6 +195,11 @@ pub(super) fn deduced_param_attrs<'tcx>( // Grab the optimized MIR. Analyze it to determine which arguments have been mutated. let body: &Body<'tcx> = tcx.optimized_mir(def_id); + // Arguments spread at ABI level are currently unsupported. + if body.spread_arg.is_some() { + return &[]; + } + let mut deduce = DeduceParamAttrs::new(body); deduce.visit_body(body); tracing::trace!(?deduce.usage); diff --git a/tests/codegen-llvm/deduced-param-attrs.rs b/tests/codegen-llvm/deduced-param-attrs.rs index 8062419ed5c6..eec15a434645 100644 --- a/tests/codegen-llvm/deduced-param-attrs.rs +++ b/tests/codegen-llvm/deduced-param-attrs.rs @@ -3,7 +3,7 @@ //@ revisions: LLVM21 LLVM20 //@ [LLVM21] min-llvm-version: 21 //@ [LLVM20] max-llvm-major-version: 20 -#![feature(custom_mir, core_intrinsics)] +#![feature(custom_mir, core_intrinsics, unboxed_closures)] #![crate_type = "lib"] extern crate core; use core::intrinsics::mir::*; @@ -170,3 +170,11 @@ pub fn not_captured_return_place() -> [u8; 80] { pub fn captured_return_place() -> [u8; 80] { black_box([0u8; 80]) } + +// Arguments spread at ABI level are unsupported. +// +// CHECK-LABEL: @spread_arg( +// CHECK-NOT: readonly +// CHECK-SAME: ) +#[no_mangle] +pub extern "rust-call" fn spread_arg(_: (Big, Big, Big)) {} From f583ecd09d4079a6cbd249c6b4e9cf2656a790e8 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 21 Oct 2025 20:54:47 -0400 Subject: [PATCH 166/170] Update cargo submodule --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 344c4567c634..6c1b61003436 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 344c4567c634a25837e3c3476aac08af84cf9203 +Subproject commit 6c1b6100343691341b9e76c5acc594e78220f963 From 51f3cab3b9b05709031e7c5e1b2ec2f55f03e0df Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 29 Oct 2025 12:05:08 +1100 Subject: [PATCH 167/170] Always print a signed percentage in job duration changes --- src/ci/citool/src/analysis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/citool/src/analysis.rs b/src/ci/citool/src/analysis.rs index 8ba8f1ab564b..39b115154f9f 100644 --- a/src/ci/citool/src/analysis.rs +++ b/src/ci/citool/src/analysis.rs @@ -237,7 +237,7 @@ pub fn output_largest_duration_changes( println!("# Job duration changes"); for (index, entry) in changes.into_iter().take(10).enumerate() { println!( - "{}. {}: {:.1}s -> {:.1}s ({:.1}%)", + "{}. {}: {:.1}s -> {:.1}s ({:+.1}%)", index + 1, format_job_link(job_info_resolver, job_metrics, entry.job), entry.before.as_secs_f64(), From 651076aaed1778a1ec60fb6036383d764c2df208 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 30 Oct 2025 04:53:23 +0000 Subject: [PATCH 168/170] Prepare for merging from rust-lang/rust This updates the rust-version file to 292be5c7c05138d753bbd4b30db7a3f1a5c914f7. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 405e0026b305..bf33c10dd035 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -27050c0d15af664cf43ce4b0badec230e0bfcac5 +292be5c7c05138d753bbd4b30db7a3f1a5c914f7 From 8c678cc8e01d5a5d0c38503790573e298e1ed278 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 30 Oct 2025 08:24:29 +0100 Subject: [PATCH 169/170] update comment regarding disabled jobs --- src/tools/miri/.github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 3078cd35b108..27d2f4a6df30 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -31,7 +31,8 @@ jobs: os: ubuntu-24.04-arm multiarch: armhf gcc_cross: arm-linux-gnueabihf - # Disabled due to Ubuntu repo trouble + # Ubuntu mirrors are not reliable enough for these architectures + # (see ). # - host_target: riscv64gc-unknown-linux-gnu # os: ubuntu-latest # multiarch: riscv64 @@ -68,7 +69,7 @@ jobs: - name: install multiarch if: ${{ matrix.multiarch != '' }} run: | - # s390x, ppc64el, riscv64 need Ubuntu Ports to be in the mirror list + # armhf, s390x, ppc64el, riscv64 need Ubuntu Ports to be in the mirror list sudo bash -c "echo 'https://ports.ubuntu.com/ priority:4' >> /etc/apt/apt-mirrors.txt" # Add architecture sudo dpkg --add-architecture ${{ matrix.multiarch }} From 3048ef2c4b9b4110280bd3ccff53a64b9bc96ec6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 30 Oct 2025 08:25:01 +0100 Subject: [PATCH 170/170] unconditionally use Duration::from_nanos_u128 --- src/tools/miri/src/clock.rs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/tools/miri/src/clock.rs b/src/tools/miri/src/clock.rs index dbbe741a0714..47608f873a48 100644 --- a/src/tools/miri/src/clock.rs +++ b/src/tools/miri/src/clock.rs @@ -46,21 +46,7 @@ impl Instant { InstantKind::Virtual { nanoseconds: earlier }, ) => { let duration = nanoseconds.saturating_sub(earlier); - cfg_select! { - bootstrap => { - // `Duration` does not provide a nice constructor from a `u128` of nanoseconds, - // so we have to implement this ourselves. - // It is possible for second to overflow because u64::MAX < (u128::MAX / 1e9). - // It will be saturated to u64::MAX seconds if the value after division exceeds u64::MAX. - let seconds = u64::try_from(duration / 1_000_000_000).unwrap_or(u64::MAX); - // It is impossible for nanosecond to overflow because u32::MAX > 1e9. - let nanosecond = u32::try_from(duration.wrapping_rem(1_000_000_000)).unwrap(); - Duration::new(seconds, nanosecond) - } - _ => { - Duration::from_nanos_u128(duration) - } - } + Duration::from_nanos_u128(duration) } _ => panic!("all `Instant` must be of the same kind"), }