From 673e443793a4e0c47dc194209888fbc0c50cf815 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 23:46:59 +0000 Subject: [PATCH 01/82] Bump lodash from 4.17.21 to 4.17.23 in /editors/code Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.17.23 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- .../rust-analyzer/editors/code/package-lock.json | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 57f6bf69beb08..887162292d9e4 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -1486,7 +1486,6 @@ "integrity": "sha512-4gbs64bnbSzu4FpgMiQ1A+D+urxkoJk/kqlDJ2W//5SygaEiAP2B4GoS7TEdxgwol2el03gckFV9lJ4QOMiiHg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.25.0", "@typescript-eslint/types": "8.25.0", @@ -1870,7 +1869,6 @@ "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2840,7 +2838,6 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -3322,7 +3319,6 @@ "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -4410,7 +4406,6 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", "license": "MIT", - "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -4655,9 +4650,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "dev": true, "license": "MIT" }, @@ -6678,7 +6673,6 @@ "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" From 802ee0156a35e4e334eb32bec9a8f9ea16995494 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 27 Jan 2026 21:18:09 +0800 Subject: [PATCH 02/82] fix: offer `toggle_macro_delimiter` in nested macro Example --- ```rust prt!{abc!$0(3 + 5)}; ``` **Before this PR** Assist not applicable **After this PR** ```rust prt!{abc!{3 + 5}}; ``` --- .../src/handlers/toggle_macro_delimiter.rs | 54 ++++++++++++++----- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index 60b0797f028a9..e9865aec4a146 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs @@ -1,6 +1,7 @@ use ide_db::assists::AssistId; use syntax::{ - AstNode, SyntaxToken, T, + AstNode, SyntaxKind, SyntaxToken, T, + algo::{previous_non_trivia_token, skip_trivia_token}, ast::{self, syntax_factory::SyntaxFactory}, }; @@ -36,15 +37,18 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) RCur, } - let makro = ctx.find_node_at_offset::()?; + let token_tree = ctx.find_node_at_offset::()?; let cursor_offset = ctx.offset(); - let semicolon = macro_semicolon(&makro); - let token_tree = makro.token_tree()?; + let semicolon = macro_semicolon(&token_tree); let ltoken = token_tree.left_delimiter_token()?; let rtoken = token_tree.right_delimiter_token()?; + if !is_macro_call(&token_tree)? { + return None; + } + if !ltoken.text_range().contains(cursor_offset) && !rtoken.text_range().contains(cursor_offset) { return None; @@ -70,7 +74,7 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) token_tree.syntax().text_range(), |builder| { let make = SyntaxFactory::with_mappings(); - let mut editor = builder.make_editor(makro.syntax()); + let mut editor = builder.make_editor(token_tree.syntax()); match token { MacroDelims::LPar | MacroDelims::RPar => { @@ -102,12 +106,21 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) ) } -fn macro_semicolon(makro: &ast::MacroCall) -> Option { - makro.semicolon_token().or_else(|| { - let macro_expr = ast::MacroExpr::cast(makro.syntax().parent()?)?; - let expr_stmt = ast::ExprStmt::cast(macro_expr.syntax().parent()?)?; - expr_stmt.semicolon_token() - }) +fn is_macro_call(token_tree: &ast::TokenTree) -> Option { + let parent = token_tree.syntax().parent()?; + if ast::MacroCall::can_cast(parent.kind()) { + return Some(true); + } + + let token_tree = ast::TokenTree::cast(parent)?; + let prev = previous_non_trivia_token(token_tree.syntax().clone())?; + let prev_prev = previous_non_trivia_token(prev.clone())?; + Some(prev.kind() == T![!] && prev_prev.kind() == SyntaxKind::IDENT) +} + +fn macro_semicolon(token_tree: &ast::TokenTree) -> Option { + let next_token = token_tree.syntax().last_token()?.next_token()?; + skip_trivia_token(next_token, syntax::Direction::Next).filter(|it| it.kind() == T![;]) } fn needs_semicolon(tt: ast::TokenTree) -> bool { @@ -405,7 +418,7 @@ prt!{(3 + 5)} // FIXME @alibektas : Inner macro_call is not seen as such. So this doesn't work. #[test] fn test_nested_macros() { - check_assist_not_applicable( + check_assist( toggle_macro_delimiter, r#" macro_rules! prt { @@ -420,7 +433,22 @@ macro_rules! abc { }}; } -prt!{abc!($03 + 5)}; +prt!{abc!$0(3 + 5)}; +"#, + r#" +macro_rules! prt { + ($e:expr) => {{ + println!("{}", stringify!{$e}); + }}; +} + +macro_rules! abc { + ($e:expr) => {{ + println!("{}", stringify!{$e}); + }}; +} + +prt!{abc!{3 + 5}}; "#, ) } From 2755c3759775a34068d624ce3196f92a7a82b4a4 Mon Sep 17 00:00:00 2001 From: Jesung Yang Date: Wed, 28 Jan 2026 13:48:24 +0900 Subject: [PATCH 03/82] Remove unncecessary `#[serde(default)]` Remove `#[serde(default)]` from optional fields. Fields of type `Option` are automatically deserialized to `None` when the corresponding keys are missing in `rust-project.json`, making the explicit attribute redundant. --- .../rust-analyzer/crates/project-model/src/project_json.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index 6938010cbd708..9b9111012b541 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -391,7 +391,6 @@ struct CrateData { display_name: Option, root_module: Utf8PathBuf, edition: EditionData, - #[serde(default)] version: Option, deps: Vec, #[serde(default)] @@ -408,11 +407,8 @@ struct CrateData { source: Option, #[serde(default)] is_proc_macro: bool, - #[serde(default)] repository: Option, - #[serde(default)] build: Option, - #[serde(default)] proc_macro_cwd: Option, } From c85189795bfd3a5b6de77b516f51d80e5e321eef Mon Sep 17 00:00:00 2001 From: Jesung Yang Date: Wed, 28 Jan 2026 14:14:45 +0900 Subject: [PATCH 04/82] Align with internal deserialized data structure Mark `cfg`, `env`, and `is_proc_macro` fields optional to match the corresponding data structure, `project_json::CrateData`. --- .../docs/book/src/non_cargo_based_projects.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md index f1f10ae336534..9cc3292444980 100644 --- a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md +++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md @@ -135,7 +135,7 @@ interface Crate { cfg_groups?: string[]; /// The set of cfgs activated for a given crate, like /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`. - cfg: string[]; + cfg?: string[]; /// Target tuple for this Crate. /// /// Used when running `rustc --print cfg` @@ -143,7 +143,7 @@ interface Crate { target?: string; /// Environment variables, used for /// the `env!` macro - env: { [key: string]: string; }; + env?: { [key: string]: string; }; /// Extra crate-level attributes applied to this crate. /// /// rust-analyzer will behave as if these attributes @@ -155,7 +155,8 @@ interface Crate { crate_attrs?: string[]; /// Whether the crate is a proc-macro crate. - is_proc_macro: boolean; + /// Defaults to `false` if unspecified. + is_proc_macro?: boolean; /// For proc-macro crates, path to compiled /// proc-macro (.so file). proc_macro_dylib_path?: string; From fe9b791c5554327d7da234c48f7f27e43a901b68 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 6 Feb 2026 16:15:22 +0800 Subject: [PATCH 05/82] feat: offer block let fallback postfix complete Example --- ```rust fn main() { match 2 { bar => bar.$0 } } ``` -> ```rust fn main() { match 2 { bar => { let $1 = bar; $0 } } } ``` --- .../ide-completion/src/completions/postfix.rs | 65 ++++++++++++++++++- .../crates/ide-completion/src/render.rs | 2 + .../ide-completion/src/tests/expression.rs | 14 ++++ 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index cffc44f8afb33..3a92903d05128 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 @@ -161,7 +161,20 @@ pub(crate) fn complete_postfix( postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};")) .add_to(acc, ctx.db); } - _ => (), + _ => { + postfix_snippet( + "let", + "let", + &format!("{{\n let $1 = {receiver_text};\n $0\n}}"), + ) + .add_to(acc, ctx.db); + postfix_snippet( + "letm", + "let mut", + &format!("{{\n let mut $1 = {receiver_text};\n $0\n}}"), + ) + .add_to(acc, ctx.db); + } } } @@ -581,6 +594,8 @@ fn main() { sn dbgr dbg!(&expr) sn deref *expr sn if if expr {} + sn let let + sn letm let mut sn match match expr {} sn not !expr sn ref &expr @@ -795,6 +810,54 @@ fn main() { ); } + #[test] + fn let_fallback_block() { + check( + r#" +fn main() { + match 2 { + bar => bar.$0 + } +} +"#, + expect![[r#" + 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( + "let", + r#" +fn main() { + match 2 { + bar => bar.$0 + } +} +"#, + r#" +fn main() { + match 2 { + bar => { + let $1 = bar; + $0 +} + } +} +"#, + ); + } + #[test] fn option_letelse() { check_edit( 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 765304d8187de..7a7b054b3974b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -3033,6 +3033,8 @@ fn main() { sn dbgr dbg!(&expr) [] sn deref *expr [] sn if if expr {} [] + sn let let [] + sn letm let mut [] sn match match expr {} [] sn ref &expr [] sn refm &mut expr [] 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 df39591a33460..b9b0f76c884c3 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 @@ -2341,6 +2341,8 @@ fn main() { 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 @@ -2368,6 +2370,8 @@ fn main() { 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 @@ -2399,6 +2403,8 @@ fn main() { 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 @@ -2426,6 +2432,8 @@ fn main() { 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 @@ -2453,6 +2461,8 @@ fn main() { 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 @@ -2480,6 +2490,8 @@ fn main() { 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 @@ -3268,6 +3280,8 @@ fn foo() { 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 From 2eec6178094351111d908627511628005c220826 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 7 Feb 2026 16:02:23 +0800 Subject: [PATCH 06/82] internal: remove redundant double call in postfix --- .../crates/ide-completion/src/completions/postfix.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index cffc44f8afb33..a916d1d30566c 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 @@ -91,8 +91,7 @@ pub(crate) fn complete_postfix( // so it's better to consider references now to avoid breaking the compilation let (dot_receiver_including_refs, prefix) = include_references(dot_receiver); - let mut receiver_text = - get_receiver_text(&ctx.sema, dot_receiver, receiver_is_ambiguous_float_literal); + let mut receiver_text = receiver_text; receiver_text.insert_str(0, &prefix); let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, &dot_receiver_including_refs) { From 99c45c503eef0218c96406623808e7e37d4e430c Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 7 Feb 2026 16:07:17 +0800 Subject: [PATCH 07/82] fix: complete `.let` on block tail prefix expression Example --- ```rust fn main() { &baz.l$0 } ``` **Before this PR** ```text sn if if expr {} sn match match expr {} ``` **After this PR** ```text sn if if expr {} sn let let sn letm let mut sn match match expr {} ``` --- .../ide-completion/src/completions/postfix.rs | 85 ++++++++++++++++++- .../ide-completion/src/tests/expression.rs | 2 + 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index cffc44f8afb33..90ac1de3f1f65 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 @@ -16,7 +16,7 @@ use itertools::Itertools; use stdx::never; use syntax::{ SmolStr, - SyntaxKind::{EXPR_STMT, STMT_LIST}, + SyntaxKind::{BLOCK_EXPR, EXPR_STMT, STMT_LIST}, T, TextRange, TextSize, ToSmolStr, ast::{self, AstNode, AstToken}, format_smolstr, match_ast, @@ -155,7 +155,7 @@ pub(crate) fn complete_postfix( postfix_snippet("let", "let", &format!("let $1 = {receiver_text}")) .add_to(acc, ctx.db); } - _ if matches!(second_ancestor.kind(), STMT_LIST | EXPR_STMT) => { + _ if matches!(second_ancestor.kind(), STMT_LIST | EXPR_STMT | BLOCK_EXPR) => { postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")) .add_to(acc, ctx.db); postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};")) @@ -652,6 +652,87 @@ fn main() { baz.l$0 res } +"#, + expect![[r#" + sn box Box::new(expr) + sn call function(expr) + sn const const {} + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn if if expr {} + sn let let + sn letm let mut + sn match match expr {} + sn not !expr + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + sn while while expr {} + "#]], + ); + check( + r#" +fn main() { + &baz.l$0 + res +} +"#, + expect![[r#" + sn box Box::new(expr) + sn call function(expr) + sn const const {} + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn if if expr {} + sn let let + sn letm let mut + sn match match expr {} + sn not !expr + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + sn while while expr {} + "#]], + ); + } + + #[test] + fn let_tail_block() { + check( + r#" +fn main() { + baz.l$0 +} +"#, + expect![[r#" + sn box Box::new(expr) + sn call function(expr) + sn const const {} + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn if if expr {} + sn let let + sn letm let mut + sn match match expr {} + sn not !expr + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + sn while while expr {} + "#]], + ); + + check( + r#" +fn main() { + &baz.l$0 +} "#, expect![[r#" sn box Box::new(expr) 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 df39591a33460..5fef8c44deb12 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,8 @@ fn foo() { 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 From 0c724e57c309e5804f98097d53c188bb36e42acc Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 12 Feb 2026 18:50:20 +0200 Subject: [PATCH 08/82] Refactor handling of associated type shorthand for type parameters, i.e. `T::AssocType` without specifying the trait I believe the new code is both cleaner and more robust, and should fix some edge cases. rustc does all of this very differently with plenty of queries for various forms of predicate lowering; but we have tight memory constraints so we prefer a different approach. --- .../crates/hir-def/src/hir/generics.rs | 2 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 16 +- .../crates/hir-ty/src/generics.rs | 16 - .../rust-analyzer/crates/hir-ty/src/lib.rs | 63 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 796 ++++++------------ .../crates/hir-ty/src/lower/path.rs | 98 ++- .../rust-analyzer/crates/hir-ty/src/utils.rs | 65 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 + .../crates/ide/src/signature_help.rs | 4 +- 9 files changed, 413 insertions(+), 649 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs index 482cf36f95b0b..022f8adfdb06b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs @@ -184,7 +184,7 @@ static EMPTY: LazyLock> = LazyLock::new(|| { impl GenericParams { /// The index of the self param in the generic of the non-parent definition. - pub(crate) const SELF_PARAM_ID_IN_SELF: la_arena::Idx = + pub const SELF_PARAM_ID_IN_SELF: la_arena::Idx = LocalTypeOrConstParamId::from_raw(RawIdx::from_u32(0)); pub fn new(db: &dyn DefDatabase, def: GenericDefId) -> Arc { 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 8d6c418d75dc5..de674be05f643 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -86,7 +86,7 @@ use crate::{ builtin_type::BuiltinType, db::DefDatabase, expr_store::ExpressionStoreSourceMap, - hir::generics::{LocalLifetimeParamId, LocalTypeOrConstParamId}, + hir::generics::{GenericParams, LocalLifetimeParamId, LocalTypeOrConstParamId}, nameres::{ LocalDefMap, assoc::{ImplItems, TraitItems}, @@ -553,15 +553,25 @@ pub struct TypeOrConstParamId { pub struct TypeParamId(TypeOrConstParamId); impl TypeParamId { + #[inline] pub fn parent(&self) -> GenericDefId { self.0.parent } + + #[inline] pub fn local_id(&self) -> LocalTypeOrConstParamId { self.0.local_id } -} -impl TypeParamId { + #[inline] + pub fn trait_self(trait_: TraitId) -> TypeParamId { + TypeParamId::from_unchecked(TypeOrConstParamId { + parent: trait_.into(), + local_id: GenericParams::SELF_PARAM_ID_IN_SELF, + }) + } + + #[inline] /// Caller should check if this toc id really belongs to a type pub fn from_unchecked(it: TypeOrConstParamId) -> Self { Self(it) 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 5f0261437b77a..b1500bcdb7568 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -224,22 +224,6 @@ impl Generics { } } -pub(crate) fn trait_self_param_idx(db: &dyn DefDatabase, def: GenericDefId) -> Option { - match def { - GenericDefId::TraitId(_) => { - let params = db.generic_params(def); - params.trait_self_param().map(|idx| idx.into_raw().into_u32() as usize) - } - GenericDefId::ImplId(_) => None, - _ => { - let parent_def = parent_generic_def(db, def)?; - let parent_params = db.generic_params(parent_def); - let parent_self_idx = parent_params.trait_self_param()?.into_raw().into_u32() as usize; - Some(parent_self_idx) - } - } -} - pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option { let container = match def { GenericDefId::FunctionId(it) => it.lookup(db).container, 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 f8920904f06f2..ca817ae7134b1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -57,9 +57,12 @@ mod test_db; #[cfg(test)] mod tests; -use std::hash::Hash; +use std::{hash::Hash, ops::ControlFlow}; -use hir_def::{CallableDefId, TypeOrConstParamId, type_ref::Rawness}; +use hir_def::{ + CallableDefId, GenericDefId, TypeAliasId, TypeOrConstParamId, TypeParamId, + hir::generics::GenericParams, resolver::TypeNs, type_ref::Rawness, +}; use hir_expand::name::Name; use indexmap::{IndexMap, map::Entry}; use intern::{Symbol, sym}; @@ -77,10 +80,11 @@ use crate::{ db::HirDatabase, display::{DisplayTarget, HirDisplay}, infer::unify::InferenceTable, + lower::SupertraitsInfo, next_solver::{ AliasTy, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, Canonical, - CanonicalVarKind, CanonicalVars, Const, ConstKind, DbInterner, FnSig, GenericArgs, - PolyFnSig, Predicate, Region, RegionKind, TraitRef, Ty, TyKind, Tys, abi, + CanonicalVarKind, CanonicalVars, ClauseKind, Const, ConstKind, DbInterner, FnSig, + GenericArgs, PolyFnSig, Predicate, Region, RegionKind, TraitRef, Ty, TyKind, Tys, abi, }, }; @@ -94,7 +98,7 @@ pub use infer::{ }; pub use lower::{ GenericPredicates, ImplTraits, LifetimeElisionKind, TyDefId, TyLoweringContext, ValueTyDefId, - associated_type_shorthand_candidates, diagnostics::*, + diagnostics::*, }; pub use next_solver::interner::{attach_db, attach_db_allow_change, with_attached_db}; pub use target_feature::TargetFeatures; @@ -478,6 +482,55 @@ where } } +/// To be used from `hir` only. +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_no_crate(db); + let (def, param) = match res { + TypeNs::GenericParam(param) => (def, param), + TypeNs::SelfType(impl_) => { + let impl_trait = db.impl_trait(impl_)?.skip_binder().def_id.0; + let param = TypeParamId::from_unchecked(TypeOrConstParamId { + parent: impl_trait.into(), + local_id: GenericParams::SELF_PARAM_ID_IN_SELF, + }); + (impl_trait.into(), param) + } + _ => return None, + }; + + let mut dedup_map = FxHashSet::default(); + let param_ty = Ty::new_param(interner, param, param_idx(db, param.into()).unwrap() as u32); + // We use the ParamEnv and not the predicates because the ParamEnv elaborates bounds. + let param_env = db.trait_environment(def); + for clause in param_env.clauses { + let ClauseKind::Trait(trait_clause) = clause.kind().skip_binder() else { continue }; + if trait_clause.self_ty() != param_ty { + continue; + } + let trait_id = trait_clause.def_id().0; + dedup_map.extend( + SupertraitsInfo::query(db, trait_id) + .defined_assoc_types + .iter() + .map(|(name, id)| (name, *id)), + ); + } + + dedup_map + .into_iter() + .try_for_each( + |(name, id)| { + if cb(name, id) { ControlFlow::Break(id) } else { ControlFlow::Continue(()) } + }, + ) + .break_value() +} + /// To be used from `hir` only. pub fn callable_sig_from_fn_trait<'db>( self_ty: Ty<'db>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 386556b156843..1290874177ac4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -20,7 +20,8 @@ use hir_def::{ builtin_type::BuiltinType, expr_store::{ExpressionStore, HygieneId, path::Path}, hir::generics::{ - GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate, + GenericParamDataRef, GenericParams, TypeOrConstParamData, TypeParamProvenance, + WherePredicate, }, item_tree::FieldsShape, lang_item::LangItems, @@ -36,27 +37,23 @@ 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, BoundVarIndexKind, ConstKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, Interner, OutlivesPredicate, TermKind, TyKind::{self}, TypeFoldable, TypeVisitableExt, Upcast, UpcastFrom, elaborate, - inherent::{ - Clause as _, GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, - Ty as _, - }, + inherent::{Clause as _, GenericArgs as _, IntoKind as _, Region as _, Ty as _}, }; -use smallvec::{SmallVec, smallvec}; +use smallvec::SmallVec; use stdx::{impl_from, never}; use tracing::debug; use triomphe::{Arc, ThinArc}; use crate::{ - FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, all_super_traits, + FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, consteval::intern_const_ref, db::{HirDatabase, InternedOpaqueTyId}, - generics::{Generics, generics, trait_self_param_idx}, + generics::{Generics, generics}, next_solver::{ AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FxIndexMap, GenericArg, @@ -618,33 +615,12 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { &'b mut self, where_predicate: &'b WherePredicate, ignore_bindings: bool, - generics: &Generics, - predicate_filter: PredicateFilter, ) -> impl Iterator, GenericPredicateSource)> + 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))) + Either::Left(self.lower_type_bound(bound, self_ty, ignore_bindings)) } &WherePredicate::Lifetime { bound, target } => Either::Right(iter::once(( Clause(Predicate::new( @@ -1626,6 +1602,92 @@ pub(crate) fn field_types_with_diagnostics_query<'db>( (res, create_diagnostics(ctx.diagnostics)) } +#[derive(Debug, PartialEq, Eq, Default)] +pub(crate) struct SupertraitsInfo { + /// This includes the trait itself. + pub(crate) all_supertraits: Box<[TraitId]>, + pub(crate) direct_supertraits: Box<[TraitId]>, + pub(crate) defined_assoc_types: Box<[(Name, TypeAliasId)]>, +} + +impl SupertraitsInfo { + #[inline] + pub(crate) fn query(db: &dyn HirDatabase, trait_: TraitId) -> &Self { + return supertraits_info(db, trait_); + + #[salsa::tracked(returns(ref), cycle_result = supertraits_info_cycle)] + fn supertraits_info(db: &dyn HirDatabase, trait_: TraitId) -> SupertraitsInfo { + let mut all_supertraits = FxHashSet::default(); + let mut direct_supertraits = FxHashSet::default(); + let mut defined_assoc_types = FxHashSet::default(); + + all_supertraits.insert(trait_); + defined_assoc_types.extend(trait_.trait_items(db).items.iter().filter_map( + |(name, id)| match *id { + AssocItemId::TypeAliasId(id) => Some((name.clone(), id)), + _ => None, + }, + )); + + let resolver = trait_.resolver(db); + let signature = db.trait_signature(trait_); + for pred in signature.generic_params.where_predicates() { + let (WherePredicate::TypeBound { target, bound } + | WherePredicate::ForLifetime { lifetimes: _, target, bound }) = pred + else { + continue; + }; + let (TypeBound::Path(bounded_trait, TraitBoundModifier::None) + | TypeBound::ForLifetime(_, bounded_trait)) = *bound + else { + continue; + }; + let target = &signature.store[*target]; + match target { + TypeRef::TypeParam(param) + if param.local_id() == GenericParams::SELF_PARAM_ID_IN_SELF => {} + TypeRef::Path(path) if path.is_self_type() => {} + _ => continue, + } + let Some(TypeNs::TraitId(bounded_trait)) = + resolver.resolve_path_in_type_ns_fully(db, &signature.store[bounded_trait]) + else { + continue; + }; + let SupertraitsInfo { + all_supertraits: bounded_trait_all_supertraits, + direct_supertraits: _, + defined_assoc_types: bounded_traits_defined_assoc_types, + } = SupertraitsInfo::query(db, bounded_trait); + all_supertraits.extend(bounded_trait_all_supertraits); + direct_supertraits.insert(bounded_trait); + defined_assoc_types.extend(bounded_traits_defined_assoc_types.iter().cloned()); + } + + SupertraitsInfo { + all_supertraits: Box::from_iter(all_supertraits), + direct_supertraits: Box::from_iter(direct_supertraits), + defined_assoc_types: Box::from_iter(defined_assoc_types), + } + } + + fn supertraits_info_cycle( + _db: &dyn HirDatabase, + _: salsa::Id, + _trait_: TraitId, + ) -> SupertraitsInfo { + SupertraitsInfo::default() + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum TypeParamAssocTypeShorthandError { + AssocTypeNotFound, + AmbiguousAssocType, + Cycle, +} + /// Predicates for `param_id` of the form `P: SomeTrait`. If /// `assoc_name` is provided, only return predicates referencing traits /// that have an associated type of that name. @@ -1640,15 +1702,14 @@ pub(crate) fn field_types_with_diagnostics_query<'db>( /// following bounds are disallowed: `T: Foo, U: Foo`, but /// these are fine: `T: Foo, U: Foo<()>`. #[tracing::instrument(skip(db), ret)] -#[salsa::tracked(returns(ref), cycle_result = generic_predicates_for_param_cycle_result)] -pub(crate) fn generic_predicates_for_param<'db>( - db: &'db dyn HirDatabase, +#[salsa::tracked(returns(ref), cycle_result = resolve_type_param_assoc_type_shorthand_cycle_result)] +fn resolve_type_param_assoc_type_shorthand( + db: &dyn HirDatabase, def: GenericDefId, - param_id: TypeOrConstParamId, - assoc_name: Option, -) -> StoredEarlyBinder { + param: TypeParamId, + assoc_name: Name, +) -> Result, TypeParamAssocTypeShorthandError> { let generics = generics(db, def); - let interner = DbInterner::new_no_crate(db); let resolver = def.resolver(db); let mut ctx = TyLoweringContext::new( db, @@ -1657,128 +1718,109 @@ pub(crate) fn generic_predicates_for_param<'db>( def, LifetimeElisionKind::AnonymousReportError, ); + let interner = ctx.interner; + let mut result = None; + let param_ty = Ty::new_param( + interner, + param, + generics.type_or_const_param_idx(param.into()).unwrap() as u32, + ); - // we have to filter out all other predicates *first*, before attempting to lower them - let has_relevant_bound = |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) = ctx.lang_items.PointeeSized 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; - }; - - trait_or_supertrait_has_assoc_type(db, tr, assoc_name) - } - TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false, - } + if let GenericDefId::TraitId(containing_trait) = param.parent() + && param.local_id() == GenericParams::SELF_PARAM_ID_IN_SELF + { + // Add the trait's own associated types. + if let Some(assoc_type) = + containing_trait.trait_items(db).associated_type_by_name(&assoc_name) + { + let args = GenericArgs::identity_for_item(interner, containing_trait.into()); + result = Some(StoredEarlyBinder::bind((assoc_type, args.store()))); } - 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 has_relevant_bound(pred, &mut ctx) { - predicates.extend( - ctx.lower_where_predicate( - pred, - true, - maybe_parent_generics, - PredicateFilter::All, - ) - .map(|(pred, _)| pred), - ); + let (WherePredicate::TypeBound { target, bound } + | WherePredicate::ForLifetime { lifetimes: _, target, bound }) = pred + else { + continue; + }; + let (TypeBound::Path(bounded_trait_path, TraitBoundModifier::None) + | TypeBound::ForLifetime(_, bounded_trait_path)) = *bound + else { + continue; + }; + let Some(target) = ctx.lower_ty_only_param(*target) else { continue }; + if target != param.into() { + continue; + } + let Some(TypeNs::TraitId(bounded_trait)) = + resolver.resolve_path_in_type_ns_fully(db, &ctx.store[bounded_trait_path]) + else { + continue; + }; + if !SupertraitsInfo::query(db, bounded_trait) + .defined_assoc_types + .iter() + .any(|(name, _)| *name == assoc_name) + { + continue; } + + let Some((bounded_trait_ref, _)) = + ctx.lower_trait_ref_from_path(bounded_trait_path, param_ty) + else { + continue; + }; + // Now, search from the start on the *bounded* trait like if we wrote `Self::Assoc`. Eventually, we'll get + // the correct trait ref (or a cycle). + let lookup_on_bounded_trait = resolve_type_param_assoc_type_shorthand( + db, + bounded_trait.into(), + TypeParamId::trait_self(bounded_trait), + assoc_name.clone(), + ); + let lookup_on_bounded_trait = match lookup_on_bounded_trait { + Ok(it) => it, + Err( + err @ (TypeParamAssocTypeShorthandError::AmbiguousAssocType + | TypeParamAssocTypeShorthandError::Cycle), + ) => return Err(*err), + Err(TypeParamAssocTypeShorthandError::AssocTypeNotFound) => { + never!("we checked that the trait defines this assoc type"); + continue; + } + }; + let (assoc_type, args) = lookup_on_bounded_trait + .get_with(|(assoc_type, args)| (*assoc_type, args.as_ref())) + .skip_binder(); + let args = EarlyBinder::bind(args).instantiate(interner, bounded_trait_ref.args); + let current_result = StoredEarlyBinder::bind((assoc_type, args.store())); + // If we already have a result, this is an ambiguity - unless this is the same result, then we are fine + // (e.g. rustc allows to write the same bound twice without ambiguity). + if let Some(existing_result) = result + && existing_result != current_result + { + return Err(TypeParamAssocTypeShorthandError::AmbiguousAssocType); + } + result = Some(current_result); } } - 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, - ctx.lang_items, - param_id.parent, - &explicitly_unsized_tys, - &args, - ) { - predicates.extend(implicitly_sized_predicates); - }; - } - StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()) + result.ok_or(TypeParamAssocTypeShorthandError::AssocTypeNotFound) } -pub(crate) fn generic_predicates_for_param_cycle_result( - db: &dyn HirDatabase, +fn resolve_type_param_assoc_type_shorthand_cycle_result( + _db: &dyn HirDatabase, _: salsa::Id, _def: GenericDefId, - _param_id: TypeOrConstParamId, - _assoc_name: Option, -) -> StoredEarlyBinder { - StoredEarlyBinder::bind(Clauses::empty(DbInterner::new_no_crate(db)).store()) -} - -/// Check if this trait or any of its supertraits define an associated -/// type with the given name. -fn trait_or_supertrait_has_assoc_type( - db: &dyn HirDatabase, - tr: TraitId, - assoc_name: &Name, -) -> bool { - for trait_id in all_super_traits(db, tr) { - if trait_id - .trait_items(db) - .items - .iter() - .any(|(name, item)| matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name) - { - return true; - } - } - - false + _param: TypeParamId, + _assoc_name: Name, +) -> Result, TypeParamAssocTypeShorthandError> { + Err(TypeParamAssocTypeShorthandError::Cycle) } #[inline] @@ -1904,7 +1946,7 @@ impl<'db> GenericPredicates { db: &'db dyn HirDatabase, def: GenericDefId, ) -> (GenericPredicates, Diagnostics) { - generic_predicates_filtered_by(db, def, PredicateFilter::All, |_| true) + generic_predicates(db, def) } } @@ -2042,24 +2084,10 @@ pub(crate) fn trait_environment<'db>(db: &'db dyn HirDatabase, def: GenericDefId } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(crate) enum PredicateFilter { - SelfTrait, - All, -} - /// 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: &dyn HirDatabase, - def: GenericDefId, - predicate_filter: PredicateFilter, - filter: F, -) -> (GenericPredicates, Diagnostics) -where - F: Fn(GenericDefId) -> bool, -{ +#[tracing::instrument(skip(db), ret)] +fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredicates, Diagnostics) { let generics = generics(db, def); let resolver = def.resolver(db); let interner = DbInterner::new_no_crate(db); @@ -2081,9 +2109,9 @@ where let all_generics = std::iter::successors(Some(&generics), |generics| generics.parent_generics()) .collect::>(); - let own_implicit_trait_predicate = implicit_trait_predicate(interner, def, predicate_filter); + let own_implicit_trait_predicate = implicit_trait_predicate(interner, def); let parent_implicit_trait_predicate = if all_generics.len() > 1 { - implicit_trait_predicate(interner, all_generics.last().unwrap().def(), predicate_filter) + implicit_trait_predicate(interner, all_generics.last().unwrap().def()) } else { None }; @@ -2091,97 +2119,85 @@ where // Collect only diagnostics from the child, not including parents. ctx.diagnostics.clear(); - if filter(maybe_parent_generics.def()) { - ctx.store = maybe_parent_generics.store(); - for pred in maybe_parent_generics.where_predicates() { - tracing::debug!(?pred); - for (pred, source) in - ctx.lower_where_predicate(pred, false, maybe_parent_generics, predicate_filter) - { - match source { - GenericPredicateSource::SelfOnly => { - if maybe_parent_generics.def() == def { - own_predicates.push(pred); - } else { - parent_predicates.push(pred); - } + ctx.store = maybe_parent_generics.store(); + for pred in maybe_parent_generics.where_predicates() { + tracing::debug!(?pred); + for (pred, source) in ctx.lower_where_predicate(pred, false) { + match source { + GenericPredicateSource::SelfOnly => { + if maybe_parent_generics.def() == def { + own_predicates.push(pred); + } else { + parent_predicates.push(pred); } - GenericPredicateSource::AssocTyBound => { - if maybe_parent_generics.def() == def { - own_assoc_ty_bounds.push(pred); - } else { - parent_assoc_ty_bounds.push(pred); - } + } + GenericPredicateSource::AssocTyBound => { + if maybe_parent_generics.def() == def { + own_assoc_ty_bounds.push(pred); + } else { + parent_assoc_ty_bounds.push(pred); } } } } + } - if maybe_parent_generics.def() == def { - push_const_arg_has_type_predicates(db, &mut own_predicates, maybe_parent_generics); - } else { - push_const_arg_has_type_predicates( - db, - &mut parent_predicates, - maybe_parent_generics, - ); - } + if maybe_parent_generics.def() == def { + push_const_arg_has_type_predicates(db, &mut own_predicates, maybe_parent_generics); + } else { + push_const_arg_has_type_predicates(db, &mut parent_predicates, maybe_parent_generics); + } - 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 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; - } + if param_data.provenance == TypeParamProvenance::TraitSelf { + return; + } - let param_ty = Ty::new_param(interner, param_id, param_idx); - if ctx.unsized_types.contains(¶m_ty) { - return; - } - let trait_ref = TraitRef::new_from_args( - interner, - sized_trait.into(), - GenericArgs::new_from_slice(&[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, - }), - )), - )); - if maybe_parent_generics.def() == def { - own_predicates.push(clause); - } else { - parent_predicates.push(clause); - } - }; - let parent_params_len = maybe_parent_generics.len_parent(); - maybe_parent_generics.iter_self().enumerate().for_each( - |(param_idx, (param_id, param_data))| { - add_sized_clause( - (param_idx + parent_params_len) as u32, - param_id, - param_data, - ); - }, + let param_ty = Ty::new_param(interner, param_id, param_idx); + if ctx.unsized_types.contains(¶m_ty) { + return; + } + let trait_ref = TraitRef::new_from_args( + interner, + sized_trait.into(), + GenericArgs::new_from_slice(&[param_ty.into()]), ); - } - - // We do not clear `ctx.unsized_types`, as the `?Sized` clause of a child (e.g. an associated type) can - // be declared on the parent (e.g. the trait). It is nevertheless fine to register the implicit `Sized` - // predicates before lowering the child, as a child cannot define a `?Sized` predicate for its parent. - // But we do have to lower the parent first. + 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, + }), + )), + )); + if maybe_parent_generics.def() == def { + own_predicates.push(clause); + } else { + parent_predicates.push(clause); + } + }; + let parent_params_len = maybe_parent_generics.len_parent(); + maybe_parent_generics.iter_self().enumerate().for_each( + |(param_idx, (param_id, param_data))| { + add_sized_clause((param_idx + parent_params_len) as u32, param_id, param_data); + }, + ); } + + // We do not clear `ctx.unsized_types`, as the `?Sized` clause of a child (e.g. an associated type) can + // be declared on the parent (e.g. the trait). It is nevertheless fine to register the implicit `Sized` + // predicates before lowering the child, as a child cannot define a `?Sized` predicate for its parent. + // But we do have to lower the parent first. } let diagnostics = create_diagnostics(ctx.diagnostics); @@ -2229,7 +2245,6 @@ where fn implicit_trait_predicate<'db>( interner: DbInterner<'db>, def: GenericDefId, - predicate_filter: PredicateFilter, ) -> Option> { // For traits, add `Self: Trait` predicate. This is // not part of the predicates that a user writes, but it @@ -2243,9 +2258,7 @@ where // prove that the trait applies to the types that were // used, and adding the predicate into this list ensures // that this is done. - if let GenericDefId::TraitId(def_id) = def - && predicate_filter == PredicateFilter::All - { + if let GenericDefId::TraitId(def_id) = def { Some(TraitRef::identity(interner, def_id.into()).upcast(interner)) } else { None @@ -2282,49 +2295,6 @@ fn push_const_arg_has_type_predicates<'db>( } } -/// 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, - lang_items: &LangItems, - def: GenericDefId, - explicitly_unsized_tys: &'a FxHashSet>, - args: &'subst GenericArgs<'db>, -) -> Option> + Captures<'a> + Captures<'subst>> { - let interner = DbInterner::new_no_crate(db); - let sized_trait = lang_items.Sized?; - - 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_slice(&[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(Option>]>>); @@ -2602,235 +2572,19 @@ pub(crate) fn associated_ty_item_bounds<'db>( 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 module = trait_ref.def_id.0.module(db); - let interner = DbInterner::new_with(db, module.krate(db)); - all_supertraits_trait_refs(db, trait_ref.def_id.0) - .map(|t| t.instantiate(interner, trait_ref.args)) - .find_map(|t| { - let trait_id = t.def_id.0; - let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?; - Some((t, 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_no_crate(db); - 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); - } - let predicates = 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 - .predicates; - for pred in predicates.get().instantiate_identity() { - 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 = - generic_predicates_for_param(db, def, param_id.into(), assoc_name.clone()); - predicates - .get() - .iter_identity() - .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, - } -} - -/// During lowering, elaborating supertraits can cause cycles. To avoid that, we have a separate query -/// to only collect supertraits. -/// -/// Technically, it is possible to avoid even more cycles by only collecting the `TraitId` of supertraits -/// without their args. However rustc doesn't do that, so we don't either. -pub(crate) fn all_supertraits_trait_refs( - db: &dyn HirDatabase, - trait_: TraitId, -) -> impl ExactSizeIterator>> { + name: Name, +) -> Option<(TypeAliasId, GenericArgs<'db>)> { + let assoc_type = resolve_type_param_assoc_type_shorthand( + db, + trait_ref.def_id.0.into(), + TypeParamId::trait_self(trait_ref.def_id.0), + name.clone(), + ) + .as_ref() + .ok()?; + let (assoc_type, trait_args) = assoc_type + .get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref())) + .skip_binder(); let interner = DbInterner::new_no_crate(db); - return all_supertraits_trait_refs_query(db, trait_).iter().map(move |trait_ref| { - trait_ref.get_with(|(trait_, args)| { - TraitRef::new_from_args(interner, (*trait_).into(), args.as_ref()) - }) - }); - - #[salsa_macros::tracked(returns(deref), cycle_result = all_supertraits_trait_refs_cycle_result)] - pub(crate) fn all_supertraits_trait_refs_query( - db: &dyn HirDatabase, - trait_: TraitId, - ) -> Box<[StoredEarlyBinder<(TraitId, StoredGenericArgs)>]> { - let resolver = trait_.resolver(db); - let signature = db.trait_signature(trait_); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &signature.store, - trait_.into(), - LifetimeElisionKind::AnonymousReportError, - ); - let interner = ctx.interner; - - let self_param_ty = Ty::new_param( - interner, - TypeParamId::from_unchecked(TypeOrConstParamId { - parent: trait_.into(), - local_id: Idx::from_raw(la_arena::RawIdx::from_u32(0)), - }), - 0, - ); - - let mut supertraits = FxHashSet::default(); - supertraits.insert(StoredEarlyBinder::bind(( - trait_, - GenericArgs::identity_for_item(interner, trait_.into()).store(), - ))); - - for pred in signature.generic_params.where_predicates() { - let WherePredicate::TypeBound { target, bound } = pred else { - continue; - }; - let target = &signature.store[*target]; - if let TypeRef::TypeParam(param_id) = target - && param_id.local_id().into_raw().into_u32() == 0 - { - // This is `Self`. - } else if let TypeRef::Path(path) = target - && path.is_self_type() - { - // Also `Self`. - } else { - // Not `Self`! - continue; - } - - ctx.lower_type_bound(bound, self_param_ty, true).for_each(|(clause, _)| { - if let ClauseKind::Trait(trait_ref) = clause.kind().skip_binder() { - supertraits.extend( - all_supertraits_trait_refs(db, trait_ref.trait_ref.def_id.0).map(|t| { - let trait_ref = t.instantiate(interner, trait_ref.trait_ref.args); - StoredEarlyBinder::bind((trait_ref.def_id.0, trait_ref.args.store())) - }), - ); - } - }); - } - - Box::from_iter(supertraits) - } - - pub(crate) fn all_supertraits_trait_refs_cycle_result( - db: &dyn HirDatabase, - _: salsa::Id, - trait_: TraitId, - ) -> Box<[StoredEarlyBinder<(TraitId, StoredGenericArgs)>]> { - let interner = DbInterner::new_no_crate(db); - Box::new([StoredEarlyBinder::bind(( - trait_, - GenericArgs::identity_for_item(interner, trait_.into()).store(), - ))]) - } + Some((assoc_type, EarlyBinder::bind(trait_args).instantiate(interner, trait_ref.args))) } 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 f3d0de12275ed..ab686a9aac133 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 @@ -2,7 +2,7 @@ use either::Either; use hir_def::{ - GenericDefId, GenericParamId, Lookup, TraitId, TypeAliasId, + GenericDefId, GenericParamId, Lookup, TraitId, TypeParamId, expr_store::{ ExpressionStore, HygieneId, path::{ @@ -17,7 +17,6 @@ use hir_def::{ signatures::TraitFlags, type_ref::{TypeRef, TypeRefId}, }; -use hir_expand::name::Name; use rustc_type_ir::{ AliasTerm, AliasTy, AliasTyKind, inherent::{GenericArgs as _, Region as _, Ty as _}, @@ -31,13 +30,10 @@ use crate::{ consteval::{unknown_const, unknown_const_as_generic}, db::HirDatabase, generics::{Generics, generics}, - lower::{ - GenericPredicateSource, LifetimeElisionKind, PathDiagnosticCallbackData, - named_associated_type_shorthand_candidates, - }, + lower::{GenericPredicateSource, LifetimeElisionKind, PathDiagnosticCallbackData}, next_solver::{ - Binder, Clause, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Predicate, - ProjectionPredicate, Region, TraitRef, Ty, + Binder, Clause, Const, DbInterner, EarlyBinder, ErrorGuaranteed, GenericArg, GenericArgs, + Predicate, ProjectionPredicate, Region, TraitRef, Ty, }, }; @@ -481,43 +477,59 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { #[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 db = self.ctx.db; 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; + let error_ty = || Ty::new_error(self.ctx.interner, ErrorGuaranteed); + let (assoc_type, trait_args) = match res { + Some(TypeNs::GenericParam(param)) => { + let Ok(assoc_type) = super::resolve_type_param_assoc_type_shorthand( + db, + def, + param, + assoc_name.clone(), + ) else { + return error_ty(); + }; + assoc_type + .get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref())) + .skip_binder() } - - // 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_from_args(interner, associated_ty.into(), substs), - )) + Some(TypeNs::SelfType(impl_)) => { + let Some(impl_trait) = db.impl_trait(impl_) else { + return error_ty(); + }; + let impl_trait = impl_trait.instantiate_identity(); + // Searching for `Self::Assoc` in `impl Trait for Type` is like searching for `Self::Assoc` in `Trait`. + let Ok(assoc_type) = super::resolve_type_param_assoc_type_shorthand( + db, + impl_trait.def_id.0.into(), + TypeParamId::trait_self(impl_trait.def_id.0), + assoc_name.clone(), + ) else { + return error_ty(); + }; + let (assoc_type, trait_args) = assoc_type + .get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref())) + .skip_binder(); + (assoc_type, EarlyBinder::bind(trait_args).instantiate(interner, impl_trait.args)) + } + _ => return error_ty(), }; - named_associated_type_shorthand_candidates( + + // 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(assoc_type.into(), infer_args, None, true); + + let substs = GenericArgs::new_from_iter( interner, - def, - res, - Some(assoc_name.clone()), - check_alias, - ) - .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) + trait_args.iter().chain(substs.iter().skip(trait_args.len())), + ); + + Ty::new_projection_from_args(interner, assoc_type.into(), substs) } fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> { @@ -862,9 +874,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { let found = associated_type_by_name_including_super_traits( self.ctx.db, trait_ref, - &binding.name, + binding.name.clone(), ); - let (super_trait_ref, associated_ty) = match found { + let (associated_ty, super_trait_args) = match found { None => return SmallVec::new(), Some(t) => t, }; @@ -878,7 +890,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { binding.args.as_ref(), associated_ty.into(), false, // this is not relevant - Some(super_trait_ref.self_ty()), + Some(super_trait_args.type_at(0)), PathGenericsSource::AssocType { segment: this.current_segment_u32(), assoc_type: binding_idx as u32, @@ -889,7 +901,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }); let args = GenericArgs::new_from_iter( interner, - super_trait_ref.args.iter().chain(args.iter().skip(super_trait_ref.args.len())), + super_trait_args.iter().chain(args.iter().skip(super_trait_args.len())), ); let projection_term = AliasTerm::new_from_args(interner, associated_ty.into(), args); 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 148300deb875f..be64f55ea550e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -1,28 +1,19 @@ //! 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; - use base_db::target::{self, TargetData}; use hir_def::{ - EnumId, EnumVariantId, FunctionId, Lookup, TraitId, - attrs::AttrFlags, - db::DefDatabase, - hir::generics::WherePredicate, - lang_item::LangItems, - resolver::{HasResolver, TypeNs}, - type_ref::{TraitBoundModifier, TypeRef}, + EnumId, EnumVariantId, FunctionId, Lookup, TraitId, attrs::AttrFlags, lang_item::LangItems, }; use intern::sym; use rustc_abi::TargetDataLayout; -use smallvec::{SmallVec, smallvec}; use span::Edition; use crate::{ TargetFeatures, db::HirDatabase, layout::{Layout, TagEncoding}, - lower::all_supertraits_trait_refs, + lower::SupertraitsInfo, mir::pad16, }; @@ -51,55 +42,13 @@ pub(crate) fn fn_traits(lang_items: &LangItems) -> impl Iterator } /// Returns an iterator over the direct super traits (including the trait itself). -pub fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> { - let mut result = smallvec![trait_]; - direct_super_traits_cb(db, trait_, |tt| { - if !result.contains(&tt) { - result.push(tt); - } - }); - result -} - -/// Returns an iterator over the whole super trait hierarchy (including the -/// trait itself). -pub fn all_super_traits(db: &dyn HirDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> { - let mut supertraits = all_supertraits_trait_refs(db, trait_) - .map(|trait_ref| trait_ref.skip_binder().def_id.0) - .collect::>(); - supertraits.sort_unstable(); - supertraits.dedup(); - supertraits +pub fn direct_super_traits(db: &dyn HirDatabase, trait_: TraitId) -> &[TraitId] { + &SupertraitsInfo::query(db, trait_).direct_supertraits } -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()); - let trait_self = generic_params.trait_self_param(); - generic_params - .where_predicates() - .iter() - .filter_map(|pred| match pred { - WherePredicate::ForLifetime { target, bound, .. } - | WherePredicate::TypeBound { target, bound } => { - let is_trait = match &store[*target] { - TypeRef::Path(p) => p.is_self_type(), - TypeRef::TypeParam(p) => Some(p.local_id()) == trait_self, - _ => false, - }; - match is_trait { - true => bound.as_path(&store), - false => None, - } - } - WherePredicate::Lifetime { .. } => None, - }) - .filter(|(_, bound_modifier)| matches!(bound_modifier, TraitBoundModifier::None)) - .filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path) { - Some(TypeNs::TraitId(t)) => Some(t), - _ => None, - }) - .for_each(cb); +/// Returns the whole super trait hierarchy (including the trait itself). +pub fn all_super_traits(db: &dyn HirDatabase, trait_: TraitId) -> &[TraitId] { + &SupertraitsInfo::query(db, trait_).all_supertraits } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 5820a6714b023..9dbee16dae6b7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -6167,6 +6167,7 @@ impl<'db> Type<'db> { self.autoderef_(db) .filter_map(|ty| ty.dyn_trait()) .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db, dyn_trait_id)) + .copied() .map(Trait::from) } @@ -6184,6 +6185,7 @@ impl<'db> Type<'db> { _ => None, }) .flat_map(|t| hir_ty::all_super_traits(db, t)) + .copied() }) .map(Trait::from) } 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 f86974b4ec76c..9ab07565e9efc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -1975,8 +1975,8 @@ trait Sub: Super + Super { fn f() -> impl Sub<$0 "#, expect![[r#" - trait Sub - ^^^^^^^^^^^ --------- + trait Sub + ^^^^^^^^^ ----------- "#]], ); } From 5c13e0ced9213c883f2dea69dfccfa29da1c9dd7 Mon Sep 17 00:00:00 2001 From: Flora Hill Date: Fri, 13 Feb 2026 04:07:52 +0000 Subject: [PATCH 09/82] Squash commits for review --- .../crates/ide-db/src/imports/insert_use.rs | 41 ++++- .../ide-db/src/imports/insert_use/tests.rs | 143 ++++++++++++++++++ .../ide-db/src/imports/merge_imports.rs | 13 +- 3 files changed, 183 insertions(+), 14 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index f26952fa1535d..da8525d1fb72b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -94,7 +94,7 @@ impl ImportScope { .item_list() .map(ImportScopeKind::Module) .map(|kind| ImportScope { kind, required_cfgs }); - } else if let Some(has_attrs) = ast::AnyHasAttrs::cast(syntax) { + } else if let Some(has_attrs) = ast::AnyHasAttrs::cast(syntax.clone()) { if block.is_none() && let Some(b) = ast::BlockExpr::cast(has_attrs.syntax().clone()) && let Some(b) = sema.original_ast_node(b) @@ -105,11 +105,34 @@ impl ImportScope { .attrs() .any(|attr| attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg")) { - if let Some(b) = block { - return Some(ImportScope { - kind: ImportScopeKind::Block(b), - required_cfgs, + if let Some(b) = block.clone() { + let current_cfgs = has_attrs.attrs().filter(|attr| { + attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg") }); + + let total_cfgs: Vec<_> = + required_cfgs.iter().cloned().chain(current_cfgs).collect(); + + let parent = syntax.parent(); + let mut can_merge = false; + if let Some(parent) = parent { + can_merge = parent.children().filter_map(ast::Use::cast).any(|u| { + let u_attrs = u.attrs().filter(|attr| { + attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg") + }); + crate::imports::merge_imports::eq_attrs( + u_attrs, + total_cfgs.iter().cloned(), + ) + }); + } + + if !can_merge { + return Some(ImportScope { + kind: ImportScopeKind::Block(b), + required_cfgs, + }); + } } required_cfgs.extend(has_attrs.attrs().filter(|attr| { attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg") @@ -546,7 +569,9 @@ fn insert_use_(scope: &ImportScope, use_item: ast::Use, group_imports: bool) { // skip the curly brace .skip(l_curly.is_some() as usize) .take_while(|child| match child { - NodeOrToken::Node(node) => is_inner_attribute(node.clone()), + NodeOrToken::Node(node) => { + is_inner_attribute(node.clone()) && ast::Item::cast(node.clone()).is_none() + } NodeOrToken::Token(token) => { [SyntaxKind::WHITESPACE, SyntaxKind::COMMENT, SyntaxKind::SHEBANG] .contains(&token.kind()) @@ -667,7 +692,9 @@ fn insert_use_with_editor_( // skip the curly brace .skip(l_curly.is_some() as usize) .take_while(|child| match child { - NodeOrToken::Node(node) => is_inner_attribute(node.clone()), + NodeOrToken::Node(node) => { + is_inner_attribute(node.clone()) && ast::Item::cast(node.clone()).is_none() + } NodeOrToken::Token(token) => { [SyntaxKind::WHITESPACE, SyntaxKind::COMMENT, SyntaxKind::SHEBANG] .contains(&token.kind()) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs index 3350e1c3d207f..7763d1e595d04 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs @@ -1438,3 +1438,146 @@ fn check_guess(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: Import let file = ImportScope { kind: ImportScopeKind::File(syntax), required_cfgs: vec![] }; assert_eq!(super::guess_granularity_from_scope(&file), expected); } + +#[test] +fn insert_with_existing_imports_and_cfg_module() { + check( + "std::fmt", + r#" +use foo::bar; + +#[cfg(target_arch = "x86_64")] +pub mod api; +"#, + r#" +use std::fmt; + +use foo::bar; + +#[cfg(target_arch = "x86_64")] +pub mod api; +"#, + ImportGranularity::Crate, + ); +} + +#[test] +fn insert_before_cfg_module() { + check( + "std::fmt", + r#" +#[cfg(target_arch = "x86_64")] +pub mod api; +"#, + r#" +use std::fmt; + +#[cfg(target_arch = "x86_64")] +pub mod api; +"#, + ImportGranularity::Crate, + ); +} + +fn check_merge(ra_fixture0: &str, ra_fixture1: &str, last: &str, mb: MergeBehavior) { + let use0 = ast::SourceFile::parse(ra_fixture0, span::Edition::CURRENT) + .tree() + .syntax() + .descendants() + .find_map(ast::Use::cast) + .unwrap(); + + let use1 = ast::SourceFile::parse(ra_fixture1, span::Edition::CURRENT) + .tree() + .syntax() + .descendants() + .find_map(ast::Use::cast) + .unwrap(); + + let result = try_merge_imports(&use0, &use1, mb); + assert_eq!(result.map(|u| u.to_string().trim().to_owned()), Some(last.trim().to_owned())); +} + +#[test] +fn merge_gated_imports() { + check_merge( + r#"#[cfg(test)] use foo::bar;"#, + r#"#[cfg(test)] use foo::baz;"#, + r#"#[cfg(test)] use foo::{bar, baz};"#, + MergeBehavior::Crate, + ); +} + +#[test] +fn merge_gated_imports_with_different_values() { + let use0 = ast::SourceFile::parse(r#"#[cfg(a)] use foo::bar;"#, span::Edition::CURRENT) + .tree() + .syntax() + .descendants() + .find_map(ast::Use::cast) + .unwrap(); + + let use1 = ast::SourceFile::parse(r#"#[cfg(b)] use foo::baz;"#, span::Edition::CURRENT) + .tree() + .syntax() + .descendants() + .find_map(ast::Use::cast) + .unwrap(); + + let result = try_merge_imports(&use0, &use1, MergeBehavior::Crate); + assert_eq!(result, None); +} + +#[test] +fn merge_into_existing_cfg_import() { + check( + r#"foo::Foo"#, + r#" +#[cfg(target_os = "windows")] +use bar::Baz; + +#[cfg(target_os = "windows")] +fn buzz() { + Foo$0; +} +"#, + r#" +#[cfg(target_os = "windows")] +use bar::Baz; +#[cfg(target_os = "windows")] +use foo::Foo; + +#[cfg(target_os = "windows")] +fn buzz() { + Foo; +} +"#, + ImportGranularity::Crate, + ); +} + +#[test] +fn reproduce_user_issue_missing_semicolon() { + check( + "std::fmt", + r#" +use { + foo +} + +#[cfg(target_arch = "x86_64")] +pub mod api; +"#, + r#" +use std::fmt; + +use { + foo +} + +#[cfg(target_arch = "x86_64")] +pub mod api; +"#, + ImportGranularity::Crate, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs index 635ed7368c4c0..bc2c2d0c0c368 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs @@ -692,13 +692,12 @@ pub fn eq_attrs( attrs1: impl Iterator, ) -> bool { // FIXME order of attributes should not matter - let attrs0 = attrs0 - .flat_map(|attr| attr.syntax().descendants_with_tokens()) - .flat_map(|it| it.into_token()); - let attrs1 = attrs1 - .flat_map(|attr| attr.syntax().descendants_with_tokens()) - .flat_map(|it| it.into_token()); - stdx::iter_eq_by(attrs0, attrs1, |tok, tok2| tok.text() == tok2.text()) + let mut attrs0: Vec<_> = attrs0.map(|attr| attr.syntax().text().to_string()).collect(); + let mut attrs1: Vec<_> = attrs1.map(|attr| attr.syntax().text().to_string()).collect(); + attrs0.sort(); + attrs1.sort(); + + attrs0 == attrs1 } fn path_is_self(path: &ast::Path) -> bool { From 3a8a3c74f27ff22b2bbb11594bfe2fcbfd8698a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?flora=20=7E=20=F0=9F=8F=B3=EF=B8=8F=E2=80=8D=E2=9A=A7?= =?UTF-8?q?=EF=B8=8F=20=E2=9D=A4=20=F0=9F=A6=80?= <35181375+tascord@users.noreply.github.com> Date: Sun, 15 Feb 2026 10:47:49 +1100 Subject: [PATCH 10/82] Remove resolved FIXME --- .../rust-analyzer/crates/ide-db/src/imports/merge_imports.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs index bc2c2d0c0c368..d63b239ff43c7 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs @@ -691,7 +691,6 @@ pub fn eq_attrs( attrs0: impl Iterator, attrs1: impl Iterator, ) -> bool { - // FIXME order of attributes should not matter let mut attrs0: Vec<_> = attrs0.map(|attr| attr.syntax().text().to_string()).collect(); let mut attrs1: Vec<_> = attrs1.map(|attr| attr.syntax().text().to_string()).collect(); attrs0.sort(); From b451b42018616243f66ffd96f40b2cc1cee08561 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 14 Feb 2026 22:15:08 +0800 Subject: [PATCH 11/82] internal: to use SmolStr in fn_param --- .../src/completions/fn_param.rs | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs index 34d25c9c67283..ad8a325359837 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs @@ -4,9 +4,9 @@ use hir::HirDisplay; use ide_db::FxHashMap; use itertools::Either; use syntax::{ - AstNode, Direction, SyntaxKind, TextRange, TextSize, algo, + AstNode, Direction, SmolStr, SyntaxKind, TextRange, TextSize, ToSmolStr, algo, ast::{self, HasModuleItem}, - match_ast, + format_smolstr, match_ast, }; use crate::{ @@ -51,7 +51,10 @@ pub(crate) fn complete_fn_param( ParamKind::Closure(closure) => { let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?; params_from_stmt_list_scope(ctx, stmt_list, |name, ty| { - add_new_item_to_acc(&format!("{}: {ty}", name.display(ctx.db, ctx.edition))); + add_new_item_to_acc(&format_smolstr!( + "{}: {ty}", + name.display(ctx.db, ctx.edition) + )); }); } } @@ -71,9 +74,8 @@ fn fill_fn_params( let mut extract_params = |f: ast::Fn| { f.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| { if let Some(pat) = param.pat() { - // FIXME: We should be able to turn these into SmolStr without having to allocate a String - let whole_param = param.syntax().text().to_string(); - let binding = pat.syntax().text().to_string(); + let whole_param = param.to_smolstr(); + let binding = pat.to_smolstr(); file_params.entry(whole_param).or_insert(binding); } }); @@ -102,8 +104,8 @@ fn fill_fn_params( if let Some(stmt_list) = function.syntax().parent().and_then(ast::StmtList::cast) { params_from_stmt_list_scope(ctx, stmt_list, |name, ty| { file_params - .entry(format!("{}: {ty}", name.display(ctx.db, ctx.edition))) - .or_insert(name.display(ctx.db, ctx.edition).to_string()); + .entry(format_smolstr!("{}: {ty}", name.display(ctx.db, ctx.edition))) + .or_insert(name.display(ctx.db, ctx.edition).to_smolstr()); }); } remove_duplicated(&mut file_params, param_list.params()); @@ -139,11 +141,11 @@ fn params_from_stmt_list_scope( } fn remove_duplicated( - file_params: &mut FxHashMap, + file_params: &mut FxHashMap, fn_params: ast::AstChildren, ) { fn_params.for_each(|param| { - let whole_param = param.syntax().text().to_string(); + let whole_param = param.to_smolstr(); file_params.remove(&whole_param); match param.pat() { @@ -151,7 +153,7 @@ fn remove_duplicated( // if the type is missing we are checking the current param to be completed // in which case this would find itself removing the suggestions due to itself Some(pattern) if param.ty().is_some() => { - let binding = pattern.syntax().text().to_string(); + let binding = pattern.to_smolstr(); file_params.retain(|_, v| v != &binding); } _ => (), @@ -173,7 +175,7 @@ fn should_add_self_completions( } } -fn comma_wrapper(ctx: &CompletionContext<'_>) -> Option<(impl Fn(&str) -> String, TextRange)> { +fn comma_wrapper(ctx: &CompletionContext<'_>) -> Option<(impl Fn(&str) -> SmolStr, TextRange)> { let param = ctx.original_token.parent_ancestors().find(|node| node.kind() == SyntaxKind::PARAM)?; @@ -196,5 +198,5 @@ fn comma_wrapper(ctx: &CompletionContext<'_>) -> Option<(impl Fn(&str) -> String matches!(prev_token_kind, SyntaxKind::COMMA | SyntaxKind::L_PAREN | SyntaxKind::PIPE); let leading = if has_leading_comma { "" } else { ", " }; - Some((move |label: &_| format!("{leading}{label}{trailing}"), param.text_range())) + Some((move |label: &_| format_smolstr!("{leading}{label}{trailing}"), param.text_range())) } From 71e4b03b02dcd190d48b508e14d70146fa7007ef Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 14 Feb 2026 22:01:28 +0800 Subject: [PATCH 12/82] fix: no complete suggest param in complex pattern Example --- ```rust fn foo(bar: u32) {} fn bar((a, bar$0)) {} ``` **Before this PR** ```rust fn foo(bar: u32) {} fn bar(bar: u32)) {} ``` **After this PR** Not complete `bar: u32` --- .../src/completions/fn_param.rs | 34 ++++++++---- .../ide-completion/src/tests/fn_param.rs | 54 +++++++++++++++++++ 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs index ad8a325359837..96dac66b8a199 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs @@ -25,7 +25,7 @@ pub(crate) fn complete_fn_param( ctx: &CompletionContext<'_>, pattern_ctx: &PatternContext, ) -> Option<()> { - let (ParamContext { param_list, kind, .. }, impl_or_trait) = match pattern_ctx { + let (ParamContext { param_list, kind, param, .. }, impl_or_trait) = match pattern_ctx { PatternContext { param_ctx: Some(kind), impl_or_trait, .. } => (kind, impl_or_trait), _ => return None, }; @@ -46,16 +46,18 @@ pub(crate) fn complete_fn_param( match kind { ParamKind::Function(function) => { - fill_fn_params(ctx, function, param_list, impl_or_trait, add_new_item_to_acc); + fill_fn_params(ctx, function, param_list, param, impl_or_trait, add_new_item_to_acc); } ParamKind::Closure(closure) => { - let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?; - params_from_stmt_list_scope(ctx, stmt_list, |name, ty| { - add_new_item_to_acc(&format_smolstr!( - "{}: {ty}", - name.display(ctx.db, ctx.edition) - )); - }); + if is_simple_param(param) { + let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?; + params_from_stmt_list_scope(ctx, stmt_list, |name, ty| { + add_new_item_to_acc(&format_smolstr!( + "{}: {ty}", + name.display(ctx.db, ctx.edition) + )); + }); + } } } @@ -66,12 +68,16 @@ fn fill_fn_params( ctx: &CompletionContext<'_>, function: &ast::Fn, param_list: &ast::ParamList, + current_param: &ast::Param, impl_or_trait: &Option>, mut add_new_item_to_acc: impl FnMut(&str), ) { let mut file_params = FxHashMap::default(); let mut extract_params = |f: ast::Fn| { + if !is_simple_param(current_param) { + return; + } f.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| { if let Some(pat) = param.pat() { let whole_param = param.to_smolstr(); @@ -101,7 +107,9 @@ fn fill_fn_params( }; } - if let Some(stmt_list) = function.syntax().parent().and_then(ast::StmtList::cast) { + if let Some(stmt_list) = function.syntax().parent().and_then(ast::StmtList::cast) + && is_simple_param(current_param) + { params_from_stmt_list_scope(ctx, stmt_list, |name, ty| { file_params .entry(format_smolstr!("{}: {ty}", name.display(ctx.db, ctx.edition))) @@ -200,3 +208,9 @@ fn comma_wrapper(ctx: &CompletionContext<'_>) -> Option<(impl Fn(&str) -> SmolSt Some((move |label: &_| format_smolstr!("{leading}{label}{trailing}"), param.text_range())) } + +fn is_simple_param(param: &ast::Param) -> bool { + param + .pat() + .is_none_or(|pat| matches!(pat, ast::Pat::IdentPat(ident_pat) if ident_pat.pat().is_none())) +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs index 02cba6b6467e5..d6d73da3f140b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs @@ -292,6 +292,60 @@ fn bar(bar$0) {} ) } +#[test] +fn not_shows_fully_equal_inside_pattern_params() { + check( + r#" +fn foo(bar: u32) {} +fn bar((a, bar$0)) {} +"#, + expect![[r#" + kw mut + kw ref + "#]], + ) +} + +#[test] +fn not_shows_locals_inside_pattern_params() { + check( + r#" +fn outer() { + let foo = 3; + { + let bar = 3; + |($0)| {}; + let baz = 3; + let qux = 3; + } + let fez = 3; +} +"#, + expect![[r#" + kw mut + kw ref + "#]], + ); + check( + r#" +fn outer() { + let foo = 3; + { + let bar = 3; + fn inner(($0)) {} + let baz = 3; + let qux = 3; + } + let fez = 3; +} +"#, + expect![[r#" + kw mut + kw ref + "#]], + ); +} + #[test] fn completes_for_params_with_attributes() { check( From 25a71ea88818c2a364a915196e06e0bac39693dd Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 16 Feb 2026 00:11:51 +0200 Subject: [PATCH 13/82] Fix predicates of builtin derive traits with two parameters defaulting to `Self` I.e. `PartialEq` and `PartialOrd`. --- .../crates/hir-ty/src/builtin_derive.rs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs index 0c3c51366861b..b7a1585c808b2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs @@ -260,7 +260,22 @@ fn simple_trait_predicates<'db>( let param_idx = param_idx.into_raw().into_u32() + (generic_params.len_lifetimes() as u32); let param_ty = Ty::new_param(interner, param_id, param_idx); - let trait_ref = TraitRef::new(interner, trait_id.into(), [param_ty]); + let trait_args = match loc.trait_ { + BuiltinDeriveImplTrait::Copy + | BuiltinDeriveImplTrait::Clone + | BuiltinDeriveImplTrait::Default + | BuiltinDeriveImplTrait::Debug + | BuiltinDeriveImplTrait::Hash + | BuiltinDeriveImplTrait::Ord + | BuiltinDeriveImplTrait::Eq => GenericArgs::new_from_slice(&[param_ty.into()]), + BuiltinDeriveImplTrait::PartialOrd | BuiltinDeriveImplTrait::PartialEq => { + GenericArgs::new_from_slice(&[param_ty.into(), param_ty.into()]) + } + BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => { + unreachable!() + } + }; + let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), trait_args); trait_ref.upcast(interner) }); let mut assoc_type_bounds = Vec::new(); @@ -528,7 +543,7 @@ struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N]); Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) - Clause(Binder { value: TraitPredicate(#1: PartialEq, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(#1: PartialEq<[#1]>, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) @@ -538,7 +553,7 @@ struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N]); Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) - Clause(Binder { value: TraitPredicate(#1: PartialOrd, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(#1: PartialOrd<[#1]>, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) From 1c462c2d502bf1252788149a5d791eaccea2a742 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 15 Feb 2026 18:35:22 +0800 Subject: [PATCH 14/82] fix: complete derive helpers on empty nameref Example --- ```rust //- /mac.rs crate:mac pub fn my_derive() {} //- /lib.rs crate:lib deps:mac pub struct Foo(#[$0] i32); ``` **Before this PR** ```text ... at must_use at no_mangle ... ``` **After this PR** ```text ... at must_use at my_cool_helper_attribute derive helper of `MyDerive` at no_mangle ... ``` --- .../ide-completion/src/context/analysis.rs | 2 +- .../ide-completion/src/tests/attribute.rs | 66 ++++++++++++++++++- 2 files changed, 66 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 1c8bc656ca251..4b0cc0c7cd98e 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 @@ -1501,7 +1501,7 @@ fn classify_name_ref<'db>( | SyntaxKind::RECORD_FIELD ) }) - .and_then(|_| nameref.as_ref()?.syntax().ancestors().find_map(ast::Adt::cast)) + .and_then(|_| find_node_at_offset::(original_file, original_offset)) .and_then(|adt| sema.derive_helpers_in_scope(&adt)) .unwrap_or_default(); Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind, derive_helpers } }) 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 3701416dfc849..131911be91558 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 @@ -68,7 +68,71 @@ pub struct Foo(#[m$0] i32); kw crate:: kw self:: "#]], - ) + ); + check( + r#" +//- /mac.rs crate:mac +#![crate_type = "proc-macro"] + +#[proc_macro_derive(MyDerive, attributes(my_cool_helper_attribute))] +pub fn my_derive() {} + +//- /lib.rs crate:lib deps:mac +#[rustc_builtin_macro] +pub macro derive($item:item) {} + +#[derive(mac::MyDerive)] +pub struct Foo(#[$0] i32); +"#, + expect![[r#" + at allow(…) + at automatically_derived + at cfg(…) + at cfg_attr(…) + at cold + at deny(…) + at deprecated + at derive macro derive + at derive(…) + at diagnostic::do_not_recommend + at diagnostic::on_unimplemented + at doc = "…" + at doc = include_str!("…") + at doc(alias = "…") + at doc(hidden) + at expect(…) + at export_name = "…" + at forbid(…) + at global_allocator + at ignore = "…" + at inline + at link + at link_name = "…" + at link_section = "…" + at macro_export + at macro_use + at must_use + at my_cool_helper_attribute derive helper of `MyDerive` + at no_mangle + at non_exhaustive + at panic_handler + at path = "…" + at proc_macro + at proc_macro_attribute + at proc_macro_derive(…) + at repr(…) + at should_panic + at target_feature(enable = "…") + at test + at track_caller + at unsafe(…) + at used + at warn(…) + md mac + kw crate:: + kw self:: + "#]], + ); } #[test] From 6ac0ee92bd982b8255bc706a2078cb3cf0bf6c83 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 16 Feb 2026 20:43:40 +0530 Subject: [PATCH 15/82] add span_source to ProcMacroClientInterface --- src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index c548dc620ad13..6b770e440c7bb 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -120,6 +120,7 @@ pub trait ProcMacroClientInterface { fn line_column(&mut self, span: Span) -> Option<(u32, u32)>; fn byte_range(&mut self, span: Span) -> Range; + fn span_source(&mut self, span: Span) -> Span; } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; From 2541de34b5e10ccb4fbeb95e08d3c4cf83d20fee Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 16 Feb 2026 20:56:34 +0530 Subject: [PATCH 16/82] update utils to include span_source in the interface --- .../rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index b7c5c4fdd21f0..28d826d01ea73 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -142,6 +142,10 @@ impl ProcMacroClientInterface for MockCallback<'_> { fn byte_range(&mut self, span: Span) -> Range { Range { start: span.range.start().into(), end: span.range.end().into() } } + + fn span_source(&mut self, span: Span) -> Span { + span + } } pub fn assert_expand_with_callback( From 9378836300eaf67d9b6c628a318f2d5f7c69fcbf Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 16 Feb 2026 20:57:13 +0530 Subject: [PATCH 17/82] add span_source implementation in proc-macro-srv-cli --- .../proc-macro-srv-cli/src/main_loop.rs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 9be3199a3836a..2c54b18077bb0 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -273,6 +273,42 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { other => handle_failure(other), } } + + fn span_source( + &mut self, + proc_macro_srv::span::Span { range, anchor, ctx }: proc_macro_srv::span::Span, + ) -> proc_macro_srv::span::Span { + match self.roundtrip(bidirectional::SubRequest::SpanSource { + file_id: anchor.file_id.as_u32(), + ast_id: anchor.ast_id.into_raw(), + start: range.start().into(), + end: range.end().into(), + ctx: ctx.into_u32(), + }) { + Ok(bidirectional::SubResponse::SpanSourceResult { + file_id, + ast_id, + start, + end, + ctx, + }) => { + proc_macro_srv::span::Span { + range: proc_macro_srv::span::TextRange::new( + proc_macro_srv::span::TextSize::new(start), + proc_macro_srv::span::TextSize::new(end), + ), + anchor: proc_macro_srv::span::SpanAnchor { + file_id: proc_macro_srv::span::EditionedFileId::from_raw(file_id), + ast_id: proc_macro_srv::span::ErasedFileAstId::from_raw(ast_id), + }, + // SAFETY: We only receive spans from the server. If someone mess up the communication UB can happen, + // but that will be their problem. + ctx: unsafe { proc_macro_srv::span::SyntaxContext::from_u32(ctx) }, + } + } + other => handle_failure(other), + } + } } fn handle_expand_ra( From a7d65c7d7067432ee63cbe8dd969fc2fb1083e2c Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 16 Feb 2026 20:59:47 +0530 Subject: [PATCH 18/82] add span source implementation to proc-macro-srv --- .../proc-macro-srv/src/server_impl/rust_analyzer_span.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index c114d52ec33c6..0890e72c4fb1a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -173,7 +173,9 @@ impl server::Server for RaSpanServer<'_> { None } fn span_source(&mut self, span: Self::Span) -> Self::Span { - // FIXME requires db, returns the top level call site + if let Some(ref mut callback) = self.callback { + return callback.span_source(span); + } span } fn span_byte_range(&mut self, span: Self::Span) -> Range { From 7f2956e19e6983005772979775d0ce1364390613 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 16 Feb 2026 21:00:33 +0530 Subject: [PATCH 19/82] extend span source and span source result variant in message --- .../proc-macro-api/src/bidirectional_protocol/msg.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs index 3f0422dc5bc83..10a8d66677ac4 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs @@ -21,6 +21,7 @@ pub enum SubRequest { LocalFilePath { file_id: u32 }, LineColumn { file_id: u32, ast_id: u32, offset: u32 }, ByteRange { file_id: u32, ast_id: u32, start: u32, end: u32 }, + SpanSource { file_id: u32, ast_id: u32, start: u32, end: u32, ctx: u32 }, } #[derive(Debug, Serialize, Deserialize)] @@ -42,6 +43,13 @@ pub enum SubResponse { ByteRangeResult { range: Range, }, + SpanSourceResult { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + ctx: u32, + }, Cancel { reason: String, }, From a9d3e24a3b9026c604606f5b3d0819292fd76265 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 16 Feb 2026 21:01:11 +0530 Subject: [PATCH 20/82] add span source client implementation --- .../crates/load-cargo/src/lib.rs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 70a00cf825162..654ff4f75b0e6 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -612,6 +612,53 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::ByteRangeResult { range: range.range.into() }) } + SubRequest::SpanSource { file_id, ast_id, start, end, ctx } => { + let span = Span { + range: TextRange::new(TextSize::from(start), TextSize::from(end)), + anchor: SpanAnchor { + file_id: span::EditionedFileId::from_raw(file_id), + ast_id: span::ErasedFileAstId::from_raw(ast_id), + }, + // SAFETY: We only receive spans from the server. If someone mess up the communication UB can happen, + // but that will be their problem. + ctx: unsafe { SyntaxContext::from_u32(ctx) }, + }; + + let mut current_span = span; + let mut current_ctx = span.ctx; + + while let Some(macro_call_id) = current_ctx.outer_expn(db) { + let macro_call_loc = db.lookup_intern_macro_call(macro_call_id.into()); + + let call_site_file = macro_call_loc.kind.file_id(); + + let resolved = db.resolve_span(current_span); + + current_ctx = macro_call_loc.ctxt; + current_span = Span { + range: resolved.range, + anchor: SpanAnchor { + file_id: resolved.file_id.editioned_file_id(db), + ast_id: span::ROOT_ERASED_FILE_AST_ID, + }, + ctx: current_ctx, + }; + + if call_site_file.file_id().is_some() { + break; + } + } + + let resolved = db.resolve_span(current_span); + + Ok(SubResponse::SpanSourceResult { + file_id: resolved.file_id.editioned_file_id(db).as_u32(), + ast_id: span::ROOT_ERASED_FILE_AST_ID.into_raw(), + start: u32::from(resolved.range.start()), + end: u32::from(resolved.range.end()), + ctx: current_span.ctx.into_u32(), + }) + } }; match self.0.expand( subtree.view(), From 12cacac0fb9efc0e5802b4584f380df24f6394c0 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Tue, 17 Feb 2026 15:43:36 +0000 Subject: [PATCH 21/82] fix: Ensure cpu_profiler feature compiles on Rust edition 2024 Without `unsafe` on this block, this feature didn't compile. --- .../rust-analyzer/crates/profile/src/google_cpu_profiler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/profile/src/google_cpu_profiler.rs b/src/tools/rust-analyzer/crates/profile/src/google_cpu_profiler.rs index cae6caeaa669c..d77c945f26319 100644 --- a/src/tools/rust-analyzer/crates/profile/src/google_cpu_profiler.rs +++ b/src/tools/rust-analyzer/crates/profile/src/google_cpu_profiler.rs @@ -9,7 +9,7 @@ use std::{ #[link(name = "profiler")] #[allow(non_snake_case)] -extern "C" { +unsafe extern "C" { fn ProfilerStart(fname: *const c_char) -> i32; fn ProfilerStop(); } From cf246bd45dcb8b702ae5409df999a8a2c9f19a1d Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Tue, 17 Feb 2026 13:19:19 +0000 Subject: [PATCH 22/82] Add some basic flycheck integration tests --- .../tests/slow-tests/flycheck.rs | 76 +++++++++++++++++++ .../rust-analyzer/tests/slow-tests/main.rs | 1 + .../rust-analyzer/tests/slow-tests/support.rs | 51 ++++++++++++- 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs new file mode 100644 index 0000000000000..0aa107e8233d9 --- /dev/null +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs @@ -0,0 +1,76 @@ +use test_utils::skip_slow_tests; + +use crate::support::Project; + +#[test] +fn test_flycheck_diagnostics_for_unused_variable() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r#" +//- /Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- /src/main.rs +fn main() { + let x = 1; +} +"#, + ) + .with_config(serde_json::json!({ + "checkOnSave": true, + })) + .server() + .wait_until_workspace_is_loaded(); + + let diagnostics = server.wait_for_diagnostics(); + assert!( + diagnostics.diagnostics.iter().any(|d| d.message.contains("unused variable")), + "expected unused variable diagnostic, got: {:?}", + diagnostics.diagnostics, + ); +} + +#[test] +fn test_flycheck_diagnostic_cleared_after_fix() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r#" +//- /Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- /src/main.rs +fn main() { + let x = 1; +} +"#, + ) + .with_config(serde_json::json!({ + "checkOnSave": true, + })) + .server() + .wait_until_workspace_is_loaded(); + + // Wait for the unused variable diagnostic to appear. + let diagnostics = server.wait_for_diagnostics(); + assert!( + diagnostics.diagnostics.iter().any(|d| d.message.contains("unused variable")), + "expected unused variable diagnostic, got: {:?}", + diagnostics.diagnostics, + ); + + // Fix the code by removing the unused variable. + server.write_file_and_save("src/main.rs", "fn main() {}\n".to_owned()); + + // Wait for diagnostics to be cleared. + server.wait_for_diagnostics_cleared(); +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index b4a7b44d165ac..26ba549a29be1 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -15,6 +15,7 @@ extern crate rustc_driver as _; mod cli; +mod flycheck; mod ratoml; mod support; mod testdir; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index 195ad226ae0c5..b8592c57de51f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -8,7 +8,9 @@ use std::{ use crossbeam_channel::{Receiver, after, select}; use itertools::Itertools; use lsp_server::{Connection, Message, Notification, Request}; -use lsp_types::{TextDocumentIdentifier, Url, notification::Exit, request::Shutdown}; +use lsp_types::{ + PublishDiagnosticsParams, TextDocumentIdentifier, Url, notification::Exit, request::Shutdown, +}; use parking_lot::{Mutex, MutexGuard}; use paths::{Utf8Path, Utf8PathBuf}; use rust_analyzer::{ @@ -407,6 +409,53 @@ impl Server { .unwrap_or_else(|Timeout| panic!("timeout while waiting for ws to load")); self } + pub(crate) fn wait_for_diagnostics(&self) -> PublishDiagnosticsParams { + for msg in self.messages.borrow().iter() { + if let Message::Notification(n) = msg { + if n.method == "textDocument/publishDiagnostics" { + let params: PublishDiagnosticsParams = + serde_json::from_value(n.params.clone()).unwrap(); + if !params.diagnostics.is_empty() { + return params; + } + } + } + } + loop { + let msg = self + .recv() + .unwrap_or_else(|Timeout| panic!("timeout while waiting for diagnostics")) + .expect("connection closed while waiting for diagnostics"); + if let Message::Notification(n) = &msg { + if n.method == "textDocument/publishDiagnostics" { + let params: PublishDiagnosticsParams = + serde_json::from_value(n.params.clone()).unwrap(); + if !params.diagnostics.is_empty() { + return params; + } + } + } + } + } + + pub(crate) fn wait_for_diagnostics_cleared(&self) { + loop { + let msg = self + .recv() + .unwrap_or_else(|Timeout| panic!("timeout while waiting for diagnostics to clear")) + .expect("connection closed while waiting for diagnostics to clear"); + if let Message::Notification(n) = &msg { + if n.method == "textDocument/publishDiagnostics" { + let params: PublishDiagnosticsParams = + serde_json::from_value(n.params.clone()).unwrap(); + if params.diagnostics.is_empty() { + return; + } + } + } + } + } + fn wait_for_message_cond( &self, n: usize, From 0a6961d2e002bd7730c427c18c8e65e84a5991c0 Mon Sep 17 00:00:00 2001 From: Raushan Kumar Date: Wed, 18 Feb 2026 06:46:34 +0000 Subject: [PATCH 23/82] fix: prevent path transformation of parameter names matching modules --- .../src/handlers/add_missing_impl_members.rs | 80 +++++++++++++++++++ .../crates/ide-db/src/path_transform.rs | 5 ++ 2 files changed, 85 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 65ca1ceae11e9..afdced4215f9f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -2534,6 +2534,86 @@ impl Test for () { ${0:todo!()} } } +"#, + ); + } + + #[test] + fn test_param_name_not_qualified() { + check_assist( + add_missing_impl_members, + r#" +mod ptr { + pub struct NonNull(T); +} +mod alloc { + use super::ptr::NonNull; + pub trait Allocator { + unsafe fn deallocate(&self, ptr: NonNull); + } +} + +struct System; + +unsafe impl alloc::Allocator for System { + $0 +} +"#, + r#" +mod ptr { + pub struct NonNull(T); +} +mod alloc { + use super::ptr::NonNull; + pub trait Allocator { + unsafe fn deallocate(&self, ptr: NonNull); + } +} + +struct System; + +unsafe impl alloc::Allocator for System { + unsafe fn deallocate(&self, ptr: ptr::NonNull) { + ${0:todo!()} + } +} +"#, + ); + } + + #[test] + fn test_param_name_shadows_module() { + check_assist( + add_missing_impl_members, + r#" +mod m { } +use m as p; + +pub trait Allocator { + fn deallocate(&self, p: u8); +} + +struct System; + +impl Allocator for System { + $0 +} +"#, + r#" +mod m { } +use m as p; + +pub trait Allocator { + fn deallocate(&self, p: u8); +} + +struct System; + +impl Allocator for System { + fn deallocate(&self, p: u8) { + ${0:todo!()} + } +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 48305c20823c1..d0a28e7d2f86c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -553,6 +553,11 @@ impl Ctx<'_> { return None; } + // Similarly, modules cannot be used in pattern position. + if matches!(def, hir::ModuleDef::Module(_)) { + return None; + } + let cfg = FindPathConfig { prefer_no_std: false, prefer_prelude: true, From cc3115d386eb4836f84b3fba0971c1780ed76bd4 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Tue, 17 Feb 2026 14:37:45 +0000 Subject: [PATCH 24/82] Enable debug printing on more types --- .../crates/rust-analyzer/src/flycheck.rs | 4 ++ .../rust-analyzer/tests/slow-tests/support.rs | 42 +++++++++---------- 2 files changed, 25 insertions(+), 21 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 47f7a57f72eca..f7f861a99e8e6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -382,6 +382,7 @@ enum FlycheckCommandOrigin { ProjectJsonRunnable, } +#[derive(Debug)] enum StateChange { Restart { generation: DiagnosticsGeneration, @@ -435,6 +436,7 @@ enum DiagnosticsReceived { } #[allow(clippy::large_enum_variant)] +#[derive(Debug)] enum Event { RequestStateChange(StateChange), CheckEvent(Option), @@ -445,6 +447,7 @@ const SAVED_FILE_PLACEHOLDER_DOLLAR: &str = "$saved_file"; const LABEL_INLINE: &str = "{label}"; const SAVED_FILE_INLINE: &str = "{saved_file}"; +#[derive(Debug)] struct Substitutions<'a> { label: Option<&'a str>, saved_file: Option<&'a str>, @@ -950,6 +953,7 @@ impl FlycheckActor { } #[allow(clippy::large_enum_variant)] +#[derive(Debug)] enum CheckMessage { /// A message from `cargo check`, including details like the path /// to the relevant `Cargo.toml`. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index b8592c57de51f..7ee31f3d53eab 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -411,13 +411,13 @@ impl Server { } pub(crate) fn wait_for_diagnostics(&self) -> PublishDiagnosticsParams { for msg in self.messages.borrow().iter() { - if let Message::Notification(n) = msg { - if n.method == "textDocument/publishDiagnostics" { - let params: PublishDiagnosticsParams = - serde_json::from_value(n.params.clone()).unwrap(); - if !params.diagnostics.is_empty() { - return params; - } + if let Message::Notification(n) = msg + && n.method == "textDocument/publishDiagnostics" + { + let params: PublishDiagnosticsParams = + serde_json::from_value(n.params.clone()).unwrap(); + if !params.diagnostics.is_empty() { + return params; } } } @@ -426,13 +426,13 @@ impl Server { .recv() .unwrap_or_else(|Timeout| panic!("timeout while waiting for diagnostics")) .expect("connection closed while waiting for diagnostics"); - if let Message::Notification(n) = &msg { - if n.method == "textDocument/publishDiagnostics" { - let params: PublishDiagnosticsParams = - serde_json::from_value(n.params.clone()).unwrap(); - if !params.diagnostics.is_empty() { - return params; - } + if let Message::Notification(n) = &msg + && n.method == "textDocument/publishDiagnostics" + { + let params: PublishDiagnosticsParams = + serde_json::from_value(n.params.clone()).unwrap(); + if !params.diagnostics.is_empty() { + return params; } } } @@ -444,13 +444,13 @@ impl Server { .recv() .unwrap_or_else(|Timeout| panic!("timeout while waiting for diagnostics to clear")) .expect("connection closed while waiting for diagnostics to clear"); - if let Message::Notification(n) = &msg { - if n.method == "textDocument/publishDiagnostics" { - let params: PublishDiagnosticsParams = - serde_json::from_value(n.params.clone()).unwrap(); - if params.diagnostics.is_empty() { - return; - } + if let Message::Notification(n) = &msg + && n.method == "textDocument/publishDiagnostics" + { + let params: PublishDiagnosticsParams = + serde_json::from_value(n.params.clone()).unwrap(); + if params.diagnostics.is_empty() { + return; } } } From b7d95aab1606edcec11255fb60fd573d3ce75b5c Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Tue, 17 Feb 2026 18:26:39 +0000 Subject: [PATCH 25/82] internal: Add flycheck test for custom check command and debounce This adds an integration test for flycheck with a custom check command. On its own, the test actually fails due to an issue with the debounce logic. We would trigger a flycheck when the workspace is loaded, but the check command references $saved_file and flycheck does nothing (we don't know which file was saved). Since we debounce state changes over 50 milliseconds, we would then discard the later save request that actually specifies the file. This bug is probably very rare in practice, 50 milliseconds isn't very long. I think it's a genuine fix though. AI disclosure: Partly written by Opus 4.6. --- .../crates/rust-analyzer/src/flycheck.rs | 34 ++++++++++++++---- .../tests/slow-tests/flycheck.rs | 36 +++++++++++++++++++ 2 files changed, 63 insertions(+), 7 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 f7f861a99e8e6..cdaf944bbad42 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -559,17 +559,37 @@ impl FlycheckActor { self.cancel_check_process(); } Event::RequestStateChange(StateChange::Restart { - generation, - scope, - saved_file, - target, + mut generation, + mut scope, + mut saved_file, + mut target, }) => { // Cancel the previously spawned process self.cancel_check_process(); + + // Debounce by briefly waiting for other state changes. while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) { - // restart chained with a stop, so just cancel - if let StateChange::Cancel = restart { - continue 'event; + match restart { + StateChange::Cancel => { + // We got a cancel straight after this restart request, so + // don't do anything. + continue 'event; + } + StateChange::Restart { + generation: g, + scope: s, + saved_file: sf, + target: t, + } => { + // We got another restart request. Take the parameters + // from the last restart request in this time window, + // because the most recent request is probably the most + // relevant to the user. + generation = g; + scope = s; + saved_file = sf; + target = t; + } } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs index 0aa107e8233d9..c1d53fb33ab60 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs @@ -74,3 +74,39 @@ fn main() { // Wait for diagnostics to be cleared. server.wait_for_diagnostics_cleared(); } + +#[test] +fn test_flycheck_diagnostic_with_override_command() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r#" +//- /Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- /src/main.rs +fn main() {} +"#, + ) + .with_config(serde_json::json!({ + "checkOnSave": true, + "check": { + "overrideCommand": ["rustc", "--error-format=json", "$saved_file"] + } + })) + .server() + .wait_until_workspace_is_loaded(); + + server.write_file_and_save("src/main.rs", "fn main() {\n let x = 1;\n}\n".to_owned()); + + let diagnostics = server.wait_for_diagnostics(); + assert!( + diagnostics.diagnostics.iter().any(|d| d.message.contains("unused variable")), + "expected unused variable diagnostic, got: {:?}", + diagnostics.diagnostics, + ); +} From c99225675c5bdea059e57bdc9bea0aa4a8bf1409 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 6 Feb 2026 18:29:02 +0800 Subject: [PATCH 26/82] Only offer block let fallback in match-arm --- .../ide-completion/src/completions/postfix.rs | 7 +++---- .../crates/ide-completion/src/render.rs | 2 -- .../crates/ide-completion/src/tests/expression.rs | 14 -------------- 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 3a92903d05128..067d906cd192c 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 @@ -161,7 +161,7 @@ pub(crate) fn complete_postfix( postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};")) .add_to(acc, ctx.db); } - _ => { + _ if ast::MatchArm::can_cast(second_ancestor.kind()) => { postfix_snippet( "let", "let", @@ -175,6 +175,7 @@ pub(crate) fn complete_postfix( ) .add_to(acc, ctx.db); } + _ => (), } } @@ -594,8 +595,6 @@ fn main() { sn dbgr dbg!(&expr) sn deref *expr sn if if expr {} - sn let let - sn letm let mut sn match match expr {} sn not !expr sn ref &expr @@ -811,7 +810,7 @@ fn main() { } #[test] - fn let_fallback_block() { + fn match_arm_let_block() { check( r#" fn main() { 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 7a7b054b3974b..765304d8187de 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -3033,8 +3033,6 @@ fn main() { sn dbgr dbg!(&expr) [] sn deref *expr [] sn if if expr {} [] - sn let let [] - sn letm let mut [] sn match match expr {} [] sn ref &expr [] sn refm &mut expr [] 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 b9b0f76c884c3..df39591a33460 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 @@ -2341,8 +2341,6 @@ fn main() { 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 @@ -2370,8 +2368,6 @@ fn main() { 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 @@ -2403,8 +2399,6 @@ fn main() { 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 @@ -2432,8 +2426,6 @@ fn main() { 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 @@ -2461,8 +2453,6 @@ fn main() { 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 @@ -2490,8 +2480,6 @@ fn main() { 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 @@ -3280,8 +3268,6 @@ fn foo() { 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 From 140672a4658563ff2fe3c570ceec06e8910d212d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Wed, 18 Feb 2026 18:35:01 +0200 Subject: [PATCH 27/82] Bump rustup-toolchain-install-master --- .../rust-analyzer/.github/workflows/ci.yaml | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index ca7d3058d8f0b..04de6d11e3ebf 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -50,7 +50,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Install rustup-toolchain-install-master - run: cargo install rustup-toolchain-install-master@1.6.0 + run: cargo install rustup-toolchain-install-master@1.11.0 # Install a pinned rustc commit to avoid surprises - name: Install Rust toolchain @@ -226,7 +226,12 @@ jobs: strategy: matrix: - target: [powerpc-unknown-linux-gnu, x86_64-unknown-linux-musl, wasm32-unknown-unknown] + target: + [ + powerpc-unknown-linux-gnu, + x86_64-unknown-linux-musl, + wasm32-unknown-unknown, + ] include: # The rust-analyzer binary is not expected to compile on WASM, but the IDE # crate should @@ -330,7 +335,18 @@ jobs: run: typos conclusion: - needs: [rust, rust-cross, typescript, typo-check, proc-macro-srv, miri, rustfmt, clippy, analysis-stats] + needs: + [ + rust, + rust-cross, + typescript, + typo-check, + proc-macro-srv, + miri, + rustfmt, + clippy, + analysis-stats, + ] # We need to ensure this job does *not* get skipped if its dependencies fail, # because a skipped job is considered a success by GitHub. So we have to # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run From 053cde5303ef82c02a1a879aeba8d58ef4bf2444 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 13 Feb 2026 01:14:08 +0900 Subject: [PATCH 28/82] Switch to env var CARGO_RESOLVER_LOCKFILE_PATH for copied lockfiles for toolchains >= `1.95.0` --- .../project-model/src/build_dependencies.rs | 33 ++++++---- .../project-model/src/cargo_config_file.rs | 62 +++++++++++++++---- .../project-model/src/cargo_workspace.rs | 48 +++++++------- 3 files changed, 93 insertions(+), 50 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index fedc6944f5f86..a4eec872b62cd 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 @@ -22,8 +22,9 @@ use triomphe::Arc; use crate::{ CargoConfig, CargoFeatures, CargoWorkspace, InvocationStrategy, ManifestPath, Package, Sysroot, - TargetKind, cargo_config_file::make_lockfile_copy, - cargo_workspace::MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH, utf8_stdout, + TargetKind, + cargo_config_file::{LockfileCopy, LockfileUsage, make_lockfile_copy}, + utf8_stdout, }; /// Output of the build script and proc-macro building steps for a workspace. @@ -436,7 +437,7 @@ impl WorkspaceBuildScripts { current_dir: &AbsPath, sysroot: &Sysroot, toolchain: Option<&semver::Version>, - ) -> io::Result<(Option, Command)> { + ) -> io::Result<(Option, Command)> { match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { let mut cmd = toolchain::command(program, current_dir, &config.extra_env); @@ -461,17 +462,25 @@ impl WorkspaceBuildScripts { if let Some(target) = &config.target { cmd.args(["--target", target]); } - let mut temp_dir_guard = None; - if toolchain - .is_some_and(|v| *v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH) - { + let mut lockfile_copy = None; + if let Some(toolchain) = toolchain { let lockfile_path = <_ as AsRef>::as_ref(manifest_path).with_extension("lock"); - if let Some((temp_dir, target_lockfile)) = make_lockfile_copy(&lockfile_path) { + lockfile_copy = make_lockfile_copy(toolchain, &lockfile_path); + if let Some(lockfile_copy) = &lockfile_copy { requires_unstable_options = true; - temp_dir_guard = Some(temp_dir); - cmd.arg("--lockfile-path"); - cmd.arg(target_lockfile.as_str()); + match lockfile_copy.usage { + LockfileUsage::WithFlag => { + cmd.arg("--lockfile-path"); + cmd.arg(lockfile_copy.path.as_str()); + } + LockfileUsage::WithEnvVar => { + cmd.env( + "CARGO_RESOLVER_LOCKFILE_PATH", + lockfile_copy.path.as_os_str(), + ); + } + } } } match &config.features { @@ -542,7 +551,7 @@ impl WorkspaceBuildScripts { cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); cmd.arg("-Zunstable-options"); } - Ok((temp_dir_guard, cmd)) + Ok((lockfile_copy, cmd)) } } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs index 5d6e5fd648b38..ae36deb71f02a 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs @@ -132,25 +132,65 @@ impl<'a> CargoConfigFileReader<'a> { } } +pub(crate) struct LockfileCopy { + pub(crate) path: Utf8PathBuf, + pub(crate) usage: LockfileUsage, + _temp_dir: temp_dir::TempDir, +} + +pub(crate) enum LockfileUsage { + /// Rust [1.82.0, 1.95.0). `cargo --lockfile-path ` + WithFlag, + /// Rust >= 1.95.0. `CARGO_RESOLVER_LOCKFILE_PATH= cargo ` + WithEnvVar, +} + pub(crate) fn make_lockfile_copy( + toolchain_version: &semver::Version, lockfile_path: &Utf8Path, -) -> Option<(temp_dir::TempDir, Utf8PathBuf)> { +) -> Option { + const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_FLAG: semver::Version = + semver::Version { + major: 1, + minor: 82, + patch: 0, + pre: semver::Prerelease::EMPTY, + build: semver::BuildMetadata::EMPTY, + }; + + const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV: semver::Version = + semver::Version { + major: 1, + minor: 95, + patch: 0, + pre: semver::Prerelease::EMPTY, + build: semver::BuildMetadata::EMPTY, + }; + + let usage = if *toolchain_version >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV { + LockfileUsage::WithEnvVar + } else if *toolchain_version >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_FLAG { + LockfileUsage::WithFlag + } else { + return None; + }; + let temp_dir = temp_dir::TempDir::with_prefix("rust-analyzer").ok()?; - let target_lockfile = temp_dir.path().join("Cargo.lock").try_into().ok()?; - match std::fs::copy(lockfile_path, &target_lockfile) { + let path: Utf8PathBuf = temp_dir.path().join("Cargo.lock").try_into().ok()?; + let path = match std::fs::copy(lockfile_path, &path) { Ok(_) => { - tracing::debug!("Copied lock file from `{}` to `{}`", lockfile_path, target_lockfile); - Some((temp_dir, target_lockfile)) + tracing::debug!("Copied lock file from `{}` to `{}`", lockfile_path, path); + path } // lockfile does not yet exist, so we can just create a new one in the temp dir - Err(e) if e.kind() == std::io::ErrorKind::NotFound => Some((temp_dir, target_lockfile)), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => path, Err(e) => { - tracing::warn!( - "Failed to copy lock file from `{lockfile_path}` to `{target_lockfile}`: {e}", - ); - None + tracing::warn!("Failed to copy lock file from `{lockfile_path}` to `{path}`: {e}",); + return None; } - } + }; + + Some(LockfileCopy { path, usage, _temp_dir: temp_dir }) } #[test] 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 483ab28450455..f7e57c5b075c7 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 @@ -16,18 +16,10 @@ use toolchain::{NO_RUSTUP_AUTO_INSTALL_ENV, Tool}; use triomphe::Arc; use crate::{ - CfgOverrides, InvocationStrategy, ManifestPath, Sysroot, cargo_config_file::make_lockfile_copy, + CfgOverrides, InvocationStrategy, ManifestPath, Sysroot, + cargo_config_file::{LockfileCopy, LockfileUsage, make_lockfile_copy}, }; -pub(crate) const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH: semver::Version = - semver::Version { - major: 1, - minor: 82, - patch: 0, - pre: semver::Prerelease::EMPTY, - build: semver::BuildMetadata::EMPTY, - }; - /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo /// workspace. It pretty closely mirrors `cargo metadata` output. /// @@ -628,7 +620,7 @@ pub(crate) struct FetchMetadata { command: cargo_metadata::MetadataCommand, #[expect(dead_code)] manifest_path: ManifestPath, - lockfile_path: Option, + lockfile_copy: Option, #[expect(dead_code)] kind: &'static str, no_deps: bool, @@ -688,15 +680,14 @@ impl FetchMetadata { } } - let mut lockfile_path = None; + let mut lockfile_copy = None; if cargo_toml.is_rust_manifest() { other_options.push("-Zscript".to_owned()); - } else if config - .toolchain_version - .as_ref() - .is_some_and(|v| *v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH) - { - lockfile_path = Some(<_ as AsRef>::as_ref(cargo_toml).with_extension("lock")); + } else if let Some(v) = config.toolchain_version.as_ref() { + lockfile_copy = make_lockfile_copy( + v, + &<_ as AsRef>::as_ref(cargo_toml).with_extension("lock"), + ); } if !config.targets.is_empty() { @@ -729,7 +720,7 @@ impl FetchMetadata { Self { manifest_path: cargo_toml.clone(), command, - lockfile_path, + lockfile_copy, kind: config.kind, no_deps, no_deps_result, @@ -749,7 +740,7 @@ impl FetchMetadata { let Self { mut command, manifest_path: _, - lockfile_path, + lockfile_copy, kind: _, no_deps, no_deps_result, @@ -761,13 +752,16 @@ impl FetchMetadata { } let mut using_lockfile_copy = false; - let mut _temp_dir_guard; - if let Some(lockfile) = lockfile_path - && let Some((temp_dir, target_lockfile)) = make_lockfile_copy(&lockfile) - { - _temp_dir_guard = temp_dir; - other_options.push("--lockfile-path".to_owned()); - other_options.push(target_lockfile.to_string()); + if let Some(lockfile_copy) = &lockfile_copy { + match lockfile_copy.usage { + LockfileUsage::WithFlag => { + other_options.push("--lockfile-path".to_owned()); + other_options.push(lockfile_copy.path.to_string()); + } + LockfileUsage::WithEnvVar => { + command.env("CARGO_RESOLVER_LOCKFILE_PATH", lockfile_copy.path.as_os_str()); + } + } using_lockfile_copy = true; } if using_lockfile_copy || other_options.iter().any(|it| it.starts_with("-Z")) { From 5d19cb96c14e4f2fd721d05b3cfc39393c328072 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Thu, 19 Feb 2026 02:05:41 +0900 Subject: [PATCH 29/82] Append `-Zlockfile-path` --- .../rust-analyzer/crates/project-model/src/build_dependencies.rs | 1 + .../rust-analyzer/crates/project-model/src/cargo_workspace.rs | 1 + 2 files changed, 2 insertions(+) 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 a4eec872b62cd..aff5391697414 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 @@ -475,6 +475,7 @@ impl WorkspaceBuildScripts { cmd.arg(lockfile_copy.path.as_str()); } LockfileUsage::WithEnvVar => { + cmd.arg("-Zlockfile-path"); cmd.env( "CARGO_RESOLVER_LOCKFILE_PATH", lockfile_copy.path.as_os_str(), 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 f7e57c5b075c7..792206b74f849 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 @@ -759,6 +759,7 @@ impl FetchMetadata { other_options.push(lockfile_copy.path.to_string()); } LockfileUsage::WithEnvVar => { + other_options.push("-Zlockfile-path".to_owned()); command.env("CARGO_RESOLVER_LOCKFILE_PATH", lockfile_copy.path.as_os_str()); } } From 1278a87b81780e5fbfa0c20e38c3b26f11f0290c Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 12 Feb 2026 01:14:25 +0800 Subject: [PATCH 30/82] feat: offer on is_some_and for replace_is_method_with_if_let_method Example --- ```rust fn main() { let x = Some(1); if x.is_som$0e_and(|it| it != 3) {} } ``` **Before this PR** Assist not applicable **After this PR** ```rust fn main() { let x = Some(1); if let Some(it) = x && it != 3 {} } ``` --- .../replace_is_method_with_if_let_method.rs | 58 ++++++++++++++++--- 1 file changed, 50 insertions(+), 8 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 5a2307739cff5..d22e951b5dab0 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,6 +1,6 @@ use either::Either; use ide_db::syntax_helpers::suggest_name; -use syntax::ast::{self, AstNode, syntax_factory::SyntaxFactory}; +use syntax::ast::{self, AstNode, HasArgList, syntax_factory::SyntaxFactory}; use crate::{AssistContext, AssistId, Assists, utils::cover_let_chain}; @@ -34,8 +34,9 @@ pub(crate) fn replace_is_method_with_if_let_method( _ => return None, }; - let name_ref = call_expr.name_ref()?; - match name_ref.text().as_str() { + let token = call_expr.name_ref()?.ident_token()?; + let method_kind = token.text().strip_suffix("_and").unwrap_or(token.text()); + match method_kind { "is_some" | "is_ok" => { let receiver = call_expr.receiver()?; @@ -47,8 +48,9 @@ pub(crate) fn replace_is_method_with_if_let_method( } else { name_generator.for_variable(&receiver, &ctx.sema) }; + let (pat, predicate) = method_predicate(&call_expr).unzip(); - let (assist_id, message, text) = if name_ref.text() == "is_some" { + let (assist_id, message, text) = if method_kind == "is_some" { ("replace_is_some_with_if_let_some", "Replace `is_some` with `let Some`", "Some") } else { ("replace_is_ok_with_if_let_ok", "Replace `is_ok` with `let Ok`", "Ok") @@ -62,19 +64,29 @@ pub(crate) fn replace_is_method_with_if_let_method( let make = SyntaxFactory::with_mappings(); let mut editor = edit.make_editor(call_expr.syntax()); - let var_pat = make.ident_pat(false, false, make.name(&var_name)); - let pat = make.tuple_struct_pat(make.ident_path(text), [var_pat.into()]); - let let_expr = make.expr_let(pat.into(), receiver); + let var_pat = pat.unwrap_or_else(|| { + make.ident_pat(false, false, make.name(&var_name)).into() + }); + let pat = make.tuple_struct_pat(make.ident_path(text), [var_pat]).into(); + let let_expr = make.expr_let(pat, receiver); if let Some(cap) = ctx.config.snippet_cap && let Some(ast::Pat::TupleStructPat(pat)) = let_expr.pat() && let Some(first_var) = pat.fields().next() + && predicate.is_none() { let placeholder = edit.make_placeholder_snippet(cap); editor.add_annotation(first_var.syntax(), placeholder); } - editor.replace(call_expr.syntax(), let_expr.syntax()); + let new_expr = if let Some(predicate) = predicate { + let op = ast::BinaryOp::LogicOp(ast::LogicOp::And); + make.expr_bin(let_expr.into(), op, predicate).into() + } else { + ast::Expr::from(let_expr) + }; + editor.replace(call_expr.syntax(), new_expr.syntax()); + editor.add_mappings(make.finish_with_mappings()); edit.add_file_edits(ctx.vfs_file_id(), editor); }, @@ -84,6 +96,17 @@ pub(crate) fn replace_is_method_with_if_let_method( } } +fn method_predicate(call_expr: &ast::MethodCallExpr) -> Option<(ast::Pat, ast::Expr)> { + let argument = call_expr.arg_list()?.args().next()?; + match argument { + ast::Expr::ClosureExpr(it) => { + let pat = it.param_list()?.params().next()?.pat()?; + Some((pat, it.body()?)) + } + _ => None, + } +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -194,6 +217,25 @@ fn main() { ); } + #[test] + fn replace_is_some_and_with_if_let_chain_some_works() { + check_assist( + replace_is_method_with_if_let_method, + r#" +fn main() { + let x = Some(1); + if x.is_som$0e_and(|it| it != 3) {} +} +"#, + r#" +fn main() { + let x = Some(1); + if let Some(it) = x && it != 3 {} +} +"#, + ); + } + #[test] fn replace_is_some_with_if_let_some_in_let_chain() { check_assist( From 574c7b85a2f345e644bbba7fc33a526828b49161 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 18 Feb 2026 04:32:16 +0200 Subject: [PATCH 31/82] Add handling for cycles in `sizedness_constraint_for_ty()` Ported mostly from rustc. --- .../rust-analyzer/crates/hir-ty/src/lib.rs | 1 + .../crates/hir-ty/src/next_solver/util.rs | 31 +++-- .../crates/hir-ty/src/representability.rs | 131 ++++++++++++++++++ .../crates/hir-ty/src/tests/regression.rs | 25 ++++ 4 files changed, 178 insertions(+), 10 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/representability.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 ca817ae7134b1..8d87276a0b152 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -31,6 +31,7 @@ mod inhabitedness; mod lower; pub mod next_solver; mod opaques; +mod representability; mod specialization; mod target_feature; mod utils; 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 9a1b476976e34..c175062bda37c 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 @@ -13,13 +13,16 @@ use rustc_type_ir::{ solve::SizedTraitKind, }; -use crate::next_solver::{ - BoundConst, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion, - PolyTraitRef, - infer::{ - InferCtxt, - traits::{Obligation, ObligationCause, PredicateObligation}, +use crate::{ + next_solver::{ + BoundConst, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion, + PolyTraitRef, + infer::{ + InferCtxt, + traits::{Obligation, ObligationCause, PredicateObligation}, + }, }, + representability::Representability, }; use super::{ @@ -419,10 +422,18 @@ pub fn sizedness_constraint_for_ty<'db>( .next_back() .and_then(|ty| sizedness_constraint_for_ty(interner, sizedness, ty)), - Adt(adt, args) => adt.struct_tail_ty(interner).and_then(|tail_ty| { - let tail_ty = tail_ty.instantiate(interner, args); - sizedness_constraint_for_ty(interner, sizedness, tail_ty) - }), + Adt(adt, args) => { + if crate::representability::representability(interner.db, adt.def_id().0) + == Representability::Infinite + { + return None; + } + + adt.struct_tail_ty(interner).and_then(|tail_ty| { + let tail_ty = tail_ty.instantiate(interner, args); + sizedness_constraint_for_ty(interner, sizedness, tail_ty) + }) + } Placeholder(..) | Bound(..) | Infer(..) => { panic!("unexpected type `{ty:?}` in sizedness_constraint_for_ty") diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs new file mode 100644 index 0000000000000..7e40f2d7ac75f --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs @@ -0,0 +1,131 @@ +//! Detecting whether a type is infinitely-sized. + +use hir_def::{AdtId, VariantId}; +use rustc_type_ir::inherent::{AdtDef, IntoKind}; + +use crate::{ + db::HirDatabase, + next_solver::{GenericArgKind, GenericArgs, Ty, TyKind}, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum Representability { + Representable, + Infinite, +} + +macro_rules! rtry { + ($e:expr) => { + match $e { + e @ Representability::Infinite => return e, + Representability::Representable => {} + } + }; +} + +#[salsa::tracked(cycle_result = representability_cycle)] +pub(crate) fn representability(db: &dyn HirDatabase, id: AdtId) -> Representability { + match id { + AdtId::StructId(id) => variant_representability(db, id.into()), + AdtId::UnionId(id) => variant_representability(db, id.into()), + AdtId::EnumId(id) => { + for &(variant, ..) in &id.enum_variants(db).variants { + rtry!(variant_representability(db, variant.into())); + } + Representability::Representable + } + } +} + +pub(crate) fn representability_cycle( + _db: &dyn HirDatabase, + _: salsa::Id, + _id: AdtId, +) -> Representability { + Representability::Infinite +} + +fn variant_representability(db: &dyn HirDatabase, id: VariantId) -> Representability { + for ty in db.field_types(id).values() { + rtry!(representability_ty(db, ty.get().instantiate_identity())); + } + Representability::Representable +} + +fn representability_ty<'db>(db: &'db dyn HirDatabase, ty: Ty<'db>) -> Representability { + match ty.kind() { + TyKind::Adt(adt_id, args) => representability_adt_ty(db, adt_id.def_id().0, args), + // FIXME(#11924) allow zero-length arrays? + TyKind::Array(ty, _) => representability_ty(db, ty), + TyKind::Tuple(tys) => { + for ty in tys { + rtry!(representability_ty(db, ty)); + } + Representability::Representable + } + _ => Representability::Representable, + } +} + +fn representability_adt_ty<'db>( + db: &'db dyn HirDatabase, + def_id: AdtId, + args: GenericArgs<'db>, +) -> Representability { + rtry!(representability(db, def_id)); + + // At this point, we know that the item of the ADT type is representable; + // but the type parameters may cause a cycle with an upstream type + let params_in_repr = params_in_repr(db, def_id); + for (i, arg) in args.iter().enumerate() { + if let GenericArgKind::Type(ty) = arg.kind() + && params_in_repr[i] + { + rtry!(representability_ty(db, ty)); + } + } + Representability::Representable +} + +fn params_in_repr(db: &dyn HirDatabase, def_id: AdtId) -> Box<[bool]> { + let generics = db.generic_params(def_id.into()); + let mut params_in_repr = (0..generics.len_lifetimes() + generics.len_type_or_consts()) + .map(|_| false) + .collect::>(); + let mut handle_variant = |variant| { + for field in db.field_types(variant).values() { + params_in_repr_ty(db, field.get().instantiate_identity(), &mut params_in_repr); + } + }; + match def_id { + AdtId::StructId(def_id) => handle_variant(def_id.into()), + AdtId::UnionId(def_id) => handle_variant(def_id.into()), + AdtId::EnumId(def_id) => { + for &(variant, ..) in &def_id.enum_variants(db).variants { + handle_variant(variant.into()); + } + } + } + params_in_repr +} + +fn params_in_repr_ty<'db>(db: &'db dyn HirDatabase, ty: Ty<'db>, params_in_repr: &mut [bool]) { + match ty.kind() { + TyKind::Adt(adt, args) => { + let inner_params_in_repr = self::params_in_repr(db, adt.def_id().0); + for (i, arg) in args.iter().enumerate() { + if let GenericArgKind::Type(ty) = arg.kind() + && inner_params_in_repr[i] + { + params_in_repr_ty(db, ty, params_in_repr); + } + } + } + TyKind::Array(ty, _) => params_in_repr_ty(db, ty, params_in_repr), + TyKind::Tuple(tys) => tys.iter().for_each(|ty| params_in_repr_ty(db, ty, params_in_repr)), + TyKind::Param(param) => { + params_in_repr[param.index as usize] = true; + } + _ => {} + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 5291bf80e8219..658c304aac012 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2770,3 +2770,28 @@ unsafe extern "C" { "#, ); } + +#[test] +fn infinitely_sized_type() { + check_infer( + r#" +//- minicore: sized + +pub struct Recursive { + pub content: Recursive, +} + +fn is_sized() {} + +fn foo() { + is_sized::(); +} + "#, + expect![[r#" + 79..81 '{}': () + 92..124 '{ ...>(); }': () + 98..119 'is_siz...rsive>': fn is_sized() + 98..121 'is_siz...ive>()': () + "#]], + ); +} From 2a3d765883f559d9e626cce2edda17418fd212ac Mon Sep 17 00:00:00 2001 From: protonblu Date: Thu, 19 Feb 2026 01:51:08 +0530 Subject: [PATCH 32/82] fix: exclude macro refs in tests when excludeTests is enabled --- .../rust-analyzer/crates/ide-db/src/search.rs | 8 +++-- .../crates/ide/src/references.rs | 35 +++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) 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 1d865892a22b5..5cb5d4aa2b1cc 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -1370,8 +1370,10 @@ fn is_name_ref_in_import(name_ref: &ast::NameRef) -> bool { } fn is_name_ref_in_test(sema: &Semantics<'_, RootDatabase>, name_ref: &ast::NameRef) -> bool { - name_ref.syntax().ancestors().any(|node| match ast::Fn::cast(node) { - Some(it) => sema.to_def(&it).is_some_and(|func| func.is_test(sema.db)), - None => false, + sema.ancestors_with_macros(name_ref.syntax().clone()).any(|node| { + match ast::Fn::cast(node) { + Some(it) => sema.to_def(&it).is_some_and(|func| func.is_test(sema.db)), + None => false, + } }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 38ee09703398b..cf20224c52884 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -532,6 +532,41 @@ fn test() { "#]], ); } + #[test] + fn exclude_tests_macro_refs() { + check( + r#" +fn foo$0() -> i32 { 42 } + +fn bar() { + foo(); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn t1() { + let f = foo(); + } + + #[test] + fn t2() { + dbg!(foo()); + assert_eq!(foo(), 42); + let v = vec![foo()]; + } +} +"#, + expect![[r#" + foo Function FileId(0) 0..22 3..6 + + FileId(0) 39..42 + FileId(0) 135..138 + "#]], + ); + } #[test] fn test_struct_literal_after_space() { From 918214c19ae9f44eaca32d03f4a4cb10cc6089e9 Mon Sep 17 00:00:00 2001 From: protonblu Date: Thu, 19 Feb 2026 02:14:34 +0530 Subject: [PATCH 33/82] style: fix rustfmt formatting --- src/tools/rust-analyzer/crates/ide-db/src/search.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) 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 5cb5d4aa2b1cc..4196a13aa3fa7 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -1370,10 +1370,8 @@ fn is_name_ref_in_import(name_ref: &ast::NameRef) -> bool { } fn is_name_ref_in_test(sema: &Semantics<'_, RootDatabase>, name_ref: &ast::NameRef) -> bool { - sema.ancestors_with_macros(name_ref.syntax().clone()).any(|node| { - match ast::Fn::cast(node) { - Some(it) => sema.to_def(&it).is_some_and(|func| func.is_test(sema.db)), - None => false, - } + sema.ancestors_with_macros(name_ref.syntax().clone()).any(|node| match ast::Fn::cast(node) { + Some(it) => sema.to_def(&it).is_some_and(|func| func.is_test(sema.db)), + None => false, }) } From 9a57e950777a5d435076300c8148a3b0016d4b12 Mon Sep 17 00:00:00 2001 From: Paul Hanzlik <52867007+pjhanzlik@users.noreply.github.com> Date: Wed, 18 Feb 2026 18:44:58 -0500 Subject: [PATCH 34/82] Update vs_code.md flatpak SDK extensions to 25.08 runtime --- src/tools/rust-analyzer/docs/book/src/vs_code.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/docs/book/src/vs_code.md b/src/tools/rust-analyzer/docs/book/src/vs_code.md index 75f940e69a885..69a96156b8261 100644 --- a/src/tools/rust-analyzer/docs/book/src/vs_code.md +++ b/src/tools/rust-analyzer/docs/book/src/vs_code.md @@ -98,12 +98,12 @@ some directories, `/usr/bin` will always be mounted under system-wide installation of Rust, or any other libraries you might want to link to. Some compilers and libraries can be acquired as Flatpak SDKs, such as `org.freedesktop.Sdk.Extension.rust-stable` or -`org.freedesktop.Sdk.Extension.llvm15`. +`org.freedesktop.Sdk.Extension.llvm21`. If you use a Flatpak SDK for Rust, it must be in your `PATH`: - * install the SDK extensions with `flatpak install org.freedesktop.Sdk.Extension.{llvm15,rust-stable}//23.08` - * enable SDK extensions in the editor with the environment variable `FLATPAK_ENABLE_SDK_EXT=llvm15,rust-stable` (this can be done using flatseal or `flatpak override`) + * install the SDK extensions with `flatpak install org.freedesktop.Sdk.Extension.{llvm21,rust-stable}//25.08` + * enable SDK extensions in the editor with the environment variable `FLATPAK_ENABLE_SDK_EXT=llvm21,rust-stable` (this can be done using flatseal or `flatpak override`) If you want to use Flatpak in combination with `rustup`, the following steps might help: From 08a7b9a734c9a1d7b4bc33a4199726308c8b551d Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 19 Feb 2026 09:53:25 +0800 Subject: [PATCH 35/82] Remove fixme --- .../crates/ide-assists/src/handlers/toggle_macro_delimiter.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index e9865aec4a146..15143575e7d84 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs @@ -415,7 +415,6 @@ prt!{(3 + 5)} ) } - // FIXME @alibektas : Inner macro_call is not seen as such. So this doesn't work. #[test] fn test_nested_macros() { check_assist( From 89832e9a778217ced3deca501c1918ae6a36ace0 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 1 Aug 2025 09:15:40 +0800 Subject: [PATCH 36/82] Add partial selection for generate_getter_or_setter Example --- ```rust struct Context { data$0: Data, count$0: usize, other: usize, } ``` **Before this PR** Assist not applicable **After this PR** ```rust struct Context { data: Data, count: usize, other: usize, } impl Context { fn data(&self) -> &Data { &self.data } fn $0count(&self) -> &usize { &self.count } } ``` --- .../src/handlers/generate_getter_or_setter.rs | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index 51b967437b568..92a654743b580 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs @@ -11,7 +11,7 @@ use syntax::{ use crate::{ AssistContext, AssistId, Assists, GroupLabel, - utils::{convert_reference_type, find_struct_impl}, + utils::{convert_reference_type, find_struct_impl, is_selected}, }; // Assist: generate_setter @@ -377,7 +377,7 @@ fn extract_and_parse_record_fields( let info_of_record_fields_in_selection = ele .fields() .filter_map(|record_field| { - if selection_range.contains_range(record_field.syntax().text_range()) { + if is_selected(&record_field, selection_range, false) { let record_field_info = parse_record_field(record_field, assist_type)?; field_names.push(record_field_info.fn_name.clone()); return Some(record_field_info); @@ -934,6 +934,37 @@ struct Context { count: usize, } +impl Context { + fn data(&self) -> &Data { + &self.data + } + + fn $0count(&self) -> &usize { + &self.count + } +} + "#, + ); + } + + #[test] + fn test_generate_multiple_getters_from_partial_selection() { + check_assist( + generate_getter, + r#" +struct Context { + data$0: Data, + count$0: usize, + other: usize, +} + "#, + r#" +struct Context { + data: Data, + count: usize, + other: usize, +} + impl Context { fn data(&self) -> &Data { &self.data From 5bb10ae244facd131840cc66ca4e961a62eaef7c Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 2 Jan 2026 19:00:55 +0800 Subject: [PATCH 37/82] Fix some TryEnum reference assists - Fix `convert_to_guarded_return` - Fix `replace_let_with_if_let` - Fix `replace_if_let_with_match` --- .../src/handlers/convert_to_guarded_return.rs | 26 +++++++++++++++++++ .../src/handlers/desugar_try_expr.rs | 2 +- .../src/handlers/replace_if_let_with_match.rs | 25 ++++++++++++++++++ .../src/handlers/replace_let_with_if_let.rs | 23 ++++++++++++++++ .../ide-completion/src/completions/postfix.rs | 2 +- .../crates/ide-db/src/ty_filter.rs | 10 ++++++- 6 files changed, 85 insertions(+), 3 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 ea5c1637b7608..dc51bf4b5b8c1 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 @@ -934,6 +934,32 @@ fn foo() -> Option { None } +fn main() { + let Some(x) = foo() else { return }; +} +"#, + ); + } + + #[test] + fn convert_let_ref_stmt_inside_fn() { + check_assist( + convert_to_guarded_return, + r#" +//- minicore: option +fn foo() -> &'static Option { + &None +} + +fn main() { + let x$0 = foo(); +} +"#, + r#" +fn foo() -> &'static Option { + &None +} + fn main() { let Some(x) = foo() else { return }; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs index 9976e34e730cc..176eb1c4944e0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs @@ -61,7 +61,7 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let expr = try_expr.expr()?; let expr_type_info = ctx.sema.type_of_expr(&expr)?; - let try_enum = TryEnum::from_ty(&ctx.sema, &expr_type_info.original)?; + let try_enum = TryEnum::from_ty_without_strip(&ctx.sema, &expr_type_info.original)?; let target = try_expr.syntax().text_range(); acc.add( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 915dd3ffcaf0c..d2452f28c4d78 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -849,6 +849,31 @@ fn foo(x: Option) { ); } + #[test] + fn special_case_option_ref() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: option +fn foo(x: &Option) { + $0if let Some(x) = x { + println!("{}", x) + } else { + println!("none") + } +} +"#, + r#" +fn foo(x: &Option) { + match x { + Some(x) => println!("{}", x), + None => println!("none"), + } +} +"#, + ); + } + #[test] fn special_case_inverted_option() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs index 15977c420e642..5587f1b59c542 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs @@ -101,6 +101,29 @@ mod tests { use super::*; + #[test] + fn replace_let_try_enum_ref() { + check_assist( + replace_let_with_if_let, + r" +//- minicore: option +fn main(action: Action) { + $0let x = compute(); +} + +fn compute() -> &'static Option { &None } + ", + r" +fn main(action: Action) { + if let Some(x) = compute() { + } +} + +fn compute() -> &'static Option { &None } + ", + ) + } + #[test] fn replace_let_unknown_enum() { check_assist( 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 8e39b0aa52552..a58592b1365b8 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 @@ -110,7 +110,7 @@ pub(crate) fn complete_postfix( postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")) .add_to(acc, ctx.db); - let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty.strip_references()); + let try_enum = TryEnum::from_ty(&ctx.sema, receiver_ty); let mut is_in_cond = false; if let Some(parent) = dot_receiver_including_refs.syntax().parent() && let Some(second_ancestor) = parent.parent() diff --git a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs index 095256d8294e2..b5c43b3b369bb 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs @@ -19,8 +19,16 @@ pub enum TryEnum { impl TryEnum { const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result]; - /// Returns `Some(..)` if the provided type is an enum that implements `std::ops::Try`. + /// Returns `Some(..)` if the provided `ty.strip_references()` is an enum that implements `std::ops::Try`. pub fn from_ty(sema: &Semantics<'_, RootDatabase>, ty: &hir::Type<'_>) -> Option { + Self::from_ty_without_strip(sema, &ty.strip_references()) + } + + /// Returns `Some(..)` if the provided type is an enum that implements `std::ops::Try`. + pub fn from_ty_without_strip( + sema: &Semantics<'_, RootDatabase>, + ty: &hir::Type<'_>, + ) -> Option { let enum_ = match ty.as_adt() { Some(hir::Adt::Enum(it)) => it, _ => return None, From 94f6e7efd37f1ab2f045e4f4685076696ad690fd Mon Sep 17 00:00:00 2001 From: Raushan Kumar Date: Thu, 19 Feb 2026 04:16:52 +0000 Subject: [PATCH 38/82] fix: filter non-value definitions in path transform --- .../crates/ide-db/src/path_transform.rs | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index d0a28e7d2f86c..16f8038f56d21 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -558,6 +558,34 @@ impl Ctx<'_> { return None; } + if matches!( + def, + hir::ModuleDef::Function(_) + | hir::ModuleDef::Trait(_) + | hir::ModuleDef::TypeAlias(_) + ) { + return None; + } + + if let hir::ModuleDef::Adt(adt) = def { + match adt { + hir::Adt::Struct(s) + if s.kind(self.source_scope.db) != hir::StructKind::Unit => + { + return None; + } + hir::Adt::Union(_) => return None, + hir::Adt::Enum(_) => return None, + _ => (), + } + } + + if let hir::ModuleDef::Variant(v) = def { + if v.kind(self.source_scope.db) != hir::StructKind::Unit { + return None; + } + } + let cfg = FindPathConfig { prefer_no_std: false, prefer_prelude: true, @@ -637,3 +665,87 @@ fn find_trait_for_assoc_item( None } + +#[cfg(test)] +mod tests { + use crate::RootDatabase; + use crate::path_transform::PathTransform; + use hir::Semantics; + use syntax::{AstNode, ast::HasName}; + use test_fixture::WithFixture; + use test_utils::assert_eq_text; + + #[test] + fn test_transform_ident_pat() { + let (db, file_id) = RootDatabase::with_single_file( + r#" +mod foo { + pub struct UnitStruct; + pub struct RecordStruct {} + pub enum Enum { UnitVariant, RecordVariant {} } + pub fn function() {} + pub const CONST: i32 = 0; + pub static STATIC: i32 = 0; + pub type Alias = i32; + pub union Union { f: i32 } +} + +mod bar { + fn anchor() {} +} + +fn main() { + use foo::*; + use foo::Enum::*; + let UnitStruct = (); + let RecordStruct = (); + let Enum = (); + let UnitVariant = (); + let RecordVariant = (); + let function = (); + let CONST = (); + let STATIC = (); + let Alias = (); + let Union = (); +} +"#, + ); + let sema = Semantics::new(&db); + let source_file = sema.parse(file_id); + + let function = source_file + .syntax() + .descendants() + .filter_map(syntax::ast::Fn::cast) + .find(|it| it.name().unwrap().text() == "main") + .unwrap(); + let source_scope = sema.scope(function.body().unwrap().syntax()).unwrap(); + + let anchor = source_file + .syntax() + .descendants() + .filter_map(syntax::ast::Fn::cast) + .find(|it| it.name().unwrap().text() == "anchor") + .unwrap(); + let target_scope = sema.scope(anchor.body().unwrap().syntax()).unwrap(); + + let transform = PathTransform::generic_transformation(&target_scope, &source_scope); + let transformed = transform.apply(function.body().unwrap().syntax()); + + let expected = r#"{ + use crate::foo::*; + use crate::foo::Enum::*; + let crate::foo::UnitStruct = (); + let RecordStruct = (); + let Enum = (); + let crate::foo::Enum::UnitVariant = (); + let RecordVariant = (); + let function = (); + let crate::foo::CONST = (); + let crate::foo::STATIC = (); + let Alias = (); + let Union = (); +}"#; + assert_eq_text!(expected, &transformed.to_string()); + } +} From c9a76c4da5857546c0880c6e0fd5267532968416 Mon Sep 17 00:00:00 2001 From: Raushan Kumar Date: Thu, 19 Feb 2026 05:06:43 +0000 Subject: [PATCH 39/82] style: fix clippy collapsible_if warning --- .../rust-analyzer/crates/ide-db/src/path_transform.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 16f8038f56d21..01a326a0dc63b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -580,10 +580,10 @@ impl Ctx<'_> { } } - if let hir::ModuleDef::Variant(v) = def { - if v.kind(self.source_scope.db) != hir::StructKind::Unit { - return None; - } + if let hir::ModuleDef::Variant(v) = def + && v.kind(self.source_scope.db) != hir::StructKind::Unit + { + return None; } let cfg = FindPathConfig { From 6d80670bb2ea6e9d722641131d03be2c0eadc114 Mon Sep 17 00:00:00 2001 From: protonblu Date: Thu, 19 Feb 2026 17:22:01 +0530 Subject: [PATCH 40/82] test: fix exclude_tests_macro_refs test to use custom macro --- .../crates/ide/src/references.rs | 49 ++++--------------- 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index cf20224c52884..39d863001a6e8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -513,61 +513,32 @@ fn test() { } #[test] - fn test_access() { + fn exclude_tests_macro_refs() { check( r#" -struct S { f$0: u32 } - -#[test] -fn test() { - let mut x = S { f: 92 }; - x.f = 92; +macro_rules! my_macro { + ($e:expr) => { $e }; } -"#, - expect![[r#" - f Field FileId(0) 11..17 11..12 - FileId(0) 61..62 read test - FileId(0) 76..77 write test - "#]], - ); - } - #[test] - fn exclude_tests_macro_refs() { - check( - r#" fn foo$0() -> i32 { 42 } fn bar() { foo(); } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn t1() { - let f = foo(); - } - - #[test] - fn t2() { - dbg!(foo()); - assert_eq!(foo(), 42); - let v = vec![foo()]; - } +#[test] +fn t2() { + my_macro!(foo()); } "#, - expect![[r#" - foo Function FileId(0) 0..22 3..6 + expect![[r#" + foo Function FileId(0) 52..74 55..58 - FileId(0) 39..42 - FileId(0) 135..138 + FileId(0) 91..94 + FileId(0) 133..136 test "#]], ); } - #[test] fn test_struct_literal_after_space() { check( From 5a3f581b1fba6660c53b91af0c00d25f353b8a36 Mon Sep 17 00:00:00 2001 From: protonblu Date: Thu, 19 Feb 2026 17:25:54 +0530 Subject: [PATCH 41/82] style: fix rustfmt formatting --- src/tools/rust-analyzer/crates/ide/src/references.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 39d863001a6e8..102eb91b74f76 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -531,7 +531,7 @@ fn t2() { my_macro!(foo()); } "#, - expect![[r#" + expect![[r#" foo Function FileId(0) 52..74 55..58 FileId(0) 91..94 From 99b50e365db0123237c709386a001112fc3a8940 Mon Sep 17 00:00:00 2001 From: protonblu Date: Thu, 19 Feb 2026 17:41:49 +0530 Subject: [PATCH 42/82] ci: rerun checks From 102f2ecc91c4130b6d572698d87d11faa1f82eef Mon Sep 17 00:00:00 2001 From: Flora Hill Date: Thu, 19 Feb 2026 23:00:29 +0000 Subject: [PATCH 43/82] Perf + Test Case RE: ShoyuVanilla --- .../crates/ide-db/src/imports/insert_use/tests.rs | 10 ++++++++++ .../crates/ide-db/src/imports/merge_imports.rs | 10 +++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs index 7763d1e595d04..6c7b97458d208 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs @@ -1528,6 +1528,16 @@ fn merge_gated_imports_with_different_values() { assert_eq!(result, None); } +#[test] +fn merge_gated_imports_different_order() { + check_merge( + r#"#[cfg(a)] #[cfg(b)] use foo::bar;"#, + r#"#[cfg(b)] #[cfg(a)] use foo::baz;"#, + r#"#[cfg(a)] #[cfg(b)] use foo::{bar, baz};"#, + MergeBehavior::Crate, + ); +} + #[test] fn merge_into_existing_cfg_import() { check( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs index d63b239ff43c7..3301719f5ce29 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs @@ -4,7 +4,7 @@ use std::cmp::Ordering; use itertools::{EitherOrBoth, Itertools}; use parser::T; use syntax::{ - Direction, SyntaxElement, algo, + Direction, SyntaxElement, ToSmolStr, algo, ast::{ self, AstNode, HasAttrs, HasName, HasVisibility, PathSegmentKind, edit_in_place::Removable, make, @@ -691,10 +691,10 @@ pub fn eq_attrs( attrs0: impl Iterator, attrs1: impl Iterator, ) -> bool { - let mut attrs0: Vec<_> = attrs0.map(|attr| attr.syntax().text().to_string()).collect(); - let mut attrs1: Vec<_> = attrs1.map(|attr| attr.syntax().text().to_string()).collect(); - attrs0.sort(); - attrs1.sort(); + let mut attrs0: Vec<_> = attrs0.map(|attr| attr.syntax().text().to_smolstr()).collect(); + let mut attrs1: Vec<_> = attrs1.map(|attr| attr.syntax().text().to_smolstr()).collect(); + attrs0.sort_unstable(); + attrs1.sort_unstable(); attrs0 == attrs1 } From 01627b7441ed8aee3d0f98e9eceb541a305c11a6 Mon Sep 17 00:00:00 2001 From: jasper3108 Date: Mon, 2 Feb 2026 17:05:54 +0100 Subject: [PATCH 44/82] Support getting TypeId's Trait and vtable --- .../src/const_eval/machine.rs | 19 ++++- .../rustc_const_eval/src/interpret/mod.rs | 2 +- .../rustc_const_eval/src/interpret/util.rs | 45 +++++++++++- .../rustc_hir_analysis/src/check/intrinsic.rs | 20 ++++++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/any.rs | 66 ++++++++++++++++- library/core/src/intrinsics/mod.rs | 14 ++++ library/core/src/mem/type_info.rs | 15 ++++ library/coretests/tests/mem.rs | 1 + library/coretests/tests/mem/trait_info_of.rs | 70 +++++++++++++++++++ library/coretests/tests/mem/type_info.rs | 1 + tests/ui/reflection/trait_info_of_too_big.rs | 20 ++++++ .../reflection/trait_info_of_too_big.stderr | 27 +++++++ 13 files changed, 295 insertions(+), 6 deletions(-) create mode 100644 library/coretests/tests/mem/trait_info_of.rs create mode 100644 tests/ui/reflection/trait_info_of_too_big.rs create mode 100644 tests/ui/reflection/trait_info_of_too_big.stderr diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 3b4f7ed3261ab..1e048531ae90e 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -2,7 +2,7 @@ use std::borrow::{Borrow, Cow}; use std::fmt; use std::hash::Hash; -use rustc_abi::{Align, Size}; +use rustc_abi::{Align, FIRST_VARIANT, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; use rustc_errors::msg; @@ -21,6 +21,7 @@ use tracing::debug; use super::error::*; use crate::errors::{LongRunning, LongRunningWarn}; +use crate::interpret::util::type_implements_dyn_trait; use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar, @@ -601,6 +602,22 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { } } + sym::type_id_vtable => { + let tp_ty = ecx.read_type_id(&args[0])?; + let result_ty = ecx.read_type_id(&args[1])?; + + let (implements_trait, preds) = type_implements_dyn_trait(ecx, tp_ty, result_ty)?; + + if implements_trait { + let vtable_ptr = ecx.get_vtable_ptr(tp_ty, preds)?; + // Writing a non-null pointer into an `Option` will automatically make it `Some`. + ecx.write_pointer(vtable_ptr, dest)?; + } else { + // Write `None` + ecx.write_discriminant(FIRST_VARIANT, dest)?; + } + } + sym::type_of => { let ty = ecx.read_type_id(&args[0])?; ecx.write_type_info(ty, dest)?; diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index c8ffc0ed208a7..1bf4674789e76 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -15,7 +15,7 @@ mod projection; mod stack; mod step; mod traits; -mod util; +pub(crate) mod util; mod validity; mod visitor; diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 1e18a22be81c2..57a02769643b7 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -1,12 +1,51 @@ -use rustc_hir::def_id::LocalDefId; -use rustc_middle::mir; +use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_middle::mir::interpret::{AllocInit, Allocation, GlobalAlloc, InterpResult, Pointer}; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{TyCtxt, TypeVisitable, TypeVisitableExt}; +use rustc_middle::ty::{PolyExistentialPredicate, Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; +use rustc_middle::{mir, span_bug, ty}; +use rustc_trait_selection::traits::ObligationCtxt; use tracing::debug; use super::{InterpCx, MPlaceTy, MemoryKind, interp_ok, throw_inval}; use crate::const_eval::{CompileTimeInterpCx, CompileTimeMachine, InterpretationResult}; +use crate::interpret::Machine; + +/// Checks if a type implements predicates. +/// Calls `ensure_monomorphic_enough` on `ty` and `trait_ty` for you. +pub(crate) fn type_implements_dyn_trait<'tcx, M: Machine<'tcx>>( + ecx: &mut InterpCx<'tcx, M>, + ty: Ty<'tcx>, + trait_ty: Ty<'tcx>, +) -> InterpResult<'tcx, (bool, &'tcx ty::List>)> { + ensure_monomorphic_enough(ecx.tcx.tcx, ty)?; + ensure_monomorphic_enough(ecx.tcx.tcx, trait_ty)?; + + let ty::Dynamic(preds, _) = trait_ty.kind() else { + span_bug!( + ecx.find_closest_untracked_caller_location(), + "Invalid type provided to type_implements_predicates. U must be dyn Trait, got {trait_ty}." + ); + }; + + let (infcx, param_env) = ecx.tcx.infer_ctxt().build_with_typing_env(ecx.typing_env); + + let ocx = ObligationCtxt::new(&infcx); + ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| { + let pred = pred.with_self_ty(ecx.tcx.tcx, ty); + // Lifetimes can only be 'static because of the bound on T + let pred = rustc_middle::ty::fold_regions(ecx.tcx.tcx, pred, |r, _| { + if r == ecx.tcx.tcx.lifetimes.re_erased { ecx.tcx.tcx.lifetimes.re_static } else { r } + }); + Obligation::new(ecx.tcx.tcx, ObligationCause::dummy(), param_env, pred) + })); + let type_impls_trait = ocx.evaluate_obligations_error_on_ambiguity().is_empty(); + // Since `assumed_wf_tys=[]` the choice of LocalDefId is irrelevant, so using the "default" + let regions_are_valid = ocx.resolve_regions(CRATE_DEF_ID, param_env, []).is_empty(); + + interp_ok((regions_are_valid && type_impls_trait, preds)) +} /// Checks whether a type contains generic parameters which must be instantiated. /// diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 5669b6793add7..a5aae7b407d6b 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -213,6 +213,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::truncf128 | sym::type_id | sym::type_id_eq + | sym::type_id_vtable | sym::type_name | sym::type_of | sym::ub_checks @@ -323,6 +324,25 @@ pub(crate) fn check_intrinsic_type( let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); (0, 0, vec![type_id, type_id], tcx.types.bool) } + sym::type_id_vtable => { + let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, span); + let dyn_metadata_adt_ref = tcx.adt_def(dyn_metadata); + let dyn_metadata_args = + tcx.mk_args(&[Ty::new_ptr(tcx, tcx.types.unit, ty::Mutability::Not).into()]); + let dyn_ty = Ty::new_adt(tcx, dyn_metadata_adt_ref, dyn_metadata_args); + + let option_did = tcx.require_lang_item(LangItem::Option, span); + let option_adt_ref = tcx.adt_def(option_did); + let option_args = tcx.mk_args(&[dyn_ty.into()]); + let ret_ty = Ty::new_adt(tcx, option_adt_ref, option_args); + + ( + 0, + 0, + vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); 2], + ret_ty, + ) + } sym::type_of => ( 0, 0, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4ae4aac7f7839..4f88b5e663e6a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2358,6 +2358,7 @@ symbols! { type_changing_struct_update, type_id, type_id_eq, + type_id_vtable, type_info, type_ir, type_ir_infer_ctxt_like, diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 42f332f7d8ba8..e5eabb280323c 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -86,7 +86,10 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::{fmt, hash, intrinsics, ptr}; +use crate::intrinsics::{self, type_id_vtable}; +use crate::mem::transmute; +use crate::mem::type_info::{TraitImpl, TypeKind}; +use crate::{fmt, hash, ptr}; /////////////////////////////////////////////////////////////////////////////// // Any trait @@ -788,6 +791,67 @@ impl TypeId { const { intrinsics::type_id::() } } + /// Checks if the [TypeId] implements the trait. If it does it returns [TraitImpl] which can be used to build a fat pointer. + /// It can only be called at compile time. `self` must be the [TypeId] of a sized type or None will be returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(type_info)] + /// use std::any::{TypeId}; + /// + /// pub trait Blah {} + /// impl Blah for u8 {} + /// + /// assert!(const { TypeId::of::().trait_info_of::() }.is_some()); + /// assert!(const { TypeId::of::().trait_info_of::() }.is_none()); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + pub const fn trait_info_of< + T: ptr::Pointee> + ?Sized + 'static, + >( + self, + ) -> Option> { + // SAFETY: The vtable was obtained for `T`, so it is guaranteed to be `DynMetadata`. + // The intrinsic can't infer this because it is designed to work with arbitrary TypeIds. + unsafe { transmute(self.trait_info_of_trait_type_id(const { TypeId::of::() })) } + } + + /// Checks if the [TypeId] implements the trait of `trait_represented_by_type_id`. If it does it returns [TraitImpl] which can be used to build a fat pointer. + /// It can only be called at compile time. `self` must be the [TypeId] of a sized type or None will be returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(type_info)] + /// use std::any::{TypeId}; + /// + /// pub trait Blah {} + /// impl Blah for u8 {} + /// + /// assert!(const { TypeId::of::().trait_info_of_trait_type_id(TypeId::of::()) }.is_some()); + /// assert!(const { TypeId::of::().trait_info_of_trait_type_id(TypeId::of::()) }.is_none()); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + pub const fn trait_info_of_trait_type_id( + self, + trait_represented_by_type_id: TypeId, + ) -> Option> { + if self.info().size.is_none() { + return None; + } + + if matches!(trait_represented_by_type_id.info().kind, TypeKind::DynTrait(_)) + && let Some(vtable) = type_id_vtable(self, trait_represented_by_type_id) + { + Some(TraitImpl { vtable }) + } else { + None + } + } + fn as_u128(self) -> u128 { let mut bytes = [0; 16]; diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 9d5f49c88295a..acb86f9c466cf 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2864,6 +2864,20 @@ pub const unsafe fn size_of_val(ptr: *const T) -> usize; #[rustc_intrinsic_const_stable_indirect] pub const unsafe fn align_of_val(ptr: *const T) -> usize; +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +/// Check if a type represented by a `TypeId` implements a trait represented by a `TypeId`. +/// It can only be called at compile time, the backends do +/// not implement it. If it implements the trait the dyn metadata gets returned for vtable access. +pub const fn type_id_vtable( + _id: crate::any::TypeId, + _trait: crate::any::TypeId, +) -> Option> { + panic!( + "`TypeId::trait_info_of` and `trait_info_of_trait_type_id` can only be called at compile-time" + ) +} + /// Compute the type information of a concrete type. /// It can only be called at compile time, the backends do /// not implement it. diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 18612565aeef2..cfc4266c5b167 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -3,6 +3,8 @@ use crate::any::TypeId; use crate::intrinsics::{type_id, type_of}; +use crate::marker::PointeeSized; +use crate::ptr::DynMetadata; /// Compile-time type information. #[derive(Debug)] @@ -16,6 +18,19 @@ pub struct Type { pub size: Option, } +/// Info of a trait implementation, you can retrieve the vtable with [Self::get_vtable] +#[derive(Debug, PartialEq, Eq)] +pub struct TraitImpl { + pub(crate) vtable: DynMetadata, +} + +impl TraitImpl { + /// Gets the raw vtable for type reflection mapping + pub fn get_vtable(&self) -> DynMetadata { + self.vtable + } +} + impl TypeId { /// Compute the type information of a concrete type. /// It can only be called at compile time. diff --git a/library/coretests/tests/mem.rs b/library/coretests/tests/mem.rs index 236c02d2a243a..b95e6e13063f5 100644 --- a/library/coretests/tests/mem.rs +++ b/library/coretests/tests/mem.rs @@ -1,4 +1,5 @@ mod fn_ptr; +mod trait_info_of; mod type_info; use core::mem::*; diff --git a/library/coretests/tests/mem/trait_info_of.rs b/library/coretests/tests/mem/trait_info_of.rs new file mode 100644 index 0000000000000..c723a96095815 --- /dev/null +++ b/library/coretests/tests/mem/trait_info_of.rs @@ -0,0 +1,70 @@ +use std::any::TypeId; +use std::ptr::DynMetadata; + +struct Garlic(i32); +trait Blah { + fn get_truth(&self) -> i32; +} +impl Blah for Garlic { + fn get_truth(&self) -> i32 { + self.0 * 21 + } +} + +#[test] +fn test_implements_trait() { + const { + assert!(TypeId::of::().trait_info_of::().is_some()); + assert!(TypeId::of::().trait_info_of::().is_some()); + assert!(TypeId::of::<*const Box>().trait_info_of::().is_none()); + assert!(TypeId::of::().trait_info_of_trait_type_id(TypeId::of::()).is_none()); + } +} + +#[test] +fn test_dyn_creation() { + let garlic = Garlic(2); + unsafe { + assert_eq!( + std::ptr::from_raw_parts::( + &raw const garlic, + const { TypeId::of::().trait_info_of::() }.unwrap().get_vtable() + ) + .as_ref() + .unwrap() + .get_truth(), + 42 + ); + } + + assert_eq!( + const { + TypeId::of::() + .trait_info_of_trait_type_id(TypeId::of::()) + .unwrap() + }.get_vtable(), + unsafe { + crate::mem::transmute::<_, DynMetadata<*const ()>>( + const { + TypeId::of::().trait_info_of::() + }.unwrap().get_vtable(), + ) + } + ); +} + +#[test] +fn test_incorrect_use() { + assert_eq!( + const { TypeId::of::().trait_info_of_trait_type_id(TypeId::of::()) }, + None + ); +} + +trait DstTrait {} +impl DstTrait for [i32] {} + +#[test] +fn dst_ice() { + assert!(const { TypeId::of::<[i32]>().trait_info_of::() }.is_none()); +} diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index 2483b4c2aacd7..dbe53fc7b0f37 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -3,6 +3,7 @@ use std::any::{Any, TypeId}; use std::mem::offset_of; use std::mem::type_info::{Const, Generic, GenericType, Type, TypeKind}; +use std::ptr::DynMetadata; #[test] fn test_arrays() { diff --git a/tests/ui/reflection/trait_info_of_too_big.rs b/tests/ui/reflection/trait_info_of_too_big.rs new file mode 100644 index 0000000000000..0d5224e788ac7 --- /dev/null +++ b/tests/ui/reflection/trait_info_of_too_big.rs @@ -0,0 +1,20 @@ +//@ normalize-stderr: "\[u8; [0-9]+\]" -> "[u8; N]" +//! Test for https://github.com/rust-lang/rust/pull/152003 + +#![feature(type_info)] + +use std::any::TypeId; + +trait Trait {} +impl Trait for [u8; usize::MAX] {} + +fn main() {} + +const _: () = const { + TypeId::of::<[u8; usize::MAX]>().trait_info_of_trait_type_id(TypeId::of::()); + //~^ ERROR values of the type `[u8; usize::MAX]` are too big for the target architecture +}; +const _: () = const { + TypeId::of::<[u8; usize::MAX]>().trait_info_of::(); + //~^ ERROR values of the type `[u8; usize::MAX]` are too big for the target architecture +}; diff --git a/tests/ui/reflection/trait_info_of_too_big.stderr b/tests/ui/reflection/trait_info_of_too_big.stderr new file mode 100644 index 0000000000000..fa41d3c9ec90e --- /dev/null +++ b/tests/ui/reflection/trait_info_of_too_big.stderr @@ -0,0 +1,27 @@ +error[E0080]: values of the type `[u8; usize::MAX]` are too big for the target architecture + --> $DIR/trait_info_of_too_big.rs:14:5 + | +LL | TypeId::of::<[u8; usize::MAX]>().trait_info_of_trait_type_id(TypeId::of::()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_::{constant#0}` failed inside this call + | +note: inside `TypeId::trait_info_of_trait_type_id` + --> $SRC_DIR/core/src/any.rs:LL:COL +note: inside `type_info::::info` + --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL + +error[E0080]: values of the type `[u8; usize::MAX]` are too big for the target architecture + --> $DIR/trait_info_of_too_big.rs:18:5 + | +LL | TypeId::of::<[u8; usize::MAX]>().trait_info_of::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_::{constant#0}` failed inside this call + | +note: inside `TypeId::trait_info_of::` + --> $SRC_DIR/core/src/any.rs:LL:COL +note: inside `TypeId::trait_info_of_trait_type_id` + --> $SRC_DIR/core/src/any.rs:LL:COL +note: inside `type_info::::info` + --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`. From 7857058a6b182fba1f05f2a534bce243946f35d4 Mon Sep 17 00:00:00 2001 From: jasper3108 Date: Wed, 18 Feb 2026 17:08:47 +0100 Subject: [PATCH 45/82] nix vtable_for intrinsic --- .../src/interpret/intrinsics.rs | 45 +------------------ .../rustc_hir_analysis/src/check/intrinsic.rs | 15 ------- compiler/rustc_span/src/symbol.rs | 1 - library/core/src/any.rs | 6 ++- library/core/src/intrinsics/mod.rs | 12 ----- library/core/src/mem/type_info.rs | 2 +- library/coretests/tests/intrinsics.rs | 11 +++-- library/coretests/tests/mem/type_info.rs | 1 - .../vtable-try-as-dyn.full-debuginfo.stderr | 18 +++++++- .../vtable-try-as-dyn.no-debuginfo.stderr | 18 +++++++- 10 files changed, 48 insertions(+), 81 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index e0c75da87a4b6..fe06b0a6e0d81 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -4,19 +4,16 @@ mod simd; -use rustc_abi::{FIRST_VARIANT, FieldIdx, HasDataLayout, Size, VariantIdx}; +use rustc_abi::{FieldIdx, HasDataLayout, Size, VariantIdx}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_data_structures::assert_matches; use rustc_errors::msg; -use rustc_hir::def_id::CRATE_DEF_ID; -use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{FloatTy, PolyExistentialPredicate, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{FloatTy, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug, ty}; use rustc_span::{Symbol, sym}; -use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; use tracing::trace; use super::memory::MemoryKind; @@ -227,44 +224,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(Scalar::from_target_usize(offset, self), dest)?; } - sym::vtable_for => { - let tp_ty = instance.args.type_at(0); - let result_ty = instance.args.type_at(1); - - ensure_monomorphic_enough(tcx, tp_ty)?; - ensure_monomorphic_enough(tcx, result_ty)?; - let ty::Dynamic(preds, _) = result_ty.kind() else { - span_bug!( - self.find_closest_untracked_caller_location(), - "Invalid type provided to vtable_for::. U must be dyn Trait, got {result_ty}." - ); - }; - - let (infcx, param_env) = - self.tcx.infer_ctxt().build_with_typing_env(self.typing_env); - - let ocx = ObligationCtxt::new(&infcx); - ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| { - let pred = pred.with_self_ty(tcx, tp_ty); - // Lifetimes can only be 'static because of the bound on T - let pred = ty::fold_regions(tcx, pred, |r, _| { - if r == tcx.lifetimes.re_erased { tcx.lifetimes.re_static } else { r } - }); - Obligation::new(tcx, ObligationCause::dummy(), param_env, pred) - })); - let type_impls_trait = ocx.evaluate_obligations_error_on_ambiguity().is_empty(); - // Since `assumed_wf_tys=[]` the choice of LocalDefId is irrelevant, so using the "default" - let regions_are_valid = ocx.resolve_regions(CRATE_DEF_ID, param_env, []).is_empty(); - - if regions_are_valid && type_impls_trait { - let vtable_ptr = self.get_vtable_ptr(tp_ty, preds)?; - // Writing a non-null pointer into an `Option` will automatically make it `Some`. - self.write_pointer(vtable_ptr, dest)?; - } else { - // Write `None` - self.write_discriminant(FIRST_VARIANT, dest)?; - } - } sym::variant_count => { let tp_ty = instance.args.type_at(0); let ty = match tp_ty.kind() { diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index a5aae7b407d6b..9167116ca5717 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -219,7 +219,6 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::ub_checks | sym::va_copy | sym::variant_count - | sym::vtable_for | sym::wrapping_add | sym::wrapping_mul | sym::wrapping_sub @@ -695,20 +694,6 @@ pub(crate) fn check_intrinsic_type( (0, 0, vec![Ty::new_imm_ptr(tcx, tcx.types.unit)], tcx.types.usize) } - sym::vtable_for => { - let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, span); - let dyn_metadata_adt_ref = tcx.adt_def(dyn_metadata); - let dyn_metadata_args = tcx.mk_args(&[param(1).into()]); - let dyn_ty = Ty::new_adt(tcx, dyn_metadata_adt_ref, dyn_metadata_args); - - let option_did = tcx.require_lang_item(LangItem::Option, span); - let option_adt_ref = tcx.adt_def(option_did); - let option_args = tcx.mk_args(&[dyn_ty.into()]); - let ret_ty = Ty::new_adt(tcx, option_adt_ref, option_args); - - (2, 0, vec![], ret_ty) - } - // This type check is not particularly useful, but the `where` bounds // on the definition in `core` do the heavy lifting for checking it. sym::aggregate_raw_ptr => (3, 0, vec![param(1), param(2)], param(0)), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4f88b5e663e6a..f13f7e6fa4ce4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2523,7 +2523,6 @@ symbols! { vsreg, vsx, vtable_align, - vtable_for, vtable_size, warn, wasip2, diff --git a/library/core/src/any.rs b/library/core/src/any.rs index e5eabb280323c..71a529400511c 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -1012,7 +1012,8 @@ pub const fn try_as_dyn< >( t: &T, ) -> Option<&U> { - let vtable: Option> = const { intrinsics::vtable_for::() }; + let vtable: Option> = + const { TypeId::of::().trait_info_of::().as_ref().map(TraitImpl::get_vtable) }; match vtable { Some(dyn_metadata) => { let pointer = ptr::from_raw_parts(t, dyn_metadata); @@ -1065,7 +1066,8 @@ pub const fn try_as_dyn_mut< >( t: &mut T, ) -> Option<&mut U> { - let vtable: Option> = const { intrinsics::vtable_for::() }; + let vtable: Option> = + const { TypeId::of::().trait_info_of::().as_ref().map(TraitImpl::get_vtable) }; match vtable { Some(dyn_metadata) => { let pointer = ptr::from_raw_parts_mut(t, dyn_metadata); diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index acb86f9c466cf..95b531994d92a 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2751,18 +2751,6 @@ pub unsafe fn vtable_size(ptr: *const ()) -> usize; #[rustc_intrinsic] pub unsafe fn vtable_align(ptr: *const ()) -> usize; -/// The intrinsic returns the `U` vtable for `T` if `T` can be coerced to the trait object type `U`. -/// -/// # Compile-time failures -/// Determining whether `T` can be coerced to the trait object type `U` requires trait resolution by the compiler. -/// In some cases, that resolution can exceed the recursion limit, -/// and compilation will fail instead of this function returning `None`. -#[rustc_nounwind] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_intrinsic] -pub const fn vtable_for> + ?Sized>() --> Option>; - /// The size of a type in bytes. /// /// Note that, unlike most intrinsics, this is safe to call; diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index cfc4266c5b167..24ae0c6484a0c 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -26,7 +26,7 @@ pub struct TraitImpl { impl TraitImpl { /// Gets the raw vtable for type reflection mapping - pub fn get_vtable(&self) -> DynMetadata { + pub const fn get_vtable(&self) -> DynMetadata { self.vtable } } diff --git a/library/coretests/tests/intrinsics.rs b/library/coretests/tests/intrinsics.rs index c6d841b8383a8..e562f49b0a2fc 100644 --- a/library/coretests/tests/intrinsics.rs +++ b/library/coretests/tests/intrinsics.rs @@ -1,6 +1,7 @@ use core::any::TypeId; -use core::intrinsics::{assume, vtable_for}; +use core::intrinsics::assume; use std::fmt::Debug; +use std::intrinsics::type_id_vtable; use std::option::Option; use std::ptr::DynMetadata; @@ -198,15 +199,17 @@ fn carrying_mul_add_fallback_i128() { } #[test] -fn test_vtable_for() { +fn test_type_id_vtable() { #[derive(Debug)] struct A {} struct B {} - const A_VTABLE: Option> = vtable_for::(); + const A_VTABLE: Option> = + type_id_vtable(TypeId::of::(), TypeId::of::()); assert!(A_VTABLE.is_some()); - const B_VTABLE: Option> = vtable_for::(); + const B_VTABLE: Option> = + type_id_vtable(TypeId::of::(), TypeId::of::()); assert!(B_VTABLE.is_none()); } diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index dbe53fc7b0f37..2483b4c2aacd7 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -3,7 +3,6 @@ use std::any::{Any, TypeId}; use std::mem::offset_of; use std::mem::type_info::{Const, Generic, GenericType, Type, TypeKind}; -use std::ptr::DynMetadata; #[test] fn test_arrays() { diff --git a/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr b/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr index a3772e509ed67..700083069cb05 100644 --- a/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr +++ b/tests/ui/limits/vtable-try-as-dyn.full-debuginfo.stderr @@ -1,4 +1,20 @@ -error: values of the type `[u8; usize::MAX]` are too big for the target architecture +error[E0080]: values of the type `[u8; usize::MAX]` are too big for the target architecture + --> $SRC_DIR/core/src/any.rs:LL:COL + | + = note: evaluation of `std::any::try_as_dyn::<[u8; usize::MAX], dyn Trait>::{constant#0}` failed inside this call +note: inside `TypeId::trait_info_of::` + --> $SRC_DIR/core/src/any.rs:LL:COL +note: inside `TypeId::trait_info_of_trait_type_id` + --> $SRC_DIR/core/src/any.rs:LL:COL +note: inside `type_info::::info` + --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL + +note: the above error was encountered while instantiating `fn try_as_dyn::<[u8; usize::MAX], dyn Trait>` + --> $DIR/vtable-try-as-dyn.rs:14:13 + | +LL | let _ = std::any::try_as_dyn::<[u8; usize::MAX], dyn Trait>(x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr b/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr index a3772e509ed67..700083069cb05 100644 --- a/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr +++ b/tests/ui/limits/vtable-try-as-dyn.no-debuginfo.stderr @@ -1,4 +1,20 @@ -error: values of the type `[u8; usize::MAX]` are too big for the target architecture +error[E0080]: values of the type `[u8; usize::MAX]` are too big for the target architecture + --> $SRC_DIR/core/src/any.rs:LL:COL + | + = note: evaluation of `std::any::try_as_dyn::<[u8; usize::MAX], dyn Trait>::{constant#0}` failed inside this call +note: inside `TypeId::trait_info_of::` + --> $SRC_DIR/core/src/any.rs:LL:COL +note: inside `TypeId::trait_info_of_trait_type_id` + --> $SRC_DIR/core/src/any.rs:LL:COL +note: inside `type_info::::info` + --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL + +note: the above error was encountered while instantiating `fn try_as_dyn::<[u8; usize::MAX], dyn Trait>` + --> $DIR/vtable-try-as-dyn.rs:14:13 + | +LL | let _ = std::any::try_as_dyn::<[u8; usize::MAX], dyn Trait>(x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0080`. From b5eb4f6767b26627c363bf9f6d43197f6bca47df Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 20 Feb 2026 18:19:01 +0800 Subject: [PATCH 46/82] Do strip references for desugar_try_expr --- .../crates/ide-assists/src/handlers/desugar_try_expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs index 176eb1c4944e0..9976e34e730cc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs @@ -61,7 +61,7 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let expr = try_expr.expr()?; let expr_type_info = ctx.sema.type_of_expr(&expr)?; - let try_enum = TryEnum::from_ty_without_strip(&ctx.sema, &expr_type_info.original)?; + let try_enum = TryEnum::from_ty(&ctx.sema, &expr_type_info.original)?; let target = try_expr.syntax().text_range(); acc.add( From d778d122faf5672b3bc1c4619c7218f63b504b9f Mon Sep 17 00:00:00 2001 From: protonblu Date: Fri, 20 Feb 2026 19:01:22 +0530 Subject: [PATCH 47/82] fix: generate method assist uses enclosing impl block instead of first found --- .../src/handlers/generate_function.rs | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index ded3b0f5acb20..de52c46d57dc4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -146,10 +146,19 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { if !is_editable_crate(target_module.krate(ctx.db()), ctx.db()) { return None; } + //Change + let enclosing_impl = ctx.find_node_at_offset::(); + let cursor_impl = enclosing_impl.filter(|impl_| { + ctx.sema.to_def(impl_).map_or(false, |def| def.self_ty(ctx.sema.db).as_adt() == Some(adt)) + }); - let (impl_, file) = get_adt_source(ctx, &adt, fn_name.text().as_str())?; + let (impl_, file) = if let Some(impl_) = cursor_impl { + (Some(impl_), ctx.vfs_file_id()) + } else { + get_adt_source(ctx, &adt, fn_name.text().as_str())? + }; let target = get_method_target(ctx, &impl_, &adt)?; - + //change let function_builder = FunctionBuilder::from_method_call( ctx, &call, @@ -3205,5 +3214,45 @@ fn bar(arg: impl Fn(_) -> bool) { } "#, ); + }#[test] +fn generate_method_uses_current_impl_block() { + check_assist( + generate_function, + r" +struct Foo; + +impl Foo { + fn new() -> Self { + Foo + } +} + +impl Foo { + fn method1(&self) { + self.method2$0(42) + } +} +", + r" +struct Foo; + +impl Foo { + fn new() -> Self { + Foo } } + +impl Foo { + fn method1(&self) { + self.method2(42) + } + + fn method2(&self, arg: i32) { + ${0:todo!()} + } +} +", + ) +} +} + From aa2da37260def88070d1c6d3567b605d7677a8fa Mon Sep 17 00:00:00 2001 From: protonblu Date: Fri, 20 Feb 2026 19:21:24 +0530 Subject: [PATCH 48/82] style: apply rustfmt --- .../src/handlers/generate_function.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index de52c46d57dc4..2c5053468bca9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -3214,11 +3214,12 @@ fn bar(arg: impl Fn(_) -> bool) { } "#, ); - }#[test] -fn generate_method_uses_current_impl_block() { - check_assist( - generate_function, - r" + } + #[test] + fn generate_method_uses_current_impl_block() { + check_assist( + generate_function, + r" struct Foo; impl Foo { @@ -3233,7 +3234,7 @@ impl Foo { } } ", - r" + r" struct Foo; impl Foo { @@ -3252,7 +3253,6 @@ impl Foo { } } ", - ) -} + ) + } } - From 7daabfd04d5ba8502fa5e82e39a9c0c4f449af62 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Wed, 18 Feb 2026 15:10:47 -0500 Subject: [PATCH 49/82] adjust libdir for windows, use lld during enzyme build when needed --- src/bootstrap/src/core/build_steps/llvm.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index c04a8cd6b7ab2..51200450ecac7 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -23,7 +23,7 @@ use crate::core::config::{Config, TargetSelection}; use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; use crate::utils::helpers::{ - self, exe, get_clang_cl_resource_dir, t, unhashed_basename, up_to_date, + self, exe, get_clang_cl_resource_dir, libdir, t, unhashed_basename, up_to_date, }; use crate::{CLang, GitRepo, Kind, trace}; @@ -561,7 +561,6 @@ impl Step for Llvm { } }; - // FIXME(ZuseZ4): Do we need that for Enzyme too? // When building LLVM with LLVM_LINK_LLVM_DYLIB for macOS, an unversioned // libLLVM.dylib will be built. However, llvm-config will still look // for a versioned path like libLLVM-14.dylib. Manually create a symbolic @@ -1159,7 +1158,7 @@ impl Step for Enzyme { let llvm_version_major = llvm::get_llvm_version_major(builder, &host_llvm_config); let lib_ext = std::env::consts::DLL_EXTENSION; let libenzyme = format!("libEnzyme-{llvm_version_major}"); - let build_dir = out_dir.join("lib"); + let build_dir = out_dir.join(libdir(target)); let dylib = build_dir.join(&libenzyme).with_extension(lib_ext); trace!("checking build stamp to see if we need to rebuild enzyme artifacts"); @@ -1197,7 +1196,16 @@ impl Step for Enzyme { // hard to spot more relevant issues. let mut cflags = CcFlags::default(); cflags.push_all("-Wno-deprecated"); - configure_cmake(builder, target, &mut cfg, true, LdFlags::default(), cflags, &[]); + + // Logic copied from `configure_llvm` + // ThinLTO is only available when building with LLVM, enabling LLD is required. + // Apple's linker ld64 supports ThinLTO out of the box though, so don't use LLD on Darwin. + let mut ldflags = LdFlags::default(); + if builder.config.llvm_thin_lto && !target.contains("apple") { + ldflags.push_all("-fuse-ld=lld"); + } + + configure_cmake(builder, target, &mut cfg, true, ldflags, cflags, &[]); // Re-use the same flags as llvm to control the level of debug information // generated by Enzyme. From 4bf4ff1617680348c1316233775df97fe65fd00e Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Thu, 19 Feb 2026 15:18:45 -0500 Subject: [PATCH 50/82] Enable autodiff in CI for mingw-llvm,linux --- src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile | 2 +- src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh | 1 + src/ci/github-actions/jobs.yml | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile index 3abca36fe70db..9691d4b099b3b 100644 --- a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile +++ b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile @@ -97,7 +97,7 @@ ENV RUST_CONFIGURE_ARGS \ ENV SCRIPT python3 ../x.py build --set rust.debug=true opt-dist && \ ./build/$HOSTS/stage1-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \ - --host $HOSTS --target $HOSTS --include-default-paths build-manifest bootstrap + --host $HOSTS --target $HOSTS --include-default-paths build-manifest bootstrap enzyme ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=clang ENV LIBCURL_NO_PKG_CONFIG 1 diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh index c78b76a53f44c..46d34cd001a95 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh @@ -9,6 +9,7 @@ python3 ../x.py build --set rust.debug=true opt-dist --include-default-paths \ build-manifest \ bootstrap \ + enzyme \ rustc_codegen_gcc # Use GCC for building GCC components, as it seems to behave badly when built with Clang diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 88b6855784979..0687d142a3504 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -706,7 +706,7 @@ auto: # i686 has no dedicated job, build it here because this job is fast - name: dist-aarch64-llvm-mingw env: - SCRIPT: python x.py dist bootstrap --include-default-paths + SCRIPT: python x.py dist bootstrap enzyme --include-default-paths RUST_CONFIGURE_ARGS: >- --build=aarch64-pc-windows-gnullvm --target=aarch64-pc-windows-gnullvm,i686-pc-windows-gnullvm @@ -720,7 +720,7 @@ auto: - name: dist-x86_64-llvm-mingw env: - SCRIPT: python x.py dist bootstrap --include-default-paths + SCRIPT: python x.py dist bootstrap enzyme --include-default-paths RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-gnullvm --enable-full-tools From c033de932ec6f46ccb1fd14bf848aec0bc88eece Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Thu, 19 Feb 2026 15:22:40 -0500 Subject: [PATCH 51/82] Move aarch64-apple dist builder to dynamic llvm linking and enable autodiff in CI for it Co-authored-by: sgasho --- src/ci/github-actions/jobs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 0687d142a3504..c06c85513856b 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -504,7 +504,7 @@ auto: - name: dist-aarch64-apple env: SCRIPT: >- - ./x.py dist bootstrap + ./x.py dist bootstrap enzyme --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin @@ -513,6 +513,7 @@ auto: --enable-sanitizers --enable-profiler --set rust.jemalloc + --set llvm.link-shared=true --set rust.lto=thin --set rust.codegen-units=1 # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else From ca3d3b7f719defbc5009ddc0ee56ab55f9966b64 Mon Sep 17 00:00:00 2001 From: protonblu Date: Sat, 21 Feb 2026 07:24:22 +0530 Subject: [PATCH 52/82] fix: remove redundant comment --- .../crates/ide-assists/src/handlers/generate_function.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 2c5053468bca9..f62eccaf1952e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -146,7 +146,7 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { if !is_editable_crate(target_module.krate(ctx.db()), ctx.db()) { return None; } - //Change + let enclosing_impl = ctx.find_node_at_offset::(); let cursor_impl = enclosing_impl.filter(|impl_| { ctx.sema.to_def(impl_).map_or(false, |def| def.self_ty(ctx.sema.db).as_adt() == Some(adt)) @@ -158,7 +158,7 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { get_adt_source(ctx, &adt, fn_name.text().as_str())? }; let target = get_method_target(ctx, &impl_, &adt)?; - //change + let function_builder = FunctionBuilder::from_method_call( ctx, &call, From fba1ca8b9f841ea5e43a0790e2e45576a74c9541 Mon Sep 17 00:00:00 2001 From: protonblu Date: Sat, 21 Feb 2026 11:03:20 +0530 Subject: [PATCH 53/82] fix: correctly parenthesize inverted condition in convert_if_to_bool_then The convert_if_to_bool_then assist was producing incorrect code when the Some branch was in the else arm and the condition was a method call expression (e.g. is_empty()). Before: if test.is_empty() { None } else { Some(()) } was incorrectly rewritten to: test.is_empty().then(|| ()) After (correct): (!test.is_empty()).then(|| ()) Root cause: the parenthesization check ran on the original condition before inversion. When invert_boolean_expression produced a PrefixExpr (e.g. !test.is_empty()), it was not being parenthesized because the check had already passed on the original MethodCallExpr. Fix: move the parenthesization check to after the inversion step so it correctly detects PrefixExpr and wraps it in parentheses. A regression test has been added to cover this case. --- .../src/handlers/convert_bool_then.rs | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index 91cee59ad8d00..d2c4ed9b5a2c3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -102,6 +102,11 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_> ast::Expr::BlockExpr(block) => unwrap_trivial_block(block), e => e, }; + let cond = if invert_cond { + invert_boolean_expression(&make, cond) + } else { + cond.clone_for_update() + }; let parenthesize = matches!( cond, @@ -123,11 +128,7 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_> | ast::Expr::WhileExpr(_) | ast::Expr::YieldExpr(_) ); - let cond = if invert_cond { - invert_boolean_expression(&make, cond) - } else { - cond.clone_for_update() - }; + let cond = if parenthesize { make.expr_paren(cond).into() } else { cond }; let arg_list = make.arg_list(Some(make.expr_closure(None, closure_body).into())); let mcall = make.expr_method_call(cond, make.name_ref("then"), arg_list); @@ -588,6 +589,25 @@ fn main() { None } } +", + ); + } + #[test] + fn convert_if_to_bool_then_invert_method_call() { + check_assist( + convert_if_to_bool_then, + r" +//- minicore:option +fn main() { + let test = &[()]; + let value = if$0 test.is_empty() { None } else { Some(()) }; +} +", + r" +fn main() { + let test = &[()]; + let value = (!test.is_empty()).then(|| ()); +} ", ); } From 8b2c10ff8267893dd33a2aa27ac6e921b9e4575e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 12 Feb 2026 16:35:02 +0000 Subject: [PATCH 54/82] Replace LLVMRustModuleBuffer with generic LLVMRustBuffer --- compiler/rustc_codegen_llvm/src/back/lto.rs | 38 +++++++++++-------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 10 ++--- .../rustc_llvm/llvm-wrapper/LLVMWrapper.h | 4 ++ .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 29 ++++++-------- 4 files changed, 43 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 5d272d10930b1..106d1131dceb2 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -658,35 +658,43 @@ pub(crate) fn run_pass_manager( debug!("lto done"); } -pub struct ModuleBuffer(&'static mut llvm::ModuleBuffer); +pub(crate) struct Buffer(&'static mut llvm::Buffer); -unsafe impl Send for ModuleBuffer {} -unsafe impl Sync for ModuleBuffer {} +unsafe impl Send for Buffer {} +unsafe impl Sync for Buffer {} -impl ModuleBuffer { - pub(crate) fn new(m: &llvm::Module) -> ModuleBuffer { - ModuleBuffer(unsafe { llvm::LLVMRustModuleBufferCreate(m) }) - } -} - -impl ModuleBufferMethods for ModuleBuffer { - fn data(&self) -> &[u8] { +impl Buffer { + pub(crate) fn data(&self) -> &[u8] { unsafe { - let ptr = llvm::LLVMRustModuleBufferPtr(self.0); - let len = llvm::LLVMRustModuleBufferLen(self.0); + let ptr = llvm::LLVMRustBufferPtr(self.0); + let len = llvm::LLVMRustBufferLen(self.0); slice::from_raw_parts(ptr, len) } } } -impl Drop for ModuleBuffer { +impl Drop for Buffer { fn drop(&mut self) { unsafe { - llvm::LLVMRustModuleBufferFree(&mut *(self.0 as *mut _)); + llvm::LLVMRustBufferFree(&mut *(self.0 as *mut _)); } } } +pub struct ModuleBuffer(Buffer); + +impl ModuleBuffer { + pub(crate) fn new(m: &llvm::Module) -> ModuleBuffer { + ModuleBuffer(Buffer(unsafe { llvm::LLVMRustModuleSerialize(m) })) + } +} + +impl ModuleBufferMethods for ModuleBuffer { + fn data(&self) -> &[u8] { + self.0.data() + } +} + pub struct ThinData(&'static mut llvm::ThinLTOData); unsafe impl Send for ThinData {} diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 59bf8679d6398..bf7f8672cda9f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -848,7 +848,7 @@ bitflags! { } unsafe extern "C" { - pub(crate) type ModuleBuffer; + pub(crate) type Buffer; } pub(crate) type SelfProfileBeforePassCallback = @@ -2457,10 +2457,10 @@ unsafe extern "C" { pub(crate) fn LLVMRustSetModulePICLevel(M: &Module); pub(crate) fn LLVMRustSetModulePIELevel(M: &Module); pub(crate) fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); - pub(crate) fn LLVMRustModuleBufferCreate(M: &Module) -> &'static mut ModuleBuffer; - pub(crate) fn LLVMRustModuleBufferPtr(p: &ModuleBuffer) -> *const u8; - pub(crate) fn LLVMRustModuleBufferLen(p: &ModuleBuffer) -> usize; - pub(crate) fn LLVMRustModuleBufferFree(p: &'static mut ModuleBuffer); + pub(crate) fn LLVMRustBufferPtr(p: &Buffer) -> *const u8; + pub(crate) fn LLVMRustBufferLen(p: &Buffer) -> usize; + pub(crate) fn LLVMRustBufferFree(p: &'static mut Buffer); + pub(crate) fn LLVMRustModuleSerialize(M: &Module) -> &'static mut Buffer; pub(crate) fn LLVMRustModuleCost(M: &Module) -> u64; pub(crate) fn LLVMRustModuleInstructionStats(M: &Module) -> u64; diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index f6598f9faf522..0cbda23f384cc 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -46,4 +46,8 @@ class RawRustStringOstream : public llvm::raw_ostream { } }; +struct LLVMRustBuffer { + std::string data; +}; + #endif // INCLUDED_RUSTC_LLVM_LLVMWRAPPER_H diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index f68f4e71520b3..8192b6a7bfd44 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1529,12 +1529,18 @@ extern "C" void LLVMRustSetDSOLocal(LLVMValueRef Global, bool is_dso_local) { unwrap(Global)->setDSOLocal(is_dso_local); } -struct LLVMRustModuleBuffer { - std::string data; -}; +extern "C" void LLVMRustBufferFree(LLVMRustBuffer *Buffer) { delete Buffer; } + +extern "C" const void *LLVMRustBufferPtr(const LLVMRustBuffer *Buffer) { + return Buffer->data.data(); +} -extern "C" LLVMRustModuleBuffer *LLVMRustModuleBufferCreate(LLVMModuleRef M) { - auto Ret = std::make_unique(); +extern "C" size_t LLVMRustBufferLen(const LLVMRustBuffer *Buffer) { + return Buffer->data.length(); +} + +extern "C" LLVMRustBuffer *LLVMRustModuleSerialize(LLVMModuleRef M) { + auto Ret = std::make_unique(); { auto OS = raw_string_ostream(Ret->data); WriteBitcodeToFile(*unwrap(M), OS); @@ -1542,19 +1548,6 @@ extern "C" LLVMRustModuleBuffer *LLVMRustModuleBufferCreate(LLVMModuleRef M) { return Ret.release(); } -extern "C" void LLVMRustModuleBufferFree(LLVMRustModuleBuffer *Buffer) { - delete Buffer; -} - -extern "C" const void * -LLVMRustModuleBufferPtr(const LLVMRustModuleBuffer *Buffer) { - return Buffer->data.data(); -} - -extern "C" size_t LLVMRustModuleBufferLen(const LLVMRustModuleBuffer *Buffer) { - return Buffer->data.length(); -} - extern "C" uint64_t LLVMRustModuleCost(LLVMModuleRef M) { auto f = unwrap(M)->functions(); return std::distance(std::begin(f), std::end(f)); From a5372d1dbaab1af93d49cb28e034a49db4a0fbcc Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 12 Feb 2026 16:28:55 +0000 Subject: [PATCH 55/82] Replace LLVMRustThinLTOBuffer with separate LLVMRustBuffers for bitcode and summary --- compiler/rustc_codegen_llvm/src/back/lto.rs | 47 +++++--------- compiler/rustc_codegen_llvm/src/back/write.rs | 19 ++++-- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 16 +---- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 61 +++++-------------- 4 files changed, 44 insertions(+), 99 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 106d1131dceb2..8332dd13bb5fa 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -634,7 +634,9 @@ pub(crate) fn run_pass_manager( }; unsafe { - write::llvm_optimize(cgcx, prof, dcx, module, None, config, opt_level, opt_stage, stage); + write::llvm_optimize( + cgcx, prof, dcx, module, None, None, config, opt_level, opt_stage, stage, + ); } if cfg!(feature = "llvm_enzyme") && enable_ad && !thin { @@ -643,7 +645,7 @@ pub(crate) fn run_pass_manager( if !config.autodiff.contains(&config::AutoDiff::NoPostopt) { unsafe { write::llvm_optimize( - cgcx, prof, dcx, module, None, config, opt_level, opt_stage, stage, + cgcx, prof, dcx, module, None, None, config, opt_level, opt_stage, stage, ); } } @@ -664,6 +666,11 @@ unsafe impl Send for Buffer {} unsafe impl Sync for Buffer {} impl Buffer { + pub(crate) unsafe fn from_raw_ptr(ptr: *mut llvm::Buffer) -> Buffer { + let mut ptr = NonNull::new(ptr).unwrap(); + Buffer(unsafe { ptr.as_mut() }) + } + pub(crate) fn data(&self) -> &[u8] { unsafe { let ptr = llvm::LLVMRustBufferPtr(self.0); @@ -708,48 +715,22 @@ impl Drop for ThinData { } } -pub struct ThinBuffer(&'static mut llvm::ThinLTOBuffer); - -unsafe impl Send for ThinBuffer {} -unsafe impl Sync for ThinBuffer {} +pub struct ThinBuffer { + data: Buffer, +} impl ThinBuffer { pub(crate) fn new(m: &llvm::Module, is_thin: bool) -> ThinBuffer { unsafe { let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin); - ThinBuffer(buffer) - } - } - - pub(crate) unsafe fn from_raw_ptr(ptr: *mut llvm::ThinLTOBuffer) -> ThinBuffer { - let mut ptr = NonNull::new(ptr).unwrap(); - ThinBuffer(unsafe { ptr.as_mut() }) - } - - pub(crate) fn thin_link_data(&self) -> &[u8] { - unsafe { - let ptr = llvm::LLVMRustThinLTOBufferThinLinkDataPtr(self.0) as *const _; - let len = llvm::LLVMRustThinLTOBufferThinLinkDataLen(self.0); - slice::from_raw_parts(ptr, len) + ThinBuffer { data: Buffer(buffer) } } } } impl ThinBufferMethods for ThinBuffer { fn data(&self) -> &[u8] { - unsafe { - let ptr = llvm::LLVMRustThinLTOBufferPtr(self.0) as *const _; - let len = llvm::LLVMRustThinLTOBufferLen(self.0); - slice::from_raw_parts(ptr, len) - } - } -} - -impl Drop for ThinBuffer { - fn drop(&mut self) { - unsafe { - llvm::LLVMRustThinLTOBufferFree(&mut *(self.0 as *mut _)); - } + self.data.data() } } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index cbf82b05e4d6b..f8c8aeac7d950 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -563,7 +563,8 @@ pub(crate) unsafe fn llvm_optimize( prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, module: &ModuleCodegen, - thin_lto_buffer: Option<&mut *mut llvm::ThinLTOBuffer>, + thin_lto_buffer: Option<&mut *mut llvm::Buffer>, + thin_lto_summary_buffer: Option<&mut *mut llvm::Buffer>, config: &ModuleConfig, opt_level: config::OptLevel, opt_stage: llvm::OptStage, @@ -786,6 +787,7 @@ pub(crate) unsafe fn llvm_optimize( config.verify_llvm_ir, config.lint_llvm_ir, thin_lto_buffer, + thin_lto_summary_buffer, config.emit_thin_lto_summary, merge_functions, unroll_loops, @@ -932,13 +934,14 @@ pub(crate) fn optimize( // The bitcode obtained during the `codegen` phase is no longer suitable for performing LTO. // It may have undergone LTO due to ThinLocal, so we need to obtain the embedded bitcode at // this point. - let mut thin_lto_buffer = if (module.kind == ModuleKind::Regular + let (mut thin_lto_buffer, mut thin_lto_summary_buffer) = if (module.kind + == ModuleKind::Regular && config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full)) || config.emit_thin_lto_summary { - Some(null_mut()) + (Some(null_mut()), Some(null_mut())) } else { - None + (None, None) }; unsafe { llvm_optimize( @@ -947,6 +950,7 @@ pub(crate) fn optimize( dcx, module, thin_lto_buffer.as_mut(), + thin_lto_summary_buffer.as_mut(), config, opt_level, opt_stage, @@ -954,7 +958,10 @@ pub(crate) fn optimize( ) }; if let Some(thin_lto_buffer) = thin_lto_buffer { - let thin_lto_buffer = unsafe { ThinBuffer::from_raw_ptr(thin_lto_buffer) }; + let thin_lto_buffer = + unsafe { crate::back::lto::Buffer::from_raw_ptr(thin_lto_buffer) }; + let thin_lto_summary_buffer = + unsafe { crate::back::lto::Buffer::from_raw_ptr(thin_lto_summary_buffer.unwrap()) }; module.thin_lto_buffer = Some(thin_lto_buffer.data().to_vec()); let bc_summary_out = cgcx.output_filenames.temp_path_for_cgu( OutputType::ThinLinkBitcode, @@ -964,7 +971,7 @@ pub(crate) fn optimize( if config.emit_thin_lto_summary && let Some(thin_link_bitcode_filename) = bc_summary_out.file_name() { - let summary_data = thin_lto_buffer.thin_link_data(); + let summary_data = thin_lto_summary_buffer.data(); prof.artifact_size( "llvm_bitcode_summary", thin_link_bitcode_filename.to_string_lossy(), diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index bf7f8672cda9f..dde255f27a880 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -536,9 +536,6 @@ pub(crate) enum DiagnosticLevel { unsafe extern "C" { // LLVMRustThinLTOData pub(crate) type ThinLTOData; - - // LLVMRustThinLTOBuffer - pub(crate) type ThinLTOBuffer; } /// LLVMRustThinLTOModule @@ -2375,7 +2372,8 @@ unsafe extern "C" { NoPrepopulatePasses: bool, VerifyIR: bool, LintIR: bool, - ThinLTOBuffer: Option<&mut *mut ThinLTOBuffer>, + ThinLTOBuffer: Option<&mut *mut Buffer>, + ThinLTOSummaryBuffer: Option<&mut *mut Buffer>, EmitThinLTOSummary: bool, MergeFunctions: bool, UnrollLoops: bool, @@ -2464,15 +2462,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustModuleCost(M: &Module) -> u64; pub(crate) fn LLVMRustModuleInstructionStats(M: &Module) -> u64; - pub(crate) fn LLVMRustThinLTOBufferCreate( - M: &Module, - is_thin: bool, - ) -> &'static mut ThinLTOBuffer; - pub(crate) fn LLVMRustThinLTOBufferFree(M: &'static mut ThinLTOBuffer); - pub(crate) fn LLVMRustThinLTOBufferPtr(M: &ThinLTOBuffer) -> *const c_char; - pub(crate) fn LLVMRustThinLTOBufferLen(M: &ThinLTOBuffer) -> size_t; - pub(crate) fn LLVMRustThinLTOBufferThinLinkDataPtr(M: &ThinLTOBuffer) -> *const c_char; - pub(crate) fn LLVMRustThinLTOBufferThinLinkDataLen(M: &ThinLTOBuffer) -> size_t; + pub(crate) fn LLVMRustThinLTOBufferCreate(M: &Module, is_thin: bool) -> &'static mut Buffer; pub(crate) fn LLVMRustCreateThinLTOData( Modules: *const ThinLTOModule, NumModules: size_t, diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index f6b4d6cc15439..15ec7db3ad288 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -87,19 +87,6 @@ extern "C" void LLVMRustTimeTraceProfilerFinish(const char *FileName) { timeTraceProfilerCleanup(); } -// This struct and various functions are sort of a hack right now, but the -// problem is that we've got in-memory LLVM modules after we generate and -// optimize all codegen-units for one compilation in rustc. To be compatible -// with the LTO support above we need to serialize the modules plus their -// ThinLTO summary into memory. -// -// This structure is basically an owned version of a serialize module, with -// a ThinLTO summary attached. -struct LLVMRustThinLTOBuffer { - std::string data; - std::string thin_link_data; -}; - extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM, const char *Feature) { TargetMachine *Target = unwrap(TM); @@ -566,11 +553,12 @@ extern "C" LLVMRustResult LLVMRustOptimize( LLVMModuleRef ModuleRef, LLVMTargetMachineRef TMRef, LLVMRustPassBuilderOptLevel OptLevelRust, LLVMRustOptStage OptStage, bool IsLinkerPluginLTO, bool NoPrepopulatePasses, bool VerifyIR, - bool LintIR, LLVMRustThinLTOBuffer **ThinLTOBufferRef, - bool EmitThinLTOSummary, bool MergeFunctions, bool UnrollLoops, - bool SLPVectorize, bool LoopVectorize, bool DisableSimplifyLibCalls, - bool EmitLifetimeMarkers, registerEnzymeAndPassPipelineFn EnzymePtr, - bool PrintBeforeEnzyme, bool PrintAfterEnzyme, bool PrintPasses, + bool LintIR, LLVMRustBuffer **ThinLTOBufferRef, + LLVMRustBuffer **ThinLTOSummaryBufferRef, bool EmitThinLTOSummary, + bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, + bool LoopVectorize, bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers, + registerEnzymeAndPassPipelineFn EnzymePtr, bool PrintBeforeEnzyme, + bool PrintAfterEnzyme, bool PrintPasses, LLVMRustSanitizerOptions *SanitizerOptions, const char *PGOGenPath, const char *PGOUsePath, bool InstrumentCoverage, const char *InstrProfileOutput, const char *PGOSampleUsePath, @@ -809,9 +797,10 @@ extern "C" LLVMRustResult LLVMRustOptimize( ModulePassManager MPM; bool NeedThinLTOBufferPasses = true; - auto ThinLTOBuffer = std::make_unique(); + auto ThinLTOBuffer = std::make_unique(); + auto ThinLTOSummaryBuffer = std::make_unique(); raw_string_ostream ThinLTODataOS(ThinLTOBuffer->data); - raw_string_ostream ThinLinkDataOS(ThinLTOBuffer->thin_link_data); + raw_string_ostream ThinLinkDataOS(ThinLTOSummaryBuffer->data); bool IsLTO = OptStage == LLVMRustOptStage::ThinLTO || OptStage == LLVMRustOptStage::FatLTO; if (!NoPrepopulatePasses) { @@ -838,6 +827,7 @@ extern "C" LLVMRustResult LLVMRustOptimize( MPM.addPass(ThinLTOBitcodeWriterPass( ThinLTODataOS, EmitThinLTOSummary ? &ThinLinkDataOS : nullptr)); *ThinLTOBufferRef = ThinLTOBuffer.release(); + *ThinLTOSummaryBufferRef = ThinLTOSummaryBuffer.release(); MPM.addPass(PB.buildModuleOptimizationPipeline( OptLevel, ThinOrFullLTOPhase::None)); MPM.addPass( @@ -898,6 +888,7 @@ extern "C" LLVMRustResult LLVMRustOptimize( MPM.addPass(BitcodeWriterPass(ThinLTODataOS)); } *ThinLTOBufferRef = ThinLTOBuffer.release(); + *ThinLTOSummaryBufferRef = ThinLTOSummaryBuffer.release(); } // now load "-enzyme" pass: @@ -1408,9 +1399,9 @@ extern "C" bool LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, return true; } -extern "C" LLVMRustThinLTOBuffer *LLVMRustThinLTOBufferCreate(LLVMModuleRef M, - bool is_thin) { - auto Ret = std::make_unique(); +extern "C" LLVMRustBuffer *LLVMRustThinLTOBufferCreate(LLVMModuleRef M, + bool is_thin) { + auto Ret = std::make_unique(); { auto OS = raw_string_ostream(Ret->data); { @@ -1436,30 +1427,6 @@ extern "C" LLVMRustThinLTOBuffer *LLVMRustThinLTOBufferCreate(LLVMModuleRef M, return Ret.release(); } -extern "C" void LLVMRustThinLTOBufferFree(LLVMRustThinLTOBuffer *Buffer) { - delete Buffer; -} - -extern "C" const void * -LLVMRustThinLTOBufferPtr(const LLVMRustThinLTOBuffer *Buffer) { - return Buffer->data.data(); -} - -extern "C" size_t -LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) { - return Buffer->data.length(); -} - -extern "C" const void * -LLVMRustThinLTOBufferThinLinkDataPtr(const LLVMRustThinLTOBuffer *Buffer) { - return Buffer->thin_link_data.data(); -} - -extern "C" size_t -LLVMRustThinLTOBufferThinLinkDataLen(const LLVMRustThinLTOBuffer *Buffer) { - return Buffer->thin_link_data.length(); -} - // This is what we used to parse upstream bitcode for actual ThinLTO // processing. We'll call this once per module optimized through ThinLTO, and // it'll be called concurrently on many threads. From a086b3617ecb8df7fae796542fdbf18f5fe3b61f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:33:17 +0000 Subject: [PATCH 56/82] Remove ModuleBuffer ThinBuffer duplication --- compiler/rustc_codegen_gcc/src/lib.rs | 4 +-- compiler/rustc_codegen_llvm/src/back/lto.rs | 36 ++++++------------- compiler/rustc_codegen_llvm/src/back/write.rs | 4 +-- compiler/rustc_codegen_llvm/src/lib.rs | 4 +-- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 +- compiler/rustc_codegen_ssa/src/traits/mod.rs | 2 +- .../rustc_codegen_ssa/src/traits/write.rs | 6 +--- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 4 +-- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 9 ----- 9 files changed, 22 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 5d03d2406870f..6517e12ba81ab 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -87,7 +87,7 @@ use rustc_codegen_ssa::back::write::{ use rustc_codegen_ssa::base::codegen_crate; use rustc_codegen_ssa::target_features::cfg_target_feature; use rustc_codegen_ssa::traits::{ - CodegenBackend, ExtraBackendMethods, ThinBufferMethods, WriteBackendMethods, + CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, WriteBackendMethods, }; use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig}; use rustc_data_structures::fx::FxIndexMap; @@ -425,7 +425,7 @@ unsafe impl Sync for SyncContext {} pub struct ThinBuffer; -impl ThinBufferMethods for ThinBuffer { +impl ModuleBufferMethods for ThinBuffer { fn data(&self) -> &[u8] { &[] } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 8332dd13bb5fa..3b1a425f7d917 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -187,7 +187,7 @@ pub(crate) fn run_thin( dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], - modules: Vec<(String, ThinBuffer)>, + modules: Vec<(String, ModuleBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> (Vec>, Vec) { let (symbols_below_threshold, upstream_modules) = @@ -203,9 +203,9 @@ pub(crate) fn run_thin( thin_lto(cgcx, prof, dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold) } -pub(crate) fn prepare_thin(module: ModuleCodegen) -> (String, ThinBuffer) { +pub(crate) fn prepare_thin(module: ModuleCodegen) -> (String, ModuleBuffer) { let name = module.name; - let buffer = ThinBuffer::new(module.module_llvm.llmod(), true); + let buffer = ModuleBuffer::new(module.module_llvm.llmod(), true); (name, buffer) } @@ -297,7 +297,7 @@ fn fat_lto( // way we know of to do that is to serialize them to a string and them parse // them later. Not great but hey, that's why it's "fat" LTO, right? for module in in_memory { - let buffer = ModuleBuffer::new(module.module_llvm.llmod()); + let buffer = ModuleBuffer::new(module.module_llvm.llmod(), false); let llmod_id = CString::new(&module.name[..]).unwrap(); serialized_modules.push((SerializedModule::Local(buffer), llmod_id)); } @@ -400,7 +400,7 @@ fn thin_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, - modules: Vec<(String, ThinBuffer)>, + modules: Vec<(String, ModuleBuffer)>, serialized_modules: Vec<(SerializedModule, CString)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, symbols_below_threshold: &[*const libc::c_char], @@ -688,20 +688,6 @@ impl Drop for Buffer { } } -pub struct ModuleBuffer(Buffer); - -impl ModuleBuffer { - pub(crate) fn new(m: &llvm::Module) -> ModuleBuffer { - ModuleBuffer(Buffer(unsafe { llvm::LLVMRustModuleSerialize(m) })) - } -} - -impl ModuleBufferMethods for ModuleBuffer { - fn data(&self) -> &[u8] { - self.0.data() - } -} - pub struct ThinData(&'static mut llvm::ThinLTOData); unsafe impl Send for ThinData {} @@ -715,20 +701,20 @@ impl Drop for ThinData { } } -pub struct ThinBuffer { +pub struct ModuleBuffer { data: Buffer, } -impl ThinBuffer { - pub(crate) fn new(m: &llvm::Module, is_thin: bool) -> ThinBuffer { +impl ModuleBuffer { + pub(crate) fn new(m: &llvm::Module, is_thin: bool) -> ModuleBuffer { unsafe { - let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin); - ThinBuffer { data: Buffer(buffer) } + let buffer = llvm::LLVMRustModuleSerialize(m, is_thin); + ModuleBuffer { data: Buffer(buffer) } } } } -impl ThinBufferMethods for ThinBuffer { +impl ModuleBufferMethods for ModuleBuffer { fn data(&self) -> &[u8] { self.data.data() } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index f8c8aeac7d950..9bb6045084d7d 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -29,7 +29,7 @@ use rustc_target::spec::{ }; use tracing::{debug, trace}; -use crate::back::lto::ThinBuffer; +use crate::back::lto::ModuleBuffer; use crate::back::owned_target_machine::OwnedTargetMachine; use crate::back::profiling::{ LlvmSelfProfiler, selfprofile_after_pass_callback, selfprofile_before_pass_callback, @@ -1039,7 +1039,7 @@ pub(crate) fn codegen( "LLVM_module_codegen_make_bitcode", &*module.name, ); - ThinBuffer::new(llmod, cgcx.lto != Lto::Fat) + ModuleBuffer::new(llmod, cgcx.lto != Lto::Fat) }; let data = thin.data(); let _timer = prof diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 83dcfb812e492..5e09012d915ba 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -153,7 +153,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { type ModuleBuffer = back::lto::ModuleBuffer; type TargetMachine = OwnedTargetMachine; type ThinData = back::lto::ThinData; - type ThinBuffer = back::lto::ThinBuffer; + type ThinBuffer = back::lto::ModuleBuffer; fn print_pass_timings(&self) { let timings = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintPassTimings(s) }).unwrap(); print!("{timings}"); @@ -237,7 +237,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { back::lto::prepare_thin(module) } fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer) { - (module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod())) + (module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod(), false)) } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index dde255f27a880..f9fa2e2769965 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2458,11 +2458,10 @@ unsafe extern "C" { pub(crate) fn LLVMRustBufferPtr(p: &Buffer) -> *const u8; pub(crate) fn LLVMRustBufferLen(p: &Buffer) -> usize; pub(crate) fn LLVMRustBufferFree(p: &'static mut Buffer); - pub(crate) fn LLVMRustModuleSerialize(M: &Module) -> &'static mut Buffer; pub(crate) fn LLVMRustModuleCost(M: &Module) -> u64; pub(crate) fn LLVMRustModuleInstructionStats(M: &Module) -> u64; - pub(crate) fn LLVMRustThinLTOBufferCreate(M: &Module, is_thin: bool) -> &'static mut Buffer; + pub(crate) fn LLVMRustModuleSerialize(M: &Module, is_thin: bool) -> &'static mut Buffer; pub(crate) fn LLVMRustCreateThinLTOData( Modules: *const ThinLTOModule, NumModules: size_t, diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 6d1ac717c0b8f..f46d07ea5008e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -48,7 +48,7 @@ pub use self::type_::{ ArgAbiBuilderMethods, BaseTypeCodegenMethods, DerivedTypeCodegenMethods, LayoutTypeCodegenMethods, TypeCodegenMethods, TypeMembershipCodegenMethods, }; -pub use self::write::{ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods}; +pub use self::write::{ModuleBufferMethods, WriteBackendMethods}; pub trait CodegenObject = Copy + fmt::Debug; diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 5f5d0ac5d9fc4..1638327aa390e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -15,7 +15,7 @@ pub trait WriteBackendMethods: Clone + 'static { type TargetMachine; type ModuleBuffer: ModuleBufferMethods; type ThinData: Send + Sync; - type ThinBuffer: ThinBufferMethods; + type ThinBuffer: ModuleBufferMethods; /// Performs fat LTO by merging all modules into a single one, running autodiff /// if necessary and running any further optimizations @@ -67,10 +67,6 @@ pub trait WriteBackendMethods: Clone + 'static { fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer); } -pub trait ThinBufferMethods: Send + Sync { - fn data(&self) -> &[u8]; -} - pub trait ModuleBufferMethods: Send + Sync { fn data(&self) -> &[u8]; } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 15ec7db3ad288..1ee67e952eadc 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1399,8 +1399,8 @@ extern "C" bool LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, return true; } -extern "C" LLVMRustBuffer *LLVMRustThinLTOBufferCreate(LLVMModuleRef M, - bool is_thin) { +extern "C" LLVMRustBuffer *LLVMRustModuleSerialize(LLVMModuleRef M, + bool is_thin) { auto Ret = std::make_unique(); { auto OS = raw_string_ostream(Ret->data); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 8192b6a7bfd44..eabc1c94f26e9 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1539,15 +1539,6 @@ extern "C" size_t LLVMRustBufferLen(const LLVMRustBuffer *Buffer) { return Buffer->data.length(); } -extern "C" LLVMRustBuffer *LLVMRustModuleSerialize(LLVMModuleRef M) { - auto Ret = std::make_unique(); - { - auto OS = raw_string_ostream(Ret->data); - WriteBitcodeToFile(*unwrap(M), OS); - } - return Ret.release(); -} - extern "C" uint64_t LLVMRustModuleCost(LLVMModuleRef M) { auto f = unwrap(M)->functions(); return std::distance(std::begin(f), std::end(f)); From 474a7168abf4237b14537c59541a64efea2dfa68 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:01:59 +0000 Subject: [PATCH 57/82] Remove explicit EmitThinLTOSummary argument In favor of passing a NULL ThinLTOSummaryBufferRef. And improve type improve type safety on the Rust side. --- compiler/rustc_codegen_llvm/src/back/lto.rs | 7 +------ compiler/rustc_codegen_llvm/src/back/write.rs | 18 +++++++----------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 5 ++--- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 19 ++++++++++++------- 4 files changed, 22 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 3b1a425f7d917..303f8761fe41e 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -2,7 +2,6 @@ use std::collections::BTreeMap; use std::ffi::{CStr, CString}; use std::fs::File; use std::path::{Path, PathBuf}; -use std::ptr::NonNull; use std::sync::Arc; use std::{io, iter, slice}; @@ -660,17 +659,13 @@ pub(crate) fn run_pass_manager( debug!("lto done"); } +#[repr(transparent)] pub(crate) struct Buffer(&'static mut llvm::Buffer); unsafe impl Send for Buffer {} unsafe impl Sync for Buffer {} impl Buffer { - pub(crate) unsafe fn from_raw_ptr(ptr: *mut llvm::Buffer) -> Buffer { - let mut ptr = NonNull::new(ptr).unwrap(); - Buffer(unsafe { ptr.as_mut() }) - } - pub(crate) fn data(&self) -> &[u8] { unsafe { let ptr = llvm::LLVMRustBufferPtr(self.0); diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 9bb6045084d7d..d7ab1356fafe7 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -1,7 +1,6 @@ use std::ffi::{CStr, CString}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; -use std::ptr::null_mut; use std::sync::Arc; use std::{fs, slice, str}; @@ -29,7 +28,7 @@ use rustc_target::spec::{ }; use tracing::{debug, trace}; -use crate::back::lto::ModuleBuffer; +use crate::back::lto::{Buffer, ModuleBuffer}; use crate::back::owned_target_machine::OwnedTargetMachine; use crate::back::profiling::{ LlvmSelfProfiler, selfprofile_after_pass_callback, selfprofile_before_pass_callback, @@ -563,8 +562,8 @@ pub(crate) unsafe fn llvm_optimize( prof: &SelfProfilerRef, dcx: DiagCtxtHandle<'_>, module: &ModuleCodegen, - thin_lto_buffer: Option<&mut *mut llvm::Buffer>, - thin_lto_summary_buffer: Option<&mut *mut llvm::Buffer>, + thin_lto_buffer: Option<&mut Option>, + thin_lto_summary_buffer: Option<&mut Option>, config: &ModuleConfig, opt_level: config::OptLevel, opt_stage: llvm::OptStage, @@ -788,7 +787,6 @@ pub(crate) unsafe fn llvm_optimize( config.lint_llvm_ir, thin_lto_buffer, thin_lto_summary_buffer, - config.emit_thin_lto_summary, merge_functions, unroll_loops, vectorize_slp, @@ -939,7 +937,7 @@ pub(crate) fn optimize( && config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full)) || config.emit_thin_lto_summary { - (Some(null_mut()), Some(null_mut())) + (Some(None), config.emit_thin_lto_summary.then_some(None)) } else { (None, None) }; @@ -958,19 +956,17 @@ pub(crate) fn optimize( ) }; if let Some(thin_lto_buffer) = thin_lto_buffer { - let thin_lto_buffer = - unsafe { crate::back::lto::Buffer::from_raw_ptr(thin_lto_buffer) }; - let thin_lto_summary_buffer = - unsafe { crate::back::lto::Buffer::from_raw_ptr(thin_lto_summary_buffer.unwrap()) }; + let thin_lto_buffer = thin_lto_buffer.unwrap(); module.thin_lto_buffer = Some(thin_lto_buffer.data().to_vec()); let bc_summary_out = cgcx.output_filenames.temp_path_for_cgu( OutputType::ThinLinkBitcode, &module.name, cgcx.invocation_temp.as_deref(), ); - if config.emit_thin_lto_summary + if let Some(thin_lto_summary_buffer) = thin_lto_summary_buffer && let Some(thin_link_bitcode_filename) = bc_summary_out.file_name() { + let thin_lto_summary_buffer = thin_lto_summary_buffer.unwrap(); let summary_data = thin_lto_summary_buffer.data(); prof.artifact_size( "llvm_bitcode_summary", diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index f9fa2e2769965..f9af42494cada 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2372,9 +2372,8 @@ unsafe extern "C" { NoPrepopulatePasses: bool, VerifyIR: bool, LintIR: bool, - ThinLTOBuffer: Option<&mut *mut Buffer>, - ThinLTOSummaryBuffer: Option<&mut *mut Buffer>, - EmitThinLTOSummary: bool, + ThinLTOBuffer: Option<&mut Option>, + ThinLTOSummaryBuffer: Option<&mut Option>, MergeFunctions: bool, UnrollLoops: bool, SLPVectorize: bool, diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 1ee67e952eadc..568335f7dcb51 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -554,9 +554,9 @@ extern "C" LLVMRustResult LLVMRustOptimize( LLVMRustPassBuilderOptLevel OptLevelRust, LLVMRustOptStage OptStage, bool IsLinkerPluginLTO, bool NoPrepopulatePasses, bool VerifyIR, bool LintIR, LLVMRustBuffer **ThinLTOBufferRef, - LLVMRustBuffer **ThinLTOSummaryBufferRef, bool EmitThinLTOSummary, - bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, - bool LoopVectorize, bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers, + LLVMRustBuffer **ThinLTOSummaryBufferRef, bool MergeFunctions, + bool UnrollLoops, bool SLPVectorize, bool LoopVectorize, + bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers, registerEnzymeAndPassPipelineFn EnzymePtr, bool PrintBeforeEnzyme, bool PrintAfterEnzyme, bool PrintPasses, LLVMRustSanitizerOptions *SanitizerOptions, const char *PGOGenPath, @@ -825,9 +825,12 @@ extern "C" LLVMRustResult LLVMRustOptimize( // `ThinLTOPreLinkDefaultPipeline`. MPM.addPass(PB.buildThinLTOPreLinkDefaultPipeline(OptLevel)); MPM.addPass(ThinLTOBitcodeWriterPass( - ThinLTODataOS, EmitThinLTOSummary ? &ThinLinkDataOS : nullptr)); + ThinLTODataOS, + ThinLTOSummaryBufferRef ? &ThinLinkDataOS : nullptr)); *ThinLTOBufferRef = ThinLTOBuffer.release(); - *ThinLTOSummaryBufferRef = ThinLTOSummaryBuffer.release(); + if (ThinLTOSummaryBufferRef) { + *ThinLTOSummaryBufferRef = ThinLTOSummaryBuffer.release(); + } MPM.addPass(PB.buildModuleOptimizationPipeline( OptLevel, ThinOrFullLTOPhase::None)); MPM.addPass( @@ -883,12 +886,14 @@ extern "C" LLVMRustResult LLVMRustOptimize( // lto is requested. See PR #136840 for background information. if (OptStage != LLVMRustOptStage::PreLinkFatLTO) { MPM.addPass(ThinLTOBitcodeWriterPass( - ThinLTODataOS, EmitThinLTOSummary ? &ThinLinkDataOS : nullptr)); + ThinLTODataOS, ThinLTOSummaryBufferRef ? &ThinLinkDataOS : nullptr)); } else { MPM.addPass(BitcodeWriterPass(ThinLTODataOS)); } *ThinLTOBufferRef = ThinLTOBuffer.release(); - *ThinLTOSummaryBufferRef = ThinLTOSummaryBuffer.release(); + if (ThinLTOSummaryBufferRef) { + *ThinLTOSummaryBufferRef = ThinLTOSummaryBuffer.release(); + } } // now load "-enzyme" pass: From ff454c12cdb95752955ea76b8f2f085cfa76e877 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:29:25 +0000 Subject: [PATCH 58/82] Simplify function signatures of serialize_module and prepare_thin --- compiler/rustc_codegen_gcc/src/lib.rs | 4 ++-- compiler/rustc_codegen_llvm/src/back/lto.rs | 6 ------ compiler/rustc_codegen_llvm/src/lib.rs | 8 ++++---- compiler/rustc_codegen_ssa/src/back/write.rs | 12 ++++++------ compiler/rustc_codegen_ssa/src/traits/write.rs | 4 ++-- 5 files changed, 14 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 6517e12ba81ab..3c4daeb51e47d 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -502,11 +502,11 @@ impl WriteBackendMethods for GccCodegenBackend { back::write::codegen(cgcx, prof, shared_emitter, module, config) } - fn prepare_thin(_module: ModuleCodegen) -> (String, Self::ThinBuffer) { + fn prepare_thin(_module: Self::Module) -> Self::ThinBuffer { unreachable!() } - fn serialize_module(_module: ModuleCodegen) -> (String, Self::ModuleBuffer) { + fn serialize_module(_module: Self::Module) -> Self::ModuleBuffer { unimplemented!(); } } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 303f8761fe41e..c235437aee751 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -202,12 +202,6 @@ pub(crate) fn run_thin( thin_lto(cgcx, prof, dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold) } -pub(crate) fn prepare_thin(module: ModuleCodegen) -> (String, ModuleBuffer) { - let name = module.name; - let buffer = ModuleBuffer::new(module.module_llvm.llmod(), true); - (name, buffer) -} - fn fat_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 5e09012d915ba..5325783c3b211 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -233,11 +233,11 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) -> CompiledModule { back::write::codegen(cgcx, prof, shared_emitter, module, config) } - fn prepare_thin(module: ModuleCodegen) -> (String, Self::ThinBuffer) { - back::lto::prepare_thin(module) + fn prepare_thin(module: Self::Module) -> Self::ThinBuffer { + back::lto::ModuleBuffer::new(module.llmod(), true) } - fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer) { - (module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod(), false)) + fn serialize_module(module: Self::Module) -> Self::ModuleBuffer { + back::lto::ModuleBuffer::new(module.llmod(), false) } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 81d173af275f9..81c45f12de7b0 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -868,22 +868,22 @@ fn execute_optimize_work_item( WorkItemResult::Finished(module) } ComputedLtoType::Thin => { - let (name, thin_buffer) = B::prepare_thin(module); + let thin_buffer = B::prepare_thin(module.module_llvm); if let Some(path) = bitcode { fs::write(&path, thin_buffer.data()).unwrap_or_else(|e| { panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); }); } - WorkItemResult::NeedsThinLto(name, thin_buffer) + WorkItemResult::NeedsThinLto(module.name, thin_buffer) } ComputedLtoType::Fat => match bitcode { Some(path) => { - let (name, buffer) = B::serialize_module(module); + let buffer = B::serialize_module(module.module_llvm); fs::write(&path, buffer.data()).unwrap_or_else(|e| { panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); }); WorkItemResult::NeedsFatLto(FatLtoInput::Serialized { - name, + name: module.name, buffer: SerializedModule::Local(buffer), }) } @@ -1804,8 +1804,8 @@ fn start_executing_work( )); } else { if let Some(allocator_module) = allocator_module.take() { - let (name, thin_buffer) = B::prepare_thin(allocator_module); - needs_thin_lto.push((name, thin_buffer)); + let thin_buffer = B::prepare_thin(allocator_module.module_llvm); + needs_thin_lto.push((allocator_module.name, thin_buffer)); } return Ok(MaybeLtoModules::ThinLto { diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 1638327aa390e..b0c065642f39e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -63,8 +63,8 @@ pub trait WriteBackendMethods: Clone + 'static { module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule; - fn prepare_thin(module: ModuleCodegen) -> (String, Self::ThinBuffer); - fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer); + fn prepare_thin(module: Self::Module) -> Self::ThinBuffer; + fn serialize_module(module: Self::Module) -> Self::ModuleBuffer; } pub trait ModuleBufferMethods: Send + Sync { From 3decb5275626e355c8c1d71b4ad5832d8fbb80e6 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 13 Feb 2026 12:53:59 +0000 Subject: [PATCH 59/82] Remove last remaining ModuleBuffer/ThinBuffer duplication --- compiler/rustc_codegen_gcc/src/lib.rs | 21 +++---------------- compiler/rustc_codegen_llvm/src/lib.rs | 10 +++------ compiler/rustc_codegen_ssa/src/back/lto.rs | 2 +- compiler/rustc_codegen_ssa/src/back/write.rs | 14 ++++++------- .../rustc_codegen_ssa/src/traits/write.rs | 6 ++---- 5 files changed, 16 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 3c4daeb51e47d..7fd9c28c43f2b 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -86,9 +86,7 @@ use rustc_codegen_ssa::back::write::{ }; use rustc_codegen_ssa::base::codegen_crate; use rustc_codegen_ssa::target_features::cfg_target_feature; -use rustc_codegen_ssa::traits::{ - CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, WriteBackendMethods, -}; +use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods}; use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::profiling::SelfProfilerRef; @@ -423,20 +421,11 @@ unsafe impl Send for SyncContext {} // FIXME(antoyo): that shouldn't be Sync. Parallel compilation is currently disabled with "CodegenBackend::supports_parallel()". unsafe impl Sync for SyncContext {} -pub struct ThinBuffer; - -impl ModuleBufferMethods for ThinBuffer { - fn data(&self) -> &[u8] { - &[] - } -} - impl WriteBackendMethods for GccCodegenBackend { type Module = GccContext; type TargetMachine = (); type ModuleBuffer = ModuleBuffer; type ThinData = (); - type ThinBuffer = ThinBuffer; fn run_and_optimize_fat_lto( cgcx: &CodegenContext, @@ -458,7 +447,7 @@ impl WriteBackendMethods for GccCodegenBackend { // FIXME(bjorn3): Limit LTO exports to these symbols _exported_symbols_for_lto: &[String], _each_linked_rlib_for_lto: &[PathBuf], - _modules: Vec<(String, Self::ThinBuffer)>, + _modules: Vec<(String, Self::ModuleBuffer)>, _cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> (Vec>, Vec) { unreachable!() @@ -502,11 +491,7 @@ impl WriteBackendMethods for GccCodegenBackend { back::write::codegen(cgcx, prof, shared_emitter, module, config) } - fn prepare_thin(_module: Self::Module) -> Self::ThinBuffer { - unreachable!() - } - - fn serialize_module(_module: Self::Module) -> Self::ModuleBuffer { + fn serialize_module(_module: Self::Module, _is_thin: bool) -> Self::ModuleBuffer { unimplemented!(); } } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 5325783c3b211..7120ee0afec96 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -153,7 +153,6 @@ impl WriteBackendMethods for LlvmCodegenBackend { type ModuleBuffer = back::lto::ModuleBuffer; type TargetMachine = OwnedTargetMachine; type ThinData = back::lto::ThinData; - type ThinBuffer = back::lto::ModuleBuffer; fn print_pass_timings(&self) { let timings = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintPassTimings(s) }).unwrap(); print!("{timings}"); @@ -193,7 +192,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], - modules: Vec<(String, Self::ThinBuffer)>, + modules: Vec<(String, Self::ModuleBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> (Vec>, Vec) { back::lto::run_thin( @@ -233,11 +232,8 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) -> CompiledModule { back::write::codegen(cgcx, prof, shared_emitter, module, config) } - fn prepare_thin(module: Self::Module) -> Self::ThinBuffer { - back::lto::ModuleBuffer::new(module.llmod(), true) - } - fn serialize_module(module: Self::Module) -> Self::ModuleBuffer { - back::lto::ModuleBuffer::new(module.llmod(), false) + fn serialize_module(module: Self::Module, is_thin: bool) -> Self::ModuleBuffer { + back::lto::ModuleBuffer::new(module.llmod(), is_thin) } } diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index 80b3b5a4d7c06..a2c951c16d28d 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -42,7 +42,7 @@ impl ThinModule { pub struct ThinShared { pub data: B::ThinData, - pub thin_buffers: Vec, + pub thin_buffers: Vec, pub serialized_modules: Vec>, pub module_names: Vec, } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 81c45f12de7b0..9898c1296142a 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -362,7 +362,7 @@ fn generate_thin_lto_work( dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], - needs_thin_lto: Vec<(String, B::ThinBuffer)>, + needs_thin_lto: Vec<(String, B::ModuleBuffer)>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Vec<(ThinLtoWorkItem, u64)> { let _prof_timer = prof.generic_activity("codegen_thin_generate_lto_work"); @@ -416,7 +416,7 @@ enum MaybeLtoModules { cgcx: CodegenContext, exported_symbols_for_lto: Arc>, each_linked_rlib_file_for_lto: Vec, - needs_thin_lto: Vec<(String, ::ThinBuffer)>, + needs_thin_lto: Vec<(String, ::ModuleBuffer)>, lto_import_only_modules: Vec<(SerializedModule<::ModuleBuffer>, WorkProduct)>, }, @@ -793,7 +793,7 @@ pub(crate) enum WorkItemResult { /// The backend has finished compiling a CGU, which now needs to go through /// thin LTO. - NeedsThinLto(String, B::ThinBuffer), + NeedsThinLto(String, B::ModuleBuffer), } pub enum FatLtoInput { @@ -868,7 +868,7 @@ fn execute_optimize_work_item( WorkItemResult::Finished(module) } ComputedLtoType::Thin => { - let thin_buffer = B::prepare_thin(module.module_llvm); + let thin_buffer = B::serialize_module(module.module_llvm, true); if let Some(path) = bitcode { fs::write(&path, thin_buffer.data()).unwrap_or_else(|e| { panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); @@ -878,7 +878,7 @@ fn execute_optimize_work_item( } ComputedLtoType::Fat => match bitcode { Some(path) => { - let buffer = B::serialize_module(module.module_llvm); + let buffer = B::serialize_module(module.module_llvm, false); fs::write(&path, buffer.data()).unwrap_or_else(|e| { panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); }); @@ -1020,7 +1020,7 @@ fn do_thin_lto( tm_factory: TargetMachineFactoryFn, exported_symbols_for_lto: Arc>, each_linked_rlib_for_lto: Vec, - needs_thin_lto: Vec<(String, ::ThinBuffer)>, + needs_thin_lto: Vec<(String, ::ModuleBuffer)>, lto_import_only_modules: Vec<( SerializedModule<::ModuleBuffer>, WorkProduct, @@ -1804,7 +1804,7 @@ fn start_executing_work( )); } else { if let Some(allocator_module) = allocator_module.take() { - let thin_buffer = B::prepare_thin(allocator_module.module_llvm); + let thin_buffer = B::serialize_module(allocator_module.module_llvm, true); needs_thin_lto.push((allocator_module.name, thin_buffer)); } diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index b0c065642f39e..d33dfc1d014b5 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -15,7 +15,6 @@ pub trait WriteBackendMethods: Clone + 'static { type TargetMachine; type ModuleBuffer: ModuleBufferMethods; type ThinData: Send + Sync; - type ThinBuffer: ModuleBufferMethods; /// Performs fat LTO by merging all modules into a single one, running autodiff /// if necessary and running any further optimizations @@ -37,7 +36,7 @@ pub trait WriteBackendMethods: Clone + 'static { dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], - modules: Vec<(String, Self::ThinBuffer)>, + modules: Vec<(String, Self::ModuleBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> (Vec>, Vec); fn print_pass_timings(&self); @@ -63,8 +62,7 @@ pub trait WriteBackendMethods: Clone + 'static { module: ModuleCodegen, config: &ModuleConfig, ) -> CompiledModule; - fn prepare_thin(module: Self::Module) -> Self::ThinBuffer; - fn serialize_module(module: Self::Module) -> Self::ModuleBuffer; + fn serialize_module(module: Self::Module, is_thin: bool) -> Self::ModuleBuffer; } pub trait ModuleBufferMethods: Send + Sync { From dbf337f5022d3cc8344717eb7018953c1840ff07 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 22 Feb 2026 16:58:09 +0200 Subject: [PATCH 60/82] Fix another case where we forgot to put the type param for `PartialOrd` and `PartialEq` in builtin derives --- .../crates/hir-ty/src/builtin_derive.rs | 82 ++++++++++++------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs index b7a1585c808b2..5a93c2b536064 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs @@ -35,6 +35,24 @@ fn coerce_pointee_new_type_param(trait_id: TraitId) -> TypeParamId { }) } +fn trait_args(trait_: BuiltinDeriveImplTrait, self_ty: Ty<'_>) -> GenericArgs<'_> { + match trait_ { + BuiltinDeriveImplTrait::Copy + | BuiltinDeriveImplTrait::Clone + | BuiltinDeriveImplTrait::Default + | BuiltinDeriveImplTrait::Debug + | BuiltinDeriveImplTrait::Hash + | BuiltinDeriveImplTrait::Eq + | BuiltinDeriveImplTrait::Ord => GenericArgs::new_from_slice(&[self_ty.into()]), + BuiltinDeriveImplTrait::PartialOrd | BuiltinDeriveImplTrait::PartialEq => { + GenericArgs::new_from_slice(&[self_ty.into(), self_ty.into()]) + } + BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => { + panic!("`CoerceUnsized` and `DispatchFromDyn` have special generics") + } + } +} + pub(crate) fn generics_of<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> Generics { let db = interner.db; let loc = id.loc(db); @@ -95,21 +113,19 @@ pub fn impl_trait<'db>( | BuiltinDeriveImplTrait::Debug | BuiltinDeriveImplTrait::Hash | BuiltinDeriveImplTrait::Ord - | BuiltinDeriveImplTrait::Eq => { + | BuiltinDeriveImplTrait::Eq + | BuiltinDeriveImplTrait::PartialOrd + | BuiltinDeriveImplTrait::PartialEq => { let self_ty = Ty::new_adt( interner, loc.adt, GenericArgs::identity_for_item(interner, loc.adt.into()), ); - EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty])) - } - BuiltinDeriveImplTrait::PartialOrd | BuiltinDeriveImplTrait::PartialEq => { - let self_ty = Ty::new_adt( + EarlyBinder::bind(TraitRef::new_from_args( interner, - loc.adt, - GenericArgs::identity_for_item(interner, loc.adt.into()), - ); - EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty, self_ty])) + trait_id.into(), + trait_args(loc.trait_, self_ty), + )) } BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => { let generic_params = GenericParams::new(db, loc.adt.into()); @@ -260,21 +276,7 @@ fn simple_trait_predicates<'db>( let param_idx = param_idx.into_raw().into_u32() + (generic_params.len_lifetimes() as u32); let param_ty = Ty::new_param(interner, param_id, param_idx); - let trait_args = match loc.trait_ { - BuiltinDeriveImplTrait::Copy - | BuiltinDeriveImplTrait::Clone - | BuiltinDeriveImplTrait::Default - | BuiltinDeriveImplTrait::Debug - | BuiltinDeriveImplTrait::Hash - | BuiltinDeriveImplTrait::Ord - | BuiltinDeriveImplTrait::Eq => GenericArgs::new_from_slice(&[param_ty.into()]), - BuiltinDeriveImplTrait::PartialOrd | BuiltinDeriveImplTrait::PartialEq => { - GenericArgs::new_from_slice(&[param_ty.into(), param_ty.into()]) - } - BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => { - unreachable!() - } - }; + let trait_args = trait_args(loc.trait_, param_ty); let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), trait_args); trait_ref.upcast(interner) }); @@ -285,12 +287,14 @@ fn simple_trait_predicates<'db>( &mut assoc_type_bounds, interner.db.field_types(id.into()), trait_id, + loc.trait_, ), AdtId::UnionId(id) => extend_assoc_type_bounds( interner, &mut assoc_type_bounds, interner.db.field_types(id.into()), trait_id, + loc.trait_, ), AdtId::EnumId(id) => { for &(variant_id, _, _) in &id.enum_variants(interner.db).variants { @@ -299,6 +303,7 @@ fn simple_trait_predicates<'db>( &mut assoc_type_bounds, interner.db.field_types(variant_id.into()), trait_id, + loc.trait_, ) } } @@ -320,12 +325,14 @@ fn extend_assoc_type_bounds<'db>( interner: DbInterner<'db>, assoc_type_bounds: &mut Vec>, fields: &ArenaMap>, - trait_: TraitId, + trait_id: TraitId, + trait_: BuiltinDeriveImplTrait, ) { struct ProjectionFinder<'a, 'db> { interner: DbInterner<'db>, assoc_type_bounds: &'a mut Vec>, - trait_: TraitId, + trait_id: TraitId, + trait_: BuiltinDeriveImplTrait, } impl<'db> TypeVisitor> for ProjectionFinder<'_, 'db> { @@ -334,7 +341,12 @@ fn extend_assoc_type_bounds<'db>( fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result { if let TyKind::Alias(AliasTyKind::Projection, _) = t.kind() { self.assoc_type_bounds.push( - TraitRef::new(self.interner, self.trait_.into(), [t]).upcast(self.interner), + TraitRef::new_from_args( + self.interner, + self.trait_id.into(), + trait_args(self.trait_, t), + ) + .upcast(self.interner), ); } @@ -342,7 +354,7 @@ fn extend_assoc_type_bounds<'db>( } } - let mut visitor = ProjectionFinder { interner, assoc_type_bounds, trait_ }; + let mut visitor = ProjectionFinder { interner, assoc_type_bounds, trait_id, trait_ }; for (_, field) in fields.iter() { field.get().instantiate_identity().visit_with(&mut visitor); } @@ -503,10 +515,12 @@ struct MultiGenericParams<'a, T, #[pointee] U: ?Sized, const N: usize>(*const U) #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] struct Simple; -trait Trait {} +trait Trait { + type Assoc; +} #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N]); +struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N], T::Assoc); "#, expect![[r#" @@ -529,41 +543,49 @@ struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N]); Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Debug, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Debug, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Clone, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Clone, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Copy, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Copy, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: PartialEq<[#1]>, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): PartialEq<[Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. })]>, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Eq, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Eq, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: PartialOrd<[#1]>, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): PartialOrd<[Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. })]>, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Ord, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Ord, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] }) Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] }) Clause(Binder { value: TraitPredicate(#1: Hash, polarity:Positive), bound_vars: [] }) + Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Hash, polarity:Positive), bound_vars: [] }) "#]], ); From d5311adf87d1522a48c1bd334319c0be86fab397 Mon Sep 17 00:00:00 2001 From: arferreira Date: Sun, 22 Feb 2026 11:13:54 -0500 Subject: [PATCH 61/82] Revert relative paths for std links in rustc-docs --- src/bootstrap/src/core/build_steps/doc.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 325f54d78a505..a918ae929d2e0 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -953,13 +953,6 @@ impl Step for Rustc { cargo.rustdocflag("--extern-html-root-url"); cargo.rustdocflag("ena=https://docs.rs/ena/latest/"); - // Point std library crate links to local docs for offline usage. - for krate in STD_PUBLIC_CRATES { - cargo.rustdocflag("--extern-html-root-url"); - cargo.rustdocflag(&format!("{krate}=../")); - } - cargo.rustdocflag("--extern-html-root-takes-precedence"); - let mut to_open = None; let out_dir = builder.stage_out(build_compiler, Mode::Rustc).join(target).join("doc"); From 2a58411a87a07f8fedf871a49440f8e9dcc9168a Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 22 Feb 2026 19:26:41 +0100 Subject: [PATCH 62/82] Port `#[feature]` to the new attribute parsing infrastructure Co-authored-by: jdonszelmann --- Cargo.lock | 1 + .../src/attributes/crate_level.rs | 46 ++++++ compiler/rustc_attr_parsing/src/context.rs | 1 + .../src/error_codes/E0556.md | 4 +- compiler/rustc_error_codes/src/lib.rs | 2 +- .../rustc_hir/src/attrs/data_structures.rs | 3 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 138 +++++++++--------- 8 files changed, 129 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cfed18eee113c..f0917ce4df51a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3515,6 +3515,7 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_feature", + "rustc_hir", "rustc_macros", "rustc_session", "rustc_span", diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 3b3bbf4f0c8af..176af5cdd192e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -301,3 +301,49 @@ impl NoArgsAttributeParser for DefaultLibAllocatorParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::DefaultLibAllocator; } + +pub(crate) struct FeatureParser; + +impl CombineAttributeParser for FeatureParser { + const PATH: &[Symbol] = &[sym::feature]; + type Item = Ident; + const CONVERT: ConvertFn = AttributeKind::Feature; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const TEMPLATE: AttributeTemplate = template!(List: &["feature1, feature2, ..."]); + + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { + let ArgParser::List(list) = args else { + cx.expected_list(cx.attr_span, args); + return Vec::new(); + }; + + if list.is_empty() { + cx.warn_empty_attribute(cx.attr_span); + } + + let mut res = Vec::new(); + + for elem in list.mixed() { + let Some(elem) = elem.meta_item() else { + cx.expected_identifier(elem.span()); + continue; + }; + if let Err(arg_span) = elem.args().no_args() { + cx.expected_no_args(arg_span); + continue; + } + + let path = elem.path(); + let Some(ident) = path.word() else { + cx.expected_identifier(path.span()); + continue; + }; + res.push(ident); + } + + res + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b4e91ecebeeb7..7212abbf81243 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -157,6 +157,7 @@ attribute_parsers!( Combine, Combine, Combine, + Combine, Combine, Combine, Combine, diff --git a/compiler/rustc_error_codes/src/error_codes/E0556.md b/compiler/rustc_error_codes/src/error_codes/E0556.md index 2aac8240d293a..d1eeddc3ab102 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0556.md +++ b/compiler/rustc_error_codes/src/error_codes/E0556.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + The `feature` attribute was badly formed. Erroneous code example: -```compile_fail,E0556 +```compile_fail #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)] // error! #![feature] // error! #![feature = "foo"] // error! diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index 0537ba23de944..eff9b9d323c25 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -334,7 +334,7 @@ macro_rules! error_codes { 0551, 0552, 0554, -0556, +0556, // REMOVED: merged with other attribute error codes 0557, 0559, 0560, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 5d154cef66a65..820c43bcd81dc 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -954,6 +954,9 @@ pub enum AttributeKind { /// Represents `#[export_stable]`. ExportStable, + /// Represents `#[feature(...)]` + Feature(ThinVec, Span), + /// Represents `#[ffi_const]`. FfiConst(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 0b20ea4d6a83a..f0684818417f5 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -45,6 +45,7 @@ impl AttributeKind { EiiImpls(..) => No, ExportName { .. } => Yes, ExportStable => No, + Feature(..) => No, FfiConst(..) => No, FfiPure(..) => No, Fundamental { .. } => Yes, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 9af9398d78b96..40f4694b5cce6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -253,6 +253,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::EiiForeignItem | AttributeKind::ExportName { .. } | AttributeKind::ExportStable + | AttributeKind::Feature(..) | AttributeKind::FfiConst(..) | AttributeKind::Fundamental | AttributeKind::Ignore { .. } @@ -1564,75 +1565,82 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option) { // Warn on useless empty attributes. // FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute` - let note = if attr.has_any_name(&[ - sym::allow, - sym::expect, - sym::warn, - sym::deny, - sym::forbid, - sym::feature, - ]) && attr.meta_item_list().is_some_and(|list| list.is_empty()) - { - errors::UnusedNote::EmptyList { name: attr.name().unwrap() } - } else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect]) - && let Some(meta) = attr.meta_item_list() - && let [meta] = meta.as_slice() - && let Some(item) = meta.meta_item() - && let MetaItemKind::NameValue(_) = &item.kind - && item.path == sym::reason - { - errors::UnusedNote::NoLints { name: attr.name().unwrap() } - } else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect]) - && let Some(meta) = attr.meta_item_list() - && meta.iter().any(|meta| { - meta.meta_item().map_or(false, |item| item.path == sym::linker_messages) - }) - { - if hir_id != CRATE_HIR_ID { - match style { - Some(ast::AttrStyle::Outer) => { - let attr_span = attr.span(); - let bang_position = self - .tcx - .sess - .source_map() - .span_until_char(attr_span, '[') - .shrink_to_hi(); - - self.tcx.emit_node_span_lint( + let note = + if attr.has_any_name(&[sym::allow, sym::expect, sym::warn, sym::deny, sym::forbid]) + && attr.meta_item_list().is_some_and(|list| list.is_empty()) + { + errors::UnusedNote::EmptyList { name: attr.name().unwrap() } + } else if attr.has_any_name(&[ + sym::allow, + sym::warn, + sym::deny, + sym::forbid, + sym::expect, + ]) && let Some(meta) = attr.meta_item_list() + && let [meta] = meta.as_slice() + && let Some(item) = meta.meta_item() + && let MetaItemKind::NameValue(_) = &item.kind + && item.path == sym::reason + { + errors::UnusedNote::NoLints { name: attr.name().unwrap() } + } else if attr.has_any_name(&[ + sym::allow, + sym::warn, + sym::deny, + sym::forbid, + sym::expect, + ]) && let Some(meta) = attr.meta_item_list() + && meta.iter().any(|meta| { + meta.meta_item().map_or(false, |item| item.path == sym::linker_messages) + }) + { + if hir_id != CRATE_HIR_ID { + match style { + Some(ast::AttrStyle::Outer) => { + let attr_span = attr.span(); + let bang_position = self + .tcx + .sess + .source_map() + .span_until_char(attr_span, '[') + .shrink_to_hi(); + + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr_span, + errors::OuterCrateLevelAttr { + suggestion: errors::OuterCrateLevelAttrSuggestion { + bang_position, + }, + }, + ) + } + Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr_span, - errors::OuterCrateLevelAttr { - suggestion: errors::OuterCrateLevelAttrSuggestion { bang_position }, - }, - ) - } - Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr.span(), - errors::InnerCrateLevelAttr, - ), - }; - return; - } else { - let never_needs_link = self - .tcx - .crate_types() - .iter() - .all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib)); - if never_needs_link { - errors::UnusedNote::LinkerMessagesBinaryCrateOnly - } else { + attr.span(), + errors::InnerCrateLevelAttr, + ), + }; return; + } else { + let never_needs_link = self + .tcx + .crate_types() + .iter() + .all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib)); + if never_needs_link { + errors::UnusedNote::LinkerMessagesBinaryCrateOnly + } else { + return; + } } - } - } else if attr.has_name(sym::default_method_body_is_const) { - errors::UnusedNote::DefaultMethodBodyConst - } else { - return; - }; + } else if attr.has_name(sym::default_method_body_is_const) { + errors::UnusedNote::DefaultMethodBodyConst + } else { + return; + }; self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, From cf9ab2dd9f5797c599186ea014a08128f433748d Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 22 Feb 2026 19:26:57 +0100 Subject: [PATCH 63/82] Use feature parser throughout the compiler Co-authored-by: jdonszelmann --- compiler/rustc_ast_passes/Cargo.toml | 1 + compiler/rustc_ast_passes/src/feature_gate.rs | 23 ++++- compiler/rustc_expand/src/config.rs | 98 ++++++++----------- compiler/rustc_expand/src/errors.rs | 28 ------ compiler/rustc_lint/src/builtin.rs | 10 +- 5 files changed, 66 insertions(+), 94 deletions(-) diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index fdc735fa8d4ff..c9def6246d1b1 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -13,6 +13,7 @@ rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } +rustc_hir = { path = "../rustc_hir" } rustc_macros = { path = "../rustc_macros" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 891570b053e53..8dfec76b22ef8 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,11 +1,14 @@ use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::{self as ast, AttrVec, NodeId, PatKind, attr, token}; +use rustc_attr_parsing::AttributeParser; use rustc_errors::msg; use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features}; +use rustc_hir::Attribute; +use rustc_hir::attrs::AttributeKind; use rustc_session::Session; use rustc_session::parse::{feature_err, feature_warn}; use rustc_span::source_map::Spanned; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use thin_vec::ThinVec; use crate::errors; @@ -636,17 +639,27 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) return; } let mut errored = false; - for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) { + + if let Some(Attribute::Parsed(AttributeKind::Feature(feature_idents, first_span))) = + AttributeParser::parse_limited( + sess, + &krate.attrs, + sym::feature, + DUMMY_SP, + krate.id, + Some(&features), + ) + { // `feature(...)` used on non-nightly. This is definitely an error. let mut err = errors::FeatureOnNonNightly { - span: attr.span, + span: first_span, channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"), stable_features: vec![], sugg: None, }; let mut all_stable = true; - for ident in attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident()) { + for ident in feature_idents { let name = ident.name; let stable_since = features .enabled_lang_features() @@ -661,7 +674,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) } } if all_stable { - err.sugg = Some(attr.span); + err.sugg = Some(first_span); } sess.dcx().emit_err(err); errored = true; diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index a8a305310238e..8e4039b32d942 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -7,8 +7,8 @@ use rustc_ast::tokenstream::{ AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree, }; use rustc_ast::{ - self as ast, AttrItemKind, AttrKind, AttrStyle, Attribute, EarlyParsedAttribute, HasAttrs, - HasTokens, MetaItem, MetaItemInner, NodeId, NormalAttr, + self as ast, AttrItemKind, AttrKind, AttrStyle, Attribute, DUMMY_NODE_ID, EarlyParsedAttribute, + HasAttrs, HasTokens, MetaItem, MetaItemInner, NodeId, NormalAttr, }; use rustc_attr_parsing as attr; use rustc_attr_parsing::{ @@ -20,18 +20,19 @@ use rustc_feature::{ ACCEPTED_LANG_FEATURES, EnabledLangFeature, EnabledLibFeature, Features, REMOVED_LANG_FEATURES, UNSTABLE_LANG_FEATURES, }; -use rustc_hir::Target; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{ + Target, {self as hir}, +}; use rustc_parse::parser::Recovery; use rustc_session::Session; use rustc_session::parse::feature_err; -use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym}; -use thin_vec::ThinVec; +use rustc_span::{DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym}; use tracing::instrument; use crate::errors::{ CrateNameInCfgAttr, CrateTypeInCfgAttr, FeatureNotAllowed, FeatureRemoved, - FeatureRemovedReason, InvalidCfg, MalformedFeatureAttribute, MalformedFeatureAttributeHelp, - RemoveExprNotSupported, + FeatureRemovedReason, InvalidCfg, RemoveExprNotSupported, }; /// A folder that strips out items that do not belong in the current configuration. @@ -46,44 +47,23 @@ pub struct StripUnconfigured<'a> { } pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -> Features { - fn feature_list(attr: &Attribute) -> ThinVec { - if attr.has_name(sym::feature) - && let Some(list) = attr.meta_item_list() - { - list - } else { - ThinVec::new() - } - } - let mut features = Features::default(); - // Process all features enabled in the code. - for attr in krate_attrs { - for mi in feature_list(attr) { - let name = match mi.ident() { - Some(ident) if mi.is_word() => ident.name, - Some(ident) => { - sess.dcx().emit_err(MalformedFeatureAttribute { - span: mi.span(), - help: MalformedFeatureAttributeHelp::Suggestion { - span: mi.span(), - suggestion: ident.name, - }, - }); - continue; - } - None => { - sess.dcx().emit_err(MalformedFeatureAttribute { - span: mi.span(), - help: MalformedFeatureAttributeHelp::Label { span: mi.span() }, - }); - continue; - } - }; - + if let Some(hir::Attribute::Parsed(AttributeKind::Feature(feature_idents, _))) = + AttributeParser::parse_limited( + sess, + krate_attrs, + sym::feature, + DUMMY_SP, + DUMMY_NODE_ID, + Some(&features), + ) + { + for feature_ident in feature_idents { // If the enabled feature has been removed, issue an error. - if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| name == f.feature.name) { + if let Some(f) = + REMOVED_LANG_FEATURES.iter().find(|f| feature_ident.name == f.feature.name) + { let pull_note = if let Some(pull) = f.pull { format!( "; see for more information", @@ -92,7 +72,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - "".to_owned() }; sess.dcx().emit_err(FeatureRemoved { - span: mi.span(), + span: feature_ident.span, reason: f.reason.map(|reason| FeatureRemovedReason { reason }), removed_rustc_version: f.feature.since, pull_note, @@ -101,10 +81,10 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - } // If the enabled feature is stable, record it. - if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| name == f.name) { + if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| feature_ident.name == f.name) { features.set_enabled_lang_feature(EnabledLangFeature { - gate_name: name, - attr_sp: mi.span(), + gate_name: feature_ident.name, + attr_sp: feature_ident.span, stable_since: Some(Symbol::intern(f.since)), }); continue; @@ -114,25 +94,30 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - // unstable and not also listed as one of the allowed features, // issue an error. if let Some(allowed) = sess.opts.unstable_opts.allow_features.as_ref() { - if allowed.iter().all(|f| name.as_str() != f) { - sess.dcx().emit_err(FeatureNotAllowed { span: mi.span(), name }); + if allowed.iter().all(|f| feature_ident.name.as_str() != f) { + sess.dcx().emit_err(FeatureNotAllowed { + span: feature_ident.span, + name: feature_ident.name, + }); continue; } } // If the enabled feature is unstable, record it. - if UNSTABLE_LANG_FEATURES.iter().find(|f| name == f.name).is_some() { + if UNSTABLE_LANG_FEATURES.iter().find(|f| feature_ident.name == f.name).is_some() { // When the ICE comes from a standard library crate, there's a chance that the person // hitting the ICE may be using -Zbuild-std or similar with an untested target. // The bug is probably in the standard library and not the compiler in that case, // but that doesn't really matter - we want a bug report. - if features.internal(name) && !STDLIB_STABLE_CRATES.contains(&crate_name) { + if features.internal(feature_ident.name) + && !STDLIB_STABLE_CRATES.contains(&crate_name) + { sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed); } features.set_enabled_lang_feature(EnabledLangFeature { - gate_name: name, - attr_sp: mi.span(), + gate_name: feature_ident.name, + attr_sp: feature_ident.span, stable_since: None, }); continue; @@ -140,12 +125,15 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - // Otherwise, the feature is unknown. Enable it as a lib feature. // It will be checked later whether the feature really exists. - features - .set_enabled_lib_feature(EnabledLibFeature { gate_name: name, attr_sp: mi.span() }); + features.set_enabled_lib_feature(EnabledLibFeature { + gate_name: feature_ident.name, + attr_sp: feature_ident.span, + }); // Similar to above, detect internal lib features to suppress // the ICE message that asks for a report. - if features.internal(name) && !STDLIB_STABLE_CRATES.contains(&crate_name) { + if features.internal(feature_ident.name) && !STDLIB_STABLE_CRATES.contains(&crate_name) + { sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed); } } diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 70e386ceb95ec..8285733c68aa7 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -183,34 +183,6 @@ pub(crate) struct RecursionLimitReached { pub crate_name: Symbol, } -#[derive(Diagnostic)] -#[diag("malformed `feature` attribute input", code = E0556)] -pub(crate) struct MalformedFeatureAttribute { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub help: MalformedFeatureAttributeHelp, -} - -#[derive(Subdiagnostic)] -pub(crate) enum MalformedFeatureAttributeHelp { - #[label("expected just one word")] - Label { - #[primary_span] - span: Span, - }, - #[suggestion( - "expected just one word", - code = "{suggestion}", - applicability = "maybe-incorrect" - )] - Suggestion { - #[primary_span] - span: Span, - suggestion: Symbol, - }, -} - #[derive(Diagnostic)] #[diag("removing an expression is not supported in this position")] pub(crate) struct RemoveExprNotSupported { diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index c83de3b38eb5b..22636959f0db5 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1120,12 +1120,10 @@ declare_lint_pass!( ); impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { - fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &hir::Attribute) { - if attr.has_name(sym::feature) - && let Some(items) = attr.meta_item_list() - { - for item in items { - cx.emit_span_lint(UNSTABLE_FEATURES, item.span(), BuiltinUnstableFeatures); + fn check_attributes(&mut self, cx: &LateContext<'_>, attrs: &[hir::Attribute]) { + if let Some(features) = find_attr!(attrs, Feature(features, _) => features) { + for feature in features { + cx.emit_span_lint(UNSTABLE_FEATURES, feature.span, BuiltinUnstableFeatures); } } } From 0b5290123d66199d3241dbba22044eb49ce2cf28 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 22 Feb 2026 19:27:05 +0100 Subject: [PATCH 64/82] Update uitests --- tests/pretty/delegation-inherit-attributes.pp | 2 +- tests/pretty/delegation-inline-attribute.pp | 2 +- tests/pretty/hir-delegation.pp | 2 +- tests/pretty/hir-fn-variadic.pp | 8 +- tests/pretty/pin-ergonomics-hir.pp | 2 +- tests/ui/attributes/attr-mix-new.rs | 2 +- tests/ui/attributes/attr-mix-new.stderr | 2 +- tests/ui/consts/const-block-items/hir.stdout | 8 +- tests/ui/empty/empty-attributes.stderr | 16 +-- .../feature-gate-feature-gate.rs | 2 +- .../ui/feature-gates/gated-bad-feature.stderr | 48 ++++--- .../issue-43106-gating-of-builtin-attrs.rs | 12 +- ...issue-43106-gating-of-builtin-attrs.stderr | 131 ++++++++++-------- tests/ui/parser/inner-attr.stderr | 11 +- .../type-alias-impl-trait/issue-60662.stdout | 8 +- tests/ui/unpretty/exhaustive.hir.stdout | 27 +--- ...ct-exprs-tuple-call-pretty-printing.stdout | 2 +- 17 files changed, 146 insertions(+), 139 deletions(-) diff --git a/tests/pretty/delegation-inherit-attributes.pp b/tests/pretty/delegation-inherit-attributes.pp index 26ca5e99b885d..bfe5ba03c37d4 100644 --- a/tests/pretty/delegation-inherit-attributes.pp +++ b/tests/pretty/delegation-inherit-attributes.pp @@ -5,7 +5,7 @@ //@ pp-exact:delegation-inherit-attributes.pp #![allow(incomplete_features)] -#![feature(fn_delegation)] +#![attr = Feature([fn_delegation#0])] extern crate std; #[attr = PreludeImport] use std::prelude::rust_2021::*; diff --git a/tests/pretty/delegation-inline-attribute.pp b/tests/pretty/delegation-inline-attribute.pp index 9f362fa863f82..54442a210d937 100644 --- a/tests/pretty/delegation-inline-attribute.pp +++ b/tests/pretty/delegation-inline-attribute.pp @@ -3,7 +3,7 @@ //@ pp-exact:delegation-inline-attribute.pp #![allow(incomplete_features)] -#![feature(fn_delegation)] +#![attr = Feature([fn_delegation#0])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; diff --git a/tests/pretty/hir-delegation.pp b/tests/pretty/hir-delegation.pp index 59491b6ebd7c1..efa643313e6b4 100644 --- a/tests/pretty/hir-delegation.pp +++ b/tests/pretty/hir-delegation.pp @@ -3,7 +3,7 @@ //@ pp-exact:hir-delegation.pp #![allow(incomplete_features)] -#![feature(fn_delegation)] +#![attr = Feature([fn_delegation#0])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; diff --git a/tests/pretty/hir-fn-variadic.pp b/tests/pretty/hir-fn-variadic.pp index 3837b260cc5d6..30423349241b7 100644 --- a/tests/pretty/hir-fn-variadic.pp +++ b/tests/pretty/hir-fn-variadic.pp @@ -1,11 +1,11 @@ +#![attr = Feature([c_variadic#0])] +extern crate std; +#[attr = PreludeImport] +use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:hir //@ pp-exact:hir-fn-variadic.pp -#![feature(c_variadic)] -extern crate std; -#[attr = PreludeImport] -use ::std::prelude::rust_2015::*; extern "C" { unsafe fn foo(x: i32, va1: ...); diff --git a/tests/pretty/pin-ergonomics-hir.pp b/tests/pretty/pin-ergonomics-hir.pp index e422edf54e0c5..6c9dec2bfb1fb 100644 --- a/tests/pretty/pin-ergonomics-hir.pp +++ b/tests/pretty/pin-ergonomics-hir.pp @@ -2,8 +2,8 @@ //@ pretty-mode:hir //@ pp-exact:pin-ergonomics-hir.pp -#![feature(pin_ergonomics)] #![allow(dead_code, incomplete_features)] +#![attr = Feature([pin_ergonomics#0])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; diff --git a/tests/ui/attributes/attr-mix-new.rs b/tests/ui/attributes/attr-mix-new.rs index c4f080eb8c302..3ddb1d0d7335c 100644 --- a/tests/ui/attributes/attr-mix-new.rs +++ b/tests/ui/attributes/attr-mix-new.rs @@ -5,7 +5,7 @@ #[rustc_dummy(bar)] mod foo { #![feature(globs)] - //~^ WARN crate-level attribute should be in the root module + //~^ WARN the `#![feature]` attribute can only be used at the crate root } fn main() {} diff --git a/tests/ui/attributes/attr-mix-new.stderr b/tests/ui/attributes/attr-mix-new.stderr index c1bb8a550bcca..628d93ff534e4 100644 --- a/tests/ui/attributes/attr-mix-new.stderr +++ b/tests/ui/attributes/attr-mix-new.stderr @@ -1,4 +1,4 @@ -warning: crate-level attribute should be in the root module +warning: the `#![feature]` attribute can only be used at the crate root --> $DIR/attr-mix-new.rs:7:3 | LL | #![feature(globs)] diff --git a/tests/ui/consts/const-block-items/hir.stdout b/tests/ui/consts/const-block-items/hir.stdout index 2b7f0818556ff..d031818fdf65b 100644 --- a/tests/ui/consts/const-block-items/hir.stdout +++ b/tests/ui/consts/const-block-items/hir.stdout @@ -1,10 +1,10 @@ -//@ build-pass -//@ compile-flags: -Zunpretty=hir - -#![feature(const_block_items)] +#![attr = Feature([const_block_items#0])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; +//@ build-pass +//@ compile-flags: -Zunpretty=hir + const _: () = { diff --git a/tests/ui/empty/empty-attributes.stderr b/tests/ui/empty/empty-attributes.stderr index 41dc790737dd1..146a9af946d6c 100644 --- a/tests/ui/empty/empty-attributes.stderr +++ b/tests/ui/empty/empty-attributes.stderr @@ -43,14 +43,6 @@ LL | #![forbid()] | = note: attribute `forbid` with an empty list has no effect -error: unused attribute - --> $DIR/empty-attributes.rs:7:1 - | -LL | #![feature()] - | ^^^^^^^^^^^^^ help: remove this attribute - | - = note: attribute `feature` with an empty list has no effect - error: unused attribute --> $DIR/empty-attributes.rs:9:1 | @@ -67,5 +59,13 @@ LL | #[target_feature()] | = note: using `target_feature` with an empty list has no effect +error: unused attribute + --> $DIR/empty-attributes.rs:7:1 + | +LL | #![feature()] + | ^^^^^^^^^^^^^ help: remove this attribute + | + = note: using `feature` with an empty list has no effect + error: aborting due to 8 previous errors diff --git a/tests/ui/feature-gates/feature-gate-feature-gate.rs b/tests/ui/feature-gates/feature-gate-feature-gate.rs index 3c98e16a136a8..b5b6a8da91bd7 100644 --- a/tests/ui/feature-gates/feature-gate-feature-gate.rs +++ b/tests/ui/feature-gates/feature-gate-feature-gate.rs @@ -1,4 +1,4 @@ #![forbid(unstable_features)] #![feature(intrinsics)] //~ ERROR unstable feature -fn main() { } +fn main() {} diff --git a/tests/ui/feature-gates/gated-bad-feature.stderr b/tests/ui/feature-gates/gated-bad-feature.stderr index e0e84d842352d..afcc6b6f11c49 100644 --- a/tests/ui/feature-gates/gated-bad-feature.stderr +++ b/tests/ui/feature-gates/gated-bad-feature.stderr @@ -1,15 +1,3 @@ -error[E0556]: malformed `feature` attribute input - --> $DIR/gated-bad-feature.rs:1:25 - | -LL | #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)] - | ^^^^^^^^ help: expected just one word: `foo` - -error[E0556]: malformed `feature` attribute input - --> $DIR/gated-bad-feature.rs:1:35 - | -LL | #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)] - | ^^^^^^^^^^^ help: expected just one word: `foo` - error[E0557]: feature has been removed --> $DIR/gated-bad-feature.rs:8:12 | @@ -18,17 +6,41 @@ LL | #![feature(test_removed_feature)] | = note: removed in 1.0.0 -error: malformed `feature` attribute input +error[E0565]: malformed `feature` attribute input + --> $DIR/gated-bad-feature.rs:1:1 + | +LL | #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#![feature(feature1, feature2, ...)]` + +error[E0565]: malformed `feature` attribute input + --> $DIR/gated-bad-feature.rs:1:1 + | +LL | #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------^^^^^^^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#![feature(feature1, feature2, ...)]` + +error[E0539]: malformed `feature` attribute input --> $DIR/gated-bad-feature.rs:6:1 | LL | #![feature] - | ^^^^^^^^^^^ help: must be of the form: `#![feature(name1, name2, ...)]` + | ^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#![feature(feature1, feature2, ...)]` -error: malformed `feature` attribute input +error[E0539]: malformed `feature` attribute input --> $DIR/gated-bad-feature.rs:7:1 | LL | #![feature = "foo"] - | ^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![feature(name1, name2, ...)]` + | ^^^^^^^^^^^-------^ + | | | + | | expected this to be a list + | help: must be of the form: `#![feature(feature1, feature2, ...)]` error[E0635]: unknown feature `foo_bar_baz` --> $DIR/gated-bad-feature.rs:1:12 @@ -44,5 +56,5 @@ LL | #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)] error: aborting due to 7 previous errors -Some errors have detailed explanations: E0556, E0557, E0635. -For more information about an error, try `rustc --explain E0556`. +Some errors have detailed explanations: E0539, E0557, E0565, E0635. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index 83debd17777ae..294cdf659744a 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -859,26 +859,26 @@ mod crate_type { #[feature(x0600)] //~^ WARN crate-level attribute should be an inner attribute -//~| HELP add a `!` mod feature { +//~^ NOTE this attribute does not have an `!`, which means it is applied to this module mod inner { #![feature(x0600)] } -//~^ WARN crate-level attribute should be in the root module + //~^ WARN the `#![feature]` attribute can only be used at the crate root #[feature(x0600)] fn f() { } //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE this attribute does not have an `!`, which means it is applied to this function #[feature(x0600)] struct S; //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE this attribute does not have an `!`, which means it is applied to this struct #[feature(x0600)] type T = S; //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE this attribute does not have an `!`, which means it is applied to this type alias #[feature(x0600)] impl S { } //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE this attribute does not have an `!`, which means it is applied to this implementation block } diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 482980dde3bf5..b282bcef8960a 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -207,17 +207,6 @@ note: the lint level is defined here LL | #![warn(unused_attributes, unknown_lints)] | ^^^^^^^^^^^^^^^^^ -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:860:1 - | -LL | #[feature(x0600)] - | ^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![feature(x0600)] - | + - warning: attribute should be applied to an `extern` block with non-Rust ABI --> $DIR/issue-43106-gating-of-builtin-attrs.rs:71:1 | @@ -282,56 +271,6 @@ LL | #[link(name = "x")] extern "Rust" {} | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:864:17 - | -LL | mod inner { #![feature(x0600)] } - | ^^^^^^^^^^^^^^^^^^ - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:867:5 - | -LL | #[feature(x0600)] fn f() { } - | ^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![feature(x0600)] fn f() { } - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:871:5 - | -LL | #[feature(x0600)] struct S; - | ^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![feature(x0600)] struct S; - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:875:5 - | -LL | #[feature(x0600)] type T = S; - | ^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![feature(x0600)] type T = S; - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:879:5 - | -LL | #[feature(x0600)] impl S { } - | ^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![feature(x0600)] impl S { } - | + - warning: `#[macro_use]` attribute cannot be used on functions --> $DIR/issue-43106-gating-of-builtin-attrs.rs:190:5 | @@ -1303,6 +1242,76 @@ note: this attribute does not have an `!`, which means it is applied to this imp LL | #[crate_type = "0800"] impl S { } | ^^^^^^^^^^ +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![feature]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:860:1 + | +LL | #[feature(x0600)] + | ^^^^^^^^^^^^^^^^^ + | +note: this attribute does not have an `!`, which means it is applied to this module + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:862:1 + | +LL | / mod feature { +LL | | +LL | | mod inner { #![feature(x0600)] } +... | +LL | | } + | |_^ + +warning: the `#![feature]` attribute can only be used at the crate root + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:864:17 + | +LL | mod inner { #![feature(x0600)] } + | ^^^^^^^^^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![feature]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:867:5 + | +LL | #[feature(x0600)] fn f() { } + | ^^^^^^^^^^^^^^^^^ + | +note: this attribute does not have an `!`, which means it is applied to this function + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:867:23 + | +LL | #[feature(x0600)] fn f() { } + | ^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![feature]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:871:5 + | +LL | #[feature(x0600)] struct S; + | ^^^^^^^^^^^^^^^^^ + | +note: this attribute does not have an `!`, which means it is applied to this struct + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:871:23 + | +LL | #[feature(x0600)] struct S; + | ^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![feature]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:875:5 + | +LL | #[feature(x0600)] type T = S; + | ^^^^^^^^^^^^^^^^^ + | +note: this attribute does not have an `!`, which means it is applied to this type alias + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:875:23 + | +LL | #[feature(x0600)] type T = S; + | ^^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![feature]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:879:5 + | +LL | #[feature(x0600)] impl S { } + | ^^^^^^^^^^^^^^^^^ + | +note: this attribute does not have an `!`, which means it is applied to this implementation block + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:879:23 + | +LL | #[feature(x0600)] impl S { } + | ^^^^^^^^^^ + warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_main]` --> $DIR/issue-43106-gating-of-builtin-attrs.rs:885:1 | diff --git a/tests/ui/parser/inner-attr.stderr b/tests/ui/parser/inner-attr.stderr index 3fb2d60ee5b66..0f9c7561490a9 100644 --- a/tests/ui/parser/inner-attr.stderr +++ b/tests/ui/parser/inner-attr.stderr @@ -11,17 +11,18 @@ LL | fn main() {} | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files -warning: crate-level attribute should be an inner attribute +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![feature]` --> $DIR/inner-attr.rs:1:1 | LL | #[feature(lang_items)] | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: requested on the command line with `-W unused-attributes` -help: add a `!` +note: this attribute does not have an `!`, which means it is applied to this function + --> $DIR/inner-attr.rs:4:1 | -LL | #![feature(lang_items)] - | + +LL | fn main() {} + | ^^^^^^^^^^^^ + = note: requested on the command line with `-W unused-attributes` error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/type-alias-impl-trait/issue-60662.stdout b/tests/ui/type-alias-impl-trait/issue-60662.stdout index dff748b43119d..99aa6caa30dbd 100644 --- a/tests/ui/type-alias-impl-trait/issue-60662.stdout +++ b/tests/ui/type-alias-impl-trait/issue-60662.stdout @@ -1,11 +1,11 @@ +#![attr = Feature([type_alias_impl_trait#0])] +extern crate std; +#[attr = PreludeImport] +use ::std::prelude::rust_2015::*; //@ check-pass //@ compile-flags: -Z unpretty=hir //@ edition: 2015 -#![feature(type_alias_impl_trait)] -extern crate std; -#[attr = PreludeImport] -use ::std::prelude::rust_2015::*; trait Animal { } diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index 7ee848491d6e5..78403e1704dca 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -8,28 +8,13 @@ // Note: the HIR revision includes a `.stderr` file because there are some // errors that only occur once we get past the AST. -#![feature(auto_traits)] -#![feature(box_patterns)] -#![feature(builtin_syntax)] -#![feature(const_trait_impl)] -#![feature(coroutines)] -#![feature(decl_macro)] -#![feature(deref_patterns)] -#![feature(explicit_tail_calls)] -#![feature(gen_blocks)] -#![feature(more_qualified_paths)] -#![feature(never_patterns)] -#![feature(never_type)] -#![feature(pattern_types)] -#![feature(pattern_type_macro)] -#![feature(prelude_import)] -#![feature(specialization)] -#![feature(trace_macros)] -#![feature(trait_alias)] -#![feature(try_blocks)] -#![feature(try_blocks_heterogeneous)] -#![feature(yeet_expr)] #![allow(incomplete_features)] +#![attr = Feature([auto_traits#0, box_patterns#0, builtin_syntax#0, +const_trait_impl#0, coroutines#0, decl_macro#0, deref_patterns#0, +explicit_tail_calls#0, gen_blocks#0, more_qualified_paths#0, never_patterns#0, +never_type#0, pattern_types#0, pattern_type_macro#0, prelude_import#0, +specialization#0, trace_macros#0, trait_alias#0, try_blocks#0, +try_blocks_heterogeneous#0, yeet_expr#0])] extern crate std; #[attr = PreludeImport] use std::prelude::rust_2024::*; diff --git a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout index c990837d2138a..8c016f5083d24 100644 --- a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout +++ b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout @@ -1,9 +1,9 @@ //@ compile-flags: -Zunpretty=hir //@ check-pass -#![feature(min_generic_const_args, adt_const_params)] #![expect(incomplete_features)] #![allow(dead_code)] +#![attr = Feature([min_generic_const_args#0, adt_const_params#0])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; From 34d616105639c373c65ec0befff0581f1997cb21 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 22 Feb 2026 22:02:20 +0100 Subject: [PATCH 65/82] Port `#[rustc_inherit_overflow_checks]` to the new attribute parsers --- .../src/attributes/rustc_internal.rs | 14 ++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 1 + compiler/rustc_hir/src/attrs/data_structures.rs | 3 +++ compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_mir_build/src/builder/mod.rs | 6 ++---- compiler/rustc_passes/src/check_attr.rs | 2 +- 6 files changed, 22 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 2bd5b9a130d1c..c18e292027517 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -175,6 +175,20 @@ impl SingleAttributeParser for RustcLegacyConstGenericsParser { } } +pub(crate) struct RustcInheritOverflowChecksParser; + +impl NoArgsAttributeParser for RustcInheritOverflowChecksParser { + const PATH: &[Symbol] = &[sym::rustc_inherit_overflow_checks]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Closure), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcInheritOverflowChecks; +} + pub(crate) struct RustcLintOptDenyFieldAccessParser; impl SingleAttributeParser for RustcLintOptDenyFieldAccessParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b4e91ecebeeb7..2233cacfaa871 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -286,6 +286,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 5d154cef66a65..484eb5d4606b8 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1274,6 +1274,9 @@ pub enum AttributeKind { /// Represents `#[rustc_if_this_changed]` RustcIfThisChanged(Span, Option), + /// Represents `#[rustc_inherit_overflow_checks]` + RustcInheritOverflowChecks, + /// Represents `#[rustc_insignificant_dtor]` RustcInsignificantDtor, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 0b20ea4d6a83a..a1c9bdba80b29 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -130,6 +130,7 @@ impl AttributeKind { RustcHasIncoherentInherentImpls => Yes, RustcHiddenTypeOfOpaques => No, RustcIfThisChanged(..) => No, + RustcInheritOverflowChecks => No, RustcInsignificantDtor => Yes, RustcIntrinsic => Yes, RustcIntrinsicConstStableIndirect => No, diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index dbd7809c2e651..c8ca7bfccc07c 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -24,7 +24,6 @@ use itertools::Itertools; use rustc_abi::{ExternAbi, FieldIdx}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; -use rustc_ast::attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sorted_map::SortedIndexMultiMap; use rustc_errors::ErrorGuaranteed; @@ -41,7 +40,7 @@ use rustc_middle::thir::{self, ExprId, LocalVarId, Param, ParamId, PatKind, Thir use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; use crate::builder::expr::as_place::PlaceBuilder; use crate::builder::scope::{DropKind, LintLevel}; @@ -751,11 +750,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { coroutine: Option>>, ) -> Builder<'a, 'tcx> { let tcx = infcx.tcx; - let attrs = tcx.hir_attrs(hir_id); // Some functions always have overflow checks enabled, // however, they may not get codegen'd, depending on // the settings for the crate they are codegened in. - let mut check_overflow = attr::contains_name(attrs, sym::rustc_inherit_overflow_checks); + let mut check_overflow = find_attr!(tcx.hir_attrs(hir_id), RustcInheritOverflowChecks); // Respect -C overflow-checks. check_overflow |= tcx.sess.overflow_checks(); // Constants always need overflow checks. diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 9af9398d78b96..0f323e6d298cf 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -325,6 +325,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcHasIncoherentInherentImpls | AttributeKind::RustcHiddenTypeOfOpaques | AttributeKind::RustcIfThisChanged(..) + | AttributeKind::RustcInheritOverflowChecks | AttributeKind::RustcInsignificantDtor | AttributeKind::RustcIntrinsic | AttributeKind::RustcIntrinsicConstStableIndirect @@ -404,7 +405,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_on_unimplemented | sym::rustc_layout | sym::rustc_autodiff - | sym::rustc_inherit_overflow_checks // crate-level attrs, are checked below | sym::feature | sym::register_tool, From 5b87b994d805b902563c08590f3b71c5c7a9b3f3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 22 Feb 2026 22:38:46 +0100 Subject: [PATCH 66/82] fix interpreter tracing output --- compiler/rustc_const_eval/src/interpret/step.rs | 4 ++-- compiler/rustc_middle/src/mir/pretty.rs | 14 ++++++++++++-- compiler/rustc_middle/src/mir/syntax.rs | 2 +- compiler/rustc_middle/src/mir/terminator.rs | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 0dd17f109be31..126d0fc9b7a1b 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -86,7 +86,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { span = ?stmt.source_info.span, tracing_separate_thread = Empty, ) - .or_if_tracing_disabled(|| info!(stmt = ?stmt.kind)); + .or_if_tracing_disabled(|| info!("{:?}", stmt.kind)); use rustc_middle::mir::StatementKind::*; @@ -490,7 +490,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { span = ?terminator.source_info.span, tracing_separate_thread = Empty, ) - .or_if_tracing_disabled(|| info!(terminator = ?terminator.kind)); + .or_if_tracing_disabled(|| info!("{:?}", terminator.kind)); use rustc_middle::mir::TerminatorKind::*; match terminator.kind { diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index e5fe01781e96d..487a3c9c4ea22 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -802,10 +802,10 @@ impl<'de, 'tcx> MirWriter<'de, 'tcx> { } } -impl Debug for Statement<'_> { +impl Debug for StatementKind<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { use self::StatementKind::*; - match self.kind { + match *self { Assign(box (ref place, ref rv)) => write!(fmt, "{place:?} = {rv:?}"), FakeRead(box (ref cause, ref place)) => { write!(fmt, "FakeRead({cause:?}, {place:?})") @@ -844,6 +844,11 @@ impl Debug for Statement<'_> { } } } +impl Debug for Statement<'_> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + self.kind.fmt(fmt) + } +} impl Debug for StmtDebugInfo<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { @@ -915,6 +920,11 @@ impl<'tcx> Debug for TerminatorKind<'tcx> { } } } +impl Debug for Terminator<'_> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + self.kind.fmt(fmt) + } +} impl<'tcx> TerminatorKind<'tcx> { /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 35f67460f51c0..c3da2f0394852 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -306,7 +306,7 @@ pub enum FakeBorrowKind { /// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which /// ones you do not have to worry about. The MIR validator will generally enforce such restrictions, /// causing an ICE if they are violated. -#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] #[derive(TypeFoldable, TypeVisitable)] pub enum StatementKind<'tcx> { /// Assign statements roughly correspond to an assignment in Rust proper (`x = ...`) except diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 1e7729bd8d657..650c44aa41fbe 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -444,7 +444,7 @@ impl AssertKind { } } -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct Terminator<'tcx> { pub source_info: SourceInfo, pub kind: TerminatorKind<'tcx>, From 5cfaadaa5083712f0f4c1debcc83d2ac1c2a8ad1 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 23 Feb 2026 09:48:22 +0800 Subject: [PATCH 67/82] Check importing crate/$crate/super after handling self --- .../rustc_resolve/src/build_reduced_graph.rs | 68 +++++++++---------- tests/ui/use/use-super-in-middle.rs | 14 ++++ tests/ui/use/use-super-in-middle.stderr | 39 +++++++++++ 3 files changed, 86 insertions(+), 35 deletions(-) create mode 100644 tests/ui/use/use-super-in-middle.rs create mode 100644 tests/ui/use/use-super-in-middle.stderr diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index c4a0cde2837a0..9fbc73618ce80 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -629,6 +629,35 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { // `true` for `...::{self [as target]}` imports, `false` otherwise. let type_ns_only = nested && source.ident.name == kw::SelfLower; + if source.ident.name == kw::SelfLower + && let Some(parent) = module_path.pop() + { + // Suggest `use prefix::{self};` for `use prefix::self;` + if !type_ns_only + && (parent.ident.name != kw::PathRoot + || self.r.path_root_is_crate_root(parent.ident)) + { + let span_with_rename = match rename { + Some(rename) => source.ident.span.to(rename.span), + None => source.ident.span, + }; + + self.r.report_error( + parent.ident.span.shrink_to_hi().to(source.ident.span), + ResolutionError::SelfImportsOnlyAllowedWithin { + root: parent.ident.name == kw::PathRoot, + span_with_rename, + }, + ); + } + + let self_span = source.ident.span; + source = parent; + if rename.is_none() { + ident = Ident::new(source.ident.name, self_span); + } + } + match source.ident.name { kw::DollarCrate => { if !module_path.is_empty() { @@ -664,45 +693,14 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { return; } } - kw::SelfLower => { - if let Some(parent) = module_path.pop() { - // Suggest `use prefix::{self};` for `use prefix::self;` - if !type_ns_only - && (parent.ident.name != kw::PathRoot - || self.r.path_root_is_crate_root(parent.ident)) - { - let span_with_rename = match rename { - Some(rename) => source.ident.span.to(rename.span), - None => source.ident.span, - }; - - self.r.report_error( - parent.ident.span.shrink_to_hi().to(source.ident.span), - ResolutionError::SelfImportsOnlyAllowedWithin { - root: parent.ident.name == kw::PathRoot, - span_with_rename, - }, - ); - } - - let self_span = source.ident.span; - source = parent; - if rename.is_none() { - ident = Ident::new(source.ident.name, self_span); - } - } + // Deny `use ::{self};` after edition 2015 + kw::PathRoot if !self.r.path_root_is_crate_root(source.ident) => { + self.r.dcx().span_err(use_tree.span, "extern prelude cannot be imported"); + return; } _ => {} } - // Deny `use ::{self};` after edition 2015 - if source.ident.name == kw::PathRoot - && !self.r.path_root_is_crate_root(source.ident) - { - self.r.dcx().span_err(use_tree.span, "extern prelude cannot be imported"); - return; - } - // Deny importing path-kw without renaming if rename.is_none() && ident.is_path_segment_keyword() { let ident = use_tree.ident(); diff --git a/tests/ui/use/use-super-in-middle.rs b/tests/ui/use/use-super-in-middle.rs new file mode 100644 index 0000000000000..be9f3675c323f --- /dev/null +++ b/tests/ui/use/use-super-in-middle.rs @@ -0,0 +1,14 @@ +pub mod x { + pub use crate::x::super::{self as crate1}; //~ ERROR `super` in paths can only be used in start position + pub use crate::x::self::super::{self as crate2}; //~ ERROR `super` in paths can only be used in start position + + pub fn foo() {} +} + +fn main() { + x::crate1::x::foo(); //~ ERROR cannot find `crate1` in `x` + x::crate2::x::foo(); //~ ERROR cannot find `crate2` in `x` + + crate::x::super::x::foo(); //~ ERROR `super` in paths can only be used in start position + crate::x::self::super::x::foo(); //~ ERROR `self` in paths can only be used in start position +} diff --git a/tests/ui/use/use-super-in-middle.stderr b/tests/ui/use/use-super-in-middle.stderr new file mode 100644 index 0000000000000..276b1274f6a89 --- /dev/null +++ b/tests/ui/use/use-super-in-middle.stderr @@ -0,0 +1,39 @@ +error: `super` in paths can only be used in start position, after `self`, or after another `super` + --> $DIR/use-super-in-middle.rs:2:23 + | +LL | pub use crate::x::super::{self as crate1}; + | ^^^^^ + +error: `super` in paths can only be used in start position, after `self`, or after another `super` + --> $DIR/use-super-in-middle.rs:3:29 + | +LL | pub use crate::x::self::super::{self as crate2}; + | ^^^^^ + +error[E0433]: cannot find `crate1` in `x` + --> $DIR/use-super-in-middle.rs:9:8 + | +LL | x::crate1::x::foo(); + | ^^^^^^ could not find `crate1` in `x` + +error[E0433]: cannot find `crate2` in `x` + --> $DIR/use-super-in-middle.rs:10:8 + | +LL | x::crate2::x::foo(); + | ^^^^^^ could not find `crate2` in `x` + +error[E0433]: `super` in paths can only be used in start position + --> $DIR/use-super-in-middle.rs:12:15 + | +LL | crate::x::super::x::foo(); + | ^^^^^ can only be used in path start position + +error[E0433]: `self` in paths can only be used in start position + --> $DIR/use-super-in-middle.rs:13:15 + | +LL | crate::x::self::super::x::foo(); + | ^^^^ can only be used in path start position + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0433`. From 1b5a80cf4bce583e97e14e3d368f6d0e8f8aeb60 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 23 Feb 2026 04:45:04 +0000 Subject: [PATCH 68/82] Prepare for merging from rust-lang/rust This updates the rust-version file to c78a29473a68f07012904af11c92ecffa68fcc75. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index b22c6c3869c62..b6e1b2bc55df4 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -139651428df86cf88443295542c12ea617cbb587 +c78a29473a68f07012904af11c92ecffa68fcc75 From 446d1e14c5e3f1791f3e1c1f4282e2e708c3914c Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 23 Feb 2026 15:53:13 +1100 Subject: [PATCH 69/82] Sort `rustc_middle::query::modifiers` --- compiler/rustc_middle/src/query/modifiers.rs | 52 +++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs index 81b9f0da6446c..68a3c0d84fb93 100644 --- a/compiler/rustc_middle/src/query/modifiers.rs +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -2,10 +2,12 @@ #![allow(unused, non_camel_case_types)] // FIXME: Update and clarify documentation for these modifiers. -/// # `desc` query modifier +// tidy-alphabetical-start +// +/// # `anon` query modifier /// -/// The description of the query. This modifier is required on every query. -pub struct desc; +/// Generate a dep node based on the dependencies of the query +pub struct anon; /// # `arena_cache` query modifier /// @@ -17,51 +19,46 @@ pub struct arena_cache; /// Cache the query to disk if the `Block` returns true. pub struct cache_on_disk_if; -/// # `cycle_fatal` query modifier -/// -/// A cycle error for this query aborting the compilation with a fatal error. -pub struct cycle_fatal; - /// # `cycle_delay_bug` query modifier /// /// A cycle error results in a delay_bug call pub struct cycle_delay_bug; +/// # `cycle_fatal` query modifier +/// +/// A cycle error for this query aborting the compilation with a fatal error. +pub struct cycle_fatal; + /// # `cycle_stash` query modifier /// /// A cycle error results in a stashed cycle error that can be unstashed and canceled later pub struct cycle_stash; -/// # `no_hash` query modifier +/// # `depth_limit` query modifier /// -/// Don't hash the result, instead just mark a query red if it runs -pub struct no_hash; +/// Whether the query has a call depth limit +pub struct depth_limit; -/// # `anon` query modifier +/// # `desc` query modifier /// -/// Generate a dep node based on the dependencies of the query -pub struct anon; +/// The description of the query. This modifier is required on every query. +pub struct desc; /// # `eval_always` query modifier /// /// Always evaluate the query, ignoring its dependencies pub struct eval_always; -/// # `depth_limit` query modifier -/// -/// Whether the query has a call depth limit -pub struct depth_limit; - -/// # `separate_provide_extern` query modifier -/// -/// Use a separate query provider for local and extern crates -pub struct separate_provide_extern; - /// # `feedable` query modifier /// /// Generate a `feed` method to set the query's value from another query. pub struct feedable; +/// # `no_hash` query modifier +/// +/// Don't hash the result, instead just mark a query red if it runs +pub struct no_hash; + /// # `return_result_from_ensure_ok` query modifier /// /// When this query is called via `tcx.ensure_ok()`, it returns @@ -75,3 +72,10 @@ pub struct feedable; /// Can only be applied to queries with a return value of /// `Result<_, ErrorGuaranteed>`. pub struct return_result_from_ensure_ok; + +/// # `separate_provide_extern` query modifier +/// +/// Use a separate query provider for local and extern crates +pub struct separate_provide_extern; + +// tidy-alphabetical-end From ab0576b7fed7657cd8322681b397d178c88e7a51 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 23 Feb 2026 15:54:19 +1100 Subject: [PATCH 70/82] Make dummy modifier items non-pub This massively speeds up find-all-references in rust-analyzer, from taking several seconds to being near-instant. --- compiler/rustc_middle/src/query/modifiers.rs | 26 ++++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs index 68a3c0d84fb93..30396a1c83539 100644 --- a/compiler/rustc_middle/src/query/modifiers.rs +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -7,57 +7,57 @@ /// # `anon` query modifier /// /// Generate a dep node based on the dependencies of the query -pub struct anon; +pub(crate) struct anon; /// # `arena_cache` query modifier /// /// Use this type for the in-memory cache. -pub struct arena_cache; +pub(crate) struct arena_cache; /// # `cache_on_disk_if` query modifier /// /// Cache the query to disk if the `Block` returns true. -pub struct cache_on_disk_if; +pub(crate) struct cache_on_disk_if; /// # `cycle_delay_bug` query modifier /// /// A cycle error results in a delay_bug call -pub struct cycle_delay_bug; +pub(crate) struct cycle_delay_bug; /// # `cycle_fatal` query modifier /// /// A cycle error for this query aborting the compilation with a fatal error. -pub struct cycle_fatal; +pub(crate) struct cycle_fatal; /// # `cycle_stash` query modifier /// /// A cycle error results in a stashed cycle error that can be unstashed and canceled later -pub struct cycle_stash; +pub(crate) struct cycle_stash; /// # `depth_limit` query modifier /// /// Whether the query has a call depth limit -pub struct depth_limit; +pub(crate) struct depth_limit; /// # `desc` query modifier /// /// The description of the query. This modifier is required on every query. -pub struct desc; +pub(crate) struct desc; /// # `eval_always` query modifier /// /// Always evaluate the query, ignoring its dependencies -pub struct eval_always; +pub(crate) struct eval_always; /// # `feedable` query modifier /// /// Generate a `feed` method to set the query's value from another query. -pub struct feedable; +pub(crate) struct feedable; /// # `no_hash` query modifier /// /// Don't hash the result, instead just mark a query red if it runs -pub struct no_hash; +pub(crate) struct no_hash; /// # `return_result_from_ensure_ok` query modifier /// @@ -71,11 +71,11 @@ pub struct no_hash; /// /// Can only be applied to queries with a return value of /// `Result<_, ErrorGuaranteed>`. -pub struct return_result_from_ensure_ok; +pub(crate) struct return_result_from_ensure_ok; /// # `separate_provide_extern` query modifier /// /// Use a separate query provider for local and extern crates -pub struct separate_provide_extern; +pub(crate) struct separate_provide_extern; // tidy-alphabetical-end From e9ee5f869c580d7dfda51001b98cdb746c0c8096 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 23 Feb 2026 15:57:46 +1100 Subject: [PATCH 71/82] More explanation of what the dummy items in `modifiers` are for --- compiler/rustc_middle/src/query/modifiers.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs index 30396a1c83539..44aaa6a3ce3d9 100644 --- a/compiler/rustc_middle/src/query/modifiers.rs +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -1,4 +1,8 @@ //! This contains documentation which is linked from query modifiers used in the `rustc_queries!` proc macro. +//! +//! The dummy items in this module are used to enable hover documentation for +//! modifier names in the query list, and to allow find-all-references to list +//! all queries that use a particular modifier. #![allow(unused, non_camel_case_types)] // FIXME: Update and clarify documentation for these modifiers. From 4e24abb78cd80c92c5b5f482e9912430d779212e Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sun, 22 Feb 2026 23:20:08 -0800 Subject: [PATCH 72/82] bootstrap.compiler.toml: update name of primary branch Those building from the latest commit are building from `main`, not `master` --- src/bootstrap/defaults/bootstrap.compiler.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/defaults/bootstrap.compiler.toml b/src/bootstrap/defaults/bootstrap.compiler.toml index 9dcb20ed48323..10fa0b3aeb591 100644 --- a/src/bootstrap/defaults/bootstrap.compiler.toml +++ b/src/bootstrap/defaults/bootstrap.compiler.toml @@ -23,7 +23,7 @@ lto = "off" frame-pointers = true # Compiler contributors often want to build rustc even without any changes to # e.g. check that it builds locally and check the baseline behavior of a -# compiler built from latest `master` commit. +# compiler built from latest `main` commit. download-rustc = false [llvm] From 04e9918656647ba48877db5054aed719c3ce6897 Mon Sep 17 00:00:00 2001 From: jasper3108 Date: Mon, 23 Feb 2026 08:55:16 +0100 Subject: [PATCH 73/82] make TraitImpl unstable --- compiler/rustc_const_eval/src/const_eval/machine.rs | 3 +-- compiler/rustc_const_eval/src/interpret/mod.rs | 4 ++-- library/core/src/mem/type_info.rs | 2 ++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 1e048531ae90e..5e81f03344343 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -21,12 +21,11 @@ use tracing::debug; use super::error::*; use crate::errors::{LongRunning, LongRunningWarn}; -use crate::interpret::util::type_implements_dyn_trait; use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar, compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub, - throw_ub_custom, throw_unsup, throw_unsup_format, + throw_ub_custom, throw_unsup, throw_unsup_format, type_implements_dyn_trait, }; /// When hitting this many interpreted terminators we emit a deny by default lint diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 1bf4674789e76..3fbb043885715 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -15,7 +15,7 @@ mod projection; mod stack; mod step; mod traits; -pub(crate) mod util; +mod util; mod validity; mod visitor; @@ -38,6 +38,6 @@ use self::place::{MemPlace, Place}; pub use self::projection::{OffsetMode, Projectable}; pub use self::stack::{Frame, FrameInfo, LocalState, ReturnContinuation}; pub use self::util::EnteredTraceSpan; -pub(crate) use self::util::create_static_alloc; +pub(crate) use self::util::{create_static_alloc, type_implements_dyn_trait}; pub use self::validity::{CtfeValidationMode, RangeSet, RefTracking}; pub use self::visitor::ValueVisitor; diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 24ae0c6484a0c..2609b9767e0f9 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -20,6 +20,8 @@ pub struct Type { /// Info of a trait implementation, you can retrieve the vtable with [Self::get_vtable] #[derive(Debug, PartialEq, Eq)] +#[unstable(feature = "type_info", issue = "146922")] +#[non_exhaustive] pub struct TraitImpl { pub(crate) vtable: DynMetadata, } From 9436e6e7d05774d6ae479f3eeb8d229542d37917 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 23 Feb 2026 11:30:53 +0100 Subject: [PATCH 74/82] Add `TyCtxt::emit_diag_node_span_lint` method --- compiler/rustc_middle/src/ty/context.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index b57de30201876..9eb41e57d4dce 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -29,7 +29,7 @@ use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{ self, DynSend, DynSync, FreezeReadGuard, Lock, RwLock, WorkerLocal, }; -use rustc_errors::{Applicability, Diag, DiagCtxtHandle, LintDiagnostic, MultiSpan}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, LintDiagnostic, MultiSpan}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::{DefPathData, Definitions, DisambiguatorState}; @@ -55,7 +55,7 @@ use crate::dep_graph::dep_node::make_metadata; use crate::dep_graph::{DepGraph, DepKindVTable, DepNodeIndex}; use crate::ich::StableHashingContext; use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarKind}; -use crate::lint::lint_level; +use crate::lint::{diag_lint_level, lint_level}; use crate::metadata::ModChild; use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature}; use crate::middle::resolve_bound_vars; @@ -2511,6 +2511,20 @@ impl<'tcx> TyCtxt<'tcx> { }) } + /// Emit a lint at `span` from a lint struct (some type that implements `Diagnostic`, + /// typically generated by `#[derive(Diagnostic)]`). + #[track_caller] + pub fn emit_diag_node_span_lint( + self, + lint: &'static Lint, + hir_id: HirId, + span: impl Into, + decorator: impl for<'a> Diagnostic<'a, ()>, + ) { + let level = self.lint_level_at_node(lint, hir_id); + diag_lint_level(self.sess, lint, level, Some(span.into()), decorator) + } + /// Emit a lint at the appropriate level for a hir node, with an associated span. /// /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature From 119b87fc610f3f9a2919712386efaf45e146b036 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 23 Feb 2026 11:35:52 +0100 Subject: [PATCH 75/82] Migrate `rustc_borrowck` to use `TyCtxt::emit_diag_node_span_lint` --- compiler/rustc_borrowck/src/lib.rs | 7 ++++++- compiler/rustc_borrowck/src/session_diagnostics.rs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 2fc057ae38823..259c6cd7728ce 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2698,7 +2698,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { let mut_span = tcx.sess.source_map().span_until_non_whitespace(span); - tcx.emit_node_span_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span }) + tcx.emit_diag_node_span_lint( + UNUSED_MUT, + lint_root, + span, + VarNeedNotMut { span: mut_span }, + ) } } } diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 7bde534dafd20..eaa41ce1caacf 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -47,7 +47,7 @@ pub(crate) struct GenericDoesNotLiveLongEnough { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("variable does not need to be mutable")] pub(crate) struct VarNeedNotMut { #[suggestion( From 1a426a0b0962127136a8f3378d85c374bedbbe26 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 23 Feb 2026 11:41:16 +0100 Subject: [PATCH 76/82] Migrate `rustc_codegen_ssa` to use `TyCtxt::emit_diag_node_span_lint` --- compiler/rustc_codegen_ssa/src/errors.rs | 4 ++-- compiler/rustc_codegen_ssa/src/target_features.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 74f7829ace95a..be1965f674911 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -10,7 +10,7 @@ use rustc_errors::codes::*; use rustc_errors::{ Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, msg, }; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::layout::LayoutError; use rustc_middle::ty::{FloatTy, Ty}; use rustc_span::{Span, Symbol}; @@ -1164,7 +1164,7 @@ pub(crate) struct XcrunSdkPathWarning { pub stderr: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("enabling the `neon` target feature on the current target is unsound due to ABI issues")] pub(crate) struct Aarch64SoftfloatNeon; diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index e9209657984e0..d7f871527e3d1 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -75,7 +75,7 @@ pub(crate) fn from_target_feature_attr( // For "neon" specifically, we emit an FCW instead of a hard error. // See . if tcx.sess.target.arch == Arch::AArch64 && name.as_str() == "neon" { - tcx.emit_node_span_lint( + tcx.emit_diag_node_span_lint( AARCH64_SOFTFLOAT_NEON, tcx.local_def_id_to_hir_id(did), feature_span, From ab3d14c2c58cf45514045b43570dfc6f4c81236b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 23 Feb 2026 11:48:01 +0100 Subject: [PATCH 77/82] Migrate `rustc_const_eval` to use `TyCtxt::emit_diag_node_span_lint` --- compiler/rustc_const_eval/src/const_eval/error.rs | 4 ++-- compiler/rustc_const_eval/src/const_eval/machine.rs | 2 +- compiler/rustc_const_eval/src/errors.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 69aef3b047df2..7efef74e70aa3 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -252,9 +252,9 @@ pub(super) fn lint<'tcx, L>( lint: &'static rustc_session::lint::Lint, decorator: impl FnOnce(Vec) -> L, ) where - L: for<'a> rustc_errors::LintDiagnostic<'a, ()>, + L: for<'a> rustc_errors::Diagnostic<'a, ()>, { let (span, frames) = get_span_and_frames(tcx, &machine.stack); - tcx.emit_node_span_lint(lint, machine.best_lint_scope(*tcx), span, decorator(frames)); + tcx.emit_diag_node_span_lint(lint, machine.best_lint_scope(*tcx), span, decorator(frames)); } diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 025c59747eaf7..eac154f0602ca 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -702,7 +702,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { .level .is_error(); let span = ecx.cur_span(); - ecx.tcx.emit_node_span_lint( + ecx.tcx.emit_diag_node_span_lint( rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL, hir_id, span, diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 6a33a9ae0597c..cdc02ae6933d2 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -9,7 +9,7 @@ use rustc_errors::{ Subdiagnostic, msg, }; use rustc_hir::ConstContext; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::mir::interpret::{ CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind, InvalidProgramInfo, Misalignment, Pointer, PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo, @@ -318,7 +318,7 @@ pub(crate) struct InteriorMutableBorrowEscaping { pub kind: ConstContext, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("constant evaluation is taking a long time")] #[note( "this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval. From b8677e22a582b43e34a7d8e918dee65c9f513d8b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 23 Feb 2026 11:56:45 +0100 Subject: [PATCH 78/82] Migrate `rustc_hir_analysis` to use `TyCtxt::emit_diag_node_span_lint` --- compiler/rustc_hir_analysis/src/check/check.rs | 2 +- .../src/check/compare_impl_item/refine.rs | 4 ++-- compiler/rustc_hir_analysis/src/check/wfcheck.rs | 8 ++++---- .../rustc_hir_analysis/src/coherence/orphan.rs | 4 ++-- compiler/rustc_hir_analysis/src/errors.rs | 16 ++++++++-------- .../src/hir_ty_lowering/dyn_trait.rs | 2 +- .../coherence/orphan-check-alias.classic.stderr | 4 ++-- .../ui/coherence/orphan-check-alias.next.stderr | 4 ++-- ...ections-not-covering-ambiguity.classic.stderr | 4 ++-- ...rojections-not-covering-ambiguity.next.stderr | 4 ++-- ...s-not-covering-multiple-params.classic.stderr | 8 ++++---- ...ions-not-covering-multiple-params.next.stderr | 8 ++++---- ...check-projections-not-covering.classic.stderr | 12 ++++++------ ...an-check-projections-not-covering.next.stderr | 12 ++++++------ ...check-projections-unsat-bounds.classic.stderr | 4 ++-- ...an-check-projections-unsat-bounds.next.stderr | 4 ++-- 16 files changed, 50 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 349ad4f7fc43b..e8323a218e7a8 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1264,7 +1264,7 @@ fn check_impl_items_against_trait<'tcx>( } if self_is_guaranteed_unsize_self && tcx.generics_require_sized_self(ty_trait_item.def_id) { - tcx.emit_node_span_lint( + tcx.emit_diag_node_span_lint( rustc_lint_defs::builtin::DEAD_CODE, tcx.local_def_id_to_hir_id(ty_impl_item.def_id.expect_local()), tcx.def_span(ty_impl_item.def_id), diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index 31104411ab55b..0944edd86ef19 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -345,7 +345,7 @@ fn report_mismatched_rpitit_signature<'tcx>( with_no_trimmed_paths!(with_types_for_signature!(format!("{return_ty}"))); let span = unmatched_bound.unwrap_or(span); - tcx.emit_node_span_lint( + tcx.emit_diag_node_span_lint( if is_internal { REFINING_IMPL_TRAIT_INTERNAL } else { REFINING_IMPL_TRAIT_REACHABLE }, tcx.local_def_id_to_hir_id(impl_m_def_id.expect_local()), span, @@ -442,7 +442,7 @@ fn report_mismatched_rpitit_captures<'tcx>( .sort_by_cached_key(|arg| !matches!(arg.kind(), ty::GenericArgKind::Lifetime(_))); let suggestion = format!("use<{}>", trait_captured_args.iter().join(", ")); - tcx.emit_node_span_lint( + tcx.emit_diag_node_span_lint( if is_internal { REFINING_IMPL_TRAIT_INTERNAL } else { REFINING_IMPL_TRAIT_REACHABLE }, tcx.local_def_id_to_hir_id(impl_opaque_def_id), use_bound_span, diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index ed951015a4f19..5a4d1794623f1 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -14,7 +14,7 @@ use rustc_hir::{AmbigArg, ItemKind, find_attr}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, SubregionOrigin, TyCtxtInferExt}; use rustc_lint_defs::builtin::SHADOWING_SUPERTRAIT_ITEMS; -use rustc_macros::LintDiagnostic; +use rustc_macros::Diagnostic; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::traits::solve::NoSolution; use rustc_middle::ty::trait_def::TraitSpecializationKind; @@ -797,7 +797,7 @@ fn lint_item_shadowing_supertrait_item<'tcx>(tcx: TyCtxt<'tcx>, trait_item_def_i errors::SupertraitItemShadowee::Several { traits: traits.into(), spans: spans.into() } }; - tcx.emit_node_span_lint( + tcx.emit_diag_node_span_lint( SHADOWING_SUPERTRAIT_ITEMS, tcx.local_def_id_to_hir_id(trait_item_def_id), tcx.def_span(trait_item_def_id), @@ -2458,7 +2458,7 @@ fn lint_redundant_lifetimes<'tcx>( && outlives_env.free_region_map().sub_free_regions(tcx, victim, candidate) { shadowed.insert(victim); - tcx.emit_node_span_lint( + tcx.emit_diag_node_span_lint( rustc_lint_defs::builtin::REDUNDANT_LIFETIMES, tcx.local_def_id_to_hir_id(def_id.expect_local()), tcx.def_span(def_id), @@ -2469,7 +2469,7 @@ fn lint_redundant_lifetimes<'tcx>( } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unnecessary lifetime parameter `{$victim}`")] #[note("you can use the `{$candidate}` lifetime directly, in place of `{$victim}`")] struct RedundantLifetimeArgsLint<'tcx> { diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index f1e138dbcb97a..b5e40058e7533 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -497,13 +497,13 @@ fn lint_uncovered_ty_params<'tcx>( let name = tcx.item_ident(param_def_id); match local_ty { - Some(local_type) => tcx.emit_node_span_lint( + Some(local_type) => tcx.emit_diag_node_span_lint( UNCOVERED_PARAM_IN_PROJECTION, hir_id, span, errors::TyParamFirstLocalLint { span, note: (), param: name, local_type }, ), - None => tcx.emit_node_span_lint( + None => tcx.emit_diag_node_span_lint( UNCOVERED_PARAM_IN_PROJECTION, hir_id, span, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index be10d5ffad471..c91b67e763811 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -7,7 +7,7 @@ use rustc_errors::{ MultiSpan, listify, msg, }; use rustc_hir::limit::Limit; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; use rustc_span::{Ident, Span, Symbol}; pub(crate) mod wrong_number_of_generic_args; @@ -888,7 +888,7 @@ pub(crate) enum ImplNotMarkedDefault { }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("this item cannot be used as its where bounds are not satisfied for the `Self` type")] pub(crate) struct UselessImplItem; @@ -1114,7 +1114,7 @@ pub(crate) enum LateBoundInApit { }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("unnecessary associated type bound for dyn-incompatible associated type")] #[note( "this associated type has a `where Self: Sized` bound, and while the associated type can be specified, it cannot be used because trait objects are never `Sized`" @@ -1124,7 +1124,7 @@ pub(crate) struct UnusedAssociatedTypeBounds { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("impl trait in impl method signature does not match trait method signature")] #[note( "add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate" @@ -1149,7 +1149,7 @@ pub(crate) struct ReturnPositionImplTraitInTraitRefined { pub return_ty: String, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("impl trait in impl method captures fewer lifetimes than in trait")] #[note( "add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate" @@ -1467,7 +1467,7 @@ pub(crate) struct TyParamFirstLocal<'tcx> { pub local_type: Ty<'tcx>, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`)", code = E0210)] #[note( "implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type" @@ -1499,7 +1499,7 @@ pub(crate) struct TyParamSome { pub param: Ident, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("type parameter `{$param}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param}>`)", code = E0210)] #[note( "implementing a foreign trait is only possible if at least one of the types for which it is implemented is local" @@ -1805,7 +1805,7 @@ pub(crate) struct BadReturnTypeNotation { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait")] pub(crate) struct SupertraitItemShadowing { pub item: Symbol, 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 7b8e09943df71..459d13e46e854 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 @@ -291,7 +291,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // FIXME(mgca): Ideally we would generalize the name of this lint to sth. like // `unused_associated_item_bindings` since this can now also trigger on *const* // projections / assoc *const* bindings. - tcx.emit_node_span_lint( + tcx.emit_diag_node_span_lint( UNUSED_ASSOCIATED_TYPE_BOUNDS, hir_id, span, diff --git a/tests/ui/coherence/orphan-check-alias.classic.stderr b/tests/ui/coherence/orphan-check-alias.classic.stderr index 25fde6ee1d23d..06b6bd4eb0fd5 100644 --- a/tests/ui/coherence/orphan-check-alias.classic.stderr +++ b/tests/ui/coherence/orphan-check-alias.classic.stderr @@ -4,10 +4,10 @@ warning[E0210]: type parameter `T` must be covered by another type when it appea LL | impl foreign::Trait2 for ::Assoc { | ^ type parameter `T` must be covered by another type when it appears before the first local type (`B`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 = note: `#[warn(uncovered_param_in_projection)]` (part of `#[warn(future_incompatible)]`) on by default warning: 1 warning emitted diff --git a/tests/ui/coherence/orphan-check-alias.next.stderr b/tests/ui/coherence/orphan-check-alias.next.stderr index 25fde6ee1d23d..06b6bd4eb0fd5 100644 --- a/tests/ui/coherence/orphan-check-alias.next.stderr +++ b/tests/ui/coherence/orphan-check-alias.next.stderr @@ -4,10 +4,10 @@ warning[E0210]: type parameter `T` must be covered by another type when it appea LL | impl foreign::Trait2 for ::Assoc { | ^ type parameter `T` must be covered by another type when it appears before the first local type (`B`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 = note: `#[warn(uncovered_param_in_projection)]` (part of `#[warn(future_incompatible)]`) on by default warning: 1 warning emitted diff --git a/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.classic.stderr b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.classic.stderr index 464413e9f38c8..a000fc2f0bc11 100644 --- a/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.classic.stderr +++ b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.classic.stderr @@ -4,10 +4,10 @@ warning[E0210]: type parameter `T` must be covered by another type when it appea LL | impl foreign::Trait1 for ::Output {} | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 = note: `#[warn(uncovered_param_in_projection)]` (part of `#[warn(future_incompatible)]`) on by default warning: 1 warning emitted diff --git a/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.next.stderr b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.next.stderr index 464413e9f38c8..a000fc2f0bc11 100644 --- a/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.next.stderr +++ b/tests/ui/coherence/orphan-check-projections-not-covering-ambiguity.next.stderr @@ -4,10 +4,10 @@ warning[E0210]: type parameter `T` must be covered by another type when it appea LL | impl foreign::Trait1 for ::Output {} | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 = note: `#[warn(uncovered_param_in_projection)]` (part of `#[warn(future_incompatible)]`) on by default warning: 1 warning emitted diff --git a/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.classic.stderr b/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.classic.stderr index 0465fad21194f..1d71966b18cb7 100644 --- a/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.classic.stderr +++ b/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.classic.stderr @@ -4,10 +4,10 @@ warning[E0210]: type parameter `T` must be covered by another type when it appea LL | impl foreign::Trait0 for <() as Trait>::Assoc {} | ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 = note: `#[warn(uncovered_param_in_projection)]` (part of `#[warn(future_incompatible)]`) on by default warning[E0210]: type parameter `U` must be covered by another type when it appears before the first local type (`LocalTy`) @@ -16,10 +16,10 @@ warning[E0210]: type parameter `U` must be covered by another type when it appea LL | impl foreign::Trait0 for <() as Trait>::Assoc {} | ^ type parameter `U` must be covered by another type when it appears before the first local type (`LocalTy`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 warning: 2 warnings emitted diff --git a/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.next.stderr b/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.next.stderr index 0465fad21194f..1d71966b18cb7 100644 --- a/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.next.stderr +++ b/tests/ui/coherence/orphan-check-projections-not-covering-multiple-params.next.stderr @@ -4,10 +4,10 @@ warning[E0210]: type parameter `T` must be covered by another type when it appea LL | impl foreign::Trait0 for <() as Trait>::Assoc {} | ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 = note: `#[warn(uncovered_param_in_projection)]` (part of `#[warn(future_incompatible)]`) on by default warning[E0210]: type parameter `U` must be covered by another type when it appears before the first local type (`LocalTy`) @@ -16,10 +16,10 @@ warning[E0210]: type parameter `U` must be covered by another type when it appea LL | impl foreign::Trait0 for <() as Trait>::Assoc {} | ^ type parameter `U` must be covered by another type when it appears before the first local type (`LocalTy`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 warning: 2 warnings emitted diff --git a/tests/ui/coherence/orphan-check-projections-not-covering.classic.stderr b/tests/ui/coherence/orphan-check-projections-not-covering.classic.stderr index de34ef7cfd3a1..8ea6496a42d70 100644 --- a/tests/ui/coherence/orphan-check-projections-not-covering.classic.stderr +++ b/tests/ui/coherence/orphan-check-projections-not-covering.classic.stderr @@ -4,10 +4,10 @@ warning[E0210]: type parameter `T` must be covered by another type when it appea LL | impl foreign::Trait0 for ::Output {} | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 = note: `#[warn(uncovered_param_in_projection)]` (part of `#[warn(future_incompatible)]`) on by default warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) @@ -16,10 +16,10 @@ warning[E0210]: type parameter `T` must be covered by another type when it appea LL | impl foreign::Trait0<::Output, Local, T> for Option {} | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) --> $DIR/orphan-check-projections-not-covering.rs:40:6 @@ -27,10 +27,10 @@ warning[E0210]: type parameter `T` must be covered by another type when it appea LL | impl foreign::Trait1 for ::Output {} | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 warning: 3 warnings emitted diff --git a/tests/ui/coherence/orphan-check-projections-not-covering.next.stderr b/tests/ui/coherence/orphan-check-projections-not-covering.next.stderr index de34ef7cfd3a1..8ea6496a42d70 100644 --- a/tests/ui/coherence/orphan-check-projections-not-covering.next.stderr +++ b/tests/ui/coherence/orphan-check-projections-not-covering.next.stderr @@ -4,10 +4,10 @@ warning[E0210]: type parameter `T` must be covered by another type when it appea LL | impl foreign::Trait0 for ::Output {} | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 = note: `#[warn(uncovered_param_in_projection)]` (part of `#[warn(future_incompatible)]`) on by default warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) @@ -16,10 +16,10 @@ warning[E0210]: type parameter `T` must be covered by another type when it appea LL | impl foreign::Trait0<::Output, Local, T> for Option {} | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) --> $DIR/orphan-check-projections-not-covering.rs:40:6 @@ -27,10 +27,10 @@ warning[E0210]: type parameter `T` must be covered by another type when it appea LL | impl foreign::Trait1 for ::Output {} | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 warning: 3 warnings emitted diff --git a/tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr b/tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr index e729bcf225eb1..1289c65b40d06 100644 --- a/tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr +++ b/tests/ui/coherence/orphan-check-projections-unsat-bounds.classic.stderr @@ -4,10 +4,10 @@ warning[E0210]: type parameter `T` must be covered by another type when it appea LL | impl foreign::Trait1 for as Discard>::Output | ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 = note: `#[warn(uncovered_param_in_projection)]` (part of `#[warn(future_incompatible)]`) on by default warning: 1 warning emitted diff --git a/tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr b/tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr index e729bcf225eb1..1289c65b40d06 100644 --- a/tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr +++ b/tests/ui/coherence/orphan-check-projections-unsat-bounds.next.stderr @@ -4,10 +4,10 @@ warning[E0210]: type parameter `T` must be covered by another type when it appea LL | impl foreign::Trait1 for as Discard>::Output | ^ type parameter `T` must be covered by another type when it appears before the first local type (`LocalTy`) | - = 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 #124559 = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + = 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 #124559 = note: `#[warn(uncovered_param_in_projection)]` (part of `#[warn(future_incompatible)]`) on by default warning: 1 warning emitted From 262744eb36c8e5621fcd2b3145c33a2f31c8044c Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 20 Feb 2026 13:27:06 +0000 Subject: [PATCH 79/82] Enable rust.remap-debuginfo in the dist profile Anyone who distributes rustc almost certainly wants to enable this option. It is necessary for reproducibility and for a distributed rustc local paths are useless anyway. And finally it improves privacy if you distribute a local build. --- src/bootstrap/defaults/bootstrap.dist.toml | 2 ++ src/bootstrap/src/core/build_steps/test.rs | 2 ++ src/ci/run.sh | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/defaults/bootstrap.dist.toml b/src/bootstrap/defaults/bootstrap.dist.toml index bb0592ce947ab..cce3f068aabcd 100644 --- a/src/bootstrap/defaults/bootstrap.dist.toml +++ b/src/bootstrap/defaults/bootstrap.dist.toml @@ -24,6 +24,8 @@ channel = "auto-detect" download-rustc = false # Build the llvm-bitcode-linker llvm-bitcode-linker = true +# Required to make builds reproducible. +remap-debuginfo = true [dist] # Use better compression when preparing tarballs. diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index f0fe1c03e7e14..71353397b207a 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3416,6 +3416,8 @@ fn distcheck_plain_source_tarball(builder: &Builder<'_>, plain_src_dir: &Path) { command("./configure") .arg("--set") .arg("rust.omit-git-hash=false") + .arg("--set") + .arg("rust.remap-debuginfo=false") .args(&configure_args) .arg("--enable-vendor") .current_dir(plain_src_dir) diff --git a/src/ci/run.sh b/src/ci/run.sh index b486f0525f40d..215292965e65d 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -116,7 +116,6 @@ if [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then else RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-static-stdcpp" fi - RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.remap-debuginfo" if [ "$DEPLOY_ALT" != "" ] && isLinux; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --debuginfo-level=2" @@ -139,6 +138,8 @@ if [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-debug-assertions" fi else + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.remap-debuginfo=false" + # We almost always want debug assertions enabled, but sometimes this takes too # long for too little benefit, so we just turn them off. if [ "$NO_DEBUG_ASSERTIONS" = "" ]; then From 266fe958ed29a2b1e7a1ea56bfc3a3083ad83a3d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 23 Feb 2026 14:55:52 +0100 Subject: [PATCH 80/82] Return earlier in `diag_lint_level` to prevent cases where a printed ty in a `Diagnostic` message is not emitted --- compiler/rustc_middle/src/lint.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 4204855f199d4..28ca6f9fcac20 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -573,6 +573,20 @@ pub fn diag_lint_level<'a, D: Diagnostic<'a, ()> + 'a>( false }; + if disable_suggestions { + // If this is a future incompatible that is not an edition fixing lint + // it'll become a hard error, so we have to emit *something*. Also, + // if this lint occurs in the expansion of a macro from an external crate, + // allow individual lints to opt-out from being reported. + let incompatible = future_incompatible.is_some_and(|f| f.reason.edition().is_none()); + + if !incompatible && !lint.report_in_external_macro { + // Don't continue further, since we don't want to have + // `diag_span_note_once` called for a diagnostic that isn't emitted. + return; + } + } + let mut err: Diag<'_, ()> = if !skip { decorate(sess.dcx(), err_level) } else { @@ -597,20 +611,6 @@ pub fn diag_lint_level<'a, D: Diagnostic<'a, ()> + 'a>( // Any suggestions made here are likely to be incorrect, so anything we // emit shouldn't be automatically fixed by rustfix. err.disable_suggestions(); - - // If this is a future incompatible that is not an edition fixing lint - // it'll become a hard error, so we have to emit *something*. Also, - // if this lint occurs in the expansion of a macro from an external crate, - // allow individual lints to opt-out from being reported. - let incompatible = future_incompatible.is_some_and(|f| f.reason.edition().is_none()); - - if !incompatible && !lint.report_in_external_macro { - err.cancel(); - - // Don't continue further, since we don't want to have - // `diag_span_note_once` called for a diagnostic that isn't emitted. - return; - } } err.is_lint(lint.name_lower(), has_future_breakage); From b8f524147be3ca2a23fef9962c3382353e6f5ecc Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 23 Feb 2026 14:56:05 +0100 Subject: [PATCH 81/82] Migrate `rustc_hir_typeck` to use `TyCtxt::emit_diag_node_span_lint` --- compiler/rustc_hir_typeck/src/cast.rs | 6 +-- compiler/rustc_hir_typeck/src/errors.rs | 16 ++++---- compiler/rustc_hir_typeck/src/fallback.rs | 4 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 2 +- .../rustc_hir_typeck/src/method/confirm.rs | 2 +- .../never-type-fallback-breaking.e2021.stderr | 40 +++++++++---------- .../dependency-on-fallback-to-unit.stderr | 16 ++++---- ...fallback-unconstrained-return.e2021.stderr | 8 ++-- ...nt-suggest-turbofish-from-expansion.stderr | 8 ++-- ...int-breaking-2024-assign-underscore.stderr | 8 ++-- ...-fallback-flowing-into-unsafe.e2015.stderr | 40 +++++++++---------- ...-fallback-flowing-into-unsafe.e2024.stderr | 40 +++++++++---------- 12 files changed, 95 insertions(+), 95 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index eaa87f1d4cbc6..af529386eee2a 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -687,7 +687,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { }; let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); - fcx.tcx.emit_node_span_lint( + fcx.tcx.emit_diag_node_span_lint( lint, self.expr.hir_id, self.span, @@ -1125,7 +1125,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { }; let lint = errors::LossyProvenancePtr2Int { expr_ty, cast_ty, sugg }; - fcx.tcx.emit_node_span_lint( + fcx.tcx.emit_diag_node_span_lint( lint::builtin::LOSSY_PROVENANCE_CASTS, self.expr.hir_id, self.span, @@ -1141,7 +1141,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); let lint = errors::LossyProvenanceInt2Ptr { expr_ty, cast_ty, sugg }; - fcx.tcx.emit_node_span_lint( + fcx.tcx.emit_diag_node_span_lint( lint::builtin::FUZZY_PROVENANCE_CASTS, self.expr.hir_id, self.span, diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 5c44836adcd01..8251669ea8d2a 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -11,7 +11,7 @@ use rustc_errors::{ }; use rustc_hir as hir; use rustc_hir::ExprKind; -use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; use rustc_span::edition::{Edition, LATEST_STABLE_EDITION}; use rustc_span::source_map::Spanned; @@ -215,7 +215,7 @@ pub(crate) struct MissingParenthesesInRange<'tcx> { pub add_missing_parentheses: Option, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] pub(crate) enum NeverTypeFallbackFlowingIntoUnsafe { #[help("specify the type explicitly")] #[diag("never type fallback affects this call to an `unsafe` function")] @@ -249,7 +249,7 @@ pub(crate) enum NeverTypeFallbackFlowingIntoUnsafe { }, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[help("specify the types explicitly")] #[diag("this function depends on never type fallback being `()`")] pub(crate) struct DependencyOnUnitNeverTypeFallback<'tcx> { @@ -372,7 +372,7 @@ impl Subdiagnostic for TypeMismatchFruTypo { } } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")] #[help( "if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead" @@ -411,7 +411,7 @@ pub(crate) struct LossyProvenanceInt2PtrSuggestion { pub hi: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "under strict provenance it is considered bad style to cast pointer `{$expr_ty}` to integer `{$cast_ty}`" )] @@ -769,7 +769,7 @@ pub(crate) struct SuggestPtrNullMut { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag( "trivial {$numeric -> [true] numeric cast @@ -1109,7 +1109,7 @@ pub(crate) struct InnerItem { pub span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("can't reference `Self` constructor from outer item")] pub(crate) struct SelfCtorFromOuterItemLint { #[label( @@ -1214,7 +1214,7 @@ pub(crate) struct ReplaceCommaWithSemicolon { pub descr: &'static str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag("trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait")] pub(crate) struct SupertraitItemShadowing { pub item: Symbol, diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index c10e7f3bfb8b9..79fdfd3563676 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -238,7 +238,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { let sugg = self.try_to_suggest_annotations(&[root_vid], coercion_graph); for (hir_id, span, reason) in affected_unsafe_infer_vars { - self.tcx.emit_node_span_lint( + self.tcx.emit_diag_node_span_lint( lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, hir_id, span, @@ -304,7 +304,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { { self.adjust_fulfillment_error_for_expr_obligation(never_error); let sugg = self.try_to_suggest_annotations(diverging_vids, coercions); - self.tcx.emit_node_span_lint( + self.tcx.emit_diag_node_span_lint( lint::builtin::DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, self.tcx.local_def_id_to_hir_id(self.body_id), self.tcx.def_span(self.body_id), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 1931222bba30e..81523cdf633d5 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1145,7 +1145,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); return (Ty::new_error(self.tcx, guar), res); } else { - self.tcx.emit_node_span_lint( + self.tcx.emit_diag_node_span_lint( SELF_CONSTRUCTOR_FROM_OUTER_ITEM, hir_id, path_span, diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 608bc7dffd9c0..6e5b05a8191fc 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -717,7 +717,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { SupertraitItemShadowee::Several { traits: traits.into(), spans: spans.into() } }; - self.tcx.emit_node_span_lint( + self.tcx.emit_diag_node_span_lint( RESOLVING_TO_ITEMS_SHADOWING_SUPERTRAIT_ITEMS, segment.hir_id, segment.ident.span, diff --git a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr index a910b19d9bf76..17dc5fc8795bc 100644 --- a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr +++ b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr @@ -4,14 +4,14 @@ error: this function depends on never type fallback being `()` LL | fn m() { | ^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/never-type-fallback-breaking.rs:24:17 | LL | true => Default::default(), | ^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -24,14 +24,14 @@ error: this function depends on never type fallback being `()` LL | fn q() -> Option<()> { | ^^^^^^^^^^^^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/never-type-fallback-breaking.rs:39:5 | LL | deserialize()?; | ^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see help: use `()` annotations to avoid fallback changes | LL | deserialize::<()>()?; @@ -43,14 +43,14 @@ error: this function depends on never type fallback being `()` LL | fn meow() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `(): From` will fail --> $DIR/never-type-fallback-breaking.rs:52:5 | LL | help(1)?; | ^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see help: use `()` annotations to avoid fallback changes | LL | help::<(), _>(1)?; @@ -62,14 +62,14 @@ error: this function depends on never type fallback being `()` LL | pub fn fallback_return() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/never-type-fallback-breaking.rs:64:19 | LL | takes_apit(|| Default::default())?; | ^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see help: use `()` annotations to avoid fallback changes | LL | takes_apit::<()>(|| Default::default())?; @@ -81,14 +81,14 @@ error: this function depends on never type fallback being `()` LL | fn fully_apit() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/never-type-fallback-breaking.rs:78:17 | LL | takes_apit2(mk()?); | ^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see help: use `()` annotations to avoid fallback changes | LL | takes_apit2(mk::<()>()?); @@ -103,14 +103,14 @@ error: this function depends on never type fallback being `()` LL | fn m() { | ^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/never-type-fallback-breaking.rs:24:17 | LL | true => Default::default(), | ^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -124,14 +124,14 @@ error: this function depends on never type fallback being `()` LL | fn q() -> Option<()> { | ^^^^^^^^^^^^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/never-type-fallback-breaking.rs:39:5 | LL | deserialize()?; | ^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -145,14 +145,14 @@ error: this function depends on never type fallback being `()` LL | fn meow() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `(): From` will fail --> $DIR/never-type-fallback-breaking.rs:52:5 | LL | help(1)?; | ^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -166,14 +166,14 @@ error: this function depends on never type fallback being `()` LL | pub fn fallback_return() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/never-type-fallback-breaking.rs:64:19 | LL | takes_apit(|| Default::default())?; | ^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -187,14 +187,14 @@ error: this function depends on never type fallback being `()` LL | fn fully_apit() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/never-type-fallback-breaking.rs:78:17 | LL | takes_apit2(mk()?); | ^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | diff --git a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr index 0b8d9657c7b0a..bb9de45637a09 100644 --- a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr +++ b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr @@ -4,14 +4,14 @@ error: this function depends on never type fallback being `()` LL | fn def() { | ^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/dependency-on-fallback-to-unit.rs:10:19 | LL | false => <_>::default(), | ^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -25,14 +25,14 @@ error: this function depends on never type fallback being `()` LL | fn question_mark() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/dependency-on-fallback-to-unit.rs:20:5 | LL | deserialize()?; | ^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see help: use `()` annotations to avoid fallback changes | LL | deserialize::<()>()?; @@ -47,14 +47,14 @@ error: this function depends on never type fallback being `()` LL | fn def() { | ^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/dependency-on-fallback-to-unit.rs:10:19 | LL | false => <_>::default(), | ^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -69,14 +69,14 @@ error: this function depends on never type fallback being `()` LL | fn question_mark() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/dependency-on-fallback-to-unit.rs:20:5 | LL | deserialize()?; | ^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | diff --git a/tests/ui/never_type/diverging-fallback-unconstrained-return.e2021.stderr b/tests/ui/never_type/diverging-fallback-unconstrained-return.e2021.stderr index eb79cf5dd1559..40e7e30274be8 100644 --- a/tests/ui/never_type/diverging-fallback-unconstrained-return.e2021.stderr +++ b/tests/ui/never_type/diverging-fallback-unconstrained-return.e2021.stderr @@ -4,14 +4,14 @@ error: this function depends on never type fallback being `()` LL | fn main() { | ^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: UnitReturn` will fail --> $DIR/diverging-fallback-unconstrained-return.rs:37:23 | LL | let _ = if true { unconstrained_return() } else { panic!() }; | ^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -27,14 +27,14 @@ error: this function depends on never type fallback being `()` LL | fn main() { | ^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: UnitReturn` will fail --> $DIR/diverging-fallback-unconstrained-return.rs:37:23 | LL | let _ = if true { unconstrained_return() } else { panic!() }; | ^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | diff --git a/tests/ui/never_type/dont-suggest-turbofish-from-expansion.stderr b/tests/ui/never_type/dont-suggest-turbofish-from-expansion.stderr index 9795acffe7058..73c2a5bd3907e 100644 --- a/tests/ui/never_type/dont-suggest-turbofish-from-expansion.stderr +++ b/tests/ui/never_type/dont-suggest-turbofish-from-expansion.stderr @@ -4,14 +4,14 @@ error: this function depends on never type fallback being `()` LL | fn main() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/dont-suggest-turbofish-from-expansion.rs:12:23 | LL | let created = create_ok_default()?; | ^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -27,14 +27,14 @@ error: this function depends on never type fallback being `()` LL | fn main() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/dont-suggest-turbofish-from-expansion.rs:12:23 | LL | let created = create_ok_default()?; | ^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | diff --git a/tests/ui/never_type/lint-breaking-2024-assign-underscore.stderr b/tests/ui/never_type/lint-breaking-2024-assign-underscore.stderr index 96749de1195eb..a0a300aae6af7 100644 --- a/tests/ui/never_type/lint-breaking-2024-assign-underscore.stderr +++ b/tests/ui/never_type/lint-breaking-2024-assign-underscore.stderr @@ -4,14 +4,14 @@ error: this function depends on never type fallback being `()` LL | fn test() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/lint-breaking-2024-assign-underscore.rs:11:9 | LL | _ = foo()?; | ^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -27,14 +27,14 @@ error: this function depends on never type fallback being `()` LL | fn test() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! - = note: for more information, see = help: specify the types explicitly note: in edition 2024, the requirement `!: Default` will fail --> $DIR/lint-breaking-2024-assign-underscore.rs:11:9 | LL | _ = foo()?; | ^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see = note: `#[deny(dependency_on_unit_never_type_fallback)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr index 84e143b024acc..730e449704859 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr @@ -4,9 +4,9 @@ error: never type fallback affects this call to an `unsafe` function LL | unsafe { mem::zeroed() } | ^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -19,9 +19,9 @@ error: never type fallback affects this call to an `unsafe` function LL | core::mem::transmute(Zst) | ^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly help: use `()` annotations to avoid fallback changes | LL | core::mem::transmute::<_, ()>(Zst) @@ -33,9 +33,9 @@ error: never type fallback affects this union access LL | unsafe { Union { a: () }.b } | ^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly error: never type fallback affects this raw pointer dereference --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:55:18 @@ -43,9 +43,9 @@ error: never type fallback affects this raw pointer dereference LL | unsafe { *ptr::from_ref(&()).cast() } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly help: use `()` annotations to avoid fallback changes | LL | unsafe { *ptr::from_ref(&()).cast::<()>() } @@ -57,9 +57,9 @@ error: never type fallback affects this call to an `unsafe` function LL | unsafe { internally_create(x) } | ^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly help: use `()` annotations to avoid fallback changes | LL | unsafe { internally_create::<()>(x) } @@ -71,9 +71,9 @@ error: never type fallback affects this call to an `unsafe` function LL | unsafe { zeroed() } | ^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly help: use `()` annotations to avoid fallback changes | LL | let zeroed = mem::zeroed::<()>; @@ -85,9 +85,9 @@ error: never type fallback affects this `unsafe` function LL | let zeroed = mem::zeroed; | ^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly help: use `()` annotations to avoid fallback changes | LL | let zeroed = mem::zeroed::<()>; @@ -99,9 +99,9 @@ error: never type fallback affects this `unsafe` function LL | let f = internally_create; | ^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly help: use `()` annotations to avoid fallback changes | LL | let f = internally_create::<()>; @@ -113,9 +113,9 @@ error: never type fallback affects this call to an `unsafe` method LL | S(marker::PhantomData).create_out_of_thin_air() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly error: never type fallback affects this call to an `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:155:19 @@ -126,9 +126,9 @@ LL | match send_message::<_ /* ?0 */>() { LL | msg_send!(); | ----------- in this macro invocation | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: this error originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 10 previous errors @@ -140,9 +140,9 @@ error: never type fallback affects this call to an `unsafe` function LL | unsafe { mem::zeroed() } | ^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -156,9 +156,9 @@ error: never type fallback affects this call to an `unsafe` function LL | core::mem::transmute(Zst) | ^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -172,9 +172,9 @@ error: never type fallback affects this union access LL | unsafe { Union { a: () }.b } | ^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default Future breakage diagnostic: @@ -184,9 +184,9 @@ error: never type fallback affects this raw pointer dereference LL | unsafe { *ptr::from_ref(&()).cast() } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -200,9 +200,9 @@ error: never type fallback affects this call to an `unsafe` function LL | unsafe { internally_create(x) } | ^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -216,9 +216,9 @@ error: never type fallback affects this call to an `unsafe` function LL | unsafe { zeroed() } | ^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -232,9 +232,9 @@ error: never type fallback affects this `unsafe` function LL | let zeroed = mem::zeroed; | ^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -248,9 +248,9 @@ error: never type fallback affects this `unsafe` function LL | let f = internally_create; | ^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -264,9 +264,9 @@ error: never type fallback affects this call to an `unsafe` method LL | S(marker::PhantomData).create_out_of_thin_air() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default Future breakage diagnostic: @@ -279,9 +279,9 @@ LL | match send_message::<_ /* ?0 */>() { LL | msg_send!(); | ----------- in this macro invocation | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default = note: this error originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr index 09e4d02384e92..631b6dae92942 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr @@ -4,9 +4,9 @@ error: never type fallback affects this call to an `unsafe` function LL | unsafe { mem::zeroed() } | ^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -19,9 +19,9 @@ error: never type fallback affects this call to an `unsafe` function LL | core::mem::transmute(Zst) | ^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly help: use `()` annotations to avoid fallback changes | LL | core::mem::transmute::<_, ()>(Zst) @@ -33,9 +33,9 @@ error: never type fallback affects this union access LL | unsafe { Union { a: () }.b } | ^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly error: never type fallback affects this raw pointer dereference --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:55:18 @@ -43,9 +43,9 @@ error: never type fallback affects this raw pointer dereference LL | unsafe { *ptr::from_ref(&()).cast() } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly help: use `()` annotations to avoid fallback changes | LL | unsafe { *ptr::from_ref(&()).cast::<()>() } @@ -57,9 +57,9 @@ error: never type fallback affects this call to an `unsafe` function LL | unsafe { internally_create(x) } | ^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly help: use `()` annotations to avoid fallback changes | LL | unsafe { internally_create::<()>(x) } @@ -71,9 +71,9 @@ error: never type fallback affects this call to an `unsafe` function LL | unsafe { zeroed() } | ^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly help: use `()` annotations to avoid fallback changes | LL | let zeroed = mem::zeroed::<()>; @@ -85,9 +85,9 @@ error: never type fallback affects this `unsafe` function LL | let zeroed = mem::zeroed; | ^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly help: use `()` annotations to avoid fallback changes | LL | let zeroed = mem::zeroed::<()>; @@ -99,9 +99,9 @@ error: never type fallback affects this `unsafe` function LL | let f = internally_create; | ^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly help: use `()` annotations to avoid fallback changes | LL | let f = internally_create::<()>; @@ -113,9 +113,9 @@ error: never type fallback affects this call to an `unsafe` method LL | S(marker::PhantomData).create_out_of_thin_air() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly error: never type fallback affects this call to an `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:155:19 @@ -126,9 +126,9 @@ LL | match send_message::<_ /* ?0 */>() { LL | msg_send!(); | ----------- in this macro invocation | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: this error originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) warning: the type `!` does not permit zero-initialization @@ -149,9 +149,9 @@ error: never type fallback affects this call to an `unsafe` function LL | unsafe { mem::zeroed() } | ^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -165,9 +165,9 @@ error: never type fallback affects this call to an `unsafe` function LL | core::mem::transmute(Zst) | ^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -181,9 +181,9 @@ error: never type fallback affects this union access LL | unsafe { Union { a: () }.b } | ^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default Future breakage diagnostic: @@ -193,9 +193,9 @@ error: never type fallback affects this raw pointer dereference LL | unsafe { *ptr::from_ref(&()).cast() } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -209,9 +209,9 @@ error: never type fallback affects this call to an `unsafe` function LL | unsafe { internally_create(x) } | ^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -225,9 +225,9 @@ error: never type fallback affects this call to an `unsafe` function LL | unsafe { zeroed() } | ^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -241,9 +241,9 @@ error: never type fallback affects this `unsafe` function LL | let zeroed = mem::zeroed; | ^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -257,9 +257,9 @@ error: never type fallback affects this `unsafe` function LL | let f = internally_create; | ^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `()` annotations to avoid fallback changes | @@ -273,9 +273,9 @@ error: never type fallback affects this call to an `unsafe` method LL | S(marker::PhantomData).create_out_of_thin_air() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default Future breakage diagnostic: @@ -288,9 +288,9 @@ LL | match send_message::<_ /* ?0 */>() { LL | msg_send!(); | ----------- in this macro invocation | + = help: specify the type explicitly = warning: this changes meaning in Rust 2024 and in a future release in all editions! = note: for more information, see - = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` (part of `#[deny(rust_2024_compatibility)]`) on by default = note: this error originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) From e62880676ad0c2ec908c20c1f3234f0629de64d2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 23 Feb 2026 16:25:27 +0100 Subject: [PATCH 82/82] rustdoc: Improve sentence for documented empty impl blocks --- src/librustdoc/html/render/mod.rs | 7 ++++--- tests/rustdoc-html/impl/empty-impl-block.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 5d6909f0bb97f..556d383a0e9fb 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2343,9 +2343,10 @@ fn render_impl_summary( if let Some(doc) = doc { if impl_is_empty { w.write_str( - "
\ -
This impl block contains no items.
\ -
", + "\ +
\ +
This impl block contains no public items.
\ +
", )?; } write!(w, "
{doc}
")?; diff --git a/tests/rustdoc-html/impl/empty-impl-block.rs b/tests/rustdoc-html/impl/empty-impl-block.rs index 91fd4a64ef939..7ca5813aae2ef 100644 --- a/tests/rustdoc-html/impl/empty-impl-block.rs +++ b/tests/rustdoc-html/impl/empty-impl-block.rs @@ -4,7 +4,7 @@ pub struct Foo; //@ has - '//*[@class="docblock"]' 'Hello empty impl block!' -//@ has - '//*[@class="item-info"]' 'This impl block contains no items.' +//@ has - '//*[@class="item-info"]' 'This impl block contains no public items.' /// Hello empty impl block! impl Foo {} // We ensure that this empty impl block without doc isn't rendered.