From d119977fad9c5c667371f1bf22a4dde206ff8cfc Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 6 May 2026 23:23:59 +0300 Subject: [PATCH 01/94] Diagnose inactive code in macros But only if the source is code passed to the macro, not code inside the macro. --- .../src/handlers/inactive_code.rs | 59 +++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs index 09f3e8bfb319b..71cac6af1346f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs @@ -10,10 +10,8 @@ pub(crate) fn inactive_code( ctx: &DiagnosticsContext<'_, '_>, d: &hir::InactiveCode, ) -> Option { - // If there's inactive code somewhere in a macro, don't propagate to the call-site. - if d.node.file_id.is_macro() { - return None; - } + // If there's inactive code somewhere in a macro that doesn't map to something in the call, don't propagate to the call-site. + d.node.map(|it| it.text_range()).original_node_file_range_rooted_opt(ctx.db())?; let inactive = DnfExpr::new(&d.cfg).why_inactive(&d.opts); let mut message = "code is inactive due to #[cfg] directives".to_owned(); @@ -252,4 +250,57 @@ fn foo() {} ide_db::FileRange { file_id: file_id.file_id(&db), range: full_file_range }, ); } + + #[test] + fn cfg_in_macro_does_not_diagnose_the_whole_call() { + check( + r#" +macro_rules! m { + ($e:item) => { + #[cfg(false)] + const _: () = (); + + $e + }; +} + +m! { + fn foo() {} +} + "#, + ); + } + + #[test] + fn in_macro() { + check( + r#" +macro_rules! m { + ($e:item) => { + $e + }; +} + +m! { + #[cfg(false)] fn foo() {} + // ^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: false is disabled +} + "#, + ); + check( + r#" +macro_rules! m { + ($e:item) => { + #[cfg(false)] + $e + }; +} + +m! { + fn foo() {} + // ^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: false is disabled +} + "#, + ); + } } From be1869080454203c0ee90ade616d87ba6d5eb2d6 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 6 May 2026 23:48:08 +0300 Subject: [PATCH 02/94] Allow disabling the inactive-code diagnostic in code With `#[allow(rust_analyzer::inactive_code)]`. --- src/tools/rust-analyzer/Cargo.lock | 1 + .../crates/ide-diagnostics/Cargo.toml | 1 + .../src/handlers/inactive_code.rs | 30 +++++++++-- .../src/handlers/mismatched_arg_count.rs | 4 ++ .../src/handlers/no_such_field.rs | 8 +++ .../crates/ide-diagnostics/src/lib.rs | 54 +++++++++++++------ .../src/tests/overly_long_real_world_cases.rs | 1 + 7 files changed, 79 insertions(+), 20 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index be9a8c491572f..00a212f7c4c2c 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1151,6 +1151,7 @@ dependencies = [ "itertools 0.14.0", "paths", "serde_json", + "smallvec", "stdx", "syntax", "test-fixture", diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml index ddf5999036d21..ce836197571a0 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml @@ -18,6 +18,7 @@ either.workspace = true itertools.workspace = true serde_json.workspace = true tracing.workspace = true +smallvec.workspace = true # local deps stdx.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs index 71cac6af1346f..9e38d8f1b9e43 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs @@ -6,6 +6,8 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: inactive-code // // This diagnostic is shown for code with inactive `#[cfg]` attributes. +// +// It can be disabled selectively with `#[allow(rust_analyzer::inactive_code)]`. pub(crate) fn inactive_code( ctx: &DiagnosticsContext<'_, '_>, d: &hir::InactiveCode, @@ -26,10 +28,11 @@ pub(crate) fn inactive_code( } } // FIXME: This shouldn't be a diagnostic - let res = Diagnostic::new( - DiagnosticCode::Ra("inactive-code", Severity::WeakWarning), + let res = Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RaLint("inactive_code", Severity::WeakWarning), message, - ctx.sema.diagnostics_display_range(d.node), + d.node, ) .stable() .with_unused(true); @@ -237,7 +240,7 @@ fn foo() {} }; assert_eq!( inactive_code.code, - DiagnosticCode::Ra("inactive-code", ide_db::Severity::WeakWarning) + DiagnosticCode::RaLint("inactive_code", ide_db::Severity::WeakWarning) ); assert_eq!( inactive_code.message, @@ -299,6 +302,25 @@ macro_rules! m { m! { fn foo() {} // ^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: false is disabled +} + "#, + ); + } + + #[test] + fn allow() { + check( + r#" +macro_rules! m { + ($e:item) => { + #[cfg(false)] + #[allow(rust_analyzer::inactive_code)] + $e + }; +} + +m! { + fn foo() {} } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index f6293e35d0c37..754d90d112b85 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -375,6 +375,8 @@ fn main() { fn cfgd_out_call_arguments() { check_diagnostics( r#" +#![allow(rust_analyzer::inactive_code)] + struct C(#[cfg(FALSE)] ()); impl C { fn new() -> Self { @@ -398,6 +400,8 @@ fn main() { fn cfgd_out_fn_params() { check_diagnostics( r#" +#![allow(rust_analyzer::inactive_code)] + fn foo(#[cfg(NEVER)] x: ()) {} struct S; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs index 7959fddc757f4..a96b92dd4a123 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -153,6 +153,8 @@ mod tests { fn dont_work_for_field_with_disabled_cfg() { check_diagnostics( r#" +#![allow(rust_analyzer::inactive_code)] + struct Test { #[cfg(feature = "hello")] test: u32, @@ -224,6 +226,8 @@ impl S { check_diagnostics( r#" //- /lib.rs crate:foo cfg:feature=foo +#![allow(rust_analyzer::inactive_code)] + struct MyStruct { my_val: usize, #[cfg(feature = "foo")] @@ -249,6 +253,8 @@ impl MyStruct { check_diagnostics( r#" //- /lib.rs crate:foo cfg:feature=foo +#![allow(rust_analyzer::inactive_code)] + enum Foo { #[cfg(not(feature = "foo"))] Buz, @@ -272,6 +278,8 @@ fn test_fn(f: Foo) { check_diagnostics( r#" //- /lib.rs crate:foo cfg:feature=foo +#![allow(rust_analyzer::inactive_code)] + struct S { #[cfg(feature = "foo")] foo: u32, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index e2e465e26c78d..f77c20b085435 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -113,9 +113,11 @@ use ide_db::{ rename::RenameConfig, source_change::SourceChange, }; +use smallvec::{SmallVec, smallvec}; use syntax::{ AstPtr, Edition, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, ast::{self, AstNode}, + format_smolstr, }; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] @@ -125,6 +127,7 @@ pub enum DiagnosticCode { RustcLint(&'static str), Clippy(&'static str), Ra(&'static str, Severity), + RaLint(&'static str, Severity), } impl DiagnosticCode { @@ -142,7 +145,7 @@ impl DiagnosticCode { DiagnosticCode::Clippy(e) => { format!("https://rust-lang.github.io/rust-clippy/master/#/{e}") } - DiagnosticCode::Ra(e, _) => { + DiagnosticCode::Ra(e, _) | DiagnosticCode::RaLint(e, _) => { format!("https://rust-analyzer.github.io/book/diagnostics.html#{e}") } } @@ -153,7 +156,8 @@ impl DiagnosticCode { DiagnosticCode::RustcHardError(r) | DiagnosticCode::RustcLint(r) | DiagnosticCode::Clippy(r) - | DiagnosticCode::Ra(r, _) => r, + | DiagnosticCode::Ra(r, _) + | DiagnosticCode::RaLint(r, _) => r, DiagnosticCode::SyntaxError => "syntax-error", } } @@ -190,7 +194,7 @@ impl Diagnostic { // FIXME: We can make this configurable, and if the user uses `cargo clippy` on flycheck, we can // make it normal warning. DiagnosticCode::Clippy(_) => Severity::WeakWarning, - DiagnosticCode::Ra(_, s) => s, + DiagnosticCode::Ra(_, s) | DiagnosticCode::RaLint(_, s) => s, }, unused: false, experimental: true, @@ -529,7 +533,14 @@ pub fn semantic_diagnostics( let mut lints = res .iter_mut() - .filter(|it| matches!(it.code, DiagnosticCode::Clippy(_) | DiagnosticCode::RustcLint(_))) + .filter(|it| { + matches!( + it.code, + DiagnosticCode::Clippy(_) + | DiagnosticCode::RustcLint(_) + | DiagnosticCode::RaLint(..) + ) + }) .filter_map(|it| Some((it.main_node(&ctx.sema)?, it))) .collect::>(); @@ -602,7 +613,7 @@ fn handle_diag_from_macros( struct BuiltLint { lint: &'static Lint, - groups: Vec<&'static str>, + groups: SmallVec<[SmolStr; 5]>, } static RUSTC_LINTS: LazyLock> = @@ -623,12 +634,17 @@ fn build_lints_map( ) -> FxHashMap<&'static str, BuiltLint> { let mut map_with_prefixes: FxHashMap<_, _> = lints .iter() - .map(|lint| (lint.label, BuiltLint { lint, groups: vec![lint.label, "__RA_EVERY_LINT"] })) + .map(|lint| { + ( + lint.label, + BuiltLint { lint, groups: smallvec![lint.label.into(), "__RA_EVERY_LINT".into()] }, + ) + }) .collect(); for g in lint_group { let mut add_children = |label: &'static str| { for child in g.children { - map_with_prefixes.get_mut(child).unwrap().groups.push(label); + map_with_prefixes.get_mut(child).unwrap().groups.push(label.into()); } }; add_children(g.lint.label); @@ -649,12 +665,15 @@ fn handle_lints( edition: Edition, ) { for (node, diag) in diagnostics { - let lint = match diag.code { - DiagnosticCode::RustcLint(lint) => RUSTC_LINTS[lint].lint, - DiagnosticCode::Clippy(lint) => CLIPPY_LINTS[lint].lint, - _ => panic!("non-lint passed to `handle_lints()`"), + let default_severity = 'find_severity: { + let lint = match diag.code { + DiagnosticCode::RustcLint(lint) => RUSTC_LINTS[lint].lint, + DiagnosticCode::Clippy(lint) => CLIPPY_LINTS[lint].lint, + DiagnosticCode::RaLint(_, severity) => break 'find_severity severity, + _ => panic!("non-lint passed to `handle_lints()`"), + }; + default_lint_severity(lint, edition) }; - let default_severity = default_lint_severity(lint, edition); if !(default_severity == Severity::Allow && diag.severity == Severity::WeakWarning) { diag.severity = default_severity; } @@ -754,13 +773,13 @@ fn lint_attrs( #[derive(Debug)] struct LintGroups { - groups: &'static [&'static str], + groups: SmallVec<[SmolStr; 5]>, inside_warnings: bool, } impl LintGroups { fn contains(&self, group: &str) -> bool { - self.groups.contains(&group) || (self.inside_warnings && group == "warnings") + self.groups.iter().any(|g| g == group) || (self.inside_warnings && group == "warnings") } } @@ -769,12 +788,15 @@ fn lint_groups(lint: &DiagnosticCode, edition: Edition) -> LintGroups { DiagnosticCode::RustcLint(name) => { let lint = &RUSTC_LINTS[name]; let inside_warnings = default_lint_severity(lint.lint, edition) == Severity::Warning; - (&lint.groups, inside_warnings) + (lint.groups.clone(), inside_warnings) } DiagnosticCode::Clippy(name) => { let lint = &CLIPPY_LINTS[name]; let inside_warnings = default_lint_severity(lint.lint, edition) == Severity::Warning; - (&lint.groups, inside_warnings) + (lint.groups.clone(), inside_warnings) + } + DiagnosticCode::RaLint(name, severity) => { + (smallvec![format_smolstr!("rust_analyzer::{name}")], *severity == Severity::Warning) } _ => panic!("non-lint passed to `handle_lints()`"), }; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs index 301613e920191..34cf80a85b98e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs @@ -2729,6 +2729,7 @@ tracing::error!(); "unresolved-macro-call", "syntax-error", "macro-error", + "inactive_code", ], ); } From 468c78acd1e70bfa5716519be4c1640f89e2dbba Mon Sep 17 00:00:00 2001 From: Hanna Kruppe Date: Thu, 4 Jun 2026 14:23:48 +0200 Subject: [PATCH 03/94] std::random: use little-endian for reproducibility See added comment for rationale. Also note that `rand` has defaulted to LE for many years, apparently without any objections. --- library/core/src/random.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/core/src/random.rs b/library/core/src/random.rs index 06f4f30efe2b5..7b2666349cd7b 100644 --- a/library/core/src/random.rs +++ b/library/core/src/random.rs @@ -40,7 +40,10 @@ macro_rules! impl_primitive { fn sample(&self, source: &mut (impl RandomSource + ?Sized)) -> $t { let mut bytes = (0 as $t).to_ne_bytes(); source.fill_bytes(&mut bytes); - <$t>::from_ne_bytes(bytes) + // Always use little-endian for reproducibility. Since the vast majority of code is + // mainly or exclusively tested on LE targets, giving different PRNG results for the + // same seed on BE targets is a serious portability hazard. + <$t>::from_le_bytes(bytes) } } }; From 6dfb135ccf074b7e24dc8e0d306c4b8b034ce09e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 29 Jun 2026 01:04:20 +0300 Subject: [PATCH 04/94] Respect `references.exclude[Tests/Imports]` in references lens --- .../crates/ide/src/annotations.rs | 45 +++++++++++++++++-- .../rust-analyzer/src/cli/analysis_stats.rs | 2 + .../crates/rust-analyzer/src/config.rs | 32 ++++++------- 3 files changed, 61 insertions(+), 18 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index f716f94d7141b..884bc111caeaf 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -43,6 +43,8 @@ pub struct AnnotationConfig<'a> { pub annotate_references: bool, pub annotate_method_references: bool, pub annotate_enum_variant_references: bool, + pub references_exclude_imports: bool, + pub references_exclude_tests: bool, pub location: AnnotationLocation, pub filter_adjacent_derive_implementations: bool, pub ra_fixture: RaFixtureConfig<'a>, @@ -219,8 +221,8 @@ pub(crate) fn resolve_annotation( &FindAllRefsConfig { search_scope: None, ra_fixture: config.ra_fixture, - exclude_imports: false, - exclude_tests: false, + exclude_imports: config.references_exclude_imports, + exclude_tests: config.references_exclude_tests, }, ) .map(|result| { @@ -262,6 +264,8 @@ mod tests { annotate_references: true, annotate_method_references: true, annotate_enum_variant_references: true, + references_exclude_imports: false, + references_exclude_tests: false, location: AnnotationLocation::AboveName, ra_fixture: RaFixtureConfig::default(), filter_adjacent_derive_implementations: false, @@ -278,7 +282,7 @@ mod tests { .annotations(config, file_id) .unwrap() .into_iter() - .map(|annotation| analysis.resolve_annotation(&DEFAULT_CONFIG, annotation).unwrap()) + .map(|annotation| analysis.resolve_annotation(config, annotation).unwrap()) .collect(); expect.assert_debug_eq(&annotations); @@ -1045,4 +1049,39 @@ struct Foo; &AnnotationConfig { location: AnnotationLocation::AboveWholeItem, ..DEFAULT_CONFIG }, ); } + + #[test] + fn refs_exclude_tests() { + check_with_config( + r#" +fn foo() {} + +#[test] +fn bar() { foo() } + "#, + expect![[r#" + [ + Annotation { + range: 3..6, + kind: HasReferences { + pos: FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 3, + }, + data: Some( + [], + ), + }, + }, + ] + "#]], + &AnnotationConfig { + references_exclude_tests: true, + annotate_runnables: false, + ..DEFAULT_CONFIG + }, + ); + } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 1a036c3b99195..00561a7bd8d76 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -1435,6 +1435,8 @@ impl flags::AnalysisStats { annotate_references: false, annotate_method_references: false, annotate_enum_variant_references: false, + references_exclude_imports: false, + references_exclude_tests: false, location: ide::AnnotationLocation::AboveName, filter_adjacent_derive_implementations: false, ra_fixture: RaFixtureConfig::default(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 64411bf73f083..67cb51f6e156b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -1548,6 +1548,9 @@ pub struct LensConfig { pub refs_trait: bool, // for Struct, Enum, Union and Trait pub enum_variant_refs: bool, + pub refs_exclude_imports: bool, + pub refs_exclude_tests: bool, + // annotations pub location: AnnotationLocation, pub filter_adjacent_derive_implementations: bool, @@ -1591,10 +1594,6 @@ impl LensConfig { self.run || self.debug || self.update_test } - pub fn references(&self) -> bool { - self.method_refs || self.refs_adt || self.refs_trait || self.enum_variant_refs - } - pub fn into_annotation_config<'a>( self, binary_target: bool, @@ -1607,6 +1606,8 @@ impl LensConfig { annotate_references: self.refs_adt, annotate_method_references: self.method_refs, annotate_enum_variant_references: self.enum_variant_refs, + references_exclude_imports: self.refs_exclude_imports, + references_exclude_tests: self.refs_exclude_tests, location: self.location.into(), ra_fixture: RaFixtureConfig { minicore, disable_ra_fixture: self.disable_ra_fixture }, filter_adjacent_derive_implementations: self.filter_adjacent_derive_implementations, @@ -2688,18 +2689,19 @@ impl Config { } pub fn lens(&self) -> LensConfig { + let enable = *self.lens_enable(); LensConfig { - run: *self.lens_enable() && *self.lens_run_enable(), - debug: *self.lens_enable() && *self.lens_debug_enable(), - update_test: *self.lens_enable() - && *self.lens_updateTest_enable() - && *self.lens_run_enable(), - interpret: *self.lens_enable() && *self.lens_run_enable() && *self.interpret_tests(), - implementations: *self.lens_enable() && *self.lens_implementations_enable(), - method_refs: *self.lens_enable() && *self.lens_references_method_enable(), - refs_adt: *self.lens_enable() && *self.lens_references_adt_enable(), - refs_trait: *self.lens_enable() && *self.lens_references_trait_enable(), - enum_variant_refs: *self.lens_enable() && *self.lens_references_enumVariant_enable(), + run: enable && *self.lens_run_enable(), + debug: enable && *self.lens_debug_enable(), + update_test: enable && *self.lens_updateTest_enable() && *self.lens_run_enable(), + interpret: enable && *self.lens_run_enable() && *self.interpret_tests(), + implementations: enable && *self.lens_implementations_enable(), + method_refs: enable && *self.lens_references_method_enable(), + refs_adt: enable && *self.lens_references_adt_enable(), + refs_trait: enable && *self.lens_references_trait_enable(), + enum_variant_refs: enable && *self.lens_references_enumVariant_enable(), + refs_exclude_imports: *self.references_excludeImports(), + refs_exclude_tests: *self.references_excludeTests(), location: *self.lens_location(), filter_adjacent_derive_implementations: *self .gotoImplementations_filterAdjacentDerives(), From 41954a2d3cd99cd24215b11e50f48eac02e94552 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 19 Jul 2026 16:55:11 +0200 Subject: [PATCH 05/94] internal: Port `ExprScopes` over to visitor fully --- .../crates/hir-def/src/expr_store/scope.rs | 390 ++++++++++-------- .../crates/hir-def/src/item_scope.rs | 2 +- 2 files changed, 227 insertions(+), 165 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs index d6568b8cfe6ae..ee5396b4cde97 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs @@ -5,9 +5,9 @@ use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx}; use crate::{ BlockId, DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, VariantId, - expr_store::{Body, ExpressionStore, HygieneId, StoreVisitor, body::Param}, + expr_store::{Body, ExpressionStore, HygieneId, StoreVisitor, StoreVisitorExt, body::Param}, hir::{ - Array, Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement, + Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement, generics::GenericParams, }, signatures::VariantFields, @@ -167,7 +167,13 @@ impl ExprScopes { scopes.add_bindings(body, root, self_param, body.binding_hygiene(self_param)); } body.params.iter().for_each(|param| scopes.add_pat_bindings(body, root, param.formal)); - compute_expr_scopes(body.root_expr(), body, &mut scopes, &mut { root }, &mut root); + ExprScopeVisitor { + store: body, + scopes: &mut scopes, + scope: &mut { root }, + const_scope: &mut root, + } + .on_expr(body.root_expr()); scopes } @@ -182,7 +188,13 @@ impl ExprScopes { let root = scopes.root_scope(); for root_expr in roots { let mut scope = scopes.new_scope(root); - compute_expr_scopes(root_expr, store, &mut scopes, &mut { scope }, &mut scope); + ExprScopeVisitor { + store, + scopes: &mut scopes, + scope: &mut { scope }, + const_scope: &mut scope, + } + .on_expr(root_expr); } scopes } @@ -282,174 +294,161 @@ struct ExprScopeVisitor<'a> { const_scope: &'a mut ScopeId, } -impl StoreVisitor for ExprScopeVisitor<'_> { - fn on_expr(&mut self, expr: ExprId) { - compute_expr_scopes(expr, self.store, self.scopes, self.scope, self.const_scope); - } - - fn on_anon_const_expr(&mut self, expr: ExprId) { - let mut scope = *self.const_scope; - compute_expr_scopes(expr, self.store, self.scopes, &mut scope, self.const_scope); - } - - fn on_pat(&mut self, pat: PatId) { - self.store.visit_pat_children(pat, &mut *self); - } +impl ExprScopeVisitor<'_> { + fn visit_block( + &mut self, + expr: ExprId, + id: Option, + statements: &[Statement], + tail: Option, + label: Option, + ) { + let mut scope = self.scopes.new_block_scope(*self.scope, id, label); + let mut const_scope = if id.is_some() { + self.scopes.new_block_scope(*self.const_scope, id, None) + } else { + // We don't need to allocate a new scope, since only items matter to us. + *self.const_scope + }; + // Overwrite the old scope for the block expr, so that every block scope can be found + // via the block itself (important for blocks that only contain items, no expressions). + self.scopes.set_scope(expr, scope); - fn on_type(&mut self, ty: TypeRefId) { - self.store.visit_type_ref_children(ty, &mut *self); + let mut visitor = ExprScopeVisitor { + store: self.store, + scopes: self.scopes, + scope: &mut scope, + const_scope: &mut const_scope, + }; + for stmt in statements { + match stmt { + Statement::Let { pat, initializer, else_branch, type_ref } => { + visitor.on_type_opt(*type_ref); + visitor.on_expr_opt(*initializer); + visitor.on_expr_opt(*else_branch); + *visitor.scope = visitor.scopes.new_scope(*visitor.scope); + visitor.scopes.add_pat_bindings(visitor.store, *visitor.scope, *pat); + } + Statement::Expr { expr, has_semi: _ } => visitor.on_expr(*expr), + Statement::Item(Item::MacroDef(macro_id)) => { + *visitor.scope = + visitor.scopes.new_macro_def_scope(*visitor.scope, macro_id.clone()); + *visitor.const_scope = + visitor.scopes.new_macro_def_scope(*visitor.const_scope, macro_id.clone()); + } + Statement::Item(Item::Other) => (), + } + } + visitor.on_expr_opt(tail); } } -fn compute_type_scopes( - ty: TypeRefId, - store: &ExpressionStore, - scopes: &mut ExprScopes, - const_scope: &mut ScopeId, -) { - let mut scope = *const_scope; - ExprScopeVisitor { store, scopes, scope: &mut scope, const_scope }.on_type(ty); -} - -fn compute_block_scopes( - statements: &[Statement], - tail: Option, - store: &ExpressionStore, - scopes: &mut ExprScopes, - scope: &mut ScopeId, - const_scope: &mut ScopeId, -) { - for stmt in statements { - match stmt { - Statement::Let { pat, initializer, else_branch, type_ref } => { - if let Some(type_ref) = type_ref { - compute_type_scopes(*type_ref, store, scopes, const_scope); +impl StoreVisitor for ExprScopeVisitor<'_> { + fn on_expr(&mut self, expr: ExprId) { + self.scopes.set_scope(expr, *self.scope); + match &self.store[expr] { + Expr::Block { statements, tail, id, label } => { + self.visit_block(expr, *id, statements, *tail, *label); + } + Expr::Const(expr) => self.on_anon_const_expr(*expr), + Expr::Unsafe { id, statements, tail } => { + self.visit_block(expr, *id, statements, *tail, None); + } + Expr::Loop { body, label, source: _ } => { + let mut scope = self.scopes.new_labeled_scope(*self.scope, *label); + ExprScopeVisitor { + store: self.store, + scopes: self.scopes, + scope: &mut scope, + const_scope: self.const_scope, } - if let Some(expr) = initializer { - compute_expr_scopes(*expr, store, scopes, scope, const_scope); + .on_expr(*body); + } + Expr::Closure { args, arg_types, ret_type, body, capture_by: _, closure_kind: _ } => { + arg_types.iter().flatten().for_each(|type_ref| self.on_type(*type_ref)); + self.on_type_opt(*ret_type); + let mut scope = self.scopes.new_scope(*self.scope); + args.iter().for_each(|arg| self.scopes.add_pat_bindings(self.store, scope, *arg)); + ExprScopeVisitor { + store: self.store, + scopes: self.scopes, + scope: &mut scope, + const_scope: self.const_scope, } - if let Some(expr) = else_branch { - compute_expr_scopes(*expr, store, scopes, scope, const_scope); + .on_expr(*body); + } + Expr::Match { expr, arms } => { + self.on_expr(*expr); + for arm in arms.iter() { + let mut scope = self.scopes.new_scope(*self.scope); + self.scopes.add_pat_bindings(self.store, scope, arm.pat); + if let Some(guard) = arm.guard { + scope = self.scopes.new_scope(scope); + ExprScopeVisitor { + store: self.store, + scopes: self.scopes, + scope: &mut scope, + const_scope: self.const_scope, + } + .on_expr(guard); + } + ExprScopeVisitor { + store: self.store, + scopes: self.scopes, + scope: &mut scope, + const_scope: self.const_scope, + } + .on_expr(arm.expr); } - - *scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(store, *scope, *pat); } - Statement::Expr { expr, .. } => { - compute_expr_scopes(*expr, store, scopes, scope, const_scope); + &Expr::If { condition, then_branch, else_branch } => { + let mut then_branch_scope = self.scopes.new_scope(*self.scope); + let mut visitor = ExprScopeVisitor { + store: self.store, + scopes: self.scopes, + scope: &mut then_branch_scope, + const_scope: self.const_scope, + }; + visitor.on_expr(condition); + visitor.on_expr(then_branch); + self.on_expr_opt(else_branch); } - Statement::Item(Item::MacroDef(macro_id)) => { - *scope = scopes.new_macro_def_scope(*scope, macro_id.clone()); - *const_scope = scopes.new_macro_def_scope(*const_scope, macro_id.clone()); + &Expr::Let { pat, expr } => { + self.on_expr(expr); + *self.scope = self.scopes.new_scope(*self.scope); + self.scopes.add_pat_bindings(self.store, *self.scope, pat); } - Statement::Item(Item::Other) => (), + _ => self.store.visit_expr_children(expr, &mut *self), } } - if let Some(expr) = tail { - compute_expr_scopes(expr, store, scopes, scope, const_scope); + + fn on_anon_const_expr(&mut self, expr: ExprId) { + let mut scope = *self.const_scope; + ExprScopeVisitor { + store: self.store, + scopes: self.scopes, + scope: &mut scope, + const_scope: self.const_scope, + } + .on_expr(expr); } -} -fn compute_expr_scopes( - expr: ExprId, - store: &ExpressionStore, - scopes: &mut ExprScopes, - scope: &mut ScopeId, - const_scope: &mut ScopeId, -) { - let compute_expr_scopes = - |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId, const_scope: &mut ScopeId| { - compute_expr_scopes(expr, store, scopes, scope, const_scope) - }; - let handle_block = |id, - statements, - tail, - label, - scopes: &mut ExprScopes, - scope: &mut ScopeId, - const_scope: &mut ScopeId| { - let mut scope = scopes.new_block_scope(*scope, id, label); - let mut const_scope = if id.is_some() { - scopes.new_block_scope(*const_scope, id, None) - } else { - // We don't need to allocate a new scope, since only items matter to us. - *const_scope - }; - // Overwrite the old scope for the block expr, so that every block scope can be found - // via the block itself (important for blocks that only contain items, no expressions). - scopes.set_scope(expr, scope); - compute_block_scopes(statements, tail, store, scopes, &mut scope, &mut const_scope); - }; + fn on_pat(&mut self, pat: PatId) { + self.store.visit_pat_children(pat, &mut *self); + } - scopes.set_scope(expr, *scope); - match &store[expr] { - Expr::Block { statements, tail, id, label } => { - handle_block(*id, statements, *tail, *label, scopes, scope, const_scope); - } - Expr::Const(id) => { - let mut scope = *const_scope; - compute_expr_scopes(scopes, *id, &mut scope, const_scope); - } - Expr::Array(Array::Repeat { initializer, repeat }) => { - compute_expr_scopes(scopes, *initializer, scope, const_scope); - let mut repeat_scope = *const_scope; - compute_expr_scopes(scopes, *repeat, &mut repeat_scope, const_scope); - } - Expr::Unsafe { id, statements, tail } => { - handle_block(*id, statements, *tail, None, scopes, scope, const_scope); - } - Expr::Loop { body: body_expr, label, source: _ } => { - let mut scope = scopes.new_labeled_scope(*scope, *label); - compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope); - } - Expr::Closure { - args, - arg_types, - ret_type, - body: body_expr, - capture_by: _, - closure_kind: _, - } => { - arg_types - .iter() - .flatten() - .for_each(|type_ref| compute_type_scopes(*type_ref, store, scopes, const_scope)); - if let Some(type_ref) = ret_type { - compute_type_scopes(*type_ref, store, scopes, const_scope); - } - let mut scope = scopes.new_scope(*scope); - args.iter().for_each(|arg| scopes.add_pat_bindings(store, scope, *arg)); - compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope); - } - Expr::Match { expr, arms } => { - compute_expr_scopes(scopes, *expr, scope, const_scope); - for arm in arms.iter() { - let mut scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(store, scope, arm.pat); - if let Some(guard) = arm.guard { - scope = scopes.new_scope(scope); - compute_expr_scopes(scopes, guard, &mut scope, const_scope); - } - compute_expr_scopes(scopes, arm.expr, &mut scope, const_scope); - } - } - &Expr::If { condition, then_branch, else_branch } => { - let mut then_branch_scope = scopes.new_scope(*scope); - compute_expr_scopes(scopes, condition, &mut then_branch_scope, const_scope); - compute_expr_scopes(scopes, then_branch, &mut then_branch_scope, const_scope); - if let Some(else_branch) = else_branch { - compute_expr_scopes(scopes, else_branch, scope, const_scope); - } - } - &Expr::Let { pat, expr } => { - compute_expr_scopes(scopes, expr, scope, const_scope); - *scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(store, *scope, pat); - } - _ => { - store.visit_expr_children(expr, ExprScopeVisitor { store, scopes, scope, const_scope }) - } - }; + fn on_type(&mut self, ty: TypeRefId) { + let mut scope = *self.const_scope; + self.store.visit_type_ref_children( + ty, + ExprScopeVisitor { + store: self.store, + scopes: self.scopes, + scope: &mut scope, + const_scope: self.const_scope, + }, + ); + } } #[cfg(test)] @@ -497,16 +496,27 @@ mod tests { let (file_id, _) = editioned_file_id.unpack(&db); let file_syntax = editioned_file_id.parse(&db).syntax_node(); - let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap(); + let marker: Option = find_node_at_offset(&file_syntax, offset); let function = find_function(&db, file_id); let scopes = ExprScopes::of(&db, DefWithBodyId::from(function)); - let (_body, source_map) = Body::with_source_map(&db, function.into()); - - let expr_id = source_map - .node_expr(InFile { file_id: editioned_file_id.into(), value: &marker.into() }) - .unwrap() - .as_expr() + let (body, source_map) = Body::with_source_map(&db, function.into()); + + let expr_id = marker + .and_then(|marker| { + source_map + .node_expr(InFile { file_id: editioned_file_id.into(), value: &marker.into() }) + .and_then(|expr| expr.as_expr()) + }) + .or_else(|| { + body.exprs().find_map(|(expr, value)| { + let crate::hir::Expr::Path(path) = value else { return None }; + path.mod_path() + .and_then(|path| path.as_ident()) + .is_some_and(|name| name.as_str() == "marker") + .then_some(expr) + }) + }) .unwrap(); let scope = scopes.scope_for(expr_id); @@ -533,6 +543,58 @@ fn f(param: usize) { ); } + #[test] + fn pattern_type_expr_scope() { + do_check( + r#" +fn f(param: usize) { + let local = 0; + let _: builtin#pattern_type (usize is 0..=$0) = 0; +} +"#, + &["param"], + ); + } + + #[test] + fn closure_pattern_type_expr_scope() { + do_check( + r#" +fn f(param: usize) { + let local = 0; + let _ = |_: builtin#pattern_type (usize is 0..=$0)| {}; +} +"#, + &["param"], + ); + } + + #[test] + fn array_repeat_expr_scope() { + do_check( + r#" +fn f(param: usize) { + let local = 0; + let _ = [(); $0]; +} +"#, + &["param"], + ); + } + + #[test] + fn inline_const_expr_scope() { + do_check( + r#" +fn f(param: usize) { + let local = 0; + let _ = const { $0 }; +} +"#, + &["param"], + ); + } + #[test] fn test_lambda_scope() { do_check( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 1443d3ea4be4c..2cc96d5db2c0a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -161,7 +161,7 @@ pub struct ItemScope { /// Module scoped macros will be inserted into `items` instead of here. // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will // be all resolved to the last one defined if shadowing happens. - legacy_macros: FxHashMap>, + legacy_macros: FxHashMap>, /// The attribute macro invocations in this scope. attr_macros: FxHashMap, MacroCallId>, /// The macro invocations in this scope. From 388818c5ea2381e2dc58ee561de473929c29dab9 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 22 Jun 2026 13:42:58 +0100 Subject: [PATCH 06/94] internal: Make stdout/stderr explicit in JsonLinesParser Currently JsonLinesParser::from_line is called for both stdout and stderr, so trait implementers cannot distinguish stdout and stderr. Define a separate JsonLinesParser::from_stderr_line to make the stdout/stderr distinction explicit, and update use sites. This is not a behaviour change. AI disclosure: Partially written by Codex and GPT-5.5. --- .../crates/rust-analyzer/src/command.rs | 4 +++- .../crates/rust-analyzer/src/discover.rs | 4 ++++ .../crates/rust-analyzer/src/flycheck.rs | 14 ++++++++++++-- .../crates/rust-analyzer/src/test_runner.rs | 4 ++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs index ff2e21c865e9b..bc3fa21c6658f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs @@ -23,6 +23,7 @@ use stdx::process::streaming_output; /// well as custom discover commands. pub(crate) trait JsonLinesParser: Send + 'static { fn from_line(&self, line: &str, error: &mut String) -> Option; + fn from_stderr_line(&self, line: &str, error: &mut String) -> Option; fn from_eof(&self) -> Option; } @@ -95,7 +96,8 @@ impl CommandActor { _ = stderr.write_all(line.as_bytes()); _ = stderr.write_all(b"\n"); } - if process_line(line, &mut stderr_errors) { + if let Some(t) = self.parser.from_stderr_line(line, &mut stderr_errors) { + self.sender.send(t).unwrap(); read_at_least_one_stderr_message = true; } }, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs index 098b6a4d986d7..459a7993201b8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs @@ -136,6 +136,10 @@ impl JsonLinesParser for DiscoverProjectParser { fn from_eof(&self) -> Option { None } + + fn from_stderr_line(&self, line: &str, error: &mut String) -> Option { + self.from_line(line, error) + } } #[test] 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 f73ffb24eea32..b927a11604158 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -1011,8 +1011,8 @@ enum CheckMessage { struct CheckParser; -impl JsonLinesParser for CheckParser { - fn from_line(&self, line: &str, error: &mut String) -> Option { +impl CheckParser { + fn parse_line(&self, line: &str, error: &mut String) -> Option { let mut deserializer = serde_json::Deserializer::from_str(line); deserializer.disable_recursion_limit(); if let Ok(message) = JsonMessage::deserialize(&mut deserializer) { @@ -1042,6 +1042,16 @@ impl JsonLinesParser for CheckParser { error.push('\n'); None } +} + +impl JsonLinesParser for CheckParser { + fn from_line(&self, line: &str, error: &mut String) -> Option { + self.parse_line(line, error) + } + + fn from_stderr_line(&self, line: &str, error: &mut String) -> Option { + self.parse_line(line, error) + } fn from_eof(&self) -> Option { None diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs index 31f35df5c796d..4f5c00192dcd5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs @@ -72,6 +72,10 @@ impl JsonLinesParser for CargoTestOutputParser { }) } + fn from_stderr_line(&self, line: &str, error: &mut String) -> Option { + self.from_line(line, error) + } + fn from_eof(&self) -> Option { Some(CargoTestMessage { target: self.target.clone(), output: CargoTestOutput::Finished }) } From 047fb62a8e885436f94dbc3f95bac7f62d7fa6f8 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 24 Jul 2026 16:53:16 +0530 Subject: [PATCH 07/94] restrict visibility of mapping methods --- .../crates/syntax/src/syntax_editor/mapping.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs index 180c2e69fa3e9..464682223cd0e 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs @@ -21,7 +21,7 @@ pub struct SyntaxMapping { impl SyntaxMapping { /// Like [`SyntaxMapping::upmap_child`] but for syntax elements. - pub fn upmap_child_element( + pub(super) fn upmap_child_element( &self, child: &SyntaxElement, input_ancestor: &SyntaxNode, @@ -48,7 +48,7 @@ impl SyntaxMapping { /// Maps a child node of the input ancestor to the corresponding node in /// the output ancestor. - pub fn upmap_child( + pub(super) fn upmap_child( &self, child: &SyntaxNode, input_ancestor: &SyntaxNode, @@ -257,7 +257,7 @@ impl SyntaxMappingBuilder { } #[derive(Debug)] -pub struct MissingMapping(pub SyntaxNode); +pub(super) struct MissingMapping(pub SyntaxNode); #[derive(Debug, Clone, Copy)] struct MappingEntry { From 95048dca9c8e6da54264194cab70540c78eba770 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 24 Jul 2026 16:53:42 +0530 Subject: [PATCH 08/94] remove clone_for_updates entirely, --- .../src/ast/syntax_factory/constructors.rs | 330 ++++++++---------- 1 file changed, 142 insertions(+), 188 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 22c8c842d890c..23fb0e2e02782 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -14,49 +14,45 @@ use super::SyntaxFactory; impl SyntaxFactory { pub fn name(&self, name: &str) -> ast::Name { - make::name(name).clone_for_update() + make::name(name) } pub fn name_ref(&self, name: &str) -> ast::NameRef { - make::name_ref(name).clone_for_update() + make::name_ref(name) } pub fn name_ref_self_ty(&self) -> ast::NameRef { - make::name_ref_self_ty().clone_for_update() + make::name_ref_self_ty() } pub fn expr_todo(&self) -> ast::Expr { - make::ext::expr_todo().clone_for_update() + make::ext::expr_todo() } pub fn expr_self(&self) -> ast::Expr { - make::ext::expr_self().clone_for_update() + make::ext::expr_self() } pub fn expr_const_value(&self, text: &str) -> ast::ConstArg { - make::expr_const_value(text).clone_for_update() + make::expr_const_value(text) } pub fn lifetime(&self, text: &str) -> ast::Lifetime { - make::lifetime(text).clone_for_update() + make::lifetime(text) } pub fn ty(&self, text: &str) -> ast::Type { - make::ty(text).clone_for_update() + make::ty(text) } pub fn ty_infer(&self) -> ast::InferType { - let ast::Type::InferType(ast) = make::ty_placeholder().clone_for_update() else { - unreachable!() - }; + let ast::Type::InferType(ast) = make::ty_placeholder() else { unreachable!() }; ast } pub fn ty_path(&self, path: ast::Path) -> ast::PathType { - let ast::Type::PathType(ast) = make::ty_path(path.clone()).clone_for_update() else { - unreachable!() - }; + let ast::Type::PathType(ast) = make::ty_path(path.clone()) else { unreachable!() }; if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -76,11 +72,11 @@ impl SyntaxFactory { } pub fn type_bound(&self, bound: ast::Type) -> ast::TypeBound { - make::type_bound(bound).clone_for_update() + make::type_bound(bound) } pub fn type_bound_text(&self, bound: &str) -> ast::TypeBound { - make::type_bound_text(bound).clone_for_update() + make::type_bound_text(bound) } pub fn use_tree_list( @@ -88,7 +84,7 @@ impl SyntaxFactory { use_trees: impl IntoIterator, ) -> ast::UseTreeList { let (use_trees, input) = iterator_input(use_trees); - let ast = make::use_tree_list(use_trees).clone_for_update(); + let ast = make::use_tree_list(use_trees); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -104,7 +100,7 @@ impl SyntaxFactory { bounds: impl IntoIterator, ) -> Option { let (bounds, input) = iterator_input(bounds); - let ast = make::type_bound_list(bounds)?.clone_for_update(); + let ast = make::type_bound_list(bounds)?; if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -122,7 +118,7 @@ impl SyntaxFactory { name: ast::Name, bounds: Option, ) -> ast::TypeParam { - let ast = make::type_param(name.clone(), bounds.clone()).clone_for_update(); + let ast = make::type_param(name.clone(), bounds.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -140,23 +136,23 @@ impl SyntaxFactory { } pub fn path_from_text(&self, text: &str) -> ast::Path { - make::path_from_text(text).clone_for_update() + make::path_from_text(text) } pub fn path_from_text_with_edition(&self, text: &str, edition: Edition) -> ast::Path { - make::path_from_text_with_edition(text, edition).clone_for_update() + make::path_from_text_with_edition(text, edition) } pub fn path_concat(&self, first: ast::Path, second: ast::Path) -> ast::Path { - make::path_concat(first, second).clone_for_update() + make::path_concat(first, second) } pub fn visibility_pub_crate(&self) -> ast::Visibility { - make::visibility_pub_crate().clone_for_update() + make::visibility_pub_crate() } pub fn visibility_pub(&self) -> ast::Visibility { - make::visibility_pub().clone_for_update() + make::visibility_pub() } pub fn struct_( @@ -171,8 +167,7 @@ impl SyntaxFactory { strukt_name.clone(), generic_param_list.clone(), field_list.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -198,7 +193,7 @@ impl SyntaxFactory { } pub fn unnamed_param(&self, ty: ast::Type) -> ast::Param { - let ast = make::unnamed_param(ty.clone()).clone_for_update(); + let ast = make::unnamed_param(ty.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -210,7 +205,7 @@ impl SyntaxFactory { } pub fn untyped_param(&self, pat: ast::Pat) -> ast::Param { - let ast = make::untyped_param(pat.clone()).clone_for_update(); + let ast = make::untyped_param(pat.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -229,8 +224,7 @@ impl SyntaxFactory { ret_type: Option, ) -> ast::FnPtrType { let (params, params_input) = iterator_input(params); - let ast = make::ty_fn_ptr(is_unsafe, abi.clone(), params.into_iter(), ret_type.clone()) - .clone_for_update(); + let ast = make::ty_fn_ptr(is_unsafe, abi.clone(), params.into_iter(), ret_type.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -257,7 +251,7 @@ impl SyntaxFactory { bounds: impl IntoIterator, ) -> ast::WherePred { let (bounds, bounds_input) = iterator_input(bounds); - let ast = make::where_pred(path.clone(), bounds).clone_for_update(); + let ast = make::where_pred(path.clone(), bounds); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -293,7 +287,7 @@ impl SyntaxFactory { predicates: impl IntoIterator, ) -> ast::WhereClause { let (predicates, input) = iterator_input(predicates); - let ast = make::where_clause(predicates).clone_for_update(); + let ast = make::where_clause(predicates); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -305,7 +299,7 @@ impl SyntaxFactory { } pub fn impl_trait_type(&self, bounds: ast::TypeBoundList) -> ast::ImplTraitType { - let ast = make::impl_trait_type(bounds.clone()).clone_for_update(); + let ast = make::impl_trait_type(bounds.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -318,9 +312,7 @@ impl SyntaxFactory { } pub fn expr_field(&self, receiver: ast::Expr, field: &str) -> ast::FieldExpr { - let ast::Expr::FieldExpr(ast) = - make::expr_field(receiver.clone(), field).clone_for_update() - else { + let ast::Expr::FieldExpr(ast) = make::expr_field(receiver.clone(), field) else { unreachable!() }; @@ -362,8 +354,7 @@ impl SyntaxFactory { trait_where_clause.clone(), ty_where_clause.clone(), body.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -411,8 +402,7 @@ impl SyntaxFactory { type_param_bounds.clone(), where_clause.clone(), assignment.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -450,7 +440,7 @@ impl SyntaxFactory { params: impl IntoIterator, ) -> ast::ParamList { let (params, input) = iterator_input(params); - let ast = make::param_list(self_param.clone(), params).clone_for_update(); + let ast = make::param_list(self_param.clone(), params); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -467,7 +457,7 @@ impl SyntaxFactory { } pub fn const_param(&self, name: ast::Name, ty: ast::Type) -> ast::ConstParam { - let ast = make::const_param(name.clone(), ty.clone()).clone_for_update(); + let ast = make::const_param(name.clone(), ty.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -480,7 +470,7 @@ impl SyntaxFactory { } pub fn lifetime_param(&self, lifetime: ast::Lifetime) -> ast::LifetimeParam { - let ast = make::lifetime_param(lifetime.clone()).clone_for_update(); + let ast = make::lifetime_param(lifetime.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -496,7 +486,7 @@ impl SyntaxFactory { params: impl IntoIterator, ) -> ast::GenericParamList { let (params, input) = iterator_input(params); - let ast = make::generic_param_list(params).clone_for_update(); + let ast = make::generic_param_list(params); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -508,7 +498,7 @@ impl SyntaxFactory { } pub fn path_segment(&self, name_ref: ast::NameRef) -> ast::PathSegment { - let ast = make::path_segment(name_ref.clone()).clone_for_update(); + let ast = make::path_segment(name_ref.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -520,15 +510,15 @@ impl SyntaxFactory { } pub fn path_segment_self(&self) -> ast::PathSegment { - make::path_segment_self().clone_for_update() + make::path_segment_self() } pub fn path_segment_super(&self) -> ast::PathSegment { - make::path_segment_super().clone_for_update() + make::path_segment_super() } pub fn path_segment_crate(&self) -> ast::PathSegment { - make::path_segment_crate().clone_for_update() + make::path_segment_crate() } pub fn generic_ty_path_segment( @@ -537,7 +527,7 @@ impl SyntaxFactory { generic_args: impl IntoIterator, ) -> ast::PathSegment { let (generic_args, input) = iterator_input(generic_args); - let ast = make::generic_ty_path_segment(name_ref.clone(), generic_args).clone_for_update(); + let ast = make::generic_ty_path_segment(name_ref.clone(), generic_args); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -555,7 +545,7 @@ impl SyntaxFactory { } pub fn tail_only_block_expr(&self, tail_expr: ast::Expr) -> ast::BlockExpr { - let ast = make::tail_only_block_expr(tail_expr.clone()).clone_for_update(); + let ast = make::tail_only_block_expr(tail_expr.clone()); if let Some(mut mapping) = self.mappings() { let stmt_list = ast.stmt_list().unwrap(); @@ -571,9 +561,7 @@ impl SyntaxFactory { } pub fn expr_bin_op(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::Expr { - let ast::Expr::BinExpr(ast) = - make::expr_bin_op(lhs.clone(), op, rhs.clone()).clone_for_update() - else { + let ast::Expr::BinExpr(ast) = make::expr_bin_op(lhs.clone(), op, rhs.clone()) else { unreachable!() }; @@ -588,16 +576,16 @@ impl SyntaxFactory { } pub fn ty_placeholder(&self) -> ast::Type { - make::ty_placeholder().clone_for_update() + make::ty_placeholder() } pub fn ty_unit(&self) -> ast::Type { - make::ty_unit().clone_for_update() + make::ty_unit() } pub fn ty_tuple(&self, types: impl IntoIterator) -> ast::Type { let (types, input) = iterator_input(types); - let ast = make::ty_tuple(types).clone_for_update(); + let ast = make::ty_tuple(types); if let Some(mut mapping) = self.mappings() && let ast::Type::TupleType(tuple_ty) = &ast @@ -631,7 +619,7 @@ impl SyntaxFactory { unreachable!(); }; - let ast = path.path().unwrap().segment().unwrap().clone_for_update(); + let ast = path.path().unwrap().segment().unwrap(); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -653,7 +641,7 @@ impl SyntaxFactory { use_tree: ast::UseTree, ) -> ast::Use { let (attrs, attrs_input) = iterator_input(attrs); - let ast = make::use_(attrs, visibility.clone(), use_tree.clone()).clone_for_update(); + let ast = make::use_(attrs, visibility.clone(), use_tree.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -678,8 +666,7 @@ impl SyntaxFactory { alias: Option, add_star: bool, ) -> ast::UseTree { - let ast = make::use_tree(path.clone(), use_tree_list.clone(), alias.clone(), add_star) - .clone_for_update(); + let ast = make::use_tree(path.clone(), use_tree_list.clone(), alias.clone(), add_star); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -700,11 +687,11 @@ impl SyntaxFactory { } pub fn use_tree_glob(&self) -> ast::UseTree { - make::use_tree_glob().clone_for_update() + make::use_tree_glob() } pub fn path_unqualified(&self, segment: ast::PathSegment) -> ast::Path { - let ast = make::path_unqualified(segment.clone()).clone_for_update(); + let ast = make::path_unqualified(segment.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -716,7 +703,7 @@ impl SyntaxFactory { } pub fn path_qualified(&self, qual: ast::Path, segment: ast::PathSegment) -> ast::Path { - let ast = make::path_qualified(qual.clone(), segment.clone()).clone_for_update(); + let ast = make::path_qualified(qual.clone(), segment.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -738,7 +725,7 @@ impl SyntaxFactory { is_abs: bool, ) -> ast::Path { let (segments, input) = iterator_input(segments); - let ast = make::path_from_segments(segments, is_abs).clone_for_update(); + let ast = make::path_from_segments(segments, is_abs); if let Some(mut mapping) = self.mappings() { let mut current_path = Some(ast.clone()); @@ -757,7 +744,7 @@ impl SyntaxFactory { } pub fn ident_pat(&self, ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat { - let ast = make::ident_pat(ref_, mut_, name.clone()).clone_for_update(); + let ast = make::ident_pat(ref_, mut_, name.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -769,7 +756,7 @@ impl SyntaxFactory { } pub fn simple_ident_pat(&self, name: ast::Name) -> ast::IdentPat { - let ast = make::ext::simple_ident_pat(name.clone()).clone_for_update(); + let ast = make::ext::simple_ident_pat(name.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -781,16 +768,16 @@ impl SyntaxFactory { } pub fn wildcard_pat(&self) -> ast::WildcardPat { - make::wildcard_pat().clone_for_update() + make::wildcard_pat() } pub fn literal_pat(&self, text: &str) -> ast::LiteralPat { - make::literal_pat(text).clone_for_update() + make::literal_pat(text) } pub fn slice_pat(&self, pats: impl IntoIterator) -> ast::SlicePat { let (pats, input) = iterator_input(pats); - let ast = make::slice_pat(pats).clone_for_update(); + let ast = make::slice_pat(pats); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -803,7 +790,7 @@ impl SyntaxFactory { pub fn tuple_pat(&self, pats: impl IntoIterator) -> ast::TuplePat { let (pats, input) = iterator_input(pats); - let ast = make::tuple_pat(pats).clone_for_update(); + let ast = make::tuple_pat(pats); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -820,7 +807,7 @@ impl SyntaxFactory { fields: impl IntoIterator, ) -> ast::TupleStructPat { let (fields, input) = iterator_input(fields); - let ast = make::tuple_struct_pat(path.clone(), fields).clone_for_update(); + let ast = make::tuple_struct_pat(path.clone(), fields); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -837,7 +824,7 @@ impl SyntaxFactory { path: ast::Path, fields: ast::RecordPatFieldList, ) -> ast::RecordPat { - let ast = make::record_pat_with_fields(path.clone(), fields.clone()).clone_for_update(); + let ast = make::record_pat_with_fields(path.clone(), fields.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -858,7 +845,7 @@ impl SyntaxFactory { rest_pat: Option, ) -> ast::RecordPatFieldList { let (fields, input) = iterator_input(fields); - let ast = make::record_pat_field_list(fields, rest_pat.clone()).clone_for_update(); + let ast = make::record_pat_field_list(fields, rest_pat.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -874,7 +861,7 @@ impl SyntaxFactory { } pub fn record_pat_field(&self, name_ref: ast::NameRef, pat: ast::Pat) -> ast::RecordPatField { - let ast = make::record_pat_field(name_ref.clone(), pat.clone()).clone_for_update(); + let ast = make::record_pat_field(name_ref.clone(), pat.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -887,7 +874,7 @@ impl SyntaxFactory { } pub fn record_pat_field_shorthand(&self, pat: ast::Pat) -> ast::RecordPatField { - let ast = make::record_pat_field_shorthand(pat.clone()).clone_for_update(); + let ast = make::record_pat_field_shorthand(pat.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -899,7 +886,7 @@ impl SyntaxFactory { } pub fn path_pat(&self, path: ast::Path) -> ast::Pat { - let ast = make::path_pat(path.clone()).clone_for_update(); + let ast = make::path_pat(path.clone()); match &ast { ast::Pat::PathPat(ast) => { @@ -923,7 +910,7 @@ impl SyntaxFactory { } pub fn rest_pat(&self) -> ast::RestPat { - make::rest_pat().clone_for_update() + make::rest_pat() } pub fn or_pat( @@ -932,7 +919,7 @@ impl SyntaxFactory { leading_pipe: bool, ) -> ast::OrPat { let (pats, input) = iterator_input(pats); - let ast = make::or_pat(pats, leading_pipe).clone_for_update(); + let ast = make::or_pat(pats, leading_pipe); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -944,7 +931,7 @@ impl SyntaxFactory { } pub fn box_pat(&self, pat: ast::Pat) -> ast::BoxPat { - let ast = make::box_pat(pat.clone()).clone_for_update(); + let ast = make::box_pat(pat.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -956,11 +943,11 @@ impl SyntaxFactory { } pub fn deref_pat(&self, pat: ast::Pat) -> ast::Pat { - make::deref_pat(pat.clone()).clone_for_update() + make::deref_pat(pat.clone()) } pub fn paren_pat(&self, pat: ast::Pat) -> ast::ParenPat { - let ast = make::paren_pat(pat.clone()).clone_for_update(); + let ast = make::paren_pat(pat.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -972,7 +959,7 @@ impl SyntaxFactory { } pub fn range_pat(&self, start: Option, end: Option) -> ast::RangePat { - let ast = make::range_pat(start.clone(), end.clone()).clone_for_update(); + let ast = make::range_pat(start.clone(), end.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -989,7 +976,7 @@ impl SyntaxFactory { } pub fn ref_pat(&self, pat: ast::Pat) -> ast::RefPat { - let ast = make::ref_pat(pat.clone()).clone_for_update(); + let ast = make::ref_pat(pat.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1007,7 +994,7 @@ impl SyntaxFactory { ) -> ast::BlockExpr { let (statements, mut input) = iterator_input(statements); - let ast = make::block_expr(statements, tail_expr.clone()).clone_for_update(); + let ast = make::block_expr(statements, tail_expr.clone()); if let Some(mut mapping) = self.mappings() { let stmt_list = ast.stmt_list().unwrap(); @@ -1040,7 +1027,7 @@ impl SyntaxFactory { ) -> ast::BlockExpr { let (statements, mut input) = iterator_input(statements); - let ast = make::async_move_block_expr(statements, tail_expr.clone()).clone_for_update(); + let ast = make::async_move_block_expr(statements, tail_expr.clone()); if let Some(mut mapping) = self.mappings() { let stmt_list = ast.stmt_list().unwrap(); @@ -1065,11 +1052,11 @@ impl SyntaxFactory { } pub fn expr_empty_block(&self) -> ast::BlockExpr { - make::expr_empty_block().clone_for_update() + make::expr_empty_block() } pub fn expr_paren(&self, expr: ast::Expr) -> ast::ParenExpr { - let ast = make::expr_paren(expr.clone()).clone_for_update(); + let ast = make::expr_paren(expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1082,7 +1069,7 @@ impl SyntaxFactory { pub fn expr_tuple(&self, fields: impl IntoIterator) -> ast::TupleExpr { let (fields, input) = iterator_input(fields); - let ast = make::expr_tuple(fields).clone_for_update(); + let ast = make::expr_tuple(fields); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1094,7 +1081,7 @@ impl SyntaxFactory { } pub fn expr_assignment(&self, lhs: ast::Expr, rhs: ast::Expr) -> ast::BinExpr { - let ast = make::expr_assignment(lhs.clone(), rhs.clone()).clone_for_update(); + let ast = make::expr_assignment(lhs.clone(), rhs.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1107,9 +1094,7 @@ impl SyntaxFactory { } pub fn expr_bin(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::BinExpr { - let ast::Expr::BinExpr(ast) = - make::expr_bin_op(lhs.clone(), op, rhs.clone()).clone_for_update() - else { + let ast::Expr::BinExpr(ast) = make::expr_bin_op(lhs.clone(), op, rhs.clone()) else { unreachable!() }; @@ -1124,13 +1109,11 @@ impl SyntaxFactory { } pub fn expr_literal(&self, text: &str) -> ast::Literal { - make::expr_literal(text).clone_for_update() + make::expr_literal(text) } pub fn expr_path(&self, path: ast::Path) -> ast::Expr { - let ast::Expr::PathExpr(ast) = make::expr_path(path.clone()).clone_for_update() else { - unreachable!() - }; + let ast::Expr::PathExpr(ast) = make::expr_path(path.clone()) else { unreachable!() }; if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1142,7 +1125,7 @@ impl SyntaxFactory { } pub fn expr_prefix(&self, op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr { - let ast = make::expr_prefix(op, expr.clone()).clone_for_update(); + let ast = make::expr_prefix(op, expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1154,7 +1137,7 @@ impl SyntaxFactory { } pub fn expr_call(&self, expr: ast::Expr, arg_list: ast::ArgList) -> ast::CallExpr { - let ast = make::expr_call(expr.clone(), arg_list.clone()).clone_for_update(); + let ast = make::expr_call(expr.clone(), arg_list.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1172,8 +1155,7 @@ impl SyntaxFactory { method: ast::NameRef, arg_list: ast::ArgList, ) -> ast::MethodCallExpr { - let ast = make::expr_method_call(receiver.clone(), method.clone(), arg_list.clone()) - .clone_for_update(); + let ast = make::expr_method_call(receiver.clone(), method.clone(), arg_list.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1188,7 +1170,7 @@ impl SyntaxFactory { pub fn arg_list(&self, args: impl IntoIterator) -> ast::ArgList { let (args, input) = iterator_input(args); - let ast = make::arg_list(args).clone_for_update(); + let ast = make::arg_list(args); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax.clone()); @@ -1200,8 +1182,7 @@ impl SyntaxFactory { } pub fn expr_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr { - let ast::Expr::RefExpr(ast) = make::expr_ref(expr.clone(), exclusive).clone_for_update() - else { + let ast::Expr::RefExpr(ast) = make::expr_ref(expr.clone(), exclusive) else { unreachable!() }; @@ -1215,9 +1196,7 @@ impl SyntaxFactory { } pub fn expr_reborrow(&self, expr: ast::Expr) -> ast::Expr { - let ast::Expr::RefExpr(ast) = make::expr_reborrow(expr.clone()).clone_for_update() else { - unreachable!() - }; + let ast::Expr::RefExpr(ast) = make::expr_reborrow(expr.clone()) else { unreachable!() }; if let Some(mut mapping) = self.mappings() { // Layout: RefExpr(&mut, PrefixExpr(*, expr)). Map `expr` to the @@ -1236,9 +1215,7 @@ impl SyntaxFactory { } pub fn expr_raw_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr { - let ast::Expr::RefExpr(ast) = - make::expr_raw_ref(expr.clone(), exclusive).clone_for_update() - else { + let ast::Expr::RefExpr(ast) = make::expr_raw_ref(expr.clone(), exclusive) else { unreachable!() }; @@ -1257,7 +1234,7 @@ impl SyntaxFactory { expr: ast::Expr, ) -> ast::ClosureExpr { let (args, input) = iterator_input(pats); - let ast = make::expr_closure(args, expr.clone()).clone_for_update(); + let ast = make::expr_closure(args, expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1275,9 +1252,7 @@ impl SyntaxFactory { } pub fn expr_return(&self, expr: Option) -> ast::ReturnExpr { - let ast::Expr::ReturnExpr(ast) = make::expr_return(expr.clone()).clone_for_update() else { - unreachable!() - }; + let ast::Expr::ReturnExpr(ast) = make::expr_return(expr.clone()) else { unreachable!() }; if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1291,9 +1266,7 @@ impl SyntaxFactory { } pub fn expr_underscore(&self) -> ast::UnderscoreExpr { - let ast::Expr::UnderscoreExpr(ast) = make::ext::expr_underscore().clone_for_update() else { - unreachable!() - }; + let ast::Expr::UnderscoreExpr(ast) = make::ext::expr_underscore() else { unreachable!() }; ast } @@ -1304,8 +1277,7 @@ impl SyntaxFactory { then_branch: ast::BlockExpr, else_branch: Option, ) -> ast::IfExpr { - let ast = make::expr_if(condition.clone(), then_branch.clone(), else_branch.clone()) - .clone_for_update(); + let ast = make::expr_if(condition.clone(), then_branch.clone(), else_branch.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1328,9 +1300,7 @@ impl SyntaxFactory { } pub fn expr_loop(&self, body: ast::BlockExpr) -> ast::LoopExpr { - let ast::Expr::LoopExpr(ast) = make::expr_loop(body.clone()).clone_for_update() else { - unreachable!() - }; + let ast::Expr::LoopExpr(ast) = make::expr_loop(body.clone()) else { unreachable!() }; if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1342,7 +1312,7 @@ impl SyntaxFactory { } pub fn expr_while_loop(&self, condition: ast::Expr, body: ast::BlockExpr) -> ast::WhileExpr { - let ast = make::expr_while_loop(condition.clone(), body.clone()).clone_for_update(); + let ast = make::expr_while_loop(condition.clone(), body.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1360,8 +1330,7 @@ impl SyntaxFactory { iterable: ast::Expr, body: ast::BlockExpr, ) -> ast::ForExpr { - let ast = - make::expr_for_loop(pat.clone(), iterable.clone(), body.clone()).clone_for_update(); + let ast = make::expr_for_loop(pat.clone(), iterable.clone(), body.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1375,7 +1344,7 @@ impl SyntaxFactory { } pub fn expr_let(&self, pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr { - let ast = make::expr_let(pattern.clone(), expr.clone()).clone_for_update(); + let ast = make::expr_let(pattern.clone(), expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1388,7 +1357,7 @@ impl SyntaxFactory { } pub fn expr_stmt(&self, expr: ast::Expr) -> ast::ExprStmt { - let ast = make::expr_stmt(expr.clone()).clone_for_update(); + let ast = make::expr_stmt(expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1400,7 +1369,7 @@ impl SyntaxFactory { } pub fn expr_match(&self, expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::MatchExpr { - let ast = make::expr_match(expr.clone(), match_arm_list.clone()).clone_for_update(); + let ast = make::expr_match(expr.clone(), match_arm_list.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1416,7 +1385,7 @@ impl SyntaxFactory { } pub fn expr_macro(&self, path: ast::Path, tt: ast::TokenTree) -> ast::MacroExpr { - let ast = make::expr_macro(path.clone(), tt.clone()).clone_for_update(); + let ast = make::expr_macro(path.clone(), tt.clone()); if let Some(mut mapping) = self.mappings() { let macro_call = ast.macro_call().unwrap(); @@ -1436,7 +1405,7 @@ impl SyntaxFactory { guard: Option, expr: ast::Expr, ) -> ast::MatchArm { - let ast = make::match_arm(pat.clone(), guard.clone(), expr.clone()).clone_for_update(); + let ast = make::match_arm(pat.clone(), guard.clone(), expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1452,7 +1421,7 @@ impl SyntaxFactory { } pub fn match_guard(&self, condition: ast::Expr) -> ast::MatchGuard { - let ast = make::match_guard(condition.clone()).clone_for_update(); + let ast = make::match_guard(condition.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1468,7 +1437,7 @@ impl SyntaxFactory { match_arms: impl IntoIterator, ) -> ast::MatchArmList { let (match_arms, input) = iterator_input(match_arms); - let ast = make::match_arm_list(match_arms).clone_for_update(); + let ast = make::match_arm_list(match_arms); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1485,8 +1454,7 @@ impl SyntaxFactory { ty: Option, initializer: Option, ) -> ast::LetStmt { - let ast = - make::let_stmt(pattern.clone(), ty.clone(), initializer.clone()).clone_for_update(); + let ast = make::let_stmt(pattern.clone(), ty.clone(), initializer.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1516,8 +1484,7 @@ impl SyntaxFactory { ty.clone(), initializer.clone(), diverging.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1537,7 +1504,7 @@ impl SyntaxFactory { } pub fn type_arg(&self, ty: ast::Type) -> ast::TypeArg { - let ast = make::type_arg(ty.clone()).clone_for_update(); + let ast = make::type_arg(ty.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1549,7 +1516,7 @@ impl SyntaxFactory { } pub fn lifetime_arg(&self, lifetime: ast::Lifetime) -> ast::LifetimeArg { - let ast = make::lifetime_arg(lifetime.clone()).clone_for_update(); + let ast = make::lifetime_arg(lifetime.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1570,8 +1537,7 @@ impl SyntaxFactory { ) -> ast::Const { let (attrs, attrs_input) = iterator_input(attrs); let ast = - make::item_const(attrs, visibility.clone(), name.clone(), ty.clone(), expr.clone()) - .clone_for_update(); + make::item_const(attrs, visibility.clone(), name.clone(), ty.clone(), expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1607,8 +1573,7 @@ impl SyntaxFactory { name.clone(), ty.clone(), expr.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1651,9 +1616,9 @@ impl SyntaxFactory { ) -> ast::GenericArgList { let (generic_args, input) = iterator_input(generic_args); let ast = if is_turbo { - make::turbofish_generic_arg_list(generic_args).clone_for_update() + make::turbofish_generic_arg_list(generic_args) } else { - make::generic_arg_list(generic_args).clone_for_update() + make::generic_arg_list(generic_args) }; if let Some(mut mapping) = self.mappings() { @@ -1670,7 +1635,7 @@ impl SyntaxFactory { path: ast::Path, fields: ast::RecordExprFieldList, ) -> ast::RecordExpr { - let ast = make::record_expr(path.clone(), fields.clone()).clone_for_update(); + let ast = make::record_expr(path.clone(), fields.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone()); @@ -1688,7 +1653,7 @@ impl SyntaxFactory { fields: impl IntoIterator, ) -> ast::RecordExprFieldList { let (fields, input) = iterator_input(fields); - let ast = make::record_expr_field_list(fields).clone_for_update(); + let ast = make::record_expr_field_list(fields); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1704,7 +1669,7 @@ impl SyntaxFactory { name: ast::NameRef, expr: Option, ) -> ast::RecordExprField { - let ast = make::record_expr_field(name.clone(), expr.clone()).clone_for_update(); + let ast = make::record_expr_field(name.clone(), expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1738,7 +1703,7 @@ impl SyntaxFactory { fields: impl IntoIterator, ) -> ast::RecordFieldList { let (fields, input) = iterator_input(fields); - let ast = make::record_field_list(fields).clone_for_update(); + let ast = make::record_field_list(fields); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1757,8 +1722,7 @@ impl SyntaxFactory { name: ast::Name, ty: ast::Type, ) -> ast::RecordField { - let ast = - make::record_field(visibility.clone(), name.clone(), ty.clone()).clone_for_update(); + let ast = make::record_field(visibility.clone(), name.clone(), ty.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1783,7 +1747,7 @@ impl SyntaxFactory { fields: impl IntoIterator, ) -> ast::TupleFieldList { let (fields, input) = iterator_input(fields); - let ast = make::tuple_field_list(fields).clone_for_update(); + let ast = make::tuple_field_list(fields); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1801,7 +1765,7 @@ impl SyntaxFactory { visibility: Option, ty: ast::Type, ) -> ast::TupleField { - let ast = make::tuple_field(visibility.clone(), ty.clone()).clone_for_update(); + let ast = make::tuple_field(visibility.clone(), ty.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1836,8 +1800,7 @@ impl SyntaxFactory { generic_param_list.clone(), where_clause.clone(), variant_list.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1880,7 +1843,7 @@ impl SyntaxFactory { variants: impl IntoIterator, ) -> ast::VariantList { let (variants, input) = iterator_input(variants); - let ast = make::variant_list(variants).clone_for_update(); + let ast = make::variant_list(variants); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -1905,8 +1868,7 @@ impl SyntaxFactory { name.clone(), field_list.clone(), discriminant.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2012,7 +1974,7 @@ impl SyntaxFactory { ) -> ast::AssocItemList { let (items, input) = iterator_input(items); let items_vec: Vec<_> = items.into_iter().collect(); - let ast = make::assoc_item_list(Some(items_vec)).clone_for_update(); + let ast = make::assoc_item_list(Some(items_vec)); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2024,13 +1986,13 @@ impl SyntaxFactory { } pub fn assoc_item_list_empty(&self) -> ast::AssocItemList { - make::assoc_item_list(None).clone_for_update() + make::assoc_item_list(None) } pub fn item_list(&self, items: impl IntoIterator) -> ast::ItemList { let (items, input) = iterator_input(items); let items_vec: Vec<_> = items.into_iter().collect(); - let ast = make::item_list(Some(items_vec)).clone_for_update(); + let ast = make::item_list(Some(items_vec)); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2042,7 +2004,7 @@ impl SyntaxFactory { } pub fn mod_(&self, name: ast::Name, body: Option) -> ast::Module { - let ast = make::mod_(name.clone(), body.clone()).clone_for_update(); + let ast = make::mod_(name.clone(), body.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2057,7 +2019,7 @@ impl SyntaxFactory { } pub fn attr_outer(&self, meta: ast::Meta) -> ast::Attr { - let ast = make::attr_outer(meta.clone()).clone_for_update(); + let ast = make::attr_outer(meta.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2069,7 +2031,7 @@ impl SyntaxFactory { } pub fn attr_inner(&self, meta: ast::Meta) -> ast::Attr { - let ast = make::attr_inner(meta.clone()).clone_for_update(); + let ast = make::attr_inner(meta.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2081,7 +2043,7 @@ impl SyntaxFactory { } pub fn meta_token_tree(&self, path: ast::Path, tt: ast::TokenTree) -> ast::Meta { - let ast = make::meta_token_tree(path.clone(), tt.clone()).clone_for_update(); + let ast = make::meta_token_tree(path.clone(), tt.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2095,7 +2057,7 @@ impl SyntaxFactory { } pub fn cfg_flag(&self, flag: &str) -> ast::CfgPredicate { - make::cfg_flag(flag).clone_for_update() + make::cfg_flag(flag) } pub fn cfg_attr_meta( @@ -2104,7 +2066,7 @@ impl SyntaxFactory { inner: impl IntoIterator, ) -> ast::CfgAttrMeta { let inner = Vec::from_iter(inner); - let ast = make::cfg_attr_meta(predicate.clone(), inner.iter().cloned()).clone_for_update(); + let ast = make::cfg_attr_meta(predicate.clone(), inner.iter().cloned()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2130,7 +2092,7 @@ impl SyntaxFactory { let tt: Vec<_> = tt.into_iter().collect(); let input: Vec<_> = tt.iter().cloned().filter_map(only_nodes).collect(); - let ast = make::token_tree(delimiter, tt).clone_for_update(); + let ast = make::token_tree(delimiter, tt); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2158,7 +2120,7 @@ impl SyntaxFactory { } pub fn mut_self_param(&self) -> ast::SelfParam { - let ast = make::mut_self_param().clone_for_update(); + let ast = make::mut_self_param(); if let Some(mut mapping) = self.mappings() { let builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2169,7 +2131,7 @@ impl SyntaxFactory { } pub fn self_param(&self) -> ast::SelfParam { - let ast = make::self_param().clone_for_update(); + let ast = make::self_param(); if let Some(mut mapping) = self.mappings() { let builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2196,8 +2158,7 @@ impl SyntaxFactory { path_type.clone(), where_clause.clone(), body.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2241,8 +2202,7 @@ impl SyntaxFactory { generic_param_list.clone(), where_clause.clone(), assoc_items.clone(), - ) - .clone_for_update(); + ); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2269,7 +2229,7 @@ impl SyntaxFactory { } pub fn ret_type(&self, ty: ast::Type) -> ast::RetType { - let ast = make::ret_type(ty.clone()).clone_for_update(); + let ast = make::ret_type(ty.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2280,7 +2240,7 @@ impl SyntaxFactory { } pub fn ty_ref(&self, ty: ast::Type, is_mut: bool) -> ast::Type { - let ast = make::ty_ref(ty.clone(), is_mut).clone_for_update(); + let ast = make::ty_ref(ty.clone(), is_mut); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2303,7 +2263,7 @@ impl SyntaxFactory { } pub fn ty_name(&self, name: ast::Name) -> ast::Type { - let ast = make::ext::ty_name(name.clone()).clone_for_update(); + let ast = make::ext::ty_name(name.clone()); if let Some(mut mapping) = self.mappings() && let ast::Type::PathType(path_ty) = &ast @@ -2318,9 +2278,7 @@ impl SyntaxFactory { } pub fn expr_await(&self, expr: ast::Expr) -> ast::AwaitExpr { - let ast::Expr::AwaitExpr(ast) = make::expr_await(expr.clone()).clone_for_update() else { - unreachable!() - }; + let ast::Expr::AwaitExpr(ast) = make::expr_await(expr.clone()) else { unreachable!() }; if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2332,7 +2290,7 @@ impl SyntaxFactory { } pub fn expr_try(&self, expr: ast::Expr) -> ast::Expr { - let ast = make::expr_try(expr.clone()).clone_for_update(); + let ast = make::expr_try(expr.clone()); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); @@ -2353,8 +2311,7 @@ impl SyntaxFactory { tail_expr: Option, ) -> ast::BlockExpr { let elements = elements.into_iter().collect::>(); - let ast = - make::hacky_block_expr(elements.iter().cloned(), tail_expr.clone()).clone_for_update(); + let ast = make::hacky_block_expr(elements.iter().cloned(), tail_expr.clone()); if let Some(mut mapping) = self.mappings() && let Some(stmt_list) = ast.stmt_list() @@ -2379,9 +2336,7 @@ impl SyntaxFactory { } pub fn expr_break(&self, label: Option, expr: Option) -> ast::BreakExpr { - let ast::Expr::BreakExpr(ast) = - make::expr_break(label.clone(), expr.clone()).clone_for_update() - else { + let ast::Expr::BreakExpr(ast) = make::expr_break(label.clone(), expr.clone()) else { unreachable!() }; @@ -2400,8 +2355,7 @@ impl SyntaxFactory { } pub fn expr_continue(&self, label: Option) -> ast::ContinueExpr { - let ast::Expr::ContinueExpr(ast) = make::expr_continue(label.clone()).clone_for_update() - else { + let ast::Expr::ContinueExpr(ast) = make::expr_continue(label.clone()) else { unreachable!() }; @@ -2427,11 +2381,11 @@ impl SyntaxFactory { &self, parts: impl IntoIterator, ) -> Option { - make::ext::path_from_idents(parts).map(|path| path.clone_for_update()) + make::ext::path_from_idents(parts) } pub fn token_tree_from_node(&self, node: &SyntaxNode) -> ast::TokenTree { - make::ext::token_tree_from_node(node).clone_for_update() + make::ext::token_tree_from_node(node) } pub fn expr_unit(&self) -> ast::Expr { From df6ded56740465f255e7fd33b99abdc8171362bb Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 24 Jul 2026 16:54:03 +0530 Subject: [PATCH 09/94] remove clone_for_update from AstNode --- src/tools/rust-analyzer/crates/syntax/src/ast.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs index d8c7e1583031d..855b5a80a5f6d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs @@ -61,12 +61,6 @@ pub trait AstNode { Self: Sized; fn syntax(&self) -> &SyntaxNode; - fn clone_for_update(&self) -> Self - where - Self: Sized, - { - Self::cast(self.syntax().clone_for_update()).unwrap() - } fn clone_subtree(&self) -> Self where Self: Sized, From 507ea9ed2b5755196bba20dd367e8a281cef3a4b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 24 Jul 2026 17:07:27 +0530 Subject: [PATCH 10/94] make syntax editor void of any mutable API --- .../crates/syntax/src/syntax_editor.rs | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index 7d15195c6f1f7..3ddc7914760eb 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -1,6 +1,6 @@ //! Syntax Tree editor //! -//! Inspired by Roslyn's [`SyntaxEditor`], but is temporarily built upon mutable syntax tree editing. +//! Inspired by Roslyn's [`SyntaxEditor`]. //! //! [`SyntaxEditor`]: https://github.com/dotnet/roslyn/blob/43b0b05cc4f492fd5de00f6f6717409091df8daa/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs @@ -39,13 +39,12 @@ impl SyntaxEditor { /// Creates a syntax editor from `root`. /// /// The returned `root` is guaranteed to be a detached, immutable node. - /// If the provided node is not a root (i.e., has a parent) or is already - /// mutable, it is cloned into a fresh subtree to satisfy syntax editor - /// invariants. + /// If the provided node is not a root (i.e., has a parent), it is cloned + /// into a fresh subtree to satisfy syntax editor invariants. pub fn new(root: SyntaxNode) -> (Self, SyntaxNode) { let mut root = root; - if root.parent().is_some() || root.is_mutable() { + if root.parent().is_some() { root = root.clone_subtree() }; @@ -603,7 +602,7 @@ mod tests { let to_replace = root.syntax().descendants().find_map(ast::BinExpr::cast).unwrap(); let name = make::name("var_name"); - let name_ref = make::name_ref("var_name").clone_for_update(); + let name_ref = make::name_ref("var_name"); let placeholder_snippet = SyntaxAnnotation::default(); editor.add_annotation(name.syntax(), placeholder_snippet); @@ -884,7 +883,7 @@ mod tests { } #[test] - fn test_more_times_replace_node_to_mutable_token() { + fn test_more_times_replace_node_to_same_token() { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); @@ -903,13 +902,13 @@ mod tests { } #[test] - fn test_more_times_replace_node_to_mutable() { + fn test_more_times_replace_node_to_same_node() { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); - let target_expr = make::expr_literal("3").clone_for_update(); + let target_expr = make::expr_literal("3"); for arg in arg_list.args() { editor.replace(arg.syntax(), target_expr.syntax()); @@ -922,13 +921,13 @@ mod tests { } #[test] - fn test_more_times_insert_node_to_mutable() { + fn test_more_times_insert_node_to_same_node() { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); - let target_expr = make::ext::expr_unit().clone_for_update(); + let target_expr = make::ext::expr_unit(); for arg in arg_list.args() { editor.insert(Position::before(arg.syntax()), target_expr.syntax()); From 01c1b9aeb5fd9bb8679a614950b2284eb6aac0a1 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 24 Jul 2026 17:10:41 +0530 Subject: [PATCH 11/94] update mapping for element and remove for node --- .../syntax/src/syntax_editor/mapping.rs | 64 +++++-------------- 1 file changed, 16 insertions(+), 48 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs index 464682223cd0e..d6498a5ec93fb 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs @@ -127,55 +127,23 @@ impl SyntaxMapping { Err(MissingMapping(current)) } - pub fn upmap_element( - &self, - input: &SyntaxElement, - output_root: &SyntaxNode, - ) -> Option> { - match input { - SyntaxElement::Node(node) => { - Some(self.upmap_node(node, output_root)?.map(SyntaxElement::Node)) - } - SyntaxElement::Token(token) => { - let upmap_parent = match self.upmap_node(&token.parent().unwrap(), output_root)? { - Ok(it) => it, - Err(err) => return Some(Err(err)), - }; + pub(super) fn upmap_element(&self, input: &SyntaxElement) -> SyntaxElement { + let mut current = input.clone(); - let element = upmap_parent.children_with_tokens().nth(token.index()).unwrap(); - debug_assert!( - element.as_token().is_some_and(|it| it.kind() == token.kind()), - "token upmapping mapped to the wrong node ({token:?} -> {element:?})" - ); - - Some(Ok(element)) - } - } - } - - pub fn upmap_node( - &self, - input: &SyntaxNode, - output_root: &SyntaxNode, - ) -> Option> { - // Try to follow the mapping tree, if it exists - let input_mapping = self.upmap_node_single(input); - let input_ancestor = - input.ancestors().find(|ancestor| self.upmap_node_single(ancestor).is_some()); - - match (input_mapping, input_ancestor) { - (Some(input_mapping), _) => { - // A mapping exists at the input, follow along the tree - Some(self.upmap_child(&input_mapping, &input_mapping, output_root)) - } - (None, Some(input_ancestor)) => { - // A mapping exists at an ancestor, follow along the tree - Some(self.upmap_child(input, &input_ancestor, output_root)) - } - (None, None) => { - // No mapping exists at all, is the same position in the final tree - None - } + loop { + let node = match ¤t { + SyntaxElement::Node(node) => node.clone(), + SyntaxElement::Token(token) => token.parent().unwrap(), + }; + let Some(input_ancestor) = + node.ancestors().find(|ancestor| self.upmap_node_single(ancestor).is_some()) + else { + return current; + }; + let output_ancestor = self.upmap_node_single(&input_ancestor).unwrap(); + current = self + .upmap_child_element(¤t, &input_ancestor, &output_ancestor.parent().unwrap()) + .expect("the nearest mapped ancestor must map its descendants"); } } From 70c3ea65c629fd6a95be63e8d0f180fce6787531 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 24 Jul 2026 17:11:01 +0530 Subject: [PATCH 12/94] update edits with new annotation semantics --- .../src/handlers/generate_blanket_trait_impl.rs | 2 +- .../src/handlers/replace_derive_with_manual_impl.rs | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index 1e3f2ac677254..acd98aed00cee 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -141,7 +141,7 @@ pub(crate) fn generate_blanket_trait_impl( if let Some(cap) = ctx.config.snippet_cap && let Some(self_ty) = impl_.self_ty() { - builder.add_tabstop_before(cap, self_ty); + editor.add_annotation(self_ty.syntax(), builder.make_tabstop_before(cap)); } builder.add_file_edits(ctx.vfs_file_id(), editor); }, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 4e85b30b58188..707b3321c338d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -187,15 +187,18 @@ fn add_assist( && m.syntax().text() == "todo!()" { // Make the `todo!()` a placeholder - builder.add_placeholder_snippet(cap, m); + editor.add_annotation(m.syntax(), builder.make_placeholder_snippet(cap)); } else { // If we haven't already added a snippet, add a tabstop before the generated function - builder.add_tabstop_before(cap, first_assoc_item); + editor.add_annotation( + first_assoc_item.syntax(), + builder.make_tabstop_before(cap), + ); } } else if let Some(l_curly) = impl_def.assoc_item_list().and_then(|it| it.l_curly_token()) { - builder.add_tabstop_after_token(cap, l_curly); + editor.add_annotation(l_curly, builder.make_tabstop_after(cap)); } } From 6877440aca121b8fc6ec64b9c04d07163d2ac1c1 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 24 Jul 2026 19:00:53 +0530 Subject: [PATCH 13/94] Refactor edit_algo around immutable syntax tree with better semantics --- .../syntax/src/syntax_editor/edit_algo.rs | 1052 ++++++++++------- 1 file changed, 653 insertions(+), 399 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs index 36f50e39186c6..71b03784e981e 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -1,415 +1,697 @@ //! Implementation of applying changes to a syntax tree. -use std::{ - cmp::Ordering, - collections::VecDeque, - ops::{Range, RangeInclusive}, -}; +use std::{cmp::Ordering, ops::Range}; use rowan::TextRange; use rustc_hash::FxHashMap; use stdx::format_to; -use crate::{ - SyntaxElement, SyntaxNode, SyntaxNodePtr, - syntax_editor::{Change, ChangeKind, PositionRepr, mapping::MissingMapping}, -}; - -use super::{SyntaxEdit, SyntaxEditor}; +use crate::{NodeOrToken, SyntaxElement, SyntaxNode}; -pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { - // Algorithm overview: - // - // - Sort changes by (range, type) - // - Ensures that parent edits are before child edits - // - Ensures that inserts will be guaranteed to be inserted at the right range - // - Validate changes - // - Checking for invalid changes is easy since the changes will be sorted by range - // - Fixup change targets - // - standalone change? map to original syntax tree - // - dependent change? - // - try to map to parent change (either independent or another dependent) - // - note: need to keep track of a parent change stack, since a change can be a parent of multiple changes - // - Apply changes - // - find changes to apply to real tree by applying nested changes first - // - changed nodes become part of the changed node set (useful for the formatter to only change those parts) - // - Propagate annotations +use super::{ + Change, ChangeKind, PositionRepr, SyntaxAnnotation, SyntaxEdit, SyntaxEditor, SyntaxMapping, + mapping::MissingMapping, +}; - let SyntaxEditor { root, changes, annotations, make } = editor; - let mut changes = changes.into_inner(); - let annotations = annotations.into_inner(); - let mappings = make.take(); +/// A validated batch of changes in the exact order in which it must execute. +/// +/// Planning is deliberately separate from tree mutation. Once an `EditPlan` +/// exists, execution does not need to reason about overlaps, dependencies, or +/// source ordering. +struct EditPlan { + changes: Vec, +} - let mut node_depths = FxHashMap::::default(); - let mut get_node_depth = |node: SyntaxNode| { - *node_depths.entry(node).or_insert_with_key(|node| node.ancestors().count()) - }; +/// A change whose target tree and output tracking are fully known. +struct PlannedChange { + tree: SyntaxNode, + change: Change, + record_as_changed: bool, +} - // Sort changes by range, then depth, then change kind, so that we can: - // - ensure that parent edits are ordered before child edits - // - ensure that inserts will be guaranteed to be inserted at the right range - // - easily check for disjoint replace ranges - changes.sort_by(|a, b| { - a.target_range() - .start() - .cmp(&b.target_range().start()) - .then_with(|| { - let a_target = a.target_parent(); - let b_target = b.target_parent(); +impl PlannedChange { + /// Returns the immutable source elements that this change will slice in. + fn replacement_elements(&self) -> &[SyntaxElement] { + match &self.change { + Change::Insert(_, element) | Change::Replace(_, Some(element)) => { + std::slice::from_ref(element) + } + Change::InsertAll(_, elements) + | Change::ReplaceWithMany(_, elements) + | Change::ReplaceAll(_, elements) => elements, + Change::Replace(_, None) => &[], + } + } +} - if a_target == b_target { - return Ordering::Equal; - } +/// The dependency info accumulated from one source ordered changes. +/// +/// `parent` is an edge to the nearest containing node replacement. A discarded +/// entry has no executable graph node because an ancestor deletion or ambiguous +/// range replacement has mde its target unavailable. +#[derive(Clone, Copy, Default)] +struct PlanEntry { + parent: Option, + discarded: bool, +} - get_node_depth(a_target).cmp(&get_node_depth(b_target)) - }) - .then(a.change_kind().cmp(&b.change_kind())) - }); +/// Planning failure containing the source-ordered changes use for diag. +struct InvalidEditPlan { + changes: Vec, +} - let disjoint_replaces_ranges = changes - .iter() - .zip(changes.iter().skip(1)) - .filter(|(l, r)| { - // We only care about checking for disjoint replace ranges - matches!( - (l.change_kind(), r.change_kind()), - ( - ChangeKind::Replace | ChangeKind::ReplaceRange, - ChangeKind::Replace | ChangeKind::ReplaceRange - ) - ) - }) - .all(|(l, r)| { - get_node_depth(l.target_parent()) != get_node_depth(r.target_parent()) - || (l.target_range().end() <= r.target_range().start()) +impl EditPlan { + /// Validates raw editor changes and turns them into an execution schedule. + /// + /// The input is first sorted in source order, dependent targets are then + /// rewritten from their input trees into ancestor replacement trees. Finally, + /// discarded changes are removed and the dependency forest is traversed in + /// postorder. + /// Independent roots and sibling changes are prioritized right to left. + fn build( + mut changes: Vec, + mappings: &SyntaxMapping, + mut node_depth: impl FnMut(SyntaxNode) -> usize, + ) -> Result { + changes.sort_by(|left, right| { + left.target_range() + .start() + .cmp(&right.target_range().start()) + .then_with(|| { + let left_target = left.target_parent(); + let right_target = right.target_parent(); + if left_target == right_target { + Ordering::Equal + } else { + node_depth(left_target).cmp(&node_depth(right_target)) + } + }) + .then(left.change_kind().cmp(&right.change_kind())) }); - if !disjoint_replaces_ranges { - report_intersecting_changes(&changes, get_node_depth, &root); + if !Self::replacements_are_disjoint(&changes, &mut node_depth) { + return Err(InvalidEditPlan { changes }); + } - return SyntaxEdit { - old_root: root.clone(), - new_root: root, - annotations: Default::default(), - changed_elements: vec![], - }; - } + let mut entries = vec![PlanEntry::default(); changes.len()]; + let mut regions_by_tree = FxHashMap::>::default(); + + for (index, change) in changes.iter().enumerate() { + let target_tree = change.target_parent().tree_top(); + let regions = regions_by_tree.entry(target_tree).or_default(); + if let Some(region_index) = regions + .iter() + .rposition(|region| region.range.contains_range(change.target_range())) + { + regions.truncate(region_index + 1); + match regions[region_index].nested_changes { + NestedChanges::Remap => { + entries[index].parent = Some(regions[region_index].change_index); + } + NestedChanges::Discard => entries[index].discarded = true, + } + } else { + regions.clear(); + } - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] - struct DependentChange { - parent: u32, - child: u32, - } + if let Some(region) = ChangedRegion::for_change(change, index, entries[index].discarded) + { + regions.push(region); + } + } - // Build change tree - let mut changed_ancestors: VecDeque = VecDeque::new(); - let mut dependent_changes = vec![]; - let mut independent_changes = vec![]; - let mut outdated_changes = vec![]; + // Work from the innermost dependency towards the outermost one. This + // lets a chain A -> B -> C rewrite C into B before B itself is mapped + // into A's replacement tree. + for (child, entry) in entries.iter().enumerate().rev() { + if let Some(parent) = entry.parent { + Self::rewrite_dependent_target(&mut changes, parent, child, mappings); + } + } - for (change_index, change) in changes.iter().enumerate() { - // Check if this change is dependent on another change (i.e. it's contained within another range) - if let Some(index) = changed_ancestors + let mut children = vec![Vec::new(); changes.len()]; + for (child, entry) in entries.iter().enumerate() { + if let Some(parent) = entry.parent { + children[parent].push(child); + } + } + for siblings in &mut children { + siblings.sort_by(|&left, &right| { + Self::execution_priority(&changes[left], &changes[right], &mut node_depth) + }); + } + + let mut roots = entries .iter() - .rposition(|ancestor| ancestor.affected_range().contains_range(change.target_range())) - { - // Pop off any ancestors that aren't applicable - changed_ancestors.drain((index + 1)..); + .enumerate() + .filter_map(|(index, entry)| { + (!entry.discarded && entry.parent.is_none()).then_some(index) + }) + .collect::>(); + roots.sort_by(|&left, &right| { + Self::execution_priority(&changes[left], &changes[right], &mut node_depth) + }); - // FIXME: Resolve changes that depend on a range of elements - let ancestor = &changed_ancestors[index]; + let mut planned = changes + .into_iter() + .zip(entries) + .map(|(change, entry)| { + (!entry.discarded).then_some(PlannedChange { + tree: change.target_parent().tree_top(), + change, + record_as_changed: entry.parent.is_none(), + }) + }) + .collect::>(); - if let Change::Replace(_, None) = changes[ancestor.change_index] { - outdated_changes.push(change_index as u32); - } else { - dependent_changes.push(DependentChange { - parent: ancestor.change_index as u32, - child: change_index as u32, - }); - } - } else { - // This change is independent of any other change + let mut ordered = Vec::new(); + for root in roots { + Self::append_postorder(root, &children, &mut planned, &mut ordered); + } + + Ok(Self { changes: ordered }) + } - // Drain the changed ancestors since we're no longer in a set of dependent changes - changed_ancestors.drain(..); + /// Orders disjoint changes from right to left, with deeper ties first. + fn execution_priority( + left: &Change, + right: &Change, + node_depth: &mut impl FnMut(SyntaxNode) -> usize, + ) -> Ordering { + right + .target_range() + .start() + .cmp(&left.target_range().start()) + .then_with(|| node_depth(right.target_parent()).cmp(&node_depth(left.target_parent()))) + .then(right.change_kind().cmp(&left.change_kind())) + } - independent_changes.push(change_index as u32); + /// Appends a dependency subtree in post order fashion. + fn append_postorder( + index: usize, + children: &[Vec], + planned: &mut [Option], + ordered: &mut Vec, + ) { + for &child in &children[index] { + Self::append_postorder(child, children, planned, ordered); } + ordered.push(planned[index].take().expect("reachable plan nodes are not discarded")); + } - // Add to changed ancestors, if applicable - match change { - Change::Replace(SyntaxElement::Node(target), _) - | Change::ReplaceWithMany(SyntaxElement::Node(target), _) => { - changed_ancestors.push_back(ChangedAncestor::single(target, change_index)) + /// Checks that replacement at the same tree depth do not overlap + fn replacements_are_disjoint( + changes: &[Change], + mut node_depth: impl FnMut(SyntaxNode) -> usize, + ) -> bool { + let mut previous = FxHashMap::<(SyntaxNode, usize), TextRange>::default(); + for change in changes { + if !matches!(change.change_kind(), ChangeKind::Replace | ChangeKind::ReplaceRange) { + continue; } - Change::ReplaceAll(range, _) => { - changed_ancestors.push_back(ChangedAncestor::multiple(range, change_index)) + + let parent = change.target_parent(); + let key = (parent.tree_top(), node_depth(parent)); + if previous + .insert(key, change.target_range()) + .is_some_and(|range| range.end() > change.target_range().start()) + { + return false; } - _ => (), } + true } - // Map change targets to the correct syntax nodes - let tree_mutator = TreeMutator::new(&root); - let mut changed_elements = vec![]; - let mut changed_elements_set = rustc_hash::FxHashSet::default(); - let mut deduplicate_node = |node_or_token: &mut SyntaxElement| { - let node; - let node = match node_or_token { - SyntaxElement::Token(token) => match token.parent() { - None => return, - Some(parent) => { - node = parent; - &node - } - }, - SyntaxElement::Node(node) => node, + /// Maps one dependent change into its ancestor replacement tree. + fn rewrite_dependent_target( + changes: &mut [Change], + parent: usize, + child: usize, + mappings: &SyntaxMapping, + ) { + let (input_ancestor, output_ancestor) = match &changes[parent] { + Change::Replace( + SyntaxElement::Node(target), + Some(SyntaxElement::Node(replacement)), + ) => (target.clone(), replacement.clone()), + _ => unreachable!("only node replacements can own dependent changes"), }; - if changed_elements_set.contains(node) { - let new_node = node.clone_subtree().clone_for_update(); - match node_or_token { - SyntaxElement::Node(node) => *node = new_node, - SyntaxElement::Token(token) => { - *token = new_node - .children_with_tokens() - .filter_map(SyntaxElement::into_token) - .find(|it| it.kind() == token.kind() && it.text() == token.text()) - .unwrap(); - } - } - } else { - changed_elements_set.insert(node.clone()); - } - }; - for index in independent_changes { - match &mut changes[index as usize] { - Change::Insert(target, _) | Change::InsertAll(target, _) => { - match &mut target.repr { - PositionRepr::FirstChild(parent) => { - *parent = tree_mutator.make_syntax_mut(parent); - } - PositionRepr::After(child) => { - *child = tree_mutator.make_element_mut(child); - } - }; - } - Change::Replace(SyntaxElement::Node(target), Some(SyntaxElement::Node(_))) => { - *target = tree_mutator.make_syntax_mut(target); + let upmap_node = |target: &SyntaxNode| { + mappings.upmap_child(target, &input_ancestor, &output_ancestor).unwrap_or_else( + |MissingMapping(current)| { + panic!( + "no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}" + ) + }, + ) + }; + let upmap_element = |target: &SyntaxElement| { + mappings.upmap_child_element(target, &input_ancestor, &output_ancestor).unwrap_or_else( + |MissingMapping(current)| { + panic!( + "no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}" + ) + }, + ) + }; + + match &mut changes[child] { + Change::Insert(position, _) | Change::InsertAll(position, _) => { + match &mut position.repr { + PositionRepr::FirstChild(parent) => *parent = upmap_node(parent), + PositionRepr::After(child) => *child = upmap_element(child), + } } Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { - *target = tree_mutator.make_element_mut(target); + *target = upmap_element(target); } Change::ReplaceAll(range, _) => { - let start = tree_mutator.make_element_mut(range.start()); - let end = tree_mutator.make_element_mut(range.end()); - - *range = start..=end; + *range = upmap_element(range.start())..=upmap_element(range.end()); } } + } +} - match &mut changes[index as usize] { - Change::Insert(_, SyntaxElement::Node(node)) - | Change::Replace(_, Some(SyntaxElement::Node(node))) => { - if node.parent().is_some() { - *node = node.clone_subtree().clone_for_update(); - } else if !node.is_mutable() { - *node = node.clone_for_update(); - } - } - Change::Insert(_, SyntaxElement::Token(token)) - | Change::Replace(_, Some(SyntaxElement::Token(token))) => { - if let Some(parent) = token.parent() { - let idx = token.index(); - let new_parent = parent.clone_subtree().clone_for_update(); - *token = new_parent - .children_with_tokens() - .nth(idx) - .and_then(SyntaxElement::into_token) - .unwrap(); - } - } - Change::InsertAll(_, elements) - | Change::ReplaceWithMany(_, elements) - | Change::ReplaceAll(_, elements) => { - for element in elements { - match element { - SyntaxElement::Node(node) => { - if node.parent().is_some() { - *node = node.clone_subtree().clone_for_update(); - } else if !node.is_mutable() { - *node = node.clone_for_update(); - } - } - SyntaxElement::Token(token) => { - if let Some(parent) = token.parent() { - let idx = token.index(); - let new_parent = parent.clone_subtree().clone_for_update(); - *token = new_parent - .children_with_tokens() - .nth(idx) - .and_then(SyntaxElement::into_token) - .unwrap(); - } - } - } - } - } - _ => {} - } +/// A stable structural address expressed as `children_with_token` indices. +#[derive(Clone)] +struct SyntaxPath { + child_indices: Vec, +} - match &mut changes[index as usize] { - Change::Insert(_, element) | Change::Replace(_, Some(element)) => { - deduplicate_node(element); - } - Change::InsertAll(_, elements) - | Change::ReplaceWithMany(_, elements) - | Change::ReplaceAll(_, elements) => { - elements.iter_mut().for_each(&mut deduplicate_node); +impl SyntaxPath { + /// Builds the root-relative path of element in its current tree. + fn new(element: &SyntaxElement) -> Self { + let mut child_indices = Vec::new(); + let mut node = match element { + SyntaxElement::Node(node) => node.clone(), + SyntaxElement::Token(token) => { + child_indices.push(token.index()); + token.parent().unwrap() } - Change::Replace(_, None) => (), + }; + + while let Some(parent) = node.parent() { + child_indices.push(node.index()); + node = parent; } + child_indices.reverse(); + Self { child_indices } + } - // Collect changed elements - match &changes[index as usize] { - Change::Insert(_, element) => changed_elements.push(element.clone()), - Change::InsertAll(_, elements) => changed_elements.extend(elements.iter().cloned()), - Change::Replace(_, Some(element)) => changed_elements.push(element.clone()), - Change::Replace(_, None) => {} - Change::ReplaceWithMany(_, elements) => { - changed_elements.extend(elements.iter().cloned()) - } - Change::ReplaceAll(_, elements) => changed_elements.extend(elements.iter().cloned()), + /// Follows this path from root, returning None if the structure differs. + fn resolve(&self, root: &SyntaxNode) -> Option { + let mut current = SyntaxElement::Node(root.clone()); + for &index in &self.child_indices { + current = current.into_node()?.children_with_tokens().nth(index)?; } + Some(current) } - for DependentChange { parent, child } in dependent_changes.into_iter().rev() { - let (input_ancestor, output_ancestor) = match &changes[parent as usize] { - // No change will depend on an insert since changes can only depend on nodes in the root tree - Change::Insert(_, _) | Change::InsertAll(_, _) => unreachable!(), - Change::Replace(target, Some(new_target)) => { - (to_owning_node(target), to_owning_node(new_target)) - } - Change::Replace(_, None) => { - unreachable!("deletions should not generate dependent changes") - } - Change::ReplaceAll(_, _) | Change::ReplaceWithMany(_, _) => { - unimplemented!("cannot resolve changes that depend on replacing many elements") - } - }; + /// Removes `ancestor`'s prefix, yielding this path within that subtree. + /// + /// Could have used LCA? + fn relative_to(&self, ancestor: &SyntaxPath) -> Option { + self.child_indices + .strip_prefix(ancestor.child_indices.as_slice()) + .map(|relative| SyntaxPath { child_indices: relative.to_vec() }) + } - let upmap_target_node = |target: &SyntaxNode| match mappings.upmap_child( - target, - &input_ancestor, - &output_ancestor, - ) { - Ok(it) => it, - Err(MissingMapping(current)) => unreachable!( - "no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}" - ), - }; + /// Appends an inserted child slot and a path relative to that child. + fn in_child(&self, index: usize, relative: &SyntaxPath) -> SyntaxPath { + let mut child_indices = + Vec::with_capacity(self.child_indices.len() + relative.child_indices.len() + 1); + child_indices.extend_from_slice(&self.child_indices); + child_indices.push(index); + child_indices.extend_from_slice(&relative.child_indices); + SyntaxPath { child_indices } + } - let upmap_target = |target: &SyntaxElement| match mappings.upmap_child_element( - target, - &input_ancestor, - &output_ancestor, - ) { - Ok(it) => it, - Err(MissingMapping(current)) => unreachable!( - "no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}" - ), + /// Updates this path for a splice and reports whether its element survives. + fn adjust_for_splice( + &mut self, + parent: &SyntaxPath, + deleted: &Range, + inserted: usize, + ) -> bool { + let Some(relative) = self.child_indices.strip_prefix(parent.child_indices.as_slice()) + else { + return true; }; + let Some((&child, _)) = relative.split_first() else { return true }; - match &mut changes[child as usize] { - Change::Insert(target, _) | Change::InsertAll(target, _) => match &mut target.repr { - PositionRepr::FirstChild(parent) => { - *parent = upmap_target_node(parent); - } - PositionRepr::After(child) => { - *child = upmap_target(child); + if deleted.contains(&child) { + return false; + } + if child >= deleted.end { + let new_child = child + inserted; + self.child_indices[parent.child_indices.len()] = + new_child - (deleted.end - deleted.start); + } + true + } +} + +/// An annotation paired with its structural location and registration order. +#[derive(Clone)] +struct TrackedAnnotation { + path: SyntaxPath, + annotation: SyntaxAnnotation, + order: usize, +} + +/// A structural edit used to translate original paths into a current tree. +enum PathEdit { + /// A child-list splice with all coordinates relative to the pre-edit tree. + Splice { parent: SyntaxPath, deleted: Range, inserted: usize }, + /// A root replacement, after which no path into the old root survives. + ReplaceRoot, +} + +/// The evolving immutable root and location metadata for one source tree. +/// +/// A syntax edit can involve the editor root plus several detached factory +/// trees. Each receives an independent state so dependent edits can be applied +/// before a generated tree is inserted elsewhere. +struct TreeState { + root: SyntaxNode, + edits: Vec, + changed: Vec, + original_annotations: Vec, + annotations: Vec, +} + +impl TreeState { + /// Starts tracking an unmodified immutable root. + fn new(root: SyntaxNode) -> Self { + Self { + root, + edits: Vec::new(), + changed: Vec::new(), + original_annotations: Vec::new(), + annotations: Vec::new(), + } + } + + /// Replay structural edits to translate an original path into this state. + fn map_original_path(&self, mut path: SyntaxPath) -> Option { + for edit in &self.edits { + match edit { + PathEdit::Splice { parent, deleted, inserted } => { + if !path.adjust_for_splice(parent, deleted, *inserted) { + return None; + } } - }, - Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { - *target = upmap_target(target); - } - Change::ReplaceAll(range, _) => { - *range = upmap_target(range.start())..=upmap_target(range.end()); + PathEdit::ReplaceRoot => return None, } } + Some(path) } - // We reverse here since we pushed to this in ascending order, - // and we want to remove elements in descending order - for idx in outdated_changes.into_iter().rev() { - changes.remove(idx as usize); + /// Finds a change target in the current root. + fn map_original_element(&self, element: &SyntaxElement) -> SyntaxElement { + self.map_original_path(SyntaxPath::new(element)) + .and_then(|path| path.resolve(&self.root)) + .expect("an edit target must still be present") } - // Apply changes - let mut root = tree_mutator.mutable_clone; + /// Applies one child-list splice and updates tracked structural path. + fn splice( + &mut self, + parent_path: SyntaxPath, + deleted: Range, + inserted: Vec, + track_as_changed: bool, + ) { + let inserted_count = inserted.len(); + self.changed + .retain_mut(|path| path.adjust_for_splice(&parent_path, &deleted, inserted_count)); + self.annotations + .retain_mut(|it| it.path.adjust_for_splice(&parent_path, &deleted, inserted_count)); + + for (offset, element) in inserted.iter().enumerate() { + let index = deleted.start + offset; + if track_as_changed { + self.changed + .push(parent_path.in_child(index, &SyntaxPath { child_indices: Vec::new() })); + } + self.annotations.extend(element.annotations.iter().map(|annotation| { + TrackedAnnotation { + path: parent_path.in_child(index, &annotation.path), + annotation: annotation.annotation, + order: annotation.order, + } + })); + } + + let parent = parent_path.resolve(&self.root).and_then(SyntaxElement::into_node).unwrap(); + let green = rowan::GreenNodeData::splice_children( + parent.green().as_ref(), + deleted.clone(), + inserted.into_iter().map(PreparedElement::into_green), + ); + self.root = SyntaxNode::new_root(parent.replace_with(green)); + self.edits.push(PathEdit::Splice { + parent: parent_path, + deleted, + inserted: inserted_count, + }); + } + + /// Replaces the tree's root with a prepared node payload. + fn replace_root(&mut self, replacement: PreparedElement, track_as_changed: bool) { + let NodeOrToken::Node(node) = replacement.syntax else { + panic!("root node replacement should be a node") + }; + self.root = SyntaxNode::new_root(node.green().into_owned()); + self.changed.clear(); + if track_as_changed { + self.changed.push(SyntaxPath { child_indices: Vec::new() }); + } + self.annotations = replacement.annotations; + self.edits.push(PathEdit::ReplaceRoot); + } - for change in changes { + /// Applies a planned change to this tree using already prepared payloads. + fn apply( + &mut self, + change: &Change, + replacement: Vec, + record_as_changed: bool, + ) { match change { - Change::Insert(position, element) => { - let (parent, index) = position.place(); - parent.splice_children(index..index, vec![element]); - } - Change::InsertAll(position, elements) => { - let (parent, index) = position.place(); - parent.splice_children(index..index, elements); - } - Change::Replace(target, None) => { - target.detach(); - } - Change::Replace(SyntaxElement::Node(target), Some(new_target)) if target == root => { - root = new_target.into_node().expect("root node replacement should be a node"); + Change::Insert(position, _) | Change::InsertAll(position, _) => { + let (parent, index) = match &position.repr { + PositionRepr::FirstChild(parent) => { + let parent = self.map_original_element(&parent.clone().into()); + (parent.into_node().unwrap(), 0) + } + PositionRepr::After(child) => { + let child = self.map_original_element(child); + (child.parent().unwrap(), child.index() + 1) + } + }; + self.splice( + SyntaxPath::new(&parent.into()), + index..index, + replacement, + record_as_changed, + ); } - Change::Replace(target, Some(new_target)) => { - let parent = target.parent().unwrap(); - parent.splice_children(target.index()..target.index() + 1, vec![new_target]); + Change::Replace(SyntaxElement::Node(target), Some(_)) if target.parent().is_none() => { + self.replace_root(replacement.into_iter().next().unwrap(), record_as_changed); } - Change::ReplaceWithMany(target, elements) => { + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { + let target = self.map_original_element(target); let parent = target.parent().unwrap(); - parent.splice_children(target.index()..target.index() + 1, elements); + let index = target.index(); + self.splice( + SyntaxPath::new(&parent.into()), + index..index + 1, + replacement, + record_as_changed, + ); } - Change::ReplaceAll(range, elements) => { - let start = range.start().index(); - let end = range.end().index(); - let parent = range.start().parent().unwrap(); - parent.splice_children(start..end + 1, elements); + Change::ReplaceAll(range, _) => { + let start = self.map_original_element(range.start()); + let end = self.map_original_element(range.end()); + let parent = start.parent().unwrap(); + self.splice( + SyntaxPath::new(&parent.into()), + start.index()..end.index() + 1, + replacement, + record_as_changed, + ); } } } +} + +/// A replacement payload paired with the annotation below it. +/// +/// The syntax element remains an immutable snapshot of its source tree. +/// Annotation paths are relative to the payload root and are rebased by +/// splice +struct PreparedElement { + syntax: SyntaxElement, + annotations: Vec, +} - // Propagate annotations - let annotations = annotations.into_iter().filter_map(|(element, annotation)| { - match mappings.upmap_element(&element, &root) { - // Needed to follow the new tree to find the resulting element - Some(Ok(mapped)) => Some((mapped, annotation)), - // Element did not need to be mapped - None => Some((element, annotation)), - // Element did not make it to the final tree - Some(Err(_)) => None, +impl PreparedElement { + fn into_green(self) -> rowan::NodeOrToken { + match self.syntax { + SyntaxElement::Node(node) => NodeOrToken::Node(node.green().into_owned()), + SyntaxElement::Token(token) => NodeOrToken::Token(token.green().to_owned()), } - }); + } +} - let mut annotation_groups = FxHashMap::default(); +/// Owns all evolving trees involved in executing an edit plan. +struct TreeStore { + states: FxHashMap, +} - for (element, annotation) in annotations { - annotation_groups.entry(annotation).or_insert(vec![]).push(element); +impl TreeStore { + /// Creates per tree state for annotations after following factory mapping. + fn with_annotations( + annotations: Vec<(SyntaxElement, SyntaxAnnotation)>, + mappings: &SyntaxMapping, + ) -> Self { + let mut states = FxHashMap::::default(); + for (order, (element, annotation)) in annotations.into_iter().enumerate() { + let element = mappings.upmap_element(&element); + let tree = element.tree_top(); + let tracked = TrackedAnnotation { path: SyntaxPath::new(&element), annotation, order }; + let state = states.entry(tree.clone()).or_insert_with(|| TreeState::new(tree)); + state.original_annotations.push(tracked.clone()); + state.annotations.push(tracked); + } + Self { states } + } + + /// Execute an already ordered plan without performing further analysis. + fn execute(&mut self, plan: EditPlan) { + for planned in plan.changes { + self.states + .entry(planned.tree.clone()) + .or_insert_with(|| TreeState::new(planned.tree.clone())); + let replacement = planned + .replacement_elements() + .iter() + .map(|element| self.prepare_element(element)) + .collect(); + self.states.get_mut(&planned.tree).unwrap().apply( + &planned.change, + replacement, + planned.record_as_changed, + ); + } } - SyntaxEdit { - old_root: tree_mutator.immutable, - new_root: root, - changed_elements, - annotations: annotation_groups, + /// Captures the source element and annotations used by a replacement. + fn prepare_element(&self, element: &SyntaxElement) -> PreparedElement { + let tree = element.tree_top(); + let original_path = SyntaxPath::new(element); + let (element, annotations) = match self.states.get(&tree) { + Some(state) => { + let annotations_below = + |annotations: &[TrackedAnnotation], ancestor: &SyntaxPath| { + annotations + .iter() + .filter_map(|annotation| { + annotation.path.relative_to(ancestor).map(|path| { + TrackedAnnotation { + path, + annotation: annotation.annotation, + order: annotation.order, + } + }) + }) + .collect() + }; + match state.map_original_path(original_path.clone()) { + Some(path) => { + let element = path.resolve(&state.root).unwrap(); + let annotations = annotations_below(&state.annotations, &path); + (element, annotations) + } + None => { + let annotations = + annotations_below(&state.original_annotations, &original_path); + (element.clone(), annotations) + } + } + } + None => (element.clone(), Vec::new()), + }; + PreparedElement { syntax: element, annotations } + } + + /// Resolves the editor roots tracked paths and constructs the public edit. + fn finish(mut self, old_root: SyntaxNode) -> SyntaxEdit { + let state = + self.states.remove(&old_root).unwrap_or_else(|| TreeState::new(old_root.clone())); + let new_root = state.root; + + let mut changed_elements = state + .changed + .into_iter() + .filter_map(|path| path.resolve(&new_root)) + .collect::>(); + changed_elements.sort_by_key(|element| element.text_range().start()); + + let mut annotations = FxHashMap::>::default(); + for annotation in state.annotations { + if let Some(element) = annotation.path.resolve(&new_root) { + annotations + .entry(annotation.annotation) + .or_default() + .push((annotation.order, element)); + } + } + let annotations = annotations + .into_iter() + .map(|(annotation, mut elements)| { + elements.sort_by_key(|(order, element)| (*order, element.text_range().start())); + (annotation, elements.into_iter().map(|(_, element)| element).collect()) + }) + .collect(); + + SyntaxEdit { old_root, new_root, changed_elements, annotations } } } +/// Plans and executes all changes recorded by a SyntaxEditor. +pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { + let SyntaxEditor { root, changes, annotations, make } = editor; + let mappings = make.take(); + let mut node_depths = FxHashMap::::default(); + let mut node_depth = |node: SyntaxNode| { + *node_depths.entry(node).or_insert_with_key(|node| node.ancestors().count()) + }; + + let plan = match EditPlan::build(changes.into_inner(), &mappings, &mut node_depth) { + Ok(plan) => plan, + Err(InvalidEditPlan { changes }) => { + report_intersecting_changes(&changes, &mut node_depth, &root); + return SyntaxEdit { + old_root: root.clone(), + new_root: root, + annotations: FxHashMap::default(), + changed_elements: Vec::new(), + }; + } + }; + + let mut trees = TreeStore::with_annotations(annotations.into_inner(), &mappings); + trees.execute(plan); + trees.finish(root) +} + fn report_intersecting_changes( changes: &[Change], - mut get_node_depth: impl FnMut(rowan::SyntaxNode) -> usize, - root: &rowan::SyntaxNode, + mut get_node_depth: impl FnMut(SyntaxNode) -> usize, + root: &SyntaxNode, ) { let intersecting_changes = changes .iter() @@ -478,77 +760,49 @@ fn report_intersecting_changes( stdx::always!(false, "{}", error_msg); } -fn to_owning_node(element: &SyntaxElement) -> SyntaxNode { - match element { - SyntaxElement::Node(node) => node.clone(), - SyntaxElement::Token(token) => token.parent().unwrap(), - } -} - -struct ChangedAncestor { - kind: ChangedAncestorKind, +/// A replacement region that can contain later source ordered changeds +struct ChangedRegion { + range: TextRange, change_index: usize, + nested_changes: NestedChanges, } -enum ChangedAncestorKind { - Single { node: SyntaxNode }, - Range { _changed_elements: RangeInclusive, _in_parent: SyntaxNode }, -} - -impl ChangedAncestor { - fn single(node: &SyntaxNode, change_index: usize) -> Self { - let kind = ChangedAncestorKind::Single { node: node.clone() }; - - Self { kind, change_index } - } - - fn multiple(range: &RangeInclusive, change_index: usize) -> Self { - Self { - kind: ChangedAncestorKind::Range { - _changed_elements: range.clone(), - _in_parent: range.start().parent().unwrap(), - }, - change_index, - } - } - - fn affected_range(&self) -> TextRange { - match &self.kind { - ChangedAncestorKind::Single { node } => node.text_range(), - ChangedAncestorKind::Range { _changed_elements: changed_nodes, _in_parent: _ } => { - TextRange::new( - changed_nodes.start().text_range().start(), - changed_nodes.end().text_range().end(), - ) - } - } - } -} - -struct TreeMutator { - immutable: SyntaxNode, - mutable_clone: SyntaxNode, +/// How changes nested within a replacement region are handled. +enum NestedChanges { + /// Map nested targets into a one-to-one node replacement. + Remap, + /// Drop nested changes because the replacement has no unique counterpart. + Discard, } -impl TreeMutator { - fn new(immutable: &SyntaxNode) -> TreeMutator { - let immutable = immutable.clone(); - let mutable_clone = immutable.clone_for_update(); - TreeMutator { immutable, mutable_clone } - } - - fn make_element_mut(&self, element: &SyntaxElement) -> SyntaxElement { - match element { - SyntaxElement::Node(node) => SyntaxElement::Node(self.make_syntax_mut(node)), - SyntaxElement::Token(token) => { - let parent = self.make_syntax_mut(&token.parent().unwrap()); - parent.children_with_tokens().nth(token.index()).unwrap() - } +impl ChangedRegion { + /// Describes a region replaced by change, if it can contain changes. + fn for_change(change: &Change, change_index: usize, discarded: bool) -> Option { + match change { + Change::Replace(SyntaxElement::Node(target), replacement) => Some(Self { + range: target.text_range(), + change_index, + nested_changes: if !discarded && matches!(replacement, Some(SyntaxElement::Node(_))) + { + NestedChanges::Remap + } else { + NestedChanges::Discard + }, + }), + Change::ReplaceWithMany(SyntaxElement::Node(target), _) => Some(Self { + range: target.text_range(), + change_index, + nested_changes: NestedChanges::Discard, + }), + Change::ReplaceAll(elements, _) => Some(Self { + range: TextRange::new( + elements.start().text_range().start(), + elements.end().text_range().end(), + ), + change_index, + nested_changes: NestedChanges::Discard, + }), + _ => None, } } - - fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode { - let ptr = SyntaxNodePtr::new(node); - ptr.to_node(&self.mutable_clone) - } } From 49d4204b082dad2bfac39cbb9a0ebaa677497397 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 17 May 2026 03:34:46 +0300 Subject: [PATCH 14/94] fix: resolve path on all namespaces and return resolution based on visibility --- .../crates/hir-def/src/per_ns.rs | 8 +- .../crates/hir-def/src/resolver.rs | 83 +++++++--- .../crates/hir-ty/src/lower/path.rs | 4 +- .../crates/hir/src/source_analyzer.rs | 156 ++++++++++++------ .../crates/ide-assists/src/tests.rs | 1 - .../highlight_module_macro_conflict.html | 50 ++++++ .../test_data/private_multi_namespace.html | 46 ++++++ .../ide/src/syntax_highlighting/tests.rs | 48 ++++++ 8 files changed, 309 insertions(+), 87 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_macro_conflict.html create mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/private_multi_namespace.html diff --git a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs index 8721cd65dbac7..f7e5ac316a2ca 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs @@ -109,16 +109,16 @@ impl PerNs { self.values.map(|it| it.def) } - pub fn take_values_import(self) -> Option<(ModuleDefId, Option)> { - self.values.map(|it| (it.def, it.import)) + pub fn take_values_full(self) -> Option { + self.values } pub fn take_macros(self) -> Option { self.macros.map(|it| it.def) } - pub fn take_macros_import(self) -> Option<(MacroId, Option)> { - self.macros.map(|it| (it.def, it.import)) + pub fn take_macros_full(self) -> Option { + self.macros } pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 63ff384de021a..5b11f5ff8bbb6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -33,7 +33,7 @@ use crate::{ item_scope::{BUILTIN_SCOPE, BuiltinShadowMode, ImportOrExternCrate, ItemScope}, lang_item::LangItemTarget, nameres::{DefMap, LocalDefMap, MacroSubNs, ResolvePathResultPrefixInfo, block_def_map}, - per_ns::PerNs, + per_ns::{MacrosItem, PerNs}, signatures::ImplSignature, src::HasSource, type_ref::LifetimeRef, @@ -174,7 +174,9 @@ impl<'db> Resolver<'db> { path: &Path, ) -> Option<(TypeNs, Option, Option)> { self.resolve_path_in_type_ns_with_prefix_info(db, path).map( - |(resolution, remaining_segments, import, _)| (resolution, remaining_segments, import), + |(resolution, remaining_segments, import, _, _)| { + (resolution, remaining_segments, import) + }, ) } @@ -182,8 +184,13 @@ impl<'db> Resolver<'db> { &self, db: &dyn SourceDatabase, path: &Path, - ) -> Option<(TypeNs, Option, Option, ResolvePathResultPrefixInfo)> - { + ) -> Option<( + TypeNs, + Option, + Option, + ResolvePathResultPrefixInfo, + Visibility, + )> { let path = match path { Path::BarePath(mod_path) => mod_path, Path::Normal(it) => &it.mod_path, @@ -206,6 +213,7 @@ impl<'db> Resolver<'db> { seg.as_ref().map(|_| 1), None, ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } }; @@ -230,6 +238,7 @@ impl<'db> Resolver<'db> { remaining_idx(), None, ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } } else if let &GenericDefId::AdtId(adt) = def @@ -240,6 +249,7 @@ impl<'db> Resolver<'db> { remaining_idx(), None, ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } if let Some(id) = params.find_type_by_name(first_name, *def) { @@ -248,6 +258,7 @@ impl<'db> Resolver<'db> { remaining_idx(), None, ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } } @@ -264,6 +275,7 @@ impl<'db> Resolver<'db> { remaining_idx(), None, ResolvePathResultPrefixInfo::default(), + Visibility::Public, ) } else { res @@ -323,7 +335,7 @@ impl<'db> Resolver<'db> { path: &Path, hygiene_id: HygieneId, ) -> Option { - self.resolve_path_in_value_ns_with_prefix_info(db, path, hygiene_id).map(|(it, _)| it) + self.resolve_path_in_value_ns_with_prefix_info(db, path, hygiene_id).map(|(it, _, _)| it) } fn skip_to_mod<'this, T>( @@ -343,7 +355,7 @@ impl<'db> Resolver<'db> { db: &dyn SourceDatabase, path: &Path, mut hygiene_id: HygieneId, - ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> { + ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo, Visibility)> { let path = match path { Path::BarePath(mod_path) => mod_path, Path::Normal(it) => &it.mod_path, @@ -363,6 +375,7 @@ impl<'db> Resolver<'db> { | LangItemTarget::MacroId(_) => return None, }), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } Path::LangItem(l, Some(_)) => { @@ -383,6 +396,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::Partial(type_ns, 0), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } }; @@ -408,6 +422,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::ValueNs(ValueNs::LocalBinding(e.binding())), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } } @@ -421,6 +436,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_)), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } if let Some(id) = params.find_const_by_name(first_name, *def) { @@ -428,6 +444,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::ValueNs(val), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } } @@ -448,6 +465,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } } else if let &GenericDefId::AdtId(adt) = def @@ -457,6 +475,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::Partial(ty, 1), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } if let Some(id) = params.find_type_by_name(first_name, *def) { @@ -464,6 +483,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::Partial(ty, 1), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } } @@ -490,6 +510,7 @@ impl<'db> Resolver<'db> { return Some(( ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1), ResolvePathResultPrefixInfo::default(), + Visibility::Public, )); } @@ -513,7 +534,7 @@ impl<'db> Resolver<'db> { db: &dyn SourceDatabase, path: &ModPath, expected_macro_kind: Option, - ) -> Option<(MacroId, Option)> { + ) -> Option { let (item_map, item_local_map, module) = self.item_scope_(); item_map .resolve_path( @@ -525,7 +546,7 @@ impl<'db> Resolver<'db> { expected_macro_kind, ) .0 - .take_macros_import() + .take_macros_full() } pub fn resolve_path_as_macro_def( @@ -534,7 +555,7 @@ impl<'db> Resolver<'db> { path: &ModPath, expected_macro_kind: Option, ) -> Option { - self.resolve_path_as_macro(db, path, expected_macro_kind).map(|(it, _)| it.definition(db)) + self.resolve_path_as_macro(db, path, expected_macro_kind).map(|it| it.def.definition(db)) } pub fn resolve_lifetime(&self, lifetime: &LifetimeRef) -> Option { @@ -1166,7 +1187,7 @@ impl<'db> ModuleItemMap<'db> { &self, db: &'db dyn SourceDatabase, path: &ModPath, - ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> { + ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo, Visibility)> { let (module_def, unresolved_idx, prefix_info) = self.def_map.resolve_path_locally( self.local_def_map, db, @@ -1176,12 +1197,12 @@ impl<'db> ModuleItemMap<'db> { ); match unresolved_idx { None => { - let value = to_value_ns(module_def, self.def_map)?; - Some((ResolveValueResult::ValueNs(value), prefix_info)) + let (value, vis) = to_value_ns(module_def, self.def_map)?; + Some((ResolveValueResult::ValueNs(value), prefix_info, vis)) } Some(unresolved_idx) => { - let def = module_def.take_types()?; - let ty = match def { + let res = module_def.take_types_full()?; + let ty = match res.def { ModuleDefId::AdtId(it) => TypeNs::AdtId(it), ModuleDefId::TraitId(it) => TypeNs::TraitId(it), ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it), @@ -1194,7 +1215,7 @@ impl<'db> ModuleItemMap<'db> { | ModuleDefId::MacroId(_) | ModuleDefId::StaticId(_) => return None, }; - Some((ResolveValueResult::Partial(ty, unresolved_idx), prefix_info)) + Some((ResolveValueResult::Partial(ty, unresolved_idx), prefix_info, res.vis)) } } } @@ -1203,8 +1224,13 @@ impl<'db> ModuleItemMap<'db> { &self, db: &dyn SourceDatabase, path: &ModPath, - ) -> Option<(TypeNs, Option, Option, ResolvePathResultPrefixInfo)> - { + ) -> Option<( + TypeNs, + Option, + Option, + ResolvePathResultPrefixInfo, + Visibility, + )> { let (module_def, idx, prefix_info) = self.def_map.resolve_path_locally( self.local_def_map, db, @@ -1212,17 +1238,22 @@ impl<'db> ModuleItemMap<'db> { path, BuiltinShadowMode::Other, ); - let (res, import) = to_type_ns(module_def)?; - Some((res, idx, import, prefix_info)) + let (res, import, vis) = to_type_ns(module_def)?; + Some((res, idx, import, prefix_info, vis)) } } -fn to_value_ns(per_ns: PerNs, def_map: &DefMap) -> Option { - let def = per_ns.take_values().or_else(|| { - let Some(MacroId::ProcMacroId(proc_macro)) = per_ns.take_macros() else { return None }; +fn to_value_ns(per_ns: PerNs, def_map: &DefMap) -> Option<(ValueNs, Visibility)> { + let (def, vis) = per_ns.take_values_full().map(|res| (res.def, res.vis)).or_else(|| { + let Some(MacrosItem { def: MacroId::ProcMacroId(proc_macro), vis, .. }) = + per_ns.take_macros_full() + else { + return None; + }; // If we cannot resolve to value ns, but we can resolve to a proc macro, and this is the crate // defining this proc macro - inside this crate, we should treat the macro as a function. - def_map.proc_macro_as_fn(proc_macro).map(ModuleDefId::FunctionId) + let def = ModuleDefId::FunctionId(def_map.proc_macro_as_fn(proc_macro)?); + Some((def, vis)) })?; let res = match def { ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it), @@ -1238,10 +1269,10 @@ fn to_value_ns(per_ns: PerNs, def_map: &DefMap) -> Option { | ModuleDefId::MacroId(_) | ModuleDefId::ModuleId(_) => return None, }; - Some(res) + Some((res, vis)) } -fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option)> { +fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option, Visibility)> { let def = per_ns.take_types_full()?; let res = match def.def { ModuleDefId::AdtId(it) => TypeNs::AdtId(it), @@ -1259,7 +1290,7 @@ fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option)> { | ModuleDefId::MacroId(_) | ModuleDefId::StaticId(_) => return None, }; - Some((res, def.import)) + Some((res, def.import, def.vis)) } #[derive(Default)] 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 77037c5b12d13..554a191d9d0dd 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 @@ -334,7 +334,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { #[tracing::instrument(skip(self), ret)] pub(crate) fn resolve_path_in_type_ns(&mut self) -> Option<(TypeNs, Option)> { - let (resolution, remaining_index, _, prefix_info) = + let (resolution, remaining_index, _, prefix_info, _) = self.ctx.resolver.resolve_path_in_type_ns_with_prefix_info(self.ctx.db, self.path)?; let segments = self.segments; @@ -385,7 +385,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { &mut self, hygiene_id: HygieneId, ) -> Option { - let (res, prefix_info) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info( + let (res, prefix_info, _) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info( self.ctx.db, self.path, hygiene_id, diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index fb27f9dec45ac..e80567641baf3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -24,8 +24,9 @@ use hir_def::{ hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat, PatId, generics::GenericParams}, lang_item::LangItems, nameres::MacroSubNs, - resolver::{Resolver, TypeNs, ValueNs, resolver_for_scope}, + resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs, resolver_for_scope}, type_ref::{Mutability, TypeRefId}, + visibility::Visibility, }; use hir_expand::{ HirFileId, InFile, @@ -1060,7 +1061,7 @@ impl<'db> SourceAnalyzer<'db> { }; let store_owner = self.resolver.expression_store_owner(); - let res = resolve_hir_value_path( + let (res, _) = resolve_hir_value_path( db, &self.resolver, store_owner, @@ -1643,7 +1644,8 @@ impl<'db> SourceAnalyzer<'db> { Some(name.clone()), )), hygiene, - ), + ) + .map(|(it, _)| it), ) }) } @@ -1683,7 +1685,8 @@ impl<'db> SourceAnalyzer<'db> { Some(name.clone()), )), hygiene, - ), + ) + .map(|(it, _)| it), ) })) } @@ -1865,7 +1868,7 @@ pub(crate) fn resolve_hir_path_as_attr_macro( ) -> Option { resolver .resolve_path_as_macro(db, path.mod_path()?, Some(MacroSubNs::Attr)) - .map(|(it, _)| it) + .map(|it| it.def) .map(Into::into) } @@ -1880,7 +1883,7 @@ fn resolve_hir_path_<'db>( resolve_per_ns: bool, ) -> PathResolutionPerNs<'db> { let types = || { - let (ty, unresolved) = match path.type_anchor() { + let (ty, unresolved, ty_is_visible) = match path.type_anchor() { Some(type_ref) => resolver.generic_def().and_then(|def| { let generics = OnceCell::new(); let (_, res) = TyLoweringContext::new( @@ -1894,19 +1897,20 @@ fn resolve_hir_path_<'db>( LifetimeLoweringMode::LateParam, ) .lower_ty_ext(type_ref); - res.map(|ty_ns| (ty_ns, path.segments().first())) + res.map(|ty_ns| (ty_ns, path.segments().first(), Visibility::Public)) }), None => { - let (ty, remaining_idx, _) = resolver.resolve_path_in_type_ns(db, path)?; + let (ty, remaining_idx, _, _, vis) = + resolver.resolve_path_in_type_ns_with_prefix_info(db, path)?; match remaining_idx { Some(remaining_idx) => { if remaining_idx + 1 == path.segments().len() { - Some((ty, path.segments().last())) + Some((ty, path.segments().last(), vis)) } else { None } } - None => Some((ty, None)), + None => Some((ty, None, vis)), } } }?; @@ -1917,7 +1921,10 @@ fn resolve_hir_path_<'db>( && let Some(type_alias_id) = trait_id.trait_items(db).associated_type_by_name(unresolved.name) { - return Some(PathResolution::Def(ModuleDefId::from(type_alias_id).into())); + return Some(( + PathResolution::Def(ModuleDefId::from(type_alias_id).into()), + ty_is_visible, + )); } let res = match ty { @@ -1945,8 +1952,8 @@ fn resolve_hir_path_<'db>( }) .map(TypeAlias::from) .map(Into::into) - .map(PathResolution::Def), - None => Some(res), + .map(|def| (PathResolution::Def(def), ty_is_visible)), + None => Some((res, ty_is_visible)), } }; @@ -1956,41 +1963,79 @@ fn resolve_hir_path_<'db>( let items = || { resolver .resolve_module_path_in_items(db, path.mod_path()?) - .take_types() - .map(|it| PathResolution::Def(it.into())) + .take_types_full() + .map(|it| (PathResolution::Def(it.def.into()), it.vis)) }; let macros = || { resolver .resolve_path_as_macro(db, path.mod_path()?, None) - .map(|(def, _)| PathResolution::Def(ModuleDef::Macro(def.into()))) + .map(|res| (PathResolution::Def(ModuleDef::Macro(res.def.into())), res.vis)) }; - if resolve_per_ns { - PathResolutionPerNs { - type_ns: types().or_else(items), - value_ns: values(), - macro_ns: macros(), - } - } else { - let res = if prefer_value_ns { - values() - .map(|value_ns| PathResolutionPerNs::new(None, Some(value_ns), None)) - .unwrap_or_else(|| PathResolutionPerNs::new(types(), None, None)) - } else { - types() - .map(|type_ns| PathResolutionPerNs::new(Some(type_ns), None, None)) - .unwrap_or_else(|| PathResolutionPerNs::new(None, values(), None)) - }; + let mut types_ns: Option> = None; + let mut values_ns: Option> = None; + + let mut types_is_visible: Option = None; + let mut values_is_visible: Option = None; - if res.any().is_some() { - res - } else if let Some(type_ns) = items() { - PathResolutionPerNs::new(Some(type_ns), None, None) + if !resolve_per_ns { + if prefer_value_ns { + values_ns = Some(values().inspect(|(_, vis)| { + values_is_visible = Some(resolver.is_visible(db, *vis)); + })); + + if let Some(Some((res, _))) = values_ns + && values_is_visible.unwrap_or_default() + { + return PathResolutionPerNs::new(None, Some(res), None); + } } else { - PathResolutionPerNs::new(None, None, macros()) + types_ns = Some(types().or_else(items).inspect(|(_, vis)| { + types_is_visible = Some(resolver.is_visible(db, *vis)); + })); + + if let Some(Some((res, _))) = types_ns + && types_is_visible.unwrap_or_default() + { + return PathResolutionPerNs::new(Some(res), None, None); + } + } + } + + let mut macros_is_visible = false; + + let mut types = types_ns.unwrap_or_else(|| types().or_else(items)).map(|(res, vis)| { + types_is_visible = Some(types_is_visible.unwrap_or_else(|| resolver.is_visible(db, vis))); + res + }); + let mut values = values_ns.unwrap_or_else(values).map(|(res, vis)| { + values_is_visible = Some(values_is_visible.unwrap_or_else(|| resolver.is_visible(db, vis))); + res + }); + let mut macros = macros().map(|(res, vis)| { + macros_is_visible = resolver.is_visible(db, vis); + res + }); + + let types_is_visible = types_is_visible.unwrap_or_default(); + let values_is_visible = values_is_visible.unwrap_or_default(); + + // If there is a visible resolution and an invisible one, we only want to include the visible one. But if all are + // invisible, we want to include them all. + if types_is_visible || values_is_visible || macros_is_visible { + if !types_is_visible { + types = None; + } + if !values_is_visible { + values = None; + } + if !macros_is_visible { + macros = None; } } + + PathResolutionPerNs { type_ns: types, value_ns: values, macro_ns: macros } } fn resolve_hir_value_path<'db>( @@ -2000,23 +2045,26 @@ fn resolve_hir_value_path<'db>( infer_body: Option>, path: &Path, hygiene: HygieneId, -) -> Option> { - resolver.resolve_path_in_value_ns_fully(db, path, hygiene).and_then(|val| { - let res = match val { - ValueNs::LocalBinding(binding_id) => { - let var = Local { parent: store_owner?, parent_infer: infer_body?, binding_id }; - PathResolution::Local(var) - } - ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), - ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), - ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), - ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), - ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), - ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()), - ValueNs::GenericParam(id) => PathResolution::ConstParam(id.into()), - }; - Some(res) - }) +) -> Option<(PathResolution<'db>, Visibility)> { + resolver.resolve_path_in_value_ns_with_prefix_info(db, path, hygiene).and_then( + |(val, _, vis)| { + let ResolveValueResult::ValueNs(val) = val else { return None }; + let res = match val { + ValueNs::LocalBinding(binding_id) => { + let var = Local { parent: store_owner?, parent_infer: infer_body?, binding_id }; + PathResolution::Local(var) + } + ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), + ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), + ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), + ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), + ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), + ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()), + ValueNs::GenericParam(id) => PathResolution::ConstParam(id.into()), + }; + Some((res, vis)) + }, + ) } /// Resolves a path where we know it is a qualifier of another path. diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index 135e750ca066c..3624099b13bc6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -354,7 +354,6 @@ fn check_with_config( handler(&mut acc, &ctx); }); let mut res = acc.finish(); - let assist = match assist_label { Some(label) => res.into_iter().find(|resolved| resolved.label == label), None if res.is_empty() => None, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_macro_conflict.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_macro_conflict.html new file mode 100644 index 0000000000000..b61f574f0e768 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_macro_conflict.html @@ -0,0 +1,50 @@ + + +
use foo::bar;
+
+fn main() {
+    bar!()
+}
+
+
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/private_multi_namespace.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/private_multi_namespace.html new file mode 100644 index 0000000000000..06fc3f0772dbe --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/private_multi_namespace.html @@ -0,0 +1,46 @@ + + +
use foo::foo;
+
+
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index f4b103902499c..6cb323b46a521 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -1601,3 +1601,51 @@ async fn get_double_async(num: u32) -> u32 { false, ); } + +#[test] +fn private_multi_namespace() { + check_highlighting( + r#" +//- /bar.rs crate:bar deps:foo +use foo::foo; + +//- /foo.rs crate:foo +struct foo; + +#[macro_export] +macro_rules! foo { + () => {}; +} + "#, + expect_file!["./test_data/private_multi_namespace.html"], + false, + ); +} + +#[test] +fn mod_and_macro_name_conflict() { + check_highlighting( + r#" +//- /main.rs crate:main deps:foo +use foo::bar; + +fn main() { + bar!() +} + +//- /foo.rs crate:foo +mod bar { + fn random() {} +} + +#[macro_export] +macro_rules! bar { + () => { + println!("Hello"); + }; +} +"#, + expect_file!["./test_data/highlight_module_macro_conflict.html"], + false, + ); +} From 9fb185d10f9534bc2b41410f881af293314235ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 27 Jul 2026 09:36:35 +0000 Subject: [PATCH 15/94] Suggest mutable method when iterating over binding When encountering a for loop that requires a mutable iterator item, we previously suggested changing the method on the iterated expression, but ignored bindings. We now go to the binding's definition and suggest changing the method there too. ``` error[E0594]: cannot assign to `v.v`, which is behind a `&` reference --> $DIR/suggest-mut-method-for-loop-hashmap.rs:31:9 | LL | for (_k, v) in x { | - this iterator yields `&` references ... LL | v.v += 1; | ^^^^^^^^ `v` is a `&` reference, so it cannot be written to | help: use mutable method | LL | let mut x = map.iter_mut(); | ++++ ``` --- .../src/diagnostics/mutability_errors.rs | 114 ++++++++++-------- .../suggest-mut-method-for-loop-hashmap.fixed | 23 +++- .../suggest-mut-method-for-loop-hashmap.rs | 23 +++- ...suggest-mut-method-for-loop-hashmap.stderr | 16 ++- 4 files changed, 121 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index cce2bba7b3365..372d935593f2f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1086,62 +1086,76 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } } } - if let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) - && let Block(block, _) = body.value.kind + let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) else { return }; + let Block(block, _) = body.value.kind else { return }; + // `span` corresponds to the expression being iterated, find the `for`-loop desugared + // expression with that span in order to identify potential fixes when encountering a + // read-only iterator that should be mutable. + let mut expr = if let ControlFlow::Break(expr) = (Finder { span }).visit_block(block) + && let Call(_, [expr]) = expr.kind { - // `span` corresponds to the expression being iterated, find the `for`-loop desugared - // expression with that span in order to identify potential fixes when encountering a - // read-only iterator that should be mutable. - if let ControlFlow::Break(expr) = (Finder { span }).visit_block(block) - && let Call(_, [expr]) = expr.kind - { - match expr.kind { - MethodCall(path_segment, _, _, span) => { - // We have `for _ in iter.read_only_iter()`, try to - // suggest `for _ in iter.mutable_iter()` instead. - let opt_suggestions = tcx - .typeck(path_segment.hir_id.owner.def_id) - .type_dependent_def_id(expr.hir_id) - .and_then(|def_id| tcx.impl_of_assoc(def_id)) - .map(|def_id| tcx.associated_items(def_id)) - .map(|assoc_items| { - assoc_items - .in_definition_order() - .map(|assoc_item_def| assoc_item_def.ident(tcx)) - .filter(|&ident| { - let original_method_ident = path_segment.ident; - original_method_ident != ident - && ident.as_str().starts_with( - &original_method_ident.name.to_string(), - ) - }) - .map(|ident| format!("{ident}()")) - .peekable() - }); + expr + } else { + return; + }; + loop { + match expr.kind { + MethodCall(path_segment, _, _, span) => { + // We have `for _ in iter.read_only_iter()`, try to + // suggest `for _ in iter.mutable_iter()` instead. + let opt_suggestions = tcx + .typeck(path_segment.hir_id.owner.def_id) + .type_dependent_def_id(expr.hir_id) + .and_then(|def_id| tcx.impl_of_assoc(def_id)) + .map(|def_id| tcx.associated_items(def_id)) + .map(|assoc_items| { + assoc_items + .in_definition_order() + .map(|assoc_item_def| assoc_item_def.ident(tcx)) + .filter(|&ident| { + let original_method_ident = path_segment.ident; + original_method_ident != ident + && ident + .as_str() + .starts_with(&original_method_ident.name.to_string()) + }) + .map(|ident| format!("{ident}()")) + .peekable() + }); - if let Some(mut suggestions) = opt_suggestions - && suggestions.peek().is_some() - { - err.span_suggestions( - span, - "use mutable method", - suggestions, - Applicability::MaybeIncorrect, - ); - } - } - AddrOf(BorrowKind::Ref, Mutability::Not, expr) => { - // We have `for _ in &i`, suggest `for _ in &mut i`. - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "use a mutable iterator instead", - "mut ", - Applicability::MachineApplicable, + if let Some(mut suggestions) = opt_suggestions + && suggestions.peek().is_some() + { + err.span_suggestions( + span, + "use mutable method", + suggestions, + Applicability::MaybeIncorrect, ); } - _ => {} } + AddrOf(BorrowKind::Ref, Mutability::Not, expr) => { + // We have `for _ in &i`, suggest `for _ in &mut i`. + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "use a mutable iterator instead", + "mut ", + Applicability::MachineApplicable, + ); + } + ExprKind::Path(hir::QPath::Resolved(None, path)) + if let hir::def::Res::Local(hir_id) = path.res + && let hir::Node::LetStmt(stmt) = + self.infcx.tcx.parent_hir_node(hir_id) + && let Some(init) = stmt.init => + { + // We're iterating over a binding, try to suggest changing the binding's expr. + expr = init; + continue; + } + _ => {} } + break; } } diff --git a/tests/ui/suggestions/suggest-mut-method-for-loop-hashmap.fixed b/tests/ui/suggestions/suggest-mut-method-for-loop-hashmap.fixed index fe445410c648e..669971a4664e7 100644 --- a/tests/ui/suggestions/suggest-mut-method-for-loop-hashmap.fixed +++ b/tests/ui/suggestions/suggest-mut-method-for-loop-hashmap.fixed @@ -1,5 +1,5 @@ //@ run-rustfix -// https://github.com/rust-lang/rust/issues/82081 +// https://github.com/rust-lang/rust/issues/82081 & https://github.com/rust-lang/rust/issues/49839 use std::collections::HashMap; @@ -7,7 +7,7 @@ struct Test { v: u32, } -fn main() { +fn a() { let mut map = HashMap::new(); map.insert("a", Test { v: 0 }); @@ -19,3 +19,22 @@ fn main() { //~| NOTE `v` is a `&` reference } } + +fn b() { + let mut map = HashMap::new(); + map.insert("a", Test { v: 0 }); + + let x = map.iter_mut(); + //~^ HELP use mutable method + for (_k, v) in x { + //~^ NOTE this iterator yields `&` references + v.v += 1; + //~^ ERROR cannot assign to `v.v` + //~| NOTE `v` is a `&` reference + } +} + +fn main() { + a(); + b(); +} diff --git a/tests/ui/suggestions/suggest-mut-method-for-loop-hashmap.rs b/tests/ui/suggestions/suggest-mut-method-for-loop-hashmap.rs index 1f8bd9ae4d8fe..f474c3b191b2b 100644 --- a/tests/ui/suggestions/suggest-mut-method-for-loop-hashmap.rs +++ b/tests/ui/suggestions/suggest-mut-method-for-loop-hashmap.rs @@ -1,5 +1,5 @@ //@ run-rustfix -// https://github.com/rust-lang/rust/issues/82081 +// https://github.com/rust-lang/rust/issues/82081 & https://github.com/rust-lang/rust/issues/49839 use std::collections::HashMap; @@ -7,7 +7,7 @@ struct Test { v: u32, } -fn main() { +fn a() { let mut map = HashMap::new(); map.insert("a", Test { v: 0 }); @@ -19,3 +19,22 @@ fn main() { //~| NOTE `v` is a `&` reference } } + +fn b() { + let mut map = HashMap::new(); + map.insert("a", Test { v: 0 }); + + let x = map.iter(); + //~^ HELP use mutable method + for (_k, v) in x { + //~^ NOTE this iterator yields `&` references + v.v += 1; + //~^ ERROR cannot assign to `v.v` + //~| NOTE `v` is a `&` reference + } +} + +fn main() { + a(); + b(); +} diff --git a/tests/ui/suggestions/suggest-mut-method-for-loop-hashmap.stderr b/tests/ui/suggestions/suggest-mut-method-for-loop-hashmap.stderr index a8f596ac8c9a0..b16da10b8b620 100644 --- a/tests/ui/suggestions/suggest-mut-method-for-loop-hashmap.stderr +++ b/tests/ui/suggestions/suggest-mut-method-for-loop-hashmap.stderr @@ -12,6 +12,20 @@ help: use mutable method LL | for (_k, v) in map.iter_mut() { | ++++ -error: aborting due to 1 previous error +error[E0594]: cannot assign to `v.v`, which is behind a `&` reference + --> $DIR/suggest-mut-method-for-loop-hashmap.rs:31:9 + | +LL | for (_k, v) in x { + | - this iterator yields `&` references +LL | +LL | v.v += 1; + | ^^^^^^^^ `v` is a `&` reference, so it cannot be written to + | +help: use mutable method + | +LL | let x = map.iter_mut(); + | ++++ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0594`. From 31086e2cc87b450a01209b2fbd42ff71f7409617 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 22 Jun 2026 13:43:54 +0100 Subject: [PATCH 16/94] fix: Only parse stdout in discover protocol Previously we read both stdout and stderr in the discover protocol. Depending on the tool generating rust-project JSON, this meant that a single stderr log message could break discovery. Instead, only look for JSON from the discover command's stdout, and forward stderr to the rust-analyzer logs. Update both the implementation and the discover protocol docs to reflect this behaviour. AI disclosure: Code partly written by GPT-5.5. --- .../rust-analyzer/crates/rust-analyzer/src/config.rs | 9 ++++++--- .../rust-analyzer/crates/rust-analyzer/src/discover.rs | 5 +++-- .../crates/rust-analyzer/src/test_runner.rs | 7 +++++-- .../docs/book/src/configuration_generated.md | 9 ++++++--- .../docs/book/src/non_cargo_based_projects.md | 4 ++-- src/tools/rust-analyzer/editors/code/package.json | 2 +- 6 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index fcb34b743adbd..8590a99f286ae 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -562,9 +562,9 @@ config_data! { /// /// **Warning**: This format is provisional and subject to change. /// - /// The discover command should output JSON objects, one per - /// line (JSONL format). These objects should correspond to - /// this Rust data type: + /// The discover command should output JSON objects to stdout, + /// one per line (JSONL format). These objects should correspond + /// to this Rust data type: /// /// ```norun /// #[derive(Debug, Clone, Deserialize, Serialize)] @@ -604,6 +604,9 @@ config_data! { /// Only the finished event is required, but the other /// variants are encouraged to give users more feedback about /// progress or errors. + /// + /// Stderr is not parsed as JSONL. It is treated as command log + /// output and forwarded to rust-analyzer's own logs. workspace_discoverConfig: Option = None, } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs index 459a7993201b8..04d0cedb3eca2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs @@ -137,8 +137,9 @@ impl JsonLinesParser for DiscoverProjectParser { None } - fn from_stderr_line(&self, line: &str, error: &mut String) -> Option { - self.from_line(line, error) + fn from_stderr_line(&self, line: &str, _error: &mut String) -> Option { + tracing::info!(%line, "discover command stderr"); + None } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs index 4f5c00192dcd5..c6f8a7c799c21 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs @@ -72,8 +72,11 @@ impl JsonLinesParser for CargoTestOutputParser { }) } - fn from_stderr_line(&self, line: &str, error: &mut String) -> Option { - self.from_line(line, error) + fn from_stderr_line(&self, line: &str, _error: &mut String) -> Option { + Some(CargoTestMessage { + target: self.target.clone(), + output: CargoTestOutput::Custom { text: line.to_owned() }, + }) } fn from_eof(&self) -> Option { diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index fd377616d9566..4df17d77edfbf 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -1769,9 +1769,9 @@ will likely be useful: **Warning**: This format is provisional and subject to change. -The discover command should output JSON objects, one per -line (JSONL format). These objects should correspond to -this Rust data type: +The discover command should output JSON objects to stdout, +one per line (JSONL format). These objects should correspond +to this Rust data type: ```norun #[derive(Debug, Clone, Deserialize, Serialize)] @@ -1812,6 +1812,9 @@ Only the finished event is required, but the other variants are encouraged to give users more feedback about progress or errors. +Stderr is not parsed as JSONL. It is treated as command log +output and forwarded to rust-analyzer's own logs. + ## rust-analyzer.workspace.symbol.search.excludeImports {#workspace.symbol.search.excludeImports} 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 9cc3292444980..75e7fc900f193 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 @@ -237,8 +237,8 @@ There are four ways to feed `rust-project.json` to rust-analyzer: - Use [`"rust-analyzer.workspace.discoverConfig": … }`](./configuration.md#workspace.discoverConfig) to specify a workspace discovery command to generate project descriptions - on-the-fly. Please note that the command output is message-oriented and must - output JSONL [as described in the configuration docs](./configuration.md#workspace.discoverConfig). + on-the-fly. Please note that the command's stdout is message-oriented and + must output JSONL [as described in the configuration docs](./configuration.md#workspace.discoverConfig). - Place `rust-project.json` file at the root of the project, and rust-analyzer will discover it. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 61bc4cb29dfb7..d152cfb5861ea 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -3258,7 +3258,7 @@ "title": "Workspace", "properties": { "rust-analyzer.workspace.discoverConfig": { - "markdownDescription": "Configure a command that rust-analyzer can invoke to\nobtain configuration.\n\nThis is an alternative to manually generating\n`rust-project.json`: it enables rust-analyzer to generate\nrust-project.json on the fly, and regenerate it when\nswitching or modifying projects.\n\nThis is an object with three fields:\n\n* `command`: the shell command to invoke\n\n* `filesToWatch`: which build system-specific files should\nbe watched to trigger regenerating the configuration\n\n* `progressLabel`: the name of the command, used in\nprogress indicators in the IDE\n\nHere's an example of a valid configuration:\n\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\",\n \"{arg}\"\n ],\n \"progressLabel\": \"buck2/rust-project\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## Argument Substitutions\n\nIf `command` includes the argument `{arg}`, that argument will be substituted\nwith the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nrust-analyzer will use the path invocation to find and\ngenerate a `rust-project.json` and therefore a\nworkspace. Example:\n\n\n```norun\nrust-project develop-json '{ \"path\": \"myproject/src/main.rs\" }'\n```\n\nrust-analyzer will use build file invocations to update an\nexisting workspace. Example:\n\nOr with a build file and the configuration above:\n\n```norun\nrust-project develop-json '{ \"buildfile\": \"myproject/BUCK\" }'\n```\n\nAs a reference for implementors, buck2's `rust-project`\nwill likely be useful:\n.\n\n## Discover Command Output\n\n**Warning**: This format is provisional and subject to change.\n\nThe discover command should output JSON objects, one per\nline (JSONL format). These objects should correspond to\nthis Rust data type:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option },\n Progress { message: String },\n}\n```\n\nFor example, a progress event:\n\n```json\n{\"kind\":\"progress\",\"message\":\"generating rust-project.json\"}\n```\n\nA finished event can look like this (expanded and\ncommented for readability):\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUCK\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nOnly the finished event is required, but the other\nvariants are encouraged to give users more feedback about\nprogress or errors.", + "markdownDescription": "Configure a command that rust-analyzer can invoke to\nobtain configuration.\n\nThis is an alternative to manually generating\n`rust-project.json`: it enables rust-analyzer to generate\nrust-project.json on the fly, and regenerate it when\nswitching or modifying projects.\n\nThis is an object with three fields:\n\n* `command`: the shell command to invoke\n\n* `filesToWatch`: which build system-specific files should\nbe watched to trigger regenerating the configuration\n\n* `progressLabel`: the name of the command, used in\nprogress indicators in the IDE\n\nHere's an example of a valid configuration:\n\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\",\n \"{arg}\"\n ],\n \"progressLabel\": \"buck2/rust-project\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## Argument Substitutions\n\nIf `command` includes the argument `{arg}`, that argument will be substituted\nwith the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nrust-analyzer will use the path invocation to find and\ngenerate a `rust-project.json` and therefore a\nworkspace. Example:\n\n\n```norun\nrust-project develop-json '{ \"path\": \"myproject/src/main.rs\" }'\n```\n\nrust-analyzer will use build file invocations to update an\nexisting workspace. Example:\n\nOr with a build file and the configuration above:\n\n```norun\nrust-project develop-json '{ \"buildfile\": \"myproject/BUCK\" }'\n```\n\nAs a reference for implementors, buck2's `rust-project`\nwill likely be useful:\n.\n\n## Discover Command Output\n\n**Warning**: This format is provisional and subject to change.\n\nThe discover command should output JSON objects to stdout,\none per line (JSONL format). These objects should correspond\nto this Rust data type:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option },\n Progress { message: String },\n}\n```\n\nFor example, a progress event:\n\n```json\n{\"kind\":\"progress\",\"message\":\"generating rust-project.json\"}\n```\n\nA finished event can look like this (expanded and\ncommented for readability):\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUCK\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nOnly the finished event is required, but the other\nvariants are encouraged to give users more feedback about\nprogress or errors.\n\nStderr is not parsed as JSONL. It is treated as command log\noutput and forwarded to rust-analyzer's own logs.", "default": null, "anyOf": [ { From de3c5b647b55472b8aba8817f83cea826e41b344 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 27 Jul 2026 15:44:42 +0100 Subject: [PATCH 17/94] internal: Spelling and grammar fixes I noticed a few "its" versus "it's" grammatical issues, so I've done a pass at fixing obvious grammar issues. AI disclosure: I fixed the first few manually, then asked GPT-5.5 to look for additional cases and kept all the obviously reasonable fixes. --- src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs | 4 ++-- .../crates/hir-def/src/nameres/path_resolution.rs | 8 ++++---- .../rust-analyzer/crates/hir-ty/src/infer/place_op.rs | 2 +- src/tools/rust-analyzer/crates/hir-ty/src/lib.rs | 2 +- .../crates/hir-ty/src/next_solver/inspect.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/display.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/semantics.rs | 4 ++-- .../crates/hir/src/semantics/source_to_def.rs | 6 +++--- .../ide-assists/src/handlers/replace_if_let_with_match.rs | 2 +- .../ide-completion/src/completions/attribute/derive.rs | 2 +- .../crates/ide-completion/src/completions/type.rs | 2 +- .../crates/ide-completion/src/context/analysis.rs | 2 +- src/tools/rust-analyzer/crates/ide-completion/src/item.rs | 2 +- .../rust-analyzer/crates/ide-db/src/source_change.rs | 4 ++-- .../crates/ide-db/src/syntax_helpers/tree_diff.rs | 2 +- src/tools/rust-analyzer/crates/ide-ssr/src/search.rs | 2 +- src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs | 2 +- src/tools/rust-analyzer/crates/ide/src/typing.rs | 2 +- .../rust-analyzer/crates/mbe/src/expander/matcher.rs | 2 +- src/tools/rust-analyzer/crates/parser/src/output.rs | 2 +- .../crates/proc-macro-api/src/legacy_protocol/msg/flat.rs | 2 +- .../rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs | 4 ++-- .../rust-analyzer/crates/rust-analyzer/src/main_loop.rs | 2 +- src/tools/rust-analyzer/crates/span/src/ast_id.rs | 4 ++-- src/tools/rust-analyzer/crates/vfs/src/lib.rs | 6 +++--- src/tools/rust-analyzer/crates/vfs/src/loader.rs | 2 +- src/tools/rust-analyzer/crates/vfs/src/path_interner.rs | 4 ++-- 27 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs index bdd8ea84a0910..55cb6f8f81aa7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs @@ -44,13 +44,13 @@ pub struct Docs { docs: String, /// A sorted map from an offset in `docs` to an offset in the source code. docs_source_map: Vec, - /// If the item is an outlined module (`mod foo;`), `docs_source_map` store the concatenated + /// If the item is an outlined module (`mod foo;`), `docs_source_map` stores the concatenated /// list of the outline and inline docs (outline first). Then, this field contains the [`HirFileId`] /// of the outline declaration, and the index in `docs` from which the inline docs /// begin. outline_mod: Option<(HirFileId, usize)>, inline_file: HirFileId, - /// The size the prepended prefix, which does not map to real doc comments. + /// The size of the prepended prefix, which does not map to real doc comments. prefix_len: TextSize, /// The offset in `docs` from which the docs are inner attributes/comments. inline_inner_docs_start: Option, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index fde1db4734a78..150b4eeb60f2b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -1,11 +1,11 @@ -//! This modules implements a function to resolve a path `foo::bar::baz` to a -//! def, which is used within the name resolution. +//! This module implements a function to resolve a path `foo::bar::baz` to a +//! def, which is used within name resolution. //! //! When name resolution is finished, the result of resolving a path is either -//! `Some(def)` or `None`. However, when we are in process of resolving imports +//! `Some(def)` or `None`. However, when we are in the process of resolving imports //! or macros, there's a third possibility: //! -//! I can't resolve this path right now, but I might be resolve this path +//! I can't resolve this path right now, but I might be able to resolve this path //! later, when more macros are expanded. //! //! `ReachedFixedPoint` signals about this. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs index b226e5ca85de0..c2e39709e8949 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs @@ -235,7 +235,7 @@ impl<'db> InferenceContext<'db> { // We have to replace the operator with the mutable variant for the // program to compile, so we don't really have a choice here and want - // to just try using `DerefMut` even if its not in the item bounds + // to just try using `DerefMut` even if it's not in the item bounds // of the opaque. let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; table.lookup_method_for_operator( 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 c631c87de5160..0dd558828fd7f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -216,7 +216,7 @@ impl<'db> MemoryMap<'db> { } } -/// Return an index of a parameter in the generic type parameter list by it's id. +/// Returns the index of a parameter in the generic type parameter list by its id. pub fn type_or_const_param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> u32 { generics::generics(db, id.parent).type_or_const_param_idx(id) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs index 7e2dfb7112d3b..f251dcfdcfb6d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs @@ -131,7 +131,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { /// Certainty passed into `evaluate_added_goals_and_make_canonical_response`. /// /// If this certainty is `Yes`, then we must be confident that the candidate - /// must hold iff it's nested goals hold. This is not true if the certainty is + /// must hold iff its nested goals hold. This is not true if the certainty is /// `Maybe(..)`, which suggests we forced ambiguity instead. /// /// This is *not* the certainty of the candidate's full nested evaluation, which diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index dc35a5c57bec4..61eda80fb4877 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -357,7 +357,7 @@ impl<'db> HirDisplay<'db> for Adt { impl<'db> HirDisplay<'db> for Struct { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { let module_id = self.module(f.db).id; - // FIXME: Render repr if its set explicitly? + // FIXME: Render repr if it's set explicitly? write_visibility(module_id, self.visibility(f.db), f)?; f.write_str("struct ")?; write!(f, "{}", self.name(f.db).display(f.db, f.edition()))?; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index fee6ae3d49527..f298e25489a59 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1563,7 +1563,7 @@ impl<'db> SemanticsImpl<'db> { } /// Attempts to map the node out of macro expanded files. - /// This only work for attribute expansions, as other ones do not have nodes as input. + /// This only works for attribute expansions, as other ones do not have nodes as input. pub fn original_ast_node(&self, node: N) -> Option { self.wrap_node_infile(node).original_ast_node_rooted(self.db).map( |InRealFile { file_id, value }| { @@ -1574,7 +1574,7 @@ impl<'db> SemanticsImpl<'db> { } /// Attempts to map the node out of macro expanded files. - /// This only work for attribute expansions, as other ones do not have nodes as input. + /// This only works for attribute expansions, as other ones do not have nodes as input. pub fn original_syntax_node_rooted(&self, node: &SyntaxNode) -> Option { let InFile { file_id, .. } = self.find_file(node); InFile::new(file_id, node).original_syntax_node_rooted(self.db).map( diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 3b39a489141e1..81005f48ddf83 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -10,7 +10,7 @@ //! This problem is a part of more-or-less every IDE feature implemented. Every //! IDE functionality (like goto to definition), conceptually starts with a //! specific cursor position in a file. Starting with this text offset, we first -//! figure out what syntactic construct are we at: is this a pattern, an +//! figure out what syntactic construct we are at: is this a pattern, an //! expression, an item definition. //! //! Knowing only the syntax gives us relatively little info. For example, @@ -32,11 +32,11 @@ //! Specifically, the algorithm goes like this: //! //! 1. Find the syntactic container for the syntax. For example, field's -//! container is the struct, and structs container is a module. +//! container is the struct, and the struct's container is a module. //! 2. Recursively get the def corresponding to container. //! 3. Ask the container def for all child defs. These child defs contain //! the answer and answer's siblings. -//! 4. For each child def, ask for it's source. +//! 4. For each child def, ask for its source. //! 5. The child def whose source is the syntax node we've started with //! is the answer. //! 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 9a61163e87e71..50d6f6d62cb3e 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 @@ -102,7 +102,7 @@ pub(crate) fn replace_if_let_with_match( if !pat_seen && cond_bodies.len() != 1 { // Don't offer turning an if (chain) without patterns into a match, - // unless its a simple `if cond { .. } (else { .. })` + // unless it's a simple `if cond { .. } (else { .. })` return None; } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs index 9e7dabbd01046..356c7e0087136 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs @@ -107,7 +107,7 @@ struct DeriveDependencies { } /// Standard Rust derives that have dependencies -/// (the dependencies are needed so that the main derive don't break the compilation when added) +/// (the dependencies are needed so that the main derive doesn't break the compilation when added) const DEFAULT_DERIVE_DEPENDENCIES: &[DeriveDependencies] = &[ DeriveDependencies { label: "Copy", dependencies: &["Clone"] }, DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] }, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs index c07c02e28538f..391152f438b61 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs @@ -26,7 +26,7 @@ pub(crate) fn complete_type_path<'db>( ScopeDef::ModuleDef(Function(_) | EnumVariant(_) | Static(_)) | ScopeDef::Local(_) => { false } - // unless its a constant in a generic arg list position + // unless it's a constant in a generic arg list position ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => { location.complete_consts() } 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 b06f52c113459..7280fd1ad5751 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 @@ -1663,7 +1663,7 @@ fn classify_name_ref<'db>( let res = sema.resolve_path(&qualifier); // For understanding how and why super_chain_len is calculated the way it - // is check the documentation at it's definition + // is check the documentation at its definition let mut segment_count = 0; let super_count = iter::successors(Some(qualifier.clone()), |p| p.qualifier()) .take_while(|p| { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 2ff726c6d03e3..675ffac040293 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -320,7 +320,7 @@ impl CompletionRelevance { } if let Some(trait_) = trait_ { - // lower rank trait methods unless its notable + // lower rank trait methods unless it's notable if !trait_.notable_trait { score -= 5; } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index a38154420af1a..540b0ee99dd21 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -1,5 +1,5 @@ -//! This modules defines type to represent changes to the source code, that flow -//! from the server to the client. +//! This module defines types that represent changes to source code flowing from +//! the server to the client. //! //! It can be viewed as a dual for [`Change`][vfs::Change]. diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/tree_diff.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/tree_diff.rs index 7163c08e1e317..af893f1ea5f38 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/tree_diff.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/tree_diff.rs @@ -115,7 +115,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { } (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {} (Some(lhs_ele), Some(rhs_ele)) => { - // nodes differ, look for lhs_ele in rhs, if its found we can mark everything up + // nodes differ, look for lhs_ele in rhs, if it's found we can mark everything up // until that element as insertions. This is important to keep the diff minimal // in regards to insertions that have been actually done, this is important for // use insertions as we do not want to replace the entire module node. diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs index c6a1d69672119..f76912b84085a 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs @@ -127,7 +127,7 @@ impl<'db> MatchFinder<'db> { usage_cache.find(&definition).unwrap() } - /// Returns the scope within which we want to search. We don't want un unrestricted search + /// Returns the scope within which we want to search. We don't want an unrestricted search /// scope, since we don't want to find references in external dependencies. fn search_scope(&self) -> SearchScope { // FIXME: We should ideally have a test that checks that we edit local roots and not library diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs index b3d09cac42cf5..d8c15cabb2fb8 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs @@ -1070,7 +1070,7 @@ fn match_failure_reasons() { #[test] fn overlapping_possible_matches() { // There are three possible matches here, however the middle one, `foo(foo(foo(42)))` shouldn't - // match because it overlaps with the outer match. The inner match is permitted since it's is + // match because it overlaps with the outer match. The inner match is permitted since it is // contained entirely within the placeholder of the outer match. assert_matches( "foo(foo($a))", diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs index b06079d8acd13..79919dac5b195 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs @@ -365,7 +365,7 @@ fn on_left_angle_typed( ) -> Option { let file_text = reparsed.syntax().text(); - // Find the next non-whitespace char in the line, check if its a `>` + // Find the next non-whitespace char in the line, check if it's a `>` let mut next_offset = offset; while file_text.char_at(next_offset) == Some(' ') { next_offset += TextSize::of(' ') diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs index fe01fb1f10637..c95e5a3ef8341 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs @@ -347,7 +347,7 @@ struct MatchState<'t> { /// Process the matcher positions of `cur_items` until it is empty. In the process, this will /// produce more items in `next_items`, `eof_items`, and `bb_items`. /// -/// For more info about the how this happens, see the module-level doc comments and the inline +/// For more info about how this happens, see the module-level doc comments and the inline /// comments of this function. /// /// # Parameters diff --git a/src/tools/rust-analyzer/crates/parser/src/output.rs b/src/tools/rust-analyzer/crates/parser/src/output.rs index ce64db8adae90..4f4728a8c5a58 100644 --- a/src/tools/rust-analyzer/crates/parser/src/output.rs +++ b/src/tools/rust-analyzer/crates/parser/src/output.rs @@ -14,7 +14,7 @@ use crate::SyntaxKind; #[derive(Default)] pub struct Output { /// 32-bit encoding of events. If LSB is zero, then that's an index into the - /// error vector. Otherwise, it's one of the thee other variants, with data encoded as + /// error vector. Otherwise, it's one of the three other variants, with data encoded as /// /// ```text /// |16 bit kind|8 bit n_input_tokens|4 bit tag|4 bit leftover| diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs index 3015bd0c0eccc..ae03be9aa7a26 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs @@ -31,7 +31,7 @@ //! ``` //! //! We probably should replace most of the code here with bincode someday, but, -//! as we don't have bincode in Cargo.toml yet, lets stick with serde_json for +//! as we don't have bincode in Cargo.toml yet, let's stick with serde_json for //! the time being. #[cfg(feature = "in-rust-tree")] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs index 7b00aebbfc4a5..ad92ebea7adb1 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs @@ -48,8 +48,8 @@ impl flags::Ssr { impl flags::Search { /// Searches for `patterns`, printing debug information for any nodes whose text exactly matches - /// `debug_snippet`. This is intended for debugging and probably isn't in it's current form useful - /// for much else. + /// `debug_snippet`. This is intended for debugging and probably isn't useful in its current + /// form for much else. pub fn run(self) -> anyhow::Result<()> { use ide_db::base_db::SourceDatabase; let cargo_config = diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 56490061a74f6..bd5c5ec87d968 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -998,7 +998,7 @@ impl GlobalState { let path = VfsPath::from(path); // If the file is in mem docs, it's managed by the client via - // notifications so only set it if its not in there. Library files are + // notifications so only set it if it's not in there. Library files are // exempt from that authority as they are considered immutable, for // them disk is always the source of truth. let is_library = self.source_root_config.path_is_library(&path); diff --git a/src/tools/rust-analyzer/crates/span/src/ast_id.rs b/src/tools/rust-analyzer/crates/span/src/ast_id.rs index f6500a9b4dbea..83a6748c01eaf 100644 --- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs +++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs @@ -1,8 +1,8 @@ //! `AstIdMap` allows to create stable IDs for "large" syntax nodes like items //! and macro calls. //! -//! Specifically, it enumerates all items in a file and uses position of a an -//! item as an ID. That way, id's don't change unless the set of items itself +//! Specifically, it enumerates all items in a file and uses the position of an +//! item as an ID. That way, IDs don't change unless the set of items itself //! changes. //! //! These IDs are tricky. If one of them invalidates, its interned ID invalidates, diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs index d48b984407e65..8d2c65f79163a 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs @@ -211,7 +211,7 @@ impl Vfs { /// /// Returns `true` if the file was modified, and saves the [change](ChangedFile). /// - /// If the path does not currently exists in the `Vfs`, allocates a new + /// If the path does not currently exist in the `Vfs`, allocates a new /// [`FileId`] for it. pub fn set_file_contents(&mut self, path: VfsPath, contents: Option>) -> bool { let _p = span!(Level::INFO, "Vfs::set_file_contents").entered(); @@ -280,7 +280,7 @@ impl Vfs { true } - /// Drain and returns all the changes in the `Vfs`. + /// Drains and returns all the changes in the `Vfs`. pub fn take_changes(&mut self) -> IndexMap> { mem::take(&mut self.changes) } @@ -292,7 +292,7 @@ impl Vfs { /// Returns the id associated with `path` /// - /// - If `path` does not exists in the `Vfs`, allocate a new id for it, associated with a + /// - If `path` does not exist in the `Vfs`, allocates a new id for it, associated with a /// deleted file; /// - Else, returns `path`'s id. /// diff --git a/src/tools/rust-analyzer/crates/vfs/src/loader.rs b/src/tools/rust-analyzer/crates/vfs/src/loader.rs index c49e4c4322d43..97e46ae894294 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/loader.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/loader.rs @@ -60,7 +60,7 @@ pub enum Message { n_total: usize, /// The files that have been loaded successfully. n_done: LoadingProgress, - /// The dir being loaded, `None` if its for a file. + /// The dir being loaded, `None` if it's for a file. dir: Option, /// The [`Config`] version. config_version: u32, diff --git a/src/tools/rust-analyzer/crates/vfs/src/path_interner.rs b/src/tools/rust-analyzer/crates/vfs/src/path_interner.rs index 225bfc7218b44..fbd1a624e3ce8 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/path_interner.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/path_interner.rs @@ -17,7 +17,7 @@ pub(crate) struct PathInterner { impl PathInterner { /// Get the id corresponding to `path`. /// - /// If `path` does not exists in `self`, returns [`None`]. + /// If `path` does not exist in `self`, returns [`None`]. pub(crate) fn get(&self, path: &VfsPath) -> Option { self.map.get_index_of(path).map(|i| FileId(i as u32)) } @@ -36,7 +36,7 @@ impl PathInterner { /// /// # Panics /// - /// Panics if `id` does not exists in `self`. + /// Panics if `id` does not exist in `self`. pub(crate) fn lookup(&self, id: FileId) -> &VfsPath { self.map.get_index(id.0 as usize).unwrap() } From 9c13c1f728455d10ae5ae23d8cdfca94391db523 Mon Sep 17 00:00:00 2001 From: shulaoda <165626830+shulaoda@users.noreply.github.com> Date: Mon, 27 Jul 2026 23:03:38 +0800 Subject: [PATCH 18/94] fix: don't pick a discriminant type larger than typeck's --- src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs | 4 ++-- src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 1f321af79493f..777d0803fc799 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -142,8 +142,8 @@ fn repr_discr( Integer::I8 }; - // If there are no negative values, we can use the unsigned fit. - Ok(if min >= 0 { + // `min` and `max` are the ends of a wrapping range, so their sign is not a usable test. + Ok(if unsigned_fit <= signed_fit { (cmp::max(unsigned_fit, at_least), false) } else { (cmp::max(signed_fit, at_least), true) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index b9ee38c44fe41..b5db24e98bc70 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -610,6 +610,13 @@ fn enums_with_discriminants() { A = 1, // This one is (perhaps surprisingly) zero sized. } } + size_and_align! { + #[allow(overflowing_literals, clippy::enum_clike_unportable_variant)] + enum Goal { + A = 0, + B = 0x8000_0000_0000_0001, // Wraps around to a negative discriminant. + } + } } #[test] From 1da028bb07e2dc99ec6e05e6caef4f918ac1bee3 Mon Sep 17 00:00:00 2001 From: AayushMainali-Github Date: Mon, 27 Jul 2026 18:54:46 +0000 Subject: [PATCH 19/94] fix: use char counts in progress bar --- .../rust-analyzer/src/cli/progress_report.rs | 105 +++++++++++++----- 1 file changed, 78 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs index 028311388c561..aff2c0d6ff52a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs @@ -65,31 +65,7 @@ impl<'a> ProgressReport<'a> { } fn update_text(&mut self, text: &str) { - // Get length of common portion - let mut common_prefix_length = 0; - let common_length = usize::min(self.text.len(), text.len()); - - while common_prefix_length < common_length - && text.chars().nth(common_prefix_length).unwrap() - == self.text.chars().nth(common_prefix_length).unwrap() - { - common_prefix_length += 1; - } - - // Backtrack to the first differing character - let mut output = String::new(); - output += &'\x08'.to_string().repeat(self.text.len() - common_prefix_length); - // Output new suffix, using chars() iter to ensure unicode compatibility - output.extend(text.chars().skip(common_prefix_length)); - - // If the new text is shorter than the old one: delete overlapping characters - if let Some(overlap_count) = self.text.len().checked_sub(text.len()) - && overlap_count > 0 - { - output += &" ".repeat(overlap_count); - output += &"\x08".repeat(overlap_count); - } - + let output = render_text_update(&self.text, text); let _ = io::stdout().write(output.as_bytes()); let _ = io::stdout().flush(); text.clone_into(&mut self.text); @@ -105,11 +81,86 @@ impl<'a> ProgressReport<'a> { } // Fill all last text to space and return the cursor - let spaces = " ".repeat(self.text.len()); - let backspaces = "\x08".repeat(self.text.len()); + let len = self.text.chars().count(); + let spaces = " ".repeat(len); + let backspaces = "\x08".repeat(len); print!("{backspaces}{spaces}{backspaces}"); let _ = io::stdout().flush(); self.text = String::new(); } } + +fn render_text_update(old: &str, new: &str) -> String { + let old_len = old.chars().count(); + let new_len = new.chars().count(); + + // Get length of common portion + let mut common_prefix_length = 0; + let common_length = usize::min(old_len, new_len); + + while common_prefix_length < common_length + && new.chars().nth(common_prefix_length).unwrap() + == old.chars().nth(common_prefix_length).unwrap() + { + common_prefix_length += 1; + } + + // Backtrack to the first differing character + let mut output = String::new(); + output += &'\x08'.to_string().repeat(old_len - common_prefix_length); + // Output new suffix, using chars() iter to ensure unicode compatibility + output.extend(new.chars().skip(common_prefix_length)); + + // If the new text is shorter than the old one: delete overlapping characters + if let Some(overlap_count) = old_len.checked_sub(new_len) + && overlap_count > 0 + { + output += &" ".repeat(overlap_count); + output += &"\x08".repeat(overlap_count); + } + + output +} + +#[cfg(test)] +mod tests { + use super::render_text_update; + + #[test] + fn ascii_prefix_reuse() { + let old = "1/7 14% processing: foo"; + let new = "1/7 28% processing: bar"; + let update = render_text_update(old, new); + + let common = "1/7 "; + let backspaces = old.chars().count() - common.chars().count(); + let expected = format!("{}{}", "\x08".repeat(backspaces), "28% processing: bar"); + assert_eq!(update, expected); + } + + #[test] + fn unicode_identifiers_do_not_panic() { + // Regression test for rust-lang/rust-analyzer#22844: previous code + // compared byte lengths with char indices, so `chars().nth(...).unwrap()` + // panicked on non-ASCII. + let old = "1/7 14% processing: f::消息"; + let new = "2/7 28% processing: f::消息内容"; + let update = render_text_update(old, new); + + let backspaces = old.chars().count(); + let expected = format!("{}{new}", "\x08".repeat(backspaces)); + assert_eq!(update, expected); + } + + #[test] + fn shorter_unicode_message_clears_overlap() { + let old = "processing: 消息内容"; + let new = "processing: 消息"; + let update = render_text_update(old, new); + + // Drop the last two chars, then blank/backspace the leftover width. + let expected = "\x08\x08 \x08\x08"; + assert_eq!(update, expected); + } +} From e86cdb59eae82e18b6e8d8151695809ede272ca4 Mon Sep 17 00:00:00 2001 From: shulaoda <165626830+shulaoda@users.noreply.github.com> Date: Tue, 28 Jul 2026 08:20:27 +0800 Subject: [PATCH 20/94] fix: don't panic on a qualified path whose trait is not a trait --- .../rust-analyzer/crates/hir-ty/src/lower/path.rs | 6 ++++++ .../crates/hir-ty/src/tests/regression.rs | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index 554a191d9d0dd..c67c69520db10 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 @@ -1234,6 +1234,12 @@ pub(crate) fn substs_from_args_and_bindings<'db>( }; params.next(); substs.push(self_ty); + } else if has_self_arg { + // A qualified path `::Assoc` where `Trait` resolved to something without a + // `Self` parameter, e.g. a struct. `check_generic_args_len()` skips the self type + // unconditionally, so drop it here too instead of matching it against a real parameter. + // FIXME: Report a diagnostic here, rustc emits `E0404: expected trait, found struct`. + args.next(); } loop { 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 c580841244f1b..2836f977a9ac5 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 @@ -3016,3 +3016,15 @@ fn f(s: S) { s.m(); } "#, ); } + +#[test] +fn regression_22799() { + check_no_mismatches( + r#" +struct S; +fn f() { + ::S; +} + "#, + ); +} From e42a814b26084408f164c5a0aed7686a461c99cc Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Wed, 22 Jul 2026 18:22:37 +0100 Subject: [PATCH 21/94] fix: Failed to lookup MACRO_CALL@... in this Semantics due to include! SemanticsImpl::find_file assumes that its caches always contain the file that has the current SyntaxNode. For macros `foo!()` we only have two files to worry about: the macro call site and the macro definition site. Hoewver, for include!("foo.rs") we also need to consider the included file. Ensure that the file cache is consistently populated for include!() invocations macro expansion, and add a test. AI disclosure: GPT-5.5 used to minimise a repro from a real project and write the initial implementation. Comments and commit message are entirely mine. --- .../crates/hir/src/semantics/source_to_def.rs | 12 ++++++++++++ .../ide-assists/src/handlers/inline_macro.rs | 17 +++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 81005f48ddf83..caa7b39885fec 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -165,9 +165,21 @@ impl<'db> SourceToDefCache<'db> { self.expansion_info_cache.entry(macro_file).or_insert_with(|| { let exp_info = macro_file.expansion_info(db); + // Ensure that the cache contains syntax nodes from expanded macros, + // whose root may be in another file. let InMacroFile { file_id, value } = exp_info.expanded(); Self::cache(&mut self.root_to_file_cache, value, file_id.into()); + // include!("foo.rs") invocations are awkward: in addition to the + // expansion site there's the included file (foo.rs), so we need to + // ensure that it exists in the cache too. + if macro_file.is_include_macro(db) { + let arg = exp_info.arg(); + if let Some(arg_node) = arg.value { + Self::cache(&mut self.root_to_file_cache, arg_node.tree_top(), arg.file_id); + } + } + exp_info }) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs index 5a185637df4ef..d934fae9253f6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs @@ -175,6 +175,23 @@ macro_rules! num { ); } + #[test] + fn inline_macro_in_included_file() { + // Regression test for climbing from the included file into an uncached includer root. + check_assist_not_applicable( + inline_macro, + r#" +//- minicore:include +//- /main.rs +include!("a.rs"); +//- /a.rs +fn foo() { + let x = 1$0; +} +"#, + ); + } + #[test] fn inline_macro_simple_not_applicable_broken_macro() { // FIXME: This is a bug. The macro should not expand, but it's From db60d94665b36d16a63ebbb48055a759646e53f0 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 28 Jul 2026 15:34:33 +0200 Subject: [PATCH 22/94] internal: update next-solver to 0.166 --- src/tools/rust-analyzer/Cargo.lock | 44 +++++++++--------- src/tools/rust-analyzer/Cargo.toml | 16 +++---- .../crates/hir-ty/src/method_resolution.rs | 45 ++++++++++++------- .../crates/hir-ty/src/next_solver/interner.rs | 37 +++++++++------ .../crates/hir-ty/src/next_solver/util.rs | 12 +++++ 5 files changed, 95 insertions(+), 59 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index a096be2f2ad82..7a2e2d493b59a 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2062,9 +2062,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "ra-ap-rustc_abi" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f25a779e21ca3bba6795193b16508c8ab159f96ee4b07349893fd272065b525" +checksum = "e2cf1b1ffe31b6226c00b40cddfda65002b7729f9f4ed2d547b5856cdab0011c" dependencies = [ "bitflags", "ra-ap-rustc_hashes", @@ -2074,33 +2074,33 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0218ca6c7b096466e85a497e6150c39be5b7bc36637fe62c1cd20370a9d9aac7" +checksum = "2ef42605e36e1305e815ccfc8830eb870f74d78534bca19a61629149536d8e98" [[package]] name = "ra-ap-rustc_hashes" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b410bacf1a7c8038f376fa6283003784d568ac012e35fc0aeefa9a5ab11a2e" +checksum = "b9f5542968215c17275920791b2fa13a43014287506ed0450777c79845102e86" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2271b55e4a5d0cc0cbe9bdf8056c07ac69e32919a48ce66722ed0526d62588c3" +checksum = "1d9e47b9ca7d92cfb0d6653503adbabd41938b84474317397a664326b208d6c6" dependencies = [ "ra-ap-rustc_index_macros", ] [[package]] name = "ra-ap-rustc_index_macros" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a89e743fb881a1e13544e3395a5ad9ad9280d56384256a121066119abd7af2" +checksum = "4d744a7a2852a22f06210bcff9e4667ed0cacbfbe94894cc294044d25e876341" dependencies = [ "proc-macro2", "quote", @@ -2109,9 +2109,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6d7c9cc05e0e6b72a214a455a106d9b22b0494164d50a657b17bd319534c218" +checksum = "527c12b3731b7d0692498012810b85b2b8dfdb8b514321ed6afc434bd1c70191" dependencies = [ "memchr", "unicode-ident", @@ -2120,9 +2120,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3017c2f0ace80b8e6068b9c613aa56ed50e0374bf44a891447511f1264e40d" +checksum = "a7a9663a8d7c369e934aac2b74a638537ad7eb4be75b4530d765384dc071c936" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -2133,9 +2133,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a737f844bdef8ac5ab54dadf2f34704b4d06beef9236d71080bb34db697220b" +checksum = "2c038b7a8b0f784d4e441ad8ab991fbbdaa5e0be482e59c639a846a1c8126951" dependencies = [ "ra-ap-rustc_lexer", "rustc-literal-escaper", @@ -2143,9 +2143,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6de3d4c7d6078cce3c40c55717b8b15002a80b9fa8849faea496a365324861b4" +checksum = "42ca286f90e99bb97cd9274c088f3c874a05d1ee90cabf40a3928afedabe99fd" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.2", @@ -2156,9 +2156,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c5d9a4d3e7bee7313599bc6d794037247ac0165f03857379cf4fc3097199e05" +checksum = "26d6efb6008f665a9485e0afecf9f4950a6c4bedd8ddd330a9df8986a6c0160b" dependencies = [ "arrayvec", "bitflags", @@ -2177,9 +2177,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.165.0" +version = "0.166.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "024598d1f54272acd83d28c121f8a2e82e216dd7be1e40158b66b2d12fa214c0" +checksum = "5f4fd2355e2bbf1f343c730f623596efc6e465b5e3685b606a437567ebb75bf8" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 2a219c3ea485c..4ef92c5bd2ca3 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -86,14 +86,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.165", default-features = false } -ra-ap-rustc_parse_format = { version = "0.165", default-features = false } -ra-ap-rustc_index = { version = "0.165", default-features = false } -ra-ap-rustc_abi = { version = "0.165", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.165", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.165", default-features = false } -ra-ap-rustc_type_ir = { version = "0.165", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.165", default-features = false } +ra-ap-rustc_lexer = { version = "0.166", default-features = false } +ra-ap-rustc_parse_format = { version = "0.166", default-features = false } +ra-ap-rustc_index = { version = "0.166", default-features = false } +ra-ap-rustc_abi = { version = "0.166", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.166", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.166", default-features = false } +ra-ap-rustc_type_ir = { version = "0.166", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.166", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index e8702bf2c99de..97e9d8bae1907 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -31,7 +31,7 @@ use hir_def::{ }; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ - TypeVisitableExt, + TypeVisitableExt, VisitorResult, fast_reject::{TreatParams, simplify_type}, inherent::{BoundExistentialPredicates, IntoKind}, }; @@ -54,6 +54,7 @@ use crate::{ obligation_ctxt::ObligationCtxt, util::clauses_as_obligations, }, + ret, traits::ParamEnvAndCrate, }; @@ -835,27 +836,34 @@ impl<'db> TraitImpls<'db> { } } - pub fn for_each_crate_and_block( + pub fn for_each_crate_and_block( db: &'db dyn HirDatabase, krate: Crate, block: Option>, - for_each: &mut dyn FnMut(&TraitImpls<'db>), - ) { + for_each: &mut dyn FnMut(&TraitImpls<'db>) -> R, + ) -> R { let blocks = std::iter::successors(block, |block| block.module(db).block(db)); - blocks.filter_map(|block| Self::for_block(db, block)).for_each(&mut *for_each); - Self::for_crate_and_deps(db, krate).iter().map(|it| &**it).for_each(for_each); + for impl_ in blocks.filter_map(|block| Self::for_block(db, block)) { + ret!(for_each(impl_)); + } + for impl_ in Self::for_crate_and_deps(db, krate) { + ret!(for_each(impl_)); + } + R::output() } /// Like [`Self::for_each_crate_and_block()`], but takes in account two blocks, one for a trait and one for a self type. - pub fn for_each_crate_and_block_trait_and_type( + pub fn for_each_crate_and_block_trait_and_type( db: &'db dyn HirDatabase, krate: Crate, type_block: Option>, trait_block: Option>, - for_each: &mut dyn FnMut(&TraitImpls<'db>), - ) { + for_each: &mut dyn FnMut(&TraitImpls<'db>) -> R, + ) -> R { let in_self_and_deps = TraitImpls::for_crate_and_deps(db, krate); - in_self_and_deps.iter().for_each(|impls| for_each(impls)); + for impl_ in in_self_and_deps { + ret!(for_each(impl_)); + } // We must not provide duplicate impls to the solver. Therefore we work with the following strategy: // start from each block, and walk ancestors until you meet the other block. If they never meet, @@ -874,13 +882,20 @@ impl<'db> TraitImpls<'db> { .filter_map(move |block| TraitImpls::for_block(db, block)) }; if trait_block == type_block { - blocks_iter(trait_block) - .filter_map(|block| TraitImpls::for_block(db, block)) - .for_each(for_each); + for impl_ in + blocks_iter(trait_block).filter_map(|block| TraitImpls::for_block(db, block)) + { + ret!(for_each(impl_)); + } } else { - for_each_block(trait_block, type_block).for_each(&mut *for_each); - for_each_block(type_block, trait_block).for_each(for_each); + for impl_ in for_each_block(trait_block, type_block) { + ret!(for_each(impl_)); + } + for impl_ in for_each_block(type_block, trait_block) { + ret!(for_each(impl_)); + } } + R::output() } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index a7216a034cc56..dc30c1e582eff 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -29,7 +29,7 @@ use rustc_index::bit_set::DenseBitSet; use rustc_type_ir::{ AliasTy, BoundVar, CoroutineWitnessTypes, DebruijnIndex, EarlyBinder, FlagComputation, Flags, FnSigKind, GenericArgKind, GenericTypeVisitable, ImplPolarity, InferTy, Interner, TraitRef, - TypeFlags, TypeVisitableExt, Upcast, Variance, + TypeFlags, TypeVisitableExt, Upcast, Variance, VisitorResult, elaborate::elaborate, error::TypeError, fast_reject, @@ -54,6 +54,7 @@ use crate::{ TraitAssocTyId, TraitIdWrapper, TypeAliasIdWrapper, UnevaluatedConst, Unnormalized, util::{explicit_item_bounds, explicit_item_self_bounds}, }, + ret, }; use super::{ @@ -1601,12 +1602,12 @@ impl<'db> Interner for DbInterner<'db> { def_id.0.trait_items(self.db()).associated_types().map(|id| id.into()) } - fn for_each_relevant_impl( + fn for_each_relevant_impl( self, trait_def_id: Self::TraitId, self_ty: Self::Ty, - mut f: impl FnMut(Self::ImplId), - ) { + mut f: impl FnMut(Self::ImplId) -> R, + ) -> R { let krate = self.krate.expect("trait solving requires setting `DbInterner::krate`"); let trait_block = trait_def_id.0.loc(self.db).container.block(self.db); let mut consider_impls_for_simplified_type = |simp: SimplifiedType<'_>| { @@ -1641,13 +1642,14 @@ impl<'db> Interner for DbInterner<'db> { let (regular_impls, builtin_derive_impls) = impls.for_trait_and_self_ty(trait_def_id.0, &simp); for &impl_ in regular_impls { - f(impl_.into()); + ret!(f(impl_.into())); } for &impl_ in builtin_derive_impls { - f(impl_.into()); + ret!(f(impl_.into())); } + R::output() }, - ); + ) }; match self_ty.kind() { @@ -1676,7 +1678,7 @@ impl<'db> Interner for DbInterner<'db> { let simp = fast_reject::simplify_type(self, self_ty, fast_reject::TreatParams::AsRigid) .unwrap(); - consider_impls_for_simplified_type(simp); + ret!(consider_impls_for_simplified_type(simp)); } // HACK: For integer and float variables we have to manually look at all impls @@ -1704,7 +1706,7 @@ impl<'db> Interner for DbInterner<'db> { SimplifiedType::Uint(Usize), ]; for simp in possible_integers { - consider_impls_for_simplified_type(simp); + ret!(consider_impls_for_simplified_type(simp)); } } @@ -1719,7 +1721,7 @@ impl<'db> Interner for DbInterner<'db> { ]; for simp in possible_floats { - consider_impls_for_simplified_type(simp); + ret!(consider_impls_for_simplified_type(simp)); } } @@ -1748,15 +1750,22 @@ impl<'db> Interner for DbInterner<'db> { self.for_each_blanket_impl(trait_def_id, f) } - fn for_each_blanket_impl(self, trait_def_id: Self::TraitId, mut f: impl FnMut(Self::ImplId)) { - let Some(krate) = self.krate else { return }; + fn for_each_blanket_impl( + self, + trait_def_id: Self::TraitId, + mut f: impl FnMut(Self::ImplId) -> R, + ) -> R { + let Some(krate) = self.krate else { + return R::output(); + }; let block = trait_def_id.0.loc(self.db).container.block(self.db); TraitImpls::for_each_crate_and_block(self.db, krate, block, &mut |impls| { for &impl_ in impls.blanket_impls(trait_def_id.0) { - f(impl_.into()); + ret!(f(impl_.into())); } - }); + R::output() + }) } fn has_item_definition(self, _def_id: Self::ImplOrTraitAssocTermId) -> bool { 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 7e40e3c17d517..e384ac455213d 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 @@ -723,3 +723,15 @@ pub(crate) fn clauses_as_obligations<'db>( recursion_depth: 0, }) } + +/// Copied from +/// +#[macro_export] +macro_rules! ret { + ($e: expr) => { + match $e.branch() { + ::std::ops::ControlFlow::Break(b) => return R::from_residual(b), + ::std::ops::ControlFlow::Continue(()) => {} + } + }; +} From 2a82c4bcb68d67261be1c5ca9439f7d26b8beb16 Mon Sep 17 00:00:00 2001 From: cuishuang Date: Tue, 28 Jul 2026 23:48:01 +0800 Subject: [PATCH 23/94] fix(vfs): use component-based path prefix matching for virtual paths --- src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs | 4 ++-- src/tools/rust-analyzer/crates/vfs/src/vfs_path/tests.rs | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs index 7e2c787afc738..eb55081c2038e 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs @@ -337,9 +337,9 @@ impl PartialEq for AbsPath { struct VirtualPath(String); impl VirtualPath { - /// Returns `true` if `other` is a prefix of `self` (as strings). + /// Returns `true` if `other` is a prefix of `self`. fn starts_with(&self, other: &VirtualPath) -> bool { - self.0.starts_with(&other.0) + <_ as AsRef>::as_ref(&self.0).starts_with(&other.0) } fn strip_prefix(&self, base: &VirtualPath) -> Option<&RelPath> { diff --git a/src/tools/rust-analyzer/crates/vfs/src/vfs_path/tests.rs b/src/tools/rust-analyzer/crates/vfs/src/vfs_path/tests.rs index 2d89362ee0691..c21fabfbf0460 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/vfs_path/tests.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/vfs_path/tests.rs @@ -1,5 +1,13 @@ use super::*; +#[test] +fn virtual_path_starts_with_is_component_based() { + let path = |path: &str| VfsPath::new_virtual_path(path.to_owned()); + + assert!(!path("/foobar").starts_with(&path("/foo"))); + assert!(path("/foo/bar").starts_with(&path("/foo"))); +} + #[test] fn virtual_path_extensions() { assert_eq!(VirtualPath("/".to_owned()).name_and_extension(), None); From 0248a75893dfbf84174591b45e09fea8c3758fd6 Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Tue, 28 Jul 2026 10:15:20 -0700 Subject: [PATCH 24/94] Reformat snippet docs so that they appear in the book --- .../rust-analyzer/crates/ide-completion/src/snippet.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index ee47c84708b46..20981433ae7ac 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -38,13 +38,12 @@ // * `description` is an optional description of the snippet, if unset the snippet name will be used. // // * `requires` is an optional list of item paths that have to be resolvable in the current crate where the completion is rendered. - // On failure of resolution the snippet won't be applicable, otherwise the snippet will insert an import for the items on insertion if // the items aren't yet in scope. // // * `scope` is an optional filter for when the snippet should be applicable. Possible values are: -// ** for Snippet-Scopes: `expr`, `item` (default: `item`) -// ** for Postfix-Snippet-Scopes: `expr`, `type` (default: `expr`) +// * for Snippet-Scopes: `expr`, `item` (default: `item`) +// * for Postfix-Snippet-Scopes: `expr`, `type` (default: `expr`) // // The `body` field also has access to placeholders as visible in the example as `$0`. // These placeholders take the form of `$number` or `${number:placeholder_text}` which can be traversed as tabstop in ascending order starting from 1, @@ -98,7 +97,7 @@ // "scope": "expr" // } // } -// ```` +// ``` use hir::{ModPath, Name, Symbol}; use ide_db::imports::import_assets::LocatedImport; From 02f2944526cea5777442a544fcc9c574f9d7387f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 28 Jul 2026 21:05:57 +0300 Subject: [PATCH 25/94] Mark auto traits as coinductive Coinductive traits are traits that when proving a predicate for them, `Type: Trait`, inside the predicate we can rely on itself to hold. For example, in `struct Foo(Foo)`, coinductive trait will succeed `Foo: Trait` and non-coinductive trait will fail unless there's an `impl Trait for Foo`. In Rust, only auto traits and `#[rustc_coinductive]` traits are coinductive, but previously we haven't considered auto traits coinductive. --- .../crates/hir-def/src/signatures.rs | 2 +- .../crates/hir-ty/src/tests/traits.rs | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 10a38ec71e372..45dab8859d270 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -545,7 +545,7 @@ impl TraitSignature { let attrs = AttrFlags::query(db, id.into()); let source = loc.source(db); if source.value.auto_token().is_some() { - flags.insert(TraitFlags::AUTO); + flags.insert(TraitFlags::AUTO | TraitFlags::COINDUCTIVE); } if source.value.unsafe_token().is_some() { flags.insert(TraitFlags::UNSAFE); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 8233a009816fc..fad944589dab6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -5377,3 +5377,37 @@ fn run_dyn<'b>(val: &dyn for<'a> Trait<'a, 'b>) {} "#]], ); } + +#[test] +fn recursive_auto_trait() { + check_types( + r#" +auto trait Send {} +impl !Send for *const T {} + +struct Vec(*const T); +impl Send for Vec {} + +struct Node { + children: Vec, +} + +struct Holder(T); + +trait Lock { + fn get(&self) -> &T; +} + +impl Lock for Holder { + fn get(&self) -> &T { + &self.0 + } +} + +fn probe(h: &Holder) { + h.get(); + // ^^^^^^^ &'? Node +} + "#, + ); +} From 13901c01ad3d7bbcb384e346230de145f5552aaa Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 29 Jul 2026 07:32:47 +0300 Subject: [PATCH 26/94] Store liberated closure sigs in InferenceResult We (will) need them in later stages, e.g. MIR building. --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 44 +++++++++++++++++-- .../crates/hir-ty/src/infer/closure.rs | 16 +++++-- .../hir-ty/src/infer/closure/analysis.rs | 5 +-- .../crates/hir-ty/src/next_solver/binder.rs | 34 +++++++++----- .../hir-ty/src/next_solver/generic_arg.rs | 42 ++---------------- .../crates/hir-ty/src/next_solver/interner.rs | 29 ++++++++++++ .../crates/hir-ty/src/next_solver/ty.rs | 26 ++--------- 7 files changed, 115 insertions(+), 81 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index bd00da84cafad..c838fcc3a7640 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -99,7 +99,7 @@ use crate::{ }, method_resolution::CandidateId, next_solver::{ - AliasTy, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region, + AliasTy, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region, StoredFnSig, StoredGenericArg, StoredGenericArgs, StoredTy, StoredTys, Term, Ty, TyKind, Tys, abi::Safety, infer::{InferCtxt, ObligationInspector, traits::ObligationCause}, @@ -820,7 +820,7 @@ pub struct InferenceResult<'db> { defined_anon_consts: ThinVec>, } -#[derive(Clone, PartialEq, Eq, Debug, Default)] +#[derive(Clone, PartialEq, Eq, Debug)] pub struct ClosureData { /// Tracks the minimum captures required for a closure; /// see `MinCaptureInformationMap` for more details. @@ -849,6 +849,42 @@ pub struct ClosureData { /// information on `t` in order to create place `t.0` and `t.1`. We can solve this /// issue by fake reading `t`. pub fake_reads: Box<[(Place, FakeReadCause, SmallVec<[CaptureSourceStack; 2]>)]>, + + /// For each fn, records the "liberated" types of its arguments + /// and return type. Liberated means that all bound regions + /// (including late-bound regions) are replaced with free + /// equivalents. This table is not used in codegen (since regions + /// are erased there) and hence is not serialized to metadata. + /// + /// This table also contains the "revealed" values for any `impl Trait` + /// that appear in the signature and whose values are being inferred + /// by this function. + /// + /// # Example + /// + /// ```rust + /// # use std::fmt::Debug; + /// fn foo(x: &u32) -> impl Debug { *x } + /// ``` + /// + /// The function signature here would be: + /// + /// ```ignore (illustrative) + /// for<'a> fn(&'a u32) -> Foo + /// ``` + /// + /// where `Foo` is an opaque type created for this function. + /// + /// + /// The *liberated* form of this would be + /// + /// ```ignore (illustrative) + /// fn(&'a u32) -> u32 + /// ``` + /// + /// Note that `'a` is not bound (it would be an `ReLateParam`) and + /// that the `Foo` opaque type is replaced by its hidden type. + pub liberated_sig: StoredFnSig, } /// Part of `MinCaptureInformationMap`; Maps a root variable to the list of `CapturedPlace`. @@ -1677,7 +1713,7 @@ impl<'db> InferenceContext<'db> { } pat_adjustments.shrink_to_fit(); for closure_data in closures_data.values_mut() { - let ClosureData { min_captures, fake_reads } = closure_data; + let ClosureData { min_captures, fake_reads, liberated_sig } = closure_data; let dummy_place = || Place { base_ty: types.types.error.store(), base: closure::analysis::expr_use_visitor::PlaceBase::Rvalue, @@ -1706,6 +1742,8 @@ impl<'db> InferenceContext<'db> { min_capture.shrink_to_fit(); } min_captures.shrink_to_fit(); + + resolver.resolve_completely(liberated_sig); } closures_data.shrink_to_fit(); *tuple_field_access_types = tuple_field_accesses_rev diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index e2948a81ac7d9..9af182fc49288 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -9,6 +9,7 @@ use hir_def::{ hir::{ClosureKind, CoroutineKind, CoroutineSource, ExprId, PatId}, type_ref::TypeRefId, }; +use indexmap::IndexMap; use rustc_abi::ExternAbi; use rustc_type_ir::{ AliasTyKind, ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, @@ -21,11 +22,11 @@ use tracing::{debug, instrument}; use crate::{ Span, db::{InternedClosure, InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId}, - infer::{BreakableKind, Diverges, coerce::CoerceMany, pat::PatOrigin}, + infer::{BreakableKind, ClosureData, Diverges, coerce::CoerceMany, pat::PatOrigin}, next_solver::{ AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArg, PolyFnSig, - PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, TermId, Ty, TyKind, - Unnormalized, + PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, StoredFnSig, TermId, Ty, + TyKind, Unnormalized, abi::Safety, infer::{ BoundRegionConversionTime, InferOk, InferResult, @@ -303,6 +304,15 @@ impl<'db> InferenceContext<'db> { } }; + self.result.closures_data.insert( + closure_expr, + ClosureData { + liberated_sig: StoredFnSig::new(liberated_sig), + fake_reads: Box::default(), + min_captures: IndexMap::default(), + }, + ); + // Now go through the argument patterns for (arg_pat, arg_ty) in args.iter().zip(liberated_sig.inputs()) { self.infer_top_pat(*arg_pat, *arg_ty, PatOrigin::Param); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs index 38e634eb7db49..0c24b82d1bde0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -515,7 +515,7 @@ impl<'db> InferenceContext<'db> { let fake_reads = delegate.fake_reads; - self.result.closures_data.entry(closure_expr_id).or_default().fake_reads = + self.result.closures_data.get_mut(&closure_expr_id).unwrap().fake_reads = fake_reads.into_boxed_slice(); // If we are also inferred the closure kind here, @@ -730,8 +730,7 @@ impl<'db> InferenceContext<'db> { return; } - let mut closure_data = - self.result.closures_data.remove(&closure_def_id).unwrap_or_default(); + let mut closure_data = self.result.closures_data.remove(&closure_def_id).unwrap(); let root_var_min_capture_list = &mut closure_data.min_captures; let mut dedup_sources_scratch = FxHashMap::default(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs index 9585cced6b114..351d0c4cda47e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs @@ -71,17 +71,33 @@ impl StoredEarlyBinder { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StoredPolyFnSig { bound_vars: StoredBoundVarKinds, - inputs_and_output: StoredTys, - fn_sig_kind: FnSigKind<'static>, + sig: StoredFnSig, } impl StoredPolyFnSig { #[inline] pub fn new(sig: PolyFnSig<'_>) -> Self { let bound_vars = sig.bound_vars().store(); - let sig = sig.skip_binder(); + Self { bound_vars, sig: StoredFnSig::new(sig.skip_binder()) } + } + + #[inline] + pub fn get(&self) -> PolyFnSig<'_> { + Binder::bind_with_vars(self.sig.get(), self.bound_vars.as_ref()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] +pub struct StoredFnSig { + inputs_and_output: StoredTys, + #[type_visitable(ignore)] + fn_sig_kind: FnSigKind<'static>, +} + +impl StoredFnSig { + #[inline] + pub fn new(sig: FnSig<'_>) -> Self { Self { - bound_vars, inputs_and_output: sig.inputs_and_output.store(), fn_sig_kind: FnSigKind::new( sig.fn_sig_kind.abi(), @@ -92,14 +108,8 @@ impl StoredPolyFnSig { } #[inline] - pub fn get(&self) -> PolyFnSig<'_> { - Binder::bind_with_vars( - FnSig { - inputs_and_output: self.inputs_and_output.as_ref(), - fn_sig_kind: self.fn_sig_kind, - }, - self.bound_vars.as_ref(), - ) + pub fn get(&self) -> FnSig<'_> { + FnSig { inputs_and_output: self.inputs_and_output.as_ref(), fn_sig_kind: self.fn_sig_kind } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 22b34b379dd86..483811f9e6f0c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -21,7 +21,8 @@ use rustc_type_ir::{ }; use crate::next_solver::{ - ConstInterned, RegionInterned, TyInterned, impl_foldable_for_interned_slice, interned_slice, + ConstInterned, RegionInterned, TyInterned, impl_foldable_for_interned_slice, + impl_foldable_for_stored_type, interned_slice, }; use super::{ @@ -194,24 +195,7 @@ impl std::fmt::Debug for StoredGenericArg { } } -impl<'db> TypeVisitable> for StoredGenericArg { - fn visit_with>>(&self, visitor: &mut V) -> V::Result { - self.as_ref().visit_with(visitor) - } -} - -impl<'db> TypeFoldable> for StoredGenericArg { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - Ok(self.as_ref().try_fold_with(folder)?.store()) - } - - fn fold_with>>(self, folder: &mut F) -> Self { - self.as_ref().fold_with(folder).store() - } -} +impl_foldable_for_stored_type!(StoredGenericArg); #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct GenericArg<'db> { @@ -473,28 +457,10 @@ interned_slice!( GenericArg<'static>, ); impl_foldable_for_interned_slice!(GenericArgs); +impl_foldable_for_stored_type!(StoredGenericArgs); impl<'db> rustc_type_ir::inherent::GenericArg> for GenericArg<'db> {} -impl<'db> TypeVisitable> for StoredGenericArgs { - fn visit_with>>(&self, visitor: &mut V) -> V::Result { - self.as_ref().visit_with(visitor) - } -} - -impl<'db> TypeFoldable> for StoredGenericArgs { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - Ok(self.as_ref().try_fold_with(folder)?.store()) - } - - fn fold_with>>(self, folder: &mut F) -> Self { - self.as_ref().fold_with(folder).store() - } -} - trait GenericArgsBuilder<'db>: AsRef<[GenericArg<'db>]> { fn push(&mut self, arg: GenericArg<'db>); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index dc30c1e582eff..7554ca6bcd025 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -265,6 +265,35 @@ macro_rules! impl_foldable_for_interned_slice { } pub(crate) use impl_foldable_for_interned_slice; +macro_rules! impl_foldable_for_stored_type { + ($name:ident) => { + impl<'db> ::rustc_type_ir::TypeVisitable> for $name { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.as_ref().visit_with(visitor) + } + } + + impl<'db> rustc_type_ir::TypeFoldable> for $name { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(self.as_ref().try_fold_with(folder)?.store()) + } + fn fold_with>>( + self, + folder: &mut F, + ) -> Self { + self.as_ref().fold_with(folder).store() + } + } + }; +} +pub(crate) use impl_foldable_for_stored_type; + macro_rules! impl_stored_interned { ( $storage:ident, $name:ident, $stored_name:ident $(,)? ) => { #[derive(Clone, PartialEq, Eq, Hash)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 05f559c0349e9..36c18ed772ca7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -33,7 +33,8 @@ use crate::{ CoroutineClosureIdWrapper, CoroutineIdWrapper, FnSig, GenericArgKind, PolyFnSig, Predicate, Region, TraitRef, TypeAliasIdWrapper, Unnormalized, abi::Safety, - impl_foldable_for_interned_slice, impl_stored_interned, interned_slice, + impl_foldable_for_interned_slice, impl_foldable_for_stored_type, impl_stored_interned, + interned_slice, util::{CoroutineArgsExt, IntegerTypeExt}, }, }; @@ -61,6 +62,7 @@ pub(super) struct TyInterned(WithCachedTypeInfo>); impl_internable!(gc; TyInterned); impl_stored_interned!(TyInterned, Ty, StoredTy); +impl_foldable_for_stored_type!(StoredTy); const _: () = { const fn is_copy() {} @@ -894,15 +896,6 @@ impl<'db> TypeVisitable> for Ty<'db> { } } -impl<'db> TypeVisitable> for StoredTy { - fn visit_with>>( - &self, - visitor: &mut V, - ) -> V::Result { - self.as_ref().visit_with(visitor) - } -} - impl<'db> TypeSuperVisitable> for Ty<'db> { fn super_visit_with>>( &self, @@ -969,18 +962,6 @@ impl<'db> TypeFoldable> for Ty<'db> { } } -impl<'db> TypeFoldable> for StoredTy { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - Ok(self.as_ref().try_fold_with(folder)?.store()) - } - fn fold_with>>(self, folder: &mut F) -> Self { - self.as_ref().fold_with(folder).store() - } -} - impl<'db> TypeSuperFoldable> for Ty<'db> { fn try_super_fold_with>>( self, @@ -1422,6 +1403,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { interned_slice!(TysStorage, Tys, StoredTys, tys, Ty<'db>, Ty<'static>); impl_foldable_for_interned_slice!(Tys); +impl_foldable_for_stored_type!(StoredTys); impl<'db> Tys<'db> { #[inline] From a6a9f65c56f27188e39a001bd24dc991d777e5d7 Mon Sep 17 00:00:00 2001 From: Joshua Isika Date: Wed, 29 Jul 2026 10:34:59 +0300 Subject: [PATCH 27/94] Report a config error for postfix snippets with item scope Postfix custom snippets are never considered by completion when `scope` is `item` (only `Expr`-scoped postfix snippets are checked in completions::postfix), so this combination previously created a snippet that silently never fired. Validate the combination during deserialization of `SnippetDef` via `#[serde(try_from = "SnippetDefRepr")]`. Both the client JSON config and `rust-analyzer.toml` configs deserialize a `SnippetDef` per map entry, so this covers both config sources with one check. Trade-off: because the check now runs during `FxIndexMap` deserialization, one invalid entry fails deserialization of the whole custom-snippets map, falling back to the built-in default snippets rather than dropping only the bad entry. This matches how any other structurally invalid `SnippetDef` field already behaves. Fixes rust-lang/rust-analyzer#22894 --- .../crates/rust-analyzer/src/config.rs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 5b6215c41f384..3ba0e47f14bcb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -2908,6 +2908,7 @@ enum SnippetScopeDef { #[derive(Serialize, Deserialize, Debug, Clone, Default)] #[serde(default)] +#[serde(try_from = "SnippetDefRepr")] pub(crate) struct SnippetDef { #[serde(with = "single_or_array")] #[serde(skip_serializing_if = "Vec::is_empty")] @@ -2931,6 +2932,46 @@ pub(crate) struct SnippetDef { scope: SnippetScopeDef, } +/// Plain deserialization target for [`SnippetDef`]. Both the client JSON +/// config and `rust-analyzer.toml` configs deserialize a `SnippetDef` per +/// map entry, so validating the field combination here (via `TryFrom`) +/// covers both config sources instead of only one. +#[derive(Deserialize, Default)] +#[serde(default)] +struct SnippetDefRepr { + #[serde(with = "single_or_array")] + prefix: Vec, + #[serde(with = "single_or_array")] + postfix: Vec, + #[serde(with = "single_or_array")] + body: Vec, + #[serde(with = "single_or_array")] + requires: Vec, + description: Option, + scope: SnippetScopeDef, +} + +impl TryFrom for SnippetDef { + type Error = String; + + fn try_from(repr: SnippetDefRepr) -> Result { + if repr.scope == SnippetScopeDef::Item && !repr.postfix.is_empty() { + return Err( + "'postfix' is not supported together with '\"scope\": \"item\"'; postfix snippets are not supported in item scope" + .to_owned(), + ); + } + Ok(SnippetDef { + prefix: repr.prefix, + postfix: repr.postfix, + body: repr.body, + requires: repr.requires, + description: repr.description, + scope: repr.scope, + }) + } +} + mod single_or_array { use serde::{Deserialize, Serialize}; @@ -4428,4 +4469,30 @@ mod tests { == Some(Utf8PathBuf::from("other_folder")) )); } + #[test] + fn postfix_snippet_item_scope_is_invalid() { + let mut config = + Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None); + let mut change = ConfigChange::default(); + change.change_client_config(serde_json::json!({ + "completion":{ + "snippets": { + "custom":{ + "foo": { + "postfix": "foo", + "body": "foo", + "scope": "item" + } + } + } + } + })); + let errors; + (config, errors, _) = config.apply_change(change); + assert!(!errors.0.is_empty(), "expected a config error for postfix+item scope"); + assert!( + config.snippets.iter().all(|s| s.postfix_triggers.iter().all(|t| &**t != "foo")), + "invalid snippet should not have been registered" + ); + } } From c5ccd5b3e5fdb32a4a44a76a5d6e053ab7ca962b Mon Sep 17 00:00:00 2001 From: Kivanc Gunalp Date: Wed, 29 Jul 2026 07:34:44 +0000 Subject: [PATCH 28/94] hir-ty, ide-diagnostics: use E0057 vs E0061 for arg-count mismatch The MismatchedArgCount diagnostic previously used code E0107, which is actually 'wrong number of generic arguments'. Split it based on how the call is made: - E0057 for calls through the Fn/FnMut/FnOnce traits (arguments bundled into a tuple via TupleArgumentsFlag::TupleArguments in the inference code) - E0061 for regular function calls This adds an is_fn_trait_call flag on InferenceDiagnostic::MismatchedArgCount and the hir-surface MismatchedArgCount struct, populated from the tuple_arguments flag already tracked by check_call_arguments. The downstream 'if !args_count_matches' push in infer/expr.rs already covers both paths, so the two FIXMEs at the top of the tuple branch are addressed by threading the kind through rather than by adding a new push site. The nightly-only fallback FIXME below (E0059-ish) is left alone per discussion on rust-lang/rust-analyzer#22140. Adds a test 'arg_count_multi_arg_closure' that exercises the multi-argument tuple case via a closure with signature |_a: u8, _b: u8|. This complements the existing 'arg_count_lambda' test (1-tuple case). Refs rust-lang/rust-analyzer#22140 --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 5 ++++ .../crates/hir-ty/src/infer/expr.rs | 8 ++--- .../crates/hir/src/diagnostics.rs | 18 +++++++++-- .../src/handlers/mismatched_arg_count.rs | 30 ++++++++++++++++++- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index bd00da84cafad..4036722bafb3a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -410,6 +410,11 @@ pub enum InferenceDiagnostic { expected: usize, #[type_visitable(ignore)] found: usize, + /// True when the call goes through the `Fn`/`FnMut`/`FnOnce` trait + /// (i.e. arguments were bundled into a tuple). Determines whether the + /// diagnostic surface uses E0057 (Fn-trait call) or E0061 (regular call). + #[type_visitable(ignore)] + is_fn_trait_call: bool, }, MismatchedTupleStructPatArgCount { #[type_visitable(ignore)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 20cfc9008a9e1..570df6b871df1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -2015,10 +2015,9 @@ impl<'db> InferenceContext<'db> { match tuple_type.kind() { // We expected a tuple and got a tuple TyKind::Tuple(arg_types) => { - // Argument length differs - if arg_types.len() != provided_args.len() { - // FIXME: Emit an error. - } + // Argument length differs. The mismatch is reported below by the + // shared `MismatchedArgCount` push (with `is_fn_trait_call = true`, + // which the diagnostic surface renders as E0057). let expected_input_tys = match expected_input_tys { Some(expected_input_tys) => match expected_input_tys.first() { Some(ty) => match ty.kind() { @@ -2068,6 +2067,7 @@ impl<'db> InferenceContext<'db> { call_expr, expected: expected_input_tys.len() + skip_indices.len(), found: provided_args.len(), + is_fn_trait_call: tuple_arguments == TupleArgumentsFlag::TupleArguments, }); } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index d0801d8efd03e..0c4191ce2dd6f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -441,6 +441,9 @@ pub struct MismatchedArgCount { pub call_expr: InFile, pub expected: usize, pub found: usize, + /// True when the call is through a `Fn`/`FnMut`/`FnOnce` trait (E0057) + /// rather than a regular function call (E0061). + pub is_fn_trait_call: bool, } #[derive(Debug)] @@ -885,9 +888,18 @@ impl<'db> AnyDiagnostic<'db> { }; DuplicateField { field: expr_or_pat, variant: variant.into() }.into() } - &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { - MismatchedArgCount { call_expr: expr_syntax(call_expr)?, expected, found }.into() - } + &InferenceDiagnostic::MismatchedArgCount { + call_expr, + expected, + found, + is_fn_trait_call, + } => MismatchedArgCount { + call_expr: expr_syntax(call_expr)?, + expected, + found, + is_fn_trait_call, + } + .into(), &InferenceDiagnostic::PrivateField { expr, field } => { let expr = expr_syntax(expr)?; let field = field.into(); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index 844431c1e5c46..fb9095e0f4bd2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -38,8 +38,12 @@ pub(crate) fn mismatched_arg_count( ) -> Diagnostic { let s = if d.expected == 1 { "" } else { "s" }; let message = format!("expected {} argument{s}, found {}", d.expected, d.found); + // E0057 is the code rustc emits when calling something via the `Fn`/`FnMut`/`FnOnce` + // traits with the wrong number of arguments; E0061 is used for direct function calls. + // (Previously this used E0107, which is actually "wrong number of generic arguments".) + let code = if d.is_fn_trait_call { "E0057" } else { "E0061" }; Diagnostic::new( - DiagnosticCode::RustcHardError("E0107"), + DiagnosticCode::RustcHardError(code), message, invalid_args_range(ctx, d.call_expr, d.expected, d.found), ) @@ -395,6 +399,30 @@ fn main() { ) } + // A multi-argument closure exercises the same tuple-arguments code path in + // hir-ty (`TupleArgumentsFlag::TupleArguments` in `crates/hir-ty/src/infer/expr.rs`) + // as calls through `Fn`/`FnMut`/`FnOnce`. The mismatch is reported with error + // code E0057 (rustc's Fn-trait code), not E0061 which is reserved for direct + // function calls. `arg_count_lambda` above covers the 1-tuple case; this one + // covers the multi-argument case to make sure the tuple size is reported + // correctly. + #[test] + fn arg_count_multi_arg_closure() { + check_diagnostics( + r#" +//- minicore: fn +fn main() { + let f = |_a: u8, _b: u8| (); + f(); + //^^ error: expected 2 arguments, found 0 + f(1, 2); + f(1, 2, 3); + //^^ error: expected 2 arguments, found 3 +} +"#, + ) + } + #[test] fn cfgd_out_call_arguments() { check_diagnostics( From d1bfa493fcdcb628f4084d7441b5029f8da5eef0 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 29 Jul 2026 13:27:47 +0200 Subject: [PATCH 29/94] Implement `slice_get_unchecked` mir shim --- .../crates/hir-ty/src/mir/eval/shim.rs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index e569b32bd779f..177b2c870f084 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -1012,6 +1012,43 @@ impl<'a, 'db> Evaluator<'a, 'db> { let dst = Interval { addr: dst, size }; dst.write_from_interval(self, src) } + "slice_get_unchecked" => { + let [slice_ptr, index] = args else { + return Err(MirEvalError::InternalError( + "slice_get_unchecked args are not provided".into(), + )); + }; + let Some(ty) = generic_args.as_slice().get(2).and_then(|it| it.ty()) else { + return Err(MirEvalError::InternalError( + "slice_get_unchecked item type is not provided".into(), + )); + }; + let slice_ptr = slice_ptr.get(self)?; + let ptr_size = self.ptr_size(); + let Some(data) = slice_ptr.get(..ptr_size) else { + return Err(MirEvalError::InternalError( + "slice_get_unchecked slice pointer is too small".into(), + )); + }; + let Some(len) = slice_ptr.get(ptr_size..2 * ptr_size) else { + return Err(MirEvalError::InternalError( + "slice_get_unchecked slice metadata is missing".into(), + )); + }; + let slice_ptr = Address::from_bytes(data)?; + let len = from_bytes!(usize, len); + let index = from_bytes!(usize, index.get(self)?); + if index >= len { + return Err(MirEvalError::UndefinedBehavior(format!( + "slice_get_unchecked index {index} is out of bounds for slice of length {len}" + ))); + } + let size = self.size_of_sized(ty, locals, "slice_get_unchecked item type")?; + let offset = index* size; + let addr = slice_ptr.to_usize() + offset; + let addr = Address::from_usize(addr); + destination.write_from_bytes(self, &addr.to_bytes()[..destination.size]) + } "offset" | "arith_offset" => { let [ptr, offset] = args else { return Err(MirEvalError::InternalError("offset args are not provided".into())); From 945c8fa4ee57b1a21ee27b8853d94f02553fcb79 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 29 Jul 2026 13:27:47 +0200 Subject: [PATCH 30/94] Recursively patch addresses of slices --- .../crates/hir-ty/src/mir/eval.rs | 28 ++++- .../crates/hir-ty/src/mir/eval/tests.rs | 103 ++++++++++++++++++ 2 files changed, 127 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index e968da5111add..b08381603470c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -2548,7 +2548,6 @@ impl<'a, 'db> Evaluator<'a, 'db> { ty: Ty<'db>, locals: &Locals<'a, 'db>, ) -> Result<'db, ()> { - // FIXME: support indirect references let layout = self.layout(ty)?; let my_size = self.size_of_sized(ty, locals, "value to patch address")?; use rustc_type_ir::TyKind; @@ -2574,9 +2573,30 @@ impl<'a, 'db> Evaluator<'a, 'db> { )?; } None => { - let current = from_bytes!(usize, self.read_memory(addr, my_size / 2)?); - if let Some(it) = patch_map.get(¤t) { - self.write_memory(addr, &it.to_le_bytes())?; + let bytes = self.read_memory(addr, my_size)?; + let (current, metadata) = bytes.split_at(my_size / 2); + let metadata = metadata.to_vec(); + let current = from_bytes!(usize, current); + let patched = match patch_map.get(¤t) { + Some(it) => { + self.write_memory(addr, &it.to_le_bytes())?; + *it + } + None => current, + }; + let patched = Address::from_usize(patched); + if let TyKind::Slice(inner) = t.kind() { + let len = from_bytes!(usize, metadata); + let size = self.size_of_sized(inner, locals, "slice item to patch")?; + for i in 0..len { + self.patch_addresses( + patch_map, + ty_of_bytes, + patched.offset(i * size), + inner, + locals, + )?; + } } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 68d19769d4811..7431ac8293e97 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -1114,6 +1114,109 @@ fn main() { ); } +#[test] +fn slice_get_unchecked_intrinsic() { + check_pass( + r#" +//- minicore: panic +#[rustc_intrinsic] +unsafe fn slice_get_unchecked( + slice_ptr: SlicePtr, + index: usize, +) -> ItemPtr; + +fn should_not_reach() { panic!() } + +fn main() { + let values = [10, 20, 30]; + let slice_ptr = &values as *const [i32]; + let item_ptr = unsafe { + slice_get_unchecked::<*const i32, *const [i32], i32>(slice_ptr, 1) + }; + if unsafe { *item_ptr } != 20 { + should_not_reach(); + } +} +"#, + ); +} + +#[test] +fn slice_get_unchecked_out_of_bounds() { + check_error_with( + r#" +#[rustc_intrinsic] +unsafe fn slice_get_unchecked( + slice_ptr: SlicePtr, + index: usize, +) -> ItemPtr; + +fn main() { + let values = [()]; + let slice_ptr = &values as *const [()]; + let _item = unsafe { + slice_get_unchecked::<*const (), *const [()], ()>(slice_ptr, 1) + }; +} +"#, + |e| { + let mut err = &e; + while let MirEvalError::InFunction(inner, _) = err { + err = inner; + } + matches!(err, MirEvalError::UndefinedBehavior(_)) + }, + ); +} + +#[test] +fn slice_get_unchecked_const_slice() { + check_pass( + r#" +//- minicore: panic +#[rustc_intrinsic] +unsafe fn slice_get_unchecked( + slice_ptr: SlicePtr, + index: usize, +) -> ItemPtr; + +struct Flag { + name: &'static str, + value: u16, +} + +const PURE: &str = "PURE"; +const NOMEM: &str = "NOMEM"; +const READONLY: &str = "READONLY"; +const PRESERVES_FLAGS: &str = "PRESERVES_FLAGS"; +const NORETURN: &str = "NORETURN"; +const NOSTACK: &str = "NOSTACK"; +const ATT_SYNTAX: &str = "ATT_SYNTAX"; +const FLAGS: &[Flag] = &[ + Flag { name: PURE, value: 1 }, + Flag { name: NOMEM, value: 2 }, + Flag { name: READONLY, value: 4 }, + Flag { name: PRESERVES_FLAGS, value: 8 }, + Flag { name: NORETURN, value: 16 }, + Flag { name: NOSTACK, value: 32 }, + Flag { name: ATT_SYNTAX, value: 64 }, +]; + +fn should_not_reach() { panic!() } + +fn main() { + let flag = unsafe { + slice_get_unchecked::<&Flag, &[Flag], Flag>(FLAGS, 6) + }; + let name = flag.name as *const str as *const u8; + if unsafe { *name } != b'A' || flag.value != 64 { + should_not_reach(); + } +} +"#, + ); +} + #[test] fn unreachable_intrinsic() { check_error_with( From 7fab15a8e2b35fcb641c84bdab29e8ec9f66e90d Mon Sep 17 00:00:00 2001 From: edragain Date: Tue, 28 Jul 2026 11:57:02 +0000 Subject: [PATCH 31/94] fix: avoid escaping bound vars in closure MIR parameter types add minimal reproduce test code and use liberated closure sig to fix --- .../crates/hir-ty/src/mir/lower.rs | 6 ++-- .../crates/hir-ty/src/mir/lower/tests.rs | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 1c7e5c2f51396..620d768cff422 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -53,7 +53,6 @@ use crate::{ next_solver::{ Const, DbInterner, ParamConst, ParamEnv, Region, StoredGenericArgs, StoredTy, TyKind, TypingMode, UnevaluatedConst, - abi::Safety, infer::{DbInternerInferExt, InferCtxt}, }, }; @@ -2147,11 +2146,10 @@ pub fn mir_body_for_closure_query<'db>( .store(), }); ctx.result.param_locals.push(closure_local); - - let sig = ctx.interner().signature_unclosure(substs.as_closure().sig(), Safety::Safe); + let sig = infer.closures_data[&expr].liberated_sig.get(); let resolver_guard = ctx.resolver.update_to_inner_scope(db, ctx.store_owner, expr); let current = ctx.lower_params_and_bindings( - args.iter().zip(sig.skip_binder().inputs().iter()).map(|(it, y)| (*it, *y)), + args.iter().zip(sig.inputs().iter()).map(|(it, y)| (*it, *y)), None, |_| true, )?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs index 8eb0a02694ce7..fdd67fc4fb626 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs @@ -150,3 +150,31 @@ fn caller(path: &PathBuf) { "#, ); } + +#[test] +fn borrowck_hrtb_closure_argument_does_not_panic() { + check_borrowck( + r#" +//- minicore: fn, copy +enum Res { + Ok(T), + Err(E), +} + +struct S; + +impl S { + fn set(&mut self, _: F) + where + F: for<'a> Fn(&mut (), &'a [u8]) -> Res<(), ()>, + { + } +} + +fn main() { + let mut s = S; + s.set(|_, _| Res::Err(())); +} + "#, + ); +} From 56a1c0d458d80bae94bc6b5cc4f9b78109c5e31a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 29 Jul 2026 22:08:42 +0300 Subject: [PATCH 32/94] Double stack size for threads to 16MiB This does not impact memory usage since this is only reserved, not committed, memory; it could crash if we allocate more than the commit limit and overcommit is disabled but for such small numbers it practically can't happen. This is an alternative to using `stacker` to grow the stack on-demand; rustc is transitioning to the same model - https://github.com/rust-lang/compiler-team/issues/1011. 16MiB was chosen because when that was chosen as the limit in rustc (https://github.com/rust-lang/rust/pull/158759), crater succeeded for practically all crates, so it should be enough for us as well. It's also two times the current number (8MiB) which is a lot. --- .../rust-analyzer/crates/rust-analyzer/src/bin/main.rs | 6 ++---- .../crates/rust-analyzer/src/cli/diagnostics.rs | 3 --- .../crates/rust-analyzer/src/cli/unresolved_references.rs | 3 --- src/tools/rust-analyzer/crates/stdx/src/thread.rs | 8 +++++++- src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs | 2 -- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index 6bd27c2621978..d3b2030205aa7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -166,8 +166,6 @@ fn setup_logging(log_file_flag: Option) -> anyhow::Result<()> { Ok(()) } -const STACK_SIZE: usize = 1024 * 1024 * 8; - /// Parts of rust-analyzer can use a lot of stack space, and some operating systems only give us /// 1 MB by default (eg. Windows), so this spawns a new thread with hopefully sufficient stack /// space. @@ -176,8 +174,7 @@ fn with_extra_thread( thread_intent: stdx::thread::ThreadIntent, f: impl FnOnce() -> anyhow::Result<()> + Send + 'static, ) -> anyhow::Result<()> { - let handle = - stdx::thread::Builder::new(thread_intent, thread_name).stack_size(STACK_SIZE).spawn(f)?; + let handle = stdx::thread::Builder::new(thread_intent, thread_name).spawn(f)?; handle.join()?; @@ -189,6 +186,7 @@ fn run_server(startup_notice: Option) -> anyhow::Result<()> { rayon::ThreadPoolBuilder::new() .thread_name(|ix| format!("RayonWorker{}", ix)) + .stack_size(stdx::thread::DEFAULT_STACK_SIZE) .build_global() .unwrap(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs index e50e1c26bb971..8e24e0bd2ea06 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs @@ -13,13 +13,10 @@ use crate::cli::{flags, progress_report::ProgressReport}; impl flags::Diagnostics { pub fn run(self) -> anyhow::Result<()> { - const STACK_SIZE: usize = 1024 * 1024 * 8; - let handle = stdx::thread::Builder::new( stdx::thread::ThreadIntent::LatencySensitive, "BIG_STACK_THREAD", ) - .stack_size(STACK_SIZE) .spawn(|| self.run_()) .unwrap(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs index f8eacbb670587..d9a56098bd9dc 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs @@ -11,13 +11,10 @@ use crate::cli::flags; impl flags::UnresolvedReferences { pub fn run(self) -> anyhow::Result<()> { - const STACK_SIZE: usize = 1024 * 1024 * 8; - let handle = stdx::thread::Builder::new( stdx::thread::ThreadIntent::LatencySensitive, "BIG_STACK_THREAD", ) - .stack_size(STACK_SIZE) .spawn(|| self.run_()) .unwrap(); diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread.rs b/src/tools/rust-analyzer/crates/stdx/src/thread.rs index 37b7a9f5edfa8..0000fd1954d40 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/thread.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/thread.rs @@ -34,6 +34,8 @@ where Builder::new(intent, name).spawn(f).expect("failed to spawn thread") } +pub const DEFAULT_STACK_SIZE: usize = 16 * 1024 * 1024; + pub struct Builder { intent: ThreadIntent, inner: jod_thread::Builder, @@ -43,7 +45,11 @@ pub struct Builder { impl Builder { #[must_use] pub fn new(intent: ThreadIntent, name: impl Into) -> Self { - Self { intent, inner: jod_thread::Builder::new().name(name.into()), allow_leak: false } + Self { + intent, + inner: jod_thread::Builder::new().name(name.into()).stack_size(DEFAULT_STACK_SIZE), + allow_leak: false, + } } #[must_use] diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs index 918b88d960f1a..1ef6954e5a6ce 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs @@ -45,7 +45,6 @@ impl Pool { /// Panics if job panics #[must_use] pub fn new(threads: usize) -> Self { - const STACK_SIZE: usize = 8 * 1024 * 1024; const INITIAL_INTENT: ThreadIntent = ThreadIntent::Worker; let (job_sender, job_receiver) = crossbeam_channel::unbounded(); @@ -54,7 +53,6 @@ impl Pool { let mut handles = Vec::with_capacity(threads); for idx in 0..threads { let handle = Builder::new(INITIAL_INTENT, format!("Worker{idx}",)) - .stack_size(STACK_SIZE) .allow_leak(true) .spawn({ let extant_tasks = Arc::clone(&extant_tasks); From 97c95c6b2bc4a5e8408acfe400a1b7ecc2c328a1 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 30 Jul 2026 04:02:24 +0800 Subject: [PATCH 33/94] fix: no hint with similar name raw-ident arg Example --- **Before this PR** ```rust fn faz(r#loop: u32) {} fn main() { faz(r#loop); } //^^^^^^ r#loop ``` **After this PR** ```rust fn faz(r#loop: u32) {} fn main() { faz(r#loop); } ``` --- .../rust-analyzer/crates/ide/src/inlay_hints/param_name.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index f1689e5f9dc8a..5da8f2e1624a3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -295,6 +295,7 @@ pub(super) fn is_argument_similar_to_param_name( debug_assert!(!param_name.is_empty()); let param_name = param_name.split('_'); let argument = argument.iter().flat_map(|it| it.text_non_mutable().split('_')); + let argument = argument.map(|it| it.strip_prefix("r#").unwrap_or(it)); let prefix_match = zip(argument.clone(), param_name.clone()) .all(|(arg, param)| arg.eq_ignore_ascii_case(param)); @@ -710,9 +711,12 @@ fn main() { let param_eter2 = 0; bar(param_eter2); //^^^^^^^^^^^ param_eter + let r#loop = true; let loop_level = 0; far(loop_level); faz(loop_level); + far(r#loop); + faz(r#loop); non_ident_pat((0, 0)); From 883659d2e945a6eb44499a17da6ff6029d95516c Mon Sep 17 00:00:00 2001 From: Nico Lehmann Date: Wed, 29 Jul 2026 19:52:20 -0400 Subject: [PATCH 34/94] Revert "Remove lockfile-path support for Cargo versions below 1.94.0" This reverts commit 05fcbfe1806fdfc0ba5c333c1929cf7869ad8a7f. --- .../crates/project-model/src/build_dependencies.rs | 4 ++++ .../crates/project-model/src/cargo_config_file.rs | 13 +++++++++++++ .../crates/project-model/src/cargo_workspace.rs | 4 ++++ 3 files changed, 21 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 926a9e327e8c4..9f84f632d5e44 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 @@ -473,6 +473,10 @@ impl WorkspaceBuildScripts { if let Some(lockfile_copy) = &lockfile_copy { requires_unstable_options = true; match lockfile_copy.usage { + LockfileUsage::WithFlag => { + cmd.arg("--lockfile-path"); + cmd.arg(lockfile_copy.path.as_str()); + } LockfileUsage::WithEnvVarUnstable => { cmd.arg("-Zlockfile-path"); cmd.env( 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 a6bfea8200c53..defd9f96ab5fb 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 @@ -143,6 +143,8 @@ pub(crate) struct LockfileCopy { } pub(crate) enum LockfileUsage { + /// Rust [1.82.0, 1.95.0). `cargo --lockfile-path ` + WithFlag, /// Rust [1.95.0, 1.97.0). `CARGO_RESOLVER_LOCKFILE_PATH= cargo -Zlockfile-path ` WithEnvVarUnstable, /// Rust >= 1.97.0. `CARGO_RESOLVER_LOCKFILE_PATH= cargo ` @@ -153,6 +155,15 @@ pub(crate) fn make_lockfile_copy( toolchain_version: &semver::Version, lockfile_path: &Utf8Path, ) -> 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_UNSTABLE: semver::Version = semver::Version { major: 1, @@ -176,6 +187,8 @@ pub(crate) fn make_lockfile_copy( } else if *toolchain_version >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV_UNSTABLE { LockfileUsage::WithEnvVarUnstable + } else if *toolchain_version >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_FLAG { + LockfileUsage::WithFlag } else { return None; }; 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 3db5a0fffce7f..97375fe9ddd1a 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 @@ -767,6 +767,10 @@ impl FetchMetadata { let mut using_lockfile_copy = false; 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::WithEnvVarUnstable => { other_options.push("-Zlockfile-path".to_owned()); command.env("CARGO_RESOLVER_LOCKFILE_PATH", lockfile_copy.path.as_os_str()); From c828771fc5c7b8b1299d02cd28c51901601eef5b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 30 Jul 2026 03:40:13 +0300 Subject: [PATCH 35/94] Support `CovariantUnsafeCell` --- .../rust-analyzer/crates/hir-def/src/signatures.rs | 7 ++++++- src/tools/rust-analyzer/crates/hir-ty/src/variance.rs | 10 +++++++++- .../rust-analyzer/crates/intern/src/symbol/symbols.rs | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 10a38ec71e372..8c097c8b0ac6b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -52,7 +52,7 @@ pub struct StructSignature { bitflags! { #[derive(Debug, Copy, Clone, PartialEq, Eq)] - pub struct StructFlags: u8 { + pub struct StructFlags: u16 { /// Indicates whether this struct has `#[repr]`. const HAS_REPR = 1 << 0; /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute. @@ -69,6 +69,8 @@ bitflags! { const IS_UNSAFE_CELL = 1 << 6; /// Indicates whether this struct is `UnsafePinned`. const IS_UNSAFE_PINNED = 1 << 7; + /// Indicates whether this struct is `CovariantUnsafeCell`. + const IS_COVARIANT_UNSAFE_CELL = 1 << 8; } } @@ -104,6 +106,9 @@ impl StructSignature { _ if lang == sym::owned_box => flags |= StructFlags::IS_BOX, _ if lang == sym::manually_drop => flags |= StructFlags::IS_MANUALLY_DROP, _ if lang == sym::unsafe_cell => flags |= StructFlags::IS_UNSAFE_CELL, + _ if lang == sym::covariant_unsafe_cell => { + flags |= StructFlags::IS_COVARIANT_UNSAFE_CELL + } _ if lang == sym::unsafe_pinned => flags |= StructFlags::IS_UNSAFE_PINNED, _ => (), } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index 9e04353087760..2690297283988 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -49,7 +49,9 @@ fn variances_of_query(db: &dyn HirDatabase, def: GenericDefId) -> StoredVariance let types = || crate::next_solver::default_types(db); if flags.contains(StructFlags::IS_UNSAFE_CELL) { return types().one_invariant.store(); - } else if flags.contains(StructFlags::IS_PHANTOM_DATA) { + } else if flags.intersects( + StructFlags::IS_PHANTOM_DATA | StructFlags::IS_COVARIANT_UNSAFE_CELL, + ) { return types().one_covariant.store(); } } @@ -433,6 +435,7 @@ struct Covariant { check( r#" //- minicore: cell +#![feature(lang_items)] use core::cell::UnsafeCell; @@ -461,6 +464,10 @@ enum Enum { //~ ERROR [A: +, B: -, C: o] Bar(Contravariant),` Zed(Covariant,Contravariant) } + +#[repr(transparent)] +#[lang = "covariant_unsafe_cell"] +pub struct CovariantUnsafeCell(UnsafeCell); //~ ERROR [T: +] "#, expect![[r#" InvariantMut['a: covariant, A: invariant, B: invariant] @@ -469,6 +476,7 @@ enum Enum { //~ ERROR [A: +, B: -, C: o] Covariant[A: covariant] Contravariant[A: contravariant] Enum[A: covariant, B: contravariant, C: invariant] + CovariantUnsafeCell[T: covariant] "#]], ); } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index fe303aa0e0c36..9a566ee687e0d 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -597,6 +597,7 @@ define_symbols! { unreachable_2021, unreachable, unsafe_cell, + covariant_unsafe_cell, unsafe_pinned, unsize, unstable, From def682350eefd030b9faa560e4d9b46ad2cca26b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 30 Jul 2026 20:05:09 +0530 Subject: [PATCH 36/94] Add comment on replacements_are_disjoint method and better upmap syntax --- .../crates/syntax/src/syntax_editor/edit_algo.rs | 3 +++ .../crates/syntax/src/syntax_editor/mapping.rs | 7 +++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs index 71b03784e981e..d24d9b1334dec 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -200,6 +200,9 @@ impl EditPlan { } /// Checks that replacement at the same tree depth do not overlap + /// + /// `changes` is sorted by range start, so overlap is a single comparison against the + /// last range at that key, and `insert` can throw away the range it evicts. fn replacements_are_disjoint( changes: &[Change], mut node_depth: impl FnMut(SyntaxNode) -> usize, diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs index d6498a5ec93fb..17319368afc32 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs @@ -135,12 +135,11 @@ impl SyntaxMapping { SyntaxElement::Node(node) => node.clone(), SyntaxElement::Token(token) => token.parent().unwrap(), }; - let Some(input_ancestor) = - node.ancestors().find(|ancestor| self.upmap_node_single(ancestor).is_some()) - else { + let Some((input_ancestor, output_ancestor)) = node.ancestors().find_map(|ancestor| { + self.upmap_node_single(&ancestor).map(|output_ancestor| (ancestor, output_ancestor)) + }) else { return current; }; - let output_ancestor = self.upmap_node_single(&input_ancestor).unwrap(); current = self .upmap_child_element(¤t, &input_ancestor, &output_ancestor.parent().unwrap()) .expect("the nearest mapped ancestor must map its descendants"); From 1d5bcea38afead533bd9ec2716d22d872eaf0af4 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 31 Jul 2026 02:38:19 +0300 Subject: [PATCH 37/94] Avoid having a separate query for defined opaques I want to push towards the goal of only lowering once. This helps perf, but more importantly this makes defining `AnonConst` a tracked struct instead of interned possible, as different queries won't create the same anon const. --- .../rust-analyzer/crates/hir-ty/src/db.rs | 17 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 6 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 211 +++++++----------- .../crates/hir-ty/src/opaques.rs | 25 +-- .../crates/hir-ty/src/tests/incremental.rs | 18 +- 5 files changed, 108 insertions(+), 169 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 8e7e55a77b349..9853f174eb76b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -27,7 +27,7 @@ use crate::{ consteval::ConstEvalError, dyn_compatibility::DynCompatibilityViolation, layout::{Layout, LayoutError}, - lower::{GenericDefaults, TrackedStructToken, TypeAliasBounds}, + lower::{GenericDefaults, TrackedStructToken, TypeAliasBounds, WithDefinedOpaques}, mir::{MirBody, MirLowerError}, next_solver::{ Allocation, Clause, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredClauses, @@ -172,7 +172,7 @@ pub trait HirDatabase: SourceDatabase + 'static { fn type_for_type_alias_with_diagnostics<'db>( &'db self, def: TypeAliasId, - ) -> &'db TyLoweringResult<'db, StoredEarlyBinder> { + ) -> &'db TyLoweringResult<'db, WithDefinedOpaques>> { let db = self.as_dyn(); crate::lower::type_for_type_alias_with_diagnostics(db, def) } @@ -275,12 +275,12 @@ pub trait HirDatabase: SourceDatabase + 'static { crate::lower::callable_item_signature(db, def) } - fn callable_item_signature_with_diagnostics<'db>( + fn fn_sig_for_fn_with_diagnostics<'db>( &'db self, - def: CallableDefId, - ) -> &'db TyLoweringResult<'db, StoredEarlyBinder> { + def: FunctionId, + ) -> &'db TyLoweringResult<'db, WithDefinedOpaques>> { let db = self.as_dyn(); - crate::lower::callable_item_signature_with_diagnostics(db, def) + crate::lower::fn_sig_for_fn(db, def) } fn trait_environment<'db>(&'db self, def: GenericDefId) -> ParamEnv<'db> { @@ -513,8 +513,9 @@ impl<'db> AnonConstId<'db> { result.push(db.type_for_type_alias_with_diagnostics(id).defined_anon_consts()); result.push(db.type_alias_bounds_with_diagnostics(id).defined_anon_consts()); } - GenericDefId::FunctionId(id) => result - .push(db.callable_item_signature_with_diagnostics(id.into()).defined_anon_consts()), + GenericDefId::FunctionId(id) => { + result.push(db.fn_sig_for_fn_with_diagnostics(id).defined_anon_consts()) + } GenericDefId::ConstId(def) => { result.push(db.type_for_const_with_diagnostics(def).defined_anon_consts()) } 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 0dd558828fd7f..afd500a3e3343 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -111,9 +111,9 @@ pub use infer::{ infer_query_with_inspect, }; pub use lower::{ - FieldType, GenericDefaults, GenericDefaultsRef, GenericPredicates, ImplTraits, - LifetimeElisionKind, LifetimeLoweringMode, LoweringMode, TyDefId, TyLoweringContext, - TyLoweringInferVarsCtx, TyLoweringResult, ValueTyDefId, diagnostics::*, + FieldType, GenericDefaults, GenericDefaultsRef, GenericPredicates, LifetimeElisionKind, + LifetimeLoweringMode, LoweringMode, TyDefId, TyLoweringContext, TyLoweringInferVarsCtx, + TyLoweringResult, ValueTyDefId, diagnostics::*, }; pub use next_solver::interner::{attach_db, attach_db_allow_change, with_attached_db}; pub use target_feature::TargetFeatures; 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 bdb882b70b259..098a43a876a7e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -10,7 +10,6 @@ pub(crate) mod path; use std::{cell::OnceCell, iter, mem, sync::OnceLock}; -use base_db::salsa::update_fallback_db; use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, EnumId, EnumVariantId, @@ -80,13 +79,14 @@ use crate::{ pub(crate) struct PathDiagnosticCallbackData(pub(crate) TypeRefId); #[derive(PartialEq, Eq, Debug, Hash)] -pub struct ImplTraits { - pub(crate) impl_traits: Arena, +pub struct WithDefinedOpaques { + value: T, + impl_traits: Option>>, } #[derive(PartialEq, Eq, Debug, Hash)] pub struct ImplTrait { - pub(crate) predicates: StoredClauses, + pub(crate) predicates: StoredEarlyBinder, pub(crate) assoc_ty_bounds_start: u32, } @@ -417,6 +417,15 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { BoundVarKinds::new_from_iter(interner, args) } + + fn take_defined_opaques(&mut self) -> Option>> { + if self.impl_trait_mode.opaque_type_data.is_empty() { + None + } else { + self.impl_trait_mode.opaque_type_data.shrink_to_fit(); + Some(Box::new(mem::take(&mut self.impl_trait_mode.opaque_type_data))) + } + } } #[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] @@ -631,7 +640,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { // place even if we encounter more opaque types while // lowering the bounds let idx = self.impl_trait_mode.opaque_type_data.alloc(ImplTrait { - predicates: Clauses::empty(interner).store(), + predicates: StoredEarlyBinder::bind(Clauses::empty(interner).store()), assoc_ty_bounds_start: 0, }); @@ -1319,7 +1328,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.is_lowering_impl_trait_bounds = prev_is_lowering_impl_trait_bounds; ImplTrait { - predicates: Clauses::new_from_slice(&predicates).store(), + predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()), assoc_ty_bounds_start, } } @@ -1365,7 +1374,6 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { pub struct TyLoweringResult<'db, T> { #[update(fallback)] pub value: T, - #[update(bounds(TyLoweringResultInfo<'db>: Update), unsafe(with(update_fallback_db::<'db, _>)))] info: Option>>, } @@ -1497,19 +1505,21 @@ pub(crate) fn impl_trait_with_diagnostics_cycle_result<'db>( impl ImplTraitId { #[inline] - pub fn predicates<'db>(self, db: &'db dyn HirDatabase) -> EarlyBinder<'db, &'db [Clause<'db>]> { + fn data(self, db: &dyn HirDatabase) -> &ImplTrait { let (impl_traits, idx) = match self { ImplTraitId::ReturnTypeImplTrait(owner, idx) => { - (ImplTraits::return_type_impl_traits(db, owner), idx) + (ImplTrait::return_type_impl_traits(db, owner), idx) } ImplTraitId::TypeAliasImplTrait(owner, idx) => { - (ImplTraits::type_alias_impl_traits(db, owner), idx) + (ImplTrait::type_alias_impl_traits(db, owner), idx) } }; - impl_traits - .as_deref() - .expect("owner should have opaque type") - .get_with(|it| it.impl_traits[idx].predicates.as_ref().as_slice()) + &impl_traits[idx] + } + + #[inline] + pub fn predicates<'db>(self, db: &'db dyn HirDatabase) -> EarlyBinder<'db, &'db [Clause<'db>]> { + self.data(db).predicates.get().map_bound(|it| it.as_slice()) } #[inline] @@ -1517,24 +1527,8 @@ impl ImplTraitId { self, db: &'db dyn HirDatabase, ) -> EarlyBinder<'db, &'db [Clause<'db>]> { - let (impl_traits, idx) = match self { - ImplTraitId::ReturnTypeImplTrait(owner, idx) => { - (ImplTraits::return_type_impl_traits(db, owner), idx) - } - ImplTraitId::TypeAliasImplTrait(owner, idx) => { - (ImplTraits::type_alias_impl_traits(db, owner), idx) - } - }; - let predicates = - impl_traits.as_deref().expect("owner should have opaque type").get_with(|it| { - let impl_trait = &it.impl_traits[idx]; - ( - impl_trait.predicates.as_ref().as_slice(), - impl_trait.assoc_ty_bounds_start as usize, - ) - }); - - predicates.map_bound(|(preds, len)| &preds[..len]) + let data = self.data(db); + data.predicates.get().map_bound(|it| &it.as_slice()[..data.assoc_ty_bounds_start as usize]) } } @@ -1553,71 +1547,25 @@ impl InternedOpaqueTyId<'_> { } } -#[salsa::tracked] -impl ImplTraits { - #[salsa::tracked(returns(ref))] +impl ImplTrait { + #[inline] pub(crate) fn return_type_impl_traits( db: &dyn HirDatabase, - def: hir_def::FunctionId, - ) -> Option>> { - // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe - let data = FunctionSignature::of(db, def); - let resolver = def.resolver(db); - let generics = OnceCell::new(); - let mut ctx_ret = TyLoweringContext::new( - db, - &resolver, - &data.store, - ExpressionStoreOwnerId::Signature(def.into()), - def.into(), - &generics, - LifetimeElisionKind::Infer, - LifetimeLoweringMode::Bound, - ) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); - if let Some(ret_type) = data.ret_type { - let _ret = ctx_ret.lower_ty(ret_type); - } - let mut return_type_impl_traits = - ImplTraits { impl_traits: ctx_ret.impl_trait_mode.opaque_type_data }; - if return_type_impl_traits.impl_traits.is_empty() { - None - } else { - return_type_impl_traits.impl_traits.shrink_to_fit(); - Some(Box::new(StoredEarlyBinder::bind(return_type_impl_traits))) - } + def: FunctionId, + ) -> &Arena { + fn_sig_for_fn(db, def).value.impl_traits.as_deref().unwrap_or(const { &Arena::new() }) } - #[salsa::tracked(returns(ref))] + #[inline] pub(crate) fn type_alias_impl_traits( db: &dyn HirDatabase, - def: hir_def::TypeAliasId, - ) -> Option>> { - let data = TypeAliasSignature::of(db, def); - let resolver = def.resolver(db); - let generics = OnceCell::new(); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &data.store, - ExpressionStoreOwnerId::Signature(def.into()), - def.into(), - &generics, - LifetimeElisionKind::AnonymousReportError, - LifetimeLoweringMode::Bound, - ) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); - if let Some(type_ref) = data.ty { - let _ty = ctx.lower_ty(type_ref); - } - let mut type_alias_impl_traits = - ImplTraits { impl_traits: ctx.impl_trait_mode.opaque_type_data }; - if type_alias_impl_traits.impl_traits.is_empty() { - None - } else { - type_alias_impl_traits.impl_traits.shrink_to_fit(); - Some(Box::new(StoredEarlyBinder::bind(type_alias_impl_traits))) - } + def: TypeAliasId, + ) -> &Arena { + type_for_type_alias_with_diagnostics(db, def) + .value + .impl_traits + .as_deref() + .unwrap_or(const { &Arena::new() }) } } @@ -1666,7 +1614,7 @@ pub(crate) fn ty_query<'db>(db: &'db dyn HirDatabase, def: TyDefId) -> EarlyBind it, GenericArgs::identity_for_item(interner, it.into()), )), - TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).value.get(), + TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).value.value.get(), } } @@ -1804,13 +1752,14 @@ pub(crate) fn value_ty<'db>( pub(crate) fn type_for_type_alias_with_diagnostics<'db>( db: &'db dyn HirDatabase, t: TypeAliasId, -) -> TyLoweringResult<'db, StoredEarlyBinder> { +) -> TyLoweringResult<'db, WithDefinedOpaques>> { let type_alias_data = TypeAliasSignature::of(db, t); let interner = DbInterner::new_no_crate(db); if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) { - TyLoweringResult::empty(StoredEarlyBinder::bind( - Ty::new_foreign(interner, t.into()).store(), - )) + TyLoweringResult::empty(WithDefinedOpaques { + value: StoredEarlyBinder::bind(Ty::new_foreign(interner, t.into()).store()), + impl_traits: None, + }) } else { let resolver = t.resolver(db); let generics = OnceCell::new(); @@ -1832,7 +1781,10 @@ pub(crate) fn type_for_type_alias_with_diagnostics<'db>( .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) .store(), ); - TyLoweringResult::from_ctx(res, ctx) + TyLoweringResult::from_ctx( + WithDefinedOpaques { value: res, impl_traits: ctx.take_defined_opaques() }, + ctx, + ) } } @@ -1840,10 +1792,13 @@ pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result<'db>( db: &'db dyn HirDatabase, _: salsa::Id, _adt: TypeAliasId, -) -> TyLoweringResult<'db, StoredEarlyBinder> { - TyLoweringResult::empty(StoredEarlyBinder::bind( - Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(), - )) +) -> TyLoweringResult<'db, WithDefinedOpaques>> { + TyLoweringResult::empty(WithDefinedOpaques { + value: StoredEarlyBinder::bind( + Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(), + ), + impl_traits: None, + }) } pub(crate) fn impl_self_ty_query<'db>( @@ -2822,27 +2777,18 @@ pub(crate) fn callable_item_signature<'db>( db: &'db dyn HirDatabase, def: CallableDefId, ) -> EarlyBinder<'db, PolyFnSig<'db>> { - callable_item_signature_with_diagnostics(db, def).value.get() -} - -#[salsa::tracked(returns(ref))] -pub(crate) fn callable_item_signature_with_diagnostics<'db>( - db: &'db dyn HirDatabase, - def: CallableDefId, -) -> TyLoweringResult<'db, StoredEarlyBinder> { match def { - CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f), - CallableDefId::StructId(s) => TyLoweringResult::empty(fn_sig_for_struct_constructor(db, s)), - CallableDefId::EnumVariantId(e) => { - TyLoweringResult::empty(fn_sig_for_enum_variant_constructor(db, e)) - } + CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f).value.value.get(), + CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s).get(), + CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e).get(), } } -fn fn_sig_for_fn<'db>( +#[salsa::tracked(returns(ref))] +pub(crate) fn fn_sig_for_fn<'db>( db: &'db dyn HirDatabase, def: FunctionId, -) -> TyLoweringResult<'db, StoredEarlyBinder> { +) -> TyLoweringResult<'db, WithDefinedOpaques>> { let data = FunctionSignature::of(db, def); let resolver = def.resolver(db); let interner = DbInterner::new_no_crate(db); @@ -2874,6 +2820,7 @@ fn fn_sig_for_fn<'db>( Some(ret_type) => ctx_ret.lower_ty(ret_type), None => Ty::new_unit(interner), }; + let impl_traits = ctx_ret.take_defined_opaques(); let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret))); ctx_params.diagnostics.extend(ctx_ret.diagnostics); @@ -2891,7 +2838,7 @@ fn fn_sig_for_fn<'db>( }, binder, ))); - TyLoweringResult::from_ctx(result, ctx_params) + TyLoweringResult::from_ctx(WithDefinedOpaques { value: result, impl_traits }, ctx_params) } fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, Ty<'db>> { @@ -2901,13 +2848,14 @@ fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, T EarlyBinder::bind(ty) } -fn fn_sig_for_struct_constructor( +fn ctor_signature( db: &dyn HirDatabase, - def: StructId, + variant: VariantId, + adt: AdtId, ) -> StoredEarlyBinder { - let field_tys = db.field_types(def.into()); + let field_tys = db.field_types(variant); let params = field_tys.iter().map(|(_, field)| field.ty().skip_binder()); - let ret = type_for_adt(db, def.into()).skip_binder(); + let ret = type_for_adt(db, adt).skip_binder(); let inputs_and_output = Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret))); @@ -2917,21 +2865,20 @@ fn fn_sig_for_struct_constructor( }))) } +#[salsa::tracked(returns(ref))] +fn fn_sig_for_struct_constructor( + db: &dyn HirDatabase, + def: StructId, +) -> StoredEarlyBinder { + ctor_signature(db, def.into(), def.into()) +} + +#[salsa::tracked(returns(ref))] fn fn_sig_for_enum_variant_constructor( db: &dyn HirDatabase, def: EnumVariantId, ) -> StoredEarlyBinder { - let field_tys = db.field_types(def.into()); - let params = field_tys.iter().map(|(_, field)| field.ty().skip_binder()); - let parent = def.lookup(db).parent; - let ret = type_for_adt(db, parent.into()).skip_binder(); - - let inputs_and_output = - Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret))); - StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig { - fn_sig_kind: FnSigKind::new(ExternAbi::Rust, Safety::Safe, false), - inputs_and_output, - }))) + ctor_signature(db, def.into(), def.lookup(db).parent.into()) } // FIXME: Remove this. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs index 9cb0022ca6bfc..194c866c68056 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs @@ -5,14 +5,14 @@ use hir_def::{ signatures::ImplSignature, }; use hir_expand::name::Name; -use la_arena::ArenaMap; +use la_arena::{Arena, ArenaMap}; use rustc_type_ir::inherent::Ty as _; use syntax::ast; use crate::{ ImplTraitId, InferBodyId, InferenceResult, db::{HirDatabase, InternedOpaqueTyId}, - lower::{ImplTraitIdx, ImplTraits}, + lower::{ImplTrait, ImplTraitIdx}, next_solver::{ DbInterner, ErrorGuaranteed, SolverDefId, StoredEarlyBinder, StoredTy, Ty, TypingMode, infer::{DbInternerInferExt, traits::ObligationCause}, @@ -29,7 +29,7 @@ pub(crate) fn opaque_types_defined_by<'db>( // A function may define its own RPITs. extend_with_opaques( db, - ImplTraits::return_type_impl_traits(db, func), + ImplTrait::return_type_impl_traits(db, func), |opaque_idx| ImplTraitId::ReturnTypeImplTrait(func, opaque_idx), result, ); @@ -38,7 +38,7 @@ pub(crate) fn opaque_types_defined_by<'db>( let extend_with_taits = |type_alias| { extend_with_opaques( db, - ImplTraits::type_alias_impl_traits(db, type_alias), + ImplTrait::type_alias_impl_traits(db, type_alias), |opaque_idx| ImplTraitId::TypeAliasImplTrait(type_alias, opaque_idx), result, ); @@ -81,15 +81,13 @@ pub(crate) fn opaque_types_defined_by<'db>( fn extend_with_opaques<'db>( db: &'db dyn HirDatabase, - opaques: &Option>>, + opaques: &Arena, mut make_impl_trait: impl FnMut(ImplTraitIdx) -> ImplTraitId, result: &mut Vec>, ) { - if let Some(opaques) = opaques { - for (opaque_idx, _) in (**opaques).as_ref().skip_binder().impl_traits.iter() { - let opaque_id = InternedOpaqueTyId::new(db, make_impl_trait(opaque_idx)); - result.push(opaque_id.into()); - } + for (opaque_idx, _) in opaques.iter() { + let opaque_id = InternedOpaqueTyId::new(db, make_impl_trait(opaque_idx)); + result.push(opaque_id.into()); } } } @@ -116,12 +114,7 @@ pub(crate) fn tait_hidden_types( type_alias: TypeAliasId, ) -> ArenaMap> { // Call this first, to not perform redundant work if there are no TAITs. - let Some(taits_count) = ImplTraits::type_alias_impl_traits(db, type_alias) - .as_deref() - .map(|taits| taits.as_ref().skip_binder().impl_traits.len()) - else { - return ArenaMap::new(); - }; + let taits_count = ImplTrait::type_alias_impl_traits(db, type_alias).len(); let loc = type_alias.loc(db); let module = loc.module(db); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 08efef10ee78a..4574e095e91f1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -47,7 +47,7 @@ fn foo() -> i32 { "lang_items", "crate_lang_items", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "body_upvars_mentioned", ] @@ -136,7 +136,7 @@ fn baz() -> i32 { "lang_items", "crate_lang_items", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "body_upvars_mentioned", "InferenceResult < 'db >::for_body_", @@ -147,7 +147,7 @@ fn baz() -> i32 { "Body::with_source_map_", "trait_environment_query", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "body_upvars_mentioned", "InferenceResult < 'db >::for_body_", @@ -158,7 +158,7 @@ fn baz() -> i32 { "Body::with_source_map_", "trait_environment_query", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "body_upvars_mentioned", ] @@ -599,21 +599,20 @@ fn main() { "crate_lang_items", "GenericPredicates::query_with_diagnostics_", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "body_upvars_mentioned", "InferenceResult < 'db >::for_body_", "FunctionSignature::of_", "FunctionSignature::with_source_map_", "trait_environment_query", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "StructSignature::of_", "StructSignature::with_source_map_", "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "InherentImpls < 'db >::for_crate_", - "callable_item_signature_with_diagnostics", "TraitImpls < 'db >::for_crate_and_deps_", "TraitImpls < 'db >::for_crate_", "impl_trait_with_diagnostics", @@ -692,18 +691,17 @@ fn main() { "crate_lang_items", "GenericPredicates::query_with_diagnostics_", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "body_upvars_mentioned", "InferenceResult < 'db >::for_body_", "FunctionSignature::with_source_map_", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "StructSignature::with_source_map_", "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "InherentImpls < 'db >::for_crate_", - "callable_item_signature_with_diagnostics", "TraitImpls < 'db >::for_crate_", "ImplSignature::with_source_map_", "ImplSignature::of_", From 735a39d7292785fbde8668eceaf8f86298846b49 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 31 Jul 2026 01:19:30 +0300 Subject: [PATCH 38/94] Do not alloc anon consts for bare paths in blocks --- .../crates/hir-ty/src/consteval.rs | 17 ++++++--- .../rust-analyzer/crates/hir-ty/src/infer.rs | 3 -- .../crates/hir-ty/src/tests/regression.rs | 35 +++++++++++++++++++ .../crates/hir-ty/src/tests/simple.rs | 7 ++-- .../crates/ide/src/hover/tests.rs | 2 +- 5 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 15dd530312067..7aa6604ac09b8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -361,7 +361,7 @@ pub(crate) fn create_anon_const<'a, 'db>( interner: DbInterner<'db>, owner: ExpressionStoreOwnerId, store: &ExpressionStore, - expr: ExprId, + expr_id: ExprId, resolver: &Resolver<'db>, expected_ty: Ty<'db>, generics: &dyn Fn() -> &'a Generics<'db>, @@ -369,10 +369,19 @@ pub(crate) fn create_anon_const<'a, 'db>( lowering_mode: LoweringMode, forbid_params_after: Option, ) -> Result, CreateConstError<'db>> { - match &store[expr] { + let mut expr = &store[expr_id]; + if let Expr::Block { statements, tail: Some(tail), .. } = expr + && statements.is_empty() + { + // rustc unwraps *one* layer of blocks, so we do too (this impacts whether the const can use generic parameters. + // Anon consts sometimes cannot while bare paths can). mGCA allows arbitrarily many blocks, but we don't implement + // it yet. + expr = &store[*tail]; + } + match expr { Expr::Literal(literal) => intern_const_ref(interner, literal, expected_ty), Expr::Underscore => match create_var { - Some(create_var) => Ok(create_var(expr.into())), + Some(create_var) => Ok(create_var(expr_id.into())), None => Err(CreateConstError::UnderscoreExpr), }, Expr::Path(path) @@ -395,7 +404,7 @@ pub(crate) fn create_anon_const<'a, 'db>( interner.db, AnonConstLoc { owner, - expr, + expr: expr_id, ty: StoredEarlyBinder::bind(expected_ty.store()), allow_using_generic_params, }, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index a5a82209c6030..319a8ae9bd7fc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1085,10 +1085,7 @@ impl<'db> InferenceResult<'db> { fn for_body(db: &dyn HirDatabase, def: DefWithBodyId) -> InferenceResult<'_> { infer_query(db, def) } -} -#[salsa::tracked] -impl<'db> InferenceResult<'db> { /// Infer types for all const expressions in an item's signature. /// /// Returns an `InferenceResult` containing type information for array lengths, 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 2836f977a9ac5..683c938fb5a25 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 @@ -3028,3 +3028,38 @@ fn f() { "#, ); } + +#[test] +fn braced_const_path() { + check_types( + r#" +//- minicore: default, builtin_impls +trait ToNum { + type Num; +} +trait Bar { + type Ty; +} +struct Gen; +struct Int; + +impl ToNum for Gen<{ B }> { + type Num = Int; +} + +impl Bar for Int { + type Ty = i32; +} +impl Bar for Int { + type Ty = f32; +} + +type A = < as ToNum>::Num as Bar>::Ty; + +fn main() { + let x = A::default(); + // ^ i32 +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index e8f378db3228a..3cdfe4edcb908 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -4201,8 +4201,6 @@ fn foo() { 248..282 'LazyLo..._LOCK)': &'? [u32; 0] 264..281 '&VALUE...Y_LOCK': &'? LazyLock<[u32; 0]> 265..281 'VALUES...Y_LOCK': LazyLock<[u32; 0]> - 197..202 '{ 0 }': usize - 199..200 '0': usize "#]], ); } @@ -4308,9 +4306,8 @@ enum Enum { } "#, expect![[r#" - 29..34 '{ 2 }': usize - 31..32 '2': usize - "#]], + +"#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 89f1cf2fc1e11..f4335e227ff62 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -9508,7 +9508,7 @@ pub fn f(x$0: impl Tr<{ 0 }>) {} *x* ```rust - x: impl Tr<{const}> + ?Sized + x: impl Tr<0> + ?Sized ``` --- From 13b4a5ecf406f7e28da8901ef9f0775f3775ecbd Mon Sep 17 00:00:00 2001 From: Tyler Breisacher Date: Thu, 30 Jul 2026 17:35:01 -0700 Subject: [PATCH 39/94] Use format! instead of string --- src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs index bc3fa21c6658f..a79e78f3377b8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs @@ -174,7 +174,7 @@ impl CommandHandle { let mut child = child .spawn() .map(JodGroupChild) - .with_context(|| "Failed to spawn command: {child:?}")?; + .with_context(|| format!("Failed to spawn command: {child:?}"))?; let stdout = child.0.stdout().take().unwrap(); let stderr = child.0.stderr().take().unwrap(); From 036c57f1b2d74d4372369acb6e0ed6bb5210d4c5 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 07:35:31 +0530 Subject: [PATCH 40/94] Remove add_tabstop_after_token from source change --- .../rust-analyzer/crates/ide-db/src/source_change.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index 540b0ee99dd21..25d8ae097182b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -386,13 +386,7 @@ impl SourceChangeBuilder { assert!(token.parent().is_some()); self.add_snippet(PlaceSnippet::Before(token.into())); } - - /// Adds a tabstop snippet to place the cursor after `token` - pub fn add_tabstop_after_token(&mut self, _cap: SnippetCap, token: SyntaxToken) { - assert!(token.parent().is_some()); - self.add_snippet(PlaceSnippet::After(token.into())); - } - + /// Adds a snippet to move the cursor selected over `node` pub fn add_placeholder_snippet(&mut self, _cap: SnippetCap, node: impl AstNode) { assert!(node.syntax().parent().is_some()); From 5030272f2d0a37bccb7ff71f70752e7677c4e074 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 07:38:04 +0530 Subject: [PATCH 41/94] Remove add_tabstop_before_token and adapt generate_derive assist --- .../ide-assists/src/handlers/generate_derive.rs | 11 +++++------ .../rust-analyzer/crates/ide-db/src/source_change.rs | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs index f293e956bca5b..ba6bb5c70bcb1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs @@ -42,9 +42,9 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> }; acc.add(AssistId::generate("generate_derive"), "Add `#[derive]`", target, |edit| { + let editor = edit.make_editor(nominal.syntax()); match derive_attr { None => { - let editor = edit.make_editor(nominal.syntax()); let make = editor.make(); let derive = make.attr_outer(make.meta_token_tree( @@ -79,16 +79,15 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> let tabstop_before = edit.make_tabstop_before(cap); editor.add_annotation(delimiter, tabstop_before); - edit.add_file_edits(ctx.vfs_file_id(), editor); } Some(_) => { + let delimiter = delimiter.expect("Right delim token could not be found."); + let tabstop_before = edit.make_tabstop_before(cap); // Just move the cursor. - edit.add_tabstop_before_token( - cap, - delimiter.expect("Right delim token could not be found."), - ); + editor.add_annotation(delimiter, tabstop_before); } }; + edit.add_file_edits(ctx.vfs_file_id(), editor); }) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index 25d8ae097182b..553aa3ae805bd 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -386,7 +386,7 @@ impl SourceChangeBuilder { assert!(token.parent().is_some()); self.add_snippet(PlaceSnippet::Before(token.into())); } - + /// Adds a snippet to move the cursor selected over `node` pub fn add_placeholder_snippet(&mut self, _cap: SnippetCap, node: impl AstNode) { assert!(node.syntax().parent().is_some()); From e761d0243ef9a2f2a6fac5c564fabff54ddf2bb8 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 08:01:17 +0530 Subject: [PATCH 42/94] Remove add_tabstop_before_token fomr source_change --- src/tools/rust-analyzer/crates/ide-db/src/source_change.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index 553aa3ae805bd..a62087fb6a018 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -381,12 +381,6 @@ impl SourceChangeBuilder { self.add_snippet(PlaceSnippet::Before(node.syntax().clone().into())); } - /// Adds a tabstop snippet to place the cursor before `token` - pub fn add_tabstop_before_token(&mut self, _cap: SnippetCap, token: SyntaxToken) { - assert!(token.parent().is_some()); - self.add_snippet(PlaceSnippet::Before(token.into())); - } - /// Adds a snippet to move the cursor selected over `node` pub fn add_placeholder_snippet(&mut self, _cap: SnippetCap, node: impl AstNode) { assert!(node.syntax().parent().is_some()); From 06917be3b3a1d1a6b3ae57ab583a2934b62c212a Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 08:02:00 +0530 Subject: [PATCH 43/94] Remove add_placeholder_snippet fomr source_change --- src/tools/rust-analyzer/crates/ide-db/src/source_change.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index a62087fb6a018..878d6566dd42f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -381,12 +381,6 @@ impl SourceChangeBuilder { self.add_snippet(PlaceSnippet::Before(node.syntax().clone().into())); } - /// Adds a snippet to move the cursor selected over `node` - pub fn add_placeholder_snippet(&mut self, _cap: SnippetCap, node: impl AstNode) { - assert!(node.syntax().parent().is_some()); - self.add_snippet(PlaceSnippet::Over(node.syntax().clone().into())) - } - fn add_snippet(&mut self, snippet: PlaceSnippet) { let snippet_builder = self.snippet_builder.get_or_insert(SnippetBuilder { places: vec![] }); snippet_builder.places.push(snippet); From 5a6c923d42eb56b3855097a6aecc510830c1b106 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 08:02:40 +0530 Subject: [PATCH 44/94] Remove add_tabstop_before from source_change --- src/tools/rust-analyzer/crates/ide-db/src/source_change.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index 878d6566dd42f..34112cd1b1e89 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -375,12 +375,6 @@ impl SourceChangeBuilder { self.command = Some(Command::Rename); } - /// Adds a tabstop snippet to place the cursor before `node` - pub fn add_tabstop_before(&mut self, _cap: SnippetCap, node: impl AstNode) { - assert!(node.syntax().parent().is_some()); - self.add_snippet(PlaceSnippet::Before(node.syntax().clone().into())); - } - fn add_snippet(&mut self, snippet: PlaceSnippet) { let snippet_builder = self.snippet_builder.get_or_insert(SnippetBuilder { places: vec![] }); snippet_builder.places.push(snippet); From f5601fac7e67377080b5c94870d0d17f85bc317f Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 08:03:05 +0530 Subject: [PATCH 45/94] Remove add_snippet from source change --- src/tools/rust-analyzer/crates/ide-db/src/source_change.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index 34112cd1b1e89..8118112d2bf92 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -375,12 +375,6 @@ impl SourceChangeBuilder { self.command = Some(Command::Rename); } - fn add_snippet(&mut self, snippet: PlaceSnippet) { - let snippet_builder = self.snippet_builder.get_or_insert(SnippetBuilder { places: vec![] }); - snippet_builder.places.push(snippet); - self.source_change.is_snippet = true; - } - fn add_snippet_annotation(&mut self, kind: AnnotationSnippet) -> SyntaxAnnotation { let annotation = SyntaxAnnotation::default(); self.snippet_annotations.push((kind, annotation)); From 72a9c717c25261a6f297a002f4465c4bdf31f67f Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 08:11:48 +0530 Subject: [PATCH 46/94] removed PlaceSnippet and its unused SnippetBuilder --- .../crates/ide-db/src/source_change.rs | 41 ++----------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index 8118112d2bf92..d403cf6e0c926 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -15,7 +15,7 @@ use rustc_hash::FxHashMap; use span::FileId; use stdx::never; use syntax::{ - AstNode, SyntaxElement, SyntaxNode, SyntaxToken, TextRange, TextSize, + AstNode, SyntaxNode, TextRange, TextSize, syntax_editor::{SyntaxAnnotation, SyntaxEditor}, }; @@ -231,15 +231,6 @@ pub struct SourceChangeBuilder { pub file_editors: FxHashMap, /// Keeps track of which annotations correspond to which snippets pub snippet_annotations: Vec<(AnnotationSnippet, SyntaxAnnotation)>, - - /// Keeps track of where to place snippets - pub snippet_builder: Option, -} - -#[derive(Default)] -pub struct SnippetBuilder { - /// Where to place snippets at - places: Vec, } impl SourceChangeBuilder { @@ -251,7 +242,6 @@ impl SourceChangeBuilder { command: None, file_editors: FxHashMap::default(), snippet_annotations: vec![], - snippet_builder: None, } } @@ -329,15 +319,9 @@ impl SourceChangeBuilder { } // Apply mutable edits - let snippet_edit = self.snippet_builder.take().map(|builder| { - SnippetEdit::new( - builder.places.into_iter().flat_map(PlaceSnippet::finalize_position).collect(), - ) - }); - let edit = mem::take(&mut self.edit).finish(); - if !edit.is_empty() || snippet_edit.is_some() { - self.source_change.insert_source_and_snippet_edit(self.file_id, edit, snippet_edit); + if !edit.is_empty() { + self.source_change.insert_source_edit(self.file_id, edit); } } @@ -439,22 +423,3 @@ pub enum AnnotationSnippet { /// Place a placeholder snippet in place of the element(s) Over, } - -enum PlaceSnippet { - /// Place a tabstop before an element - Before(SyntaxElement), - /// Place a tabstop before an element - After(SyntaxElement), - /// Place a placeholder snippet in place of the element - Over(SyntaxElement), -} - -impl PlaceSnippet { - fn finalize_position(self) -> Vec { - match self { - PlaceSnippet::Before(it) => vec![Snippet::Tabstop(it.text_range().start())], - PlaceSnippet::After(it) => vec![Snippet::Tabstop(it.text_range().end())], - PlaceSnippet::Over(it) => vec![Snippet::Placeholder(it.text_range())], - } - } -} From d19e40446a594f69d42c198aca55de3beec57763 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 08:26:46 +0530 Subject: [PATCH 47/94] Removed unused From>, and the extend variant. And also make fields private which doesn't need to be pub --- .../crates/ide-db/src/source_change.rs | 35 +++++-------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index d403cf6e0c926..58f459003ec46 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -68,7 +68,7 @@ impl SourceChange { /// Inserts a [`TextEdit`] and potentially a [`SnippetEdit`] for the given [`FileId`]. /// This properly handles merging existing edits for a file if some already exist. - pub fn insert_source_and_snippet_edit( + fn insert_source_and_snippet_edit( &mut self, file_id: impl Into, edit: TextEdit, @@ -105,7 +105,7 @@ impl SourceChange { pub fn merge(mut self, other: SourceChange) -> SourceChange { self.extend(other.source_file_edits); - self.extend(other.file_system_edits); + self.file_system_edits.extend(other.file_system_edits); self.is_snippet |= other.is_snippet; self } @@ -128,25 +128,6 @@ impl Extend<(FileId, (TextEdit, Option))> for SourceChange { } } -impl Extend for SourceChange { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each(|edit| self.push_file_system_edit(edit)); - } -} - -impl From> for SourceChange { - fn from(source_file_edits: IntMap) -> SourceChange { - let source_file_edits = - source_file_edits.into_iter().map(|(file_id, edit)| (file_id, (edit, None))).collect(); - SourceChange { - source_file_edits, - file_system_edits: Vec::new(), - is_snippet: false, - ..SourceChange::default() - } - } -} - impl FromIterator<(FileId, TextEdit)> for SourceChange { fn from_iter>(iter: T) -> Self { let mut this = SourceChange::default(); @@ -222,15 +203,15 @@ impl SnippetEdit { } pub struct SourceChangeBuilder { - pub edit: TextEditBuilder, + edit: TextEditBuilder, pub file_id: FileId, pub source_change: SourceChange, pub command: Option, /// Keeps track of all edits performed on each file - pub file_editors: FxHashMap, + file_editors: FxHashMap, /// Keeps track of which annotations correspond to which snippets - pub snippet_annotations: Vec<(AnnotationSnippet, SyntaxAnnotation)>, + snippet_annotations: Vec<(AnnotationSnippet, SyntaxAnnotation)>, } impl SourceChangeBuilder { @@ -379,7 +360,7 @@ impl SourceChangeBuilder { .is_err() ); - mem::take(&mut self.source_change) + self.source_change } } @@ -415,10 +396,10 @@ pub enum Snippet { PlaceholderGroup(Vec), } -pub enum AnnotationSnippet { +enum AnnotationSnippet { /// Place a tabstop before an element Before, - /// Place a tabstop before an element + /// Place a tabstop after an element After, /// Place a placeholder snippet in place of the element(s) Over, From 1f5c92a1d8d6b9155a3b5cfec85972bc3195c5f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20S=C3=B8holm?= Date: Wed, 22 Jul 2026 13:51:34 +0200 Subject: [PATCH 48/94] fix: Fix glob imports overriding later specific imports --- .../crates/hir-def/src/item_scope.rs | 32 ++++++++++++------- .../crates/hir-def/src/per_ns.rs | 10 ++++++ .../src/handlers/type_mismatch.rs | 25 +++++++++++++++ 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 1443d3ea4be4c..14f10651cb8af 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -621,7 +621,11 @@ impl ItemScope { // for that. } _ => { - if glob_imports.types.remove(&lookup) { + // A non-glob import either shadows a glob import of the same + // name, or re-resolves a stale binding it recorded earlier. + if glob_imports.types.remove(&lookup) + || entry.get().is_reresolved_by(&fld.def, import) + { let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_types.insert( @@ -659,19 +663,22 @@ impl ItemScope { changed = true; } Entry::Occupied(mut entry) - if !matches!(import, Some(ImportOrExternCrate::Glob(..))) - && glob_imports.values.remove(&lookup) => + if !matches!(import, Some(ImportOrExternCrate::Glob(..))) => { - cov_mark::hit!(import_shadowed); - let import = import.and_then(ImportOrExternCrate::import_or_glob); - let prev = std::mem::replace(&mut fld.import, import); - if let Some(import) = import { - self.use_imports_values - .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); + if glob_imports.values.remove(&lookup) + || entry.get().is_reresolved_by(&fld.def, import) + { + cov_mark::hit!(import_shadowed); + + let prev = std::mem::replace(&mut fld.import, import); + if let Some(import) = import { + self.use_imports_values + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); + } + entry.insert(fld); + changed = true; } - entry.insert(fld); - changed = true; } _ => {} } @@ -699,7 +706,8 @@ impl ItemScope { } Entry::Occupied(mut entry) if !matches!(import, Some(ImportOrExternCrate::Glob(..))) - && glob_imports.macros.remove(&lookup) => + && (glob_imports.macros.remove(&lookup) + || entry.get().is_reresolved_by(&fld.def, import)) => { cov_mark::hit!(import_shadowed); let prev = std::mem::replace(&mut fld.import, import); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs index 8721cd65dbac7..62947f1511d86 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs @@ -35,6 +35,16 @@ pub struct Item { pub import: Option, } +impl Item { + /// Whether `import` is the same import that produced `self`, now resolving to a different + /// `def`. This happens when an import is first recorded as an indeterminate resolution + /// (e.g. only one namespace was available at the time) and later re-resolves to another + /// def, such as an explicit import that shadows a glob only after the glob has been seen. + pub(crate) fn is_reresolved_by(&self, def: &Def, import: Option) -> bool { + import.is_some() && self.import == import && self.def != *def + } +} + pub type TypesItem = Item; pub type ValuesItem = Item; // May be Externcrate for `[macro_use]`'d macros diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 295f37ab1d6b3..b16be503effd3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -1810,6 +1810,31 @@ fn main() { // ^^ error: type annotations needed // ^^^ error: the trait bound `i32: Foo` is not satisfied } +"#, + ); + } + + #[test] + fn regression_21668() { + check_diagnostics( + r#" +mod std { + pub enum Ordering { Less } +} +pub use std::*; + +pub mod evil { + pub struct Ordering(pub i32); +} + +pub mod oblivious { + use crate::Ordering; + + pub fn what() -> Ordering { + Ordering(2) + } +} +pub use evil::Ordering; "#, ); } From 2945d4d17dcd07e55591839f1df3e7dbdab18ceb Mon Sep 17 00:00:00 2001 From: Ritesh Date: Thu, 30 Jul 2026 18:19:02 +0000 Subject: [PATCH 49/94] fix: show qualified paths when type names collide in E0308 --- .../src/handlers/type_mismatch.rs | 129 ++++++++++++++++-- 1 file changed, 120 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index b16be503effd3..df815d6322b63 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -45,18 +45,31 @@ pub(crate) fn type_mismatch( cov_mark::hit!(type_mismatch_range_adjustment); Some(salient_token_range) }); + + let expected = d + .expected + .display(ctx.db(), ctx.display_target) + .with_closure_style(ClosureStyle::ClosureWithId) + .to_string(); + let actual = d + .actual + .display(ctx.db(), ctx.display_target) + .with_closure_style(ClosureStyle::ClosureWithId) + .to_string(); + + // The types differ (that's why we're here), yet they render the same, e.g. `foo::S` and + // `bar::S` both render as `S`. Retry with qualified paths so the message isn't a useless + // "expected S, found S". + let (expected, actual) = if expected == actual { + qualified_display(ctx, d).unwrap_or((expected, actual)) + } else { + (expected, actual) + }; + Some( Diagnostic::new( DiagnosticCode::RustcHardError("E0308"), - format!( - "expected {}, found {}", - d.expected - .display(ctx.db(), ctx.display_target) - .with_closure_style(ClosureStyle::ClosureWithId), - d.actual - .display(ctx.db(), ctx.display_target) - .with_closure_style(ClosureStyle::ClosureWithId), - ), + format!("expected {expected}, found {actual}"), display_range, ) .stable() @@ -64,6 +77,20 @@ pub(crate) fn type_mismatch( ) } +/// Renders both sides of the mismatch with qualified paths, for when their plain names collide. +/// Returns `None` if either side has no renderable path, in which case both keep their plain +/// names — mixing a qualified and an unqualified name would be more confusing, not less. +fn qualified_display( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::TypeMismatch<'_>, +) -> Option<(String, String)> { + let root = d.expr_or_pat.file_id.parse_or_expand(ctx.db()); + let module = ctx.sema.scope(d.expr_or_pat.value.to_node(&root).syntax())?.module(); + let expected = d.expected.display_source_code(ctx.db(), module.into(), true).ok()?; + let actual = d.actual.display_source_code(ctx.db(), module.into(), true).ok()?; + Some((expected, actual)) +} + fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option> { let mut fixes = Vec::new(); @@ -1835,6 +1862,90 @@ pub mod oblivious { } } pub use evil::Ordering; +"#, + ); + } + + // Tests for qualified paths on name collision (issue #22331) + + #[test] + fn type_mismatch_collision_basic_structs() { + check_diagnostics( + r#" +mod foo { + pub struct S; +} +mod bar { + pub struct S; +} +fn test(_: foo::S) { + test(bar::S); + //^^^^^^ error: expected foo::S, found bar::S +} +"#, + ); + } + + #[test] + fn type_mismatch_collision_in_generic() { + check_diagnostics( + r#" +//- minicore: option +mod foo { + pub struct S; +} +mod bar { + pub struct S; +} +fn make() -> Option { loop {} } +fn test(_: Option) { + test(make()); + //^^^^^^ error: expected Option, found Option +} +"#, + ); + } + + #[test] + fn type_mismatch_no_collision_unchanged() { + check_diagnostics( + r#" +mod foo { + pub struct S; +} +mod bar { + pub struct T; +} +fn test(_: foo::S) { + test(bar::T); + //^^^^^^ error: expected S, found T +} +"#, + ); + } + + #[test] + fn type_mismatch_multiple_collisions() { + check_diagnostics( + r#" +//- minicore: result +mod foo { + pub struct T; +} +mod bar { + pub struct T; +} +mod baz { + pub struct E; +} +mod qux { + pub struct E; +} +fn make() -> Result { loop {} } +fn test(_: Result) { + test(make()); + //^^^^^^ error: expected Result, found Result +} "#, ); } From 6b5fd9605b9eaafbc6ec6a11a1a829bf60a27331 Mon Sep 17 00:00:00 2001 From: shulaoda <165626830+shulaoda@users.noreply.github.com> Date: Sun, 2 Aug 2026 05:38:27 +0800 Subject: [PATCH 50/94] fix: don't panic on a self-referential `impl Trait` function --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 2 +- .../crates/hir-ty/src/infer/unify.rs | 6 ++-- .../crates/hir-ty/src/next_solver/interner.rs | 12 +++++-- .../crates/hir-ty/src/tests/regression.rs | 31 +++++++++++++++++++ 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 319a8ae9bd7fc..935b2f9ffaba3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1438,7 +1438,7 @@ impl<'db> InferenceContext<'db> { lowering_mode: LoweringMode, ) -> Self { let trait_env = db.trait_environment(generic_def); - let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), store_owner); + let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), owner); let types = crate::next_solver::default_types(db); InferenceContext { result: InferenceResult::new(types.types.error), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 6157f51500d9e..7b589efba2f63 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -3,7 +3,7 @@ use std::fmt; use base_db::Crate; -use hir_def::{ExpressionStoreOwnerId, GenericParamId, TraitId}; +use hir_def::{GenericParamId, TraitId}; use rustc_hash::FxHashSet; use rustc_type_ir::{ TyVid, TypeFoldable, TypeVisitableExt, @@ -14,7 +14,7 @@ use smallvec::SmallVec; use thin_vec::ThinVec; use crate::{ - InferenceDiagnostic, Span, + InferBodyId, InferenceDiagnostic, Span, db::HirDatabase, next_solver::{ Canonical, ClauseKind, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, @@ -144,7 +144,7 @@ impl<'db> InferenceTable<'db> { db: &'db dyn HirDatabase, trait_env: ParamEnv<'db>, krate: Crate, - owner: ExpressionStoreOwnerId, + owner: InferBodyId<'db>, ) -> Self { let interner = DbInterner::new_with(db, krate); let typing_mode = TypingMode::typeck_for_body(interner, owner.into()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 7554ca6bcd025..02baf27e0bbf2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -2085,13 +2085,19 @@ impl<'db> Interner for DbInterner<'db> { opaque: Self::LocalOpaqueTyId, ) -> EarlyBinder { let impl_trait_id = opaque.0.loc(self.db); - match impl_trait_id { + // The entry is missing when this call cycles back into the still-running inference + // of the defining body, as the cycle fallback is an empty result. + let hidden_type = match impl_trait_id { crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - crate::opaques::rpit_hidden_types(self.db, func)[idx].get() + crate::opaques::rpit_hidden_types(self.db, func).get(idx) } crate::ImplTraitId::TypeAliasImplTrait(type_alias, idx) => { - crate::opaques::tait_hidden_types(self.db, type_alias)[idx].get() + crate::opaques::tait_hidden_types(self.db, type_alias).get(idx) } + }; + match hidden_type { + Some(hidden_type) => hidden_type.get(), + None => EarlyBinder::bind(Ty::new_error(self, ErrorGuaranteed)), } } 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 683c938fb5a25..59f33f0f1b29a 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 @@ -3063,3 +3063,34 @@ fn main() { "#, ); } + +#[test] +fn regression_22820() { + check_no_mismatches( + r#" +//- minicore: copy +trait MyTrait: Copy { + const ASSOC: usize; +} + +const fn output(_: T) -> usize { + ::ASSOC +} + +const fn yeet() -> impl Clone { + let x = [0u8; output(yeet())]; +} + "#, + ); +} + +#[test] +fn rpit_function_with_non_trivial_anon_const() { + check_no_mismatches( + r#" +fn f() -> impl Sized { + let x = [0u8; 1 + 2]; +} + "#, + ); +} From b33c51e3daf21a29d26a1b7b1e38c158d211836a Mon Sep 17 00:00:00 2001 From: shulaoda <165626830+shulaoda@users.noreply.github.com> Date: Sun, 2 Aug 2026 06:14:38 +0800 Subject: [PATCH 51/94] fix: recognize format arguments after a backslash in raw strings --- ...ighlight_raw_string_format_specifiers.html | 49 +++++++++++++++++++ .../ide/src/syntax_highlighting/tests.rs | 17 +++++++ .../crates/syntax/src/ast/token_ext.rs | 10 ++++ 3 files changed, 76 insertions(+) create mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_raw_string_format_specifiers.html diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_raw_string_format_specifiers.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_raw_string_format_specifiers.html new file mode 100644 index 0000000000000..4497e83a4d4cc --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_raw_string_format_specifiers.html @@ -0,0 +1,49 @@ + + +
fn main() {
+    let here = 1;
+    format_args!(r"backslash \{here} arg");
+    format_args!(r#"hashed \{here} arg"#);
+    format_args!("plain {here} arg");
+}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index 6cb323b46a521..7a3d71f519f84 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -452,6 +452,23 @@ macro_rules! void_2024 { ); } +#[test] +fn test_raw_string_format_specifiers() { + check_highlighting( + r####" +//- minicore: fmt +fn main() { + let here = 1; + format_args!(r"backslash \{here} arg"); + format_args!(r#"hashed \{here} arg"#); + format_args!("plain {here} arg"); +} +"####, + expect_file!["./test_data/highlight_raw_string_format_specifiers.html"], + false, + ); +} + #[test] fn test_string_highlighting() { // The format string detection is based on macro-expansion, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index b5c4e1aa9d9f1..f8bffe9d47b36 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -183,6 +183,16 @@ pub trait IsString: AstToken { let text = &self.text()[text_range_no_quotes - start]; let offset = text_range_no_quotes.start() - start; + if self.is_raw() { + let mut pos = offset; + for c in text.chars() { + let len = TextSize::of(c); + cb(TextRange::at(pos, len), Ok(c)); + pos += len; + } + return; + } + self.unescape(text, &mut |range: Range, unescaped_char| { if let Some((s, e)) = range.start.try_into().ok().zip(range.end.try_into().ok()) { cb(TextRange::new(s, e) + offset, unescaped_char); From c973eb05e512facc46a824e2687c235b0813de4f Mon Sep 17 00:00:00 2001 From: Musteab Date: Sun, 2 Aug 2026 07:30:51 +0800 Subject: [PATCH 52/94] fix: detect the rust-analyzer component in a multi-line components array --- .../editors/code/src/bootstrap.ts | 14 +++- .../editors/code/tests/unit/bootstrap.test.ts | 71 +++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts index ca5b7e3ec7855..98c2b359bf2bc 100644 --- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -176,14 +176,21 @@ async function fileExists(uri: vscode.Uri) { ); } +// Matches a `components` array that lists `rust-analyzer`. The elements are matched with +// `[^\]]` rather than `.` so that the array may be spread over several lines, which is just +// as valid TOML as keeping it on one. TOML strings come in both quote flavours. +const RA_COMPONENT_RE = /components\s*=\s*\[[^\]]*["']rust-analyzer["'][^\]]*\]/; + +function declaresRaComponent(toolchainFileContents: string): boolean { + return RA_COMPONENT_RE.test(toolchainFileContents); +} + async function hasToolchainFileWithRaDeclared(uri: vscode.Uri): Promise { try { const toolchainFileContents = new TextDecoder().decode( await vscode.workspace.fs.readFile(uri), ); - return ( - toolchainFileContents.match(/components\s*=\s*\[.*"rust-analyzer".*\]/g)?.length === 1 - ); + return declaresRaComponent(toolchainFileContents); } catch (_) { return false; } @@ -296,6 +303,7 @@ async function patchelf(dest: vscode.Uri): Promise { } export const _private = { + declaresRaComponent, earliestToolchainPath, orderFromPath, }; diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts index baabf4f89773b..8d348b9ccbd5f 100644 --- a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts +++ b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts @@ -93,4 +93,75 @@ export async function getTests(ctx: Context) { ); }); }); + + await ctx.suite("Bootstrap/Detect RA component in toolchain file", (suite) => { + suite.addTest("Single line components array", async () => { + assert.ok( + _private.declaresRaComponent( + `[toolchain] +channel = "1.88" +components = ["cargo", "rust-analyzer", "rustfmt"] +`, + ), + ); + }); + + suite.addTest("Multi line components array", async () => { + assert.ok( + _private.declaresRaComponent( + `[toolchain] +channel = "1.88" +components = [ + "cargo", + "rust-analyzer", + "rustfmt", +] +profile = "default" +`, + ), + ); + }); + + suite.addTest("Components array with literal strings", async () => { + assert.ok(_private.declaresRaComponent(`components = ['cargo', 'rust-analyzer']`)); + }); + + suite.addTest("Components array without RA", async () => { + assert.ok( + !_private.declaresRaComponent( + `[toolchain] +channel = "1.88" +components = [ + "cargo", + "rustfmt", +] +`, + ), + ); + }); + + suite.addTest("No components array", async () => { + assert.ok( + !_private.declaresRaComponent( + `[toolchain] +channel = "1.88" +`, + ), + ); + }); + + suite.addTest("RA mentioned outside the components array", async () => { + assert.ok( + !_private.declaresRaComponent( + `[toolchain] +channel = "1.88" +components = [ + "cargo", +] +path = "/opt/rust-analyzer" +`, + ), + ); + }); + }); } From 01db4e129a0eff9aca25ac5d37f5792a8ba1438b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 2 Aug 2026 03:03:53 +0300 Subject: [PATCH 53/94] Do not store references in `ExprScope`'s visitor --- .../crates/hir-def/src/expr_store/scope.rs | 189 +++++++----------- 1 file changed, 71 insertions(+), 118 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs index ee5396b4cde97..cfbfde88765f0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs @@ -1,4 +1,6 @@ //! Name resolution for expressions. +use std::mem; + use base_db::SourceDatabase; use hir_expand::{MacroDefId, name::Name}; use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx}; @@ -162,18 +164,13 @@ impl ExprScopes { body.expr_only.as_ref().map_or(0, |it| it.exprs.len()), ), }; - let mut root = scopes.root_scope(); + let root = scopes.root_scope(); if let Some(Param { formal: self_param, user_written: _ }) = body.self_param { scopes.add_bindings(body, root, self_param, body.binding_hygiene(self_param)); } body.params.iter().for_each(|param| scopes.add_pat_bindings(body, root, param.formal)); - ExprScopeVisitor { - store: body, - scopes: &mut scopes, - scope: &mut { root }, - const_scope: &mut root, - } - .on_expr(body.root_expr()); + ExprScopeVisitor { store: body, scopes: &mut scopes, scope: root, const_scope: root } + .on_expr(body.root_expr()); scopes } @@ -187,14 +184,9 @@ impl ExprScopes { }; let root = scopes.root_scope(); for root_expr in roots { - let mut scope = scopes.new_scope(root); - ExprScopeVisitor { - store, - scopes: &mut scopes, - scope: &mut { scope }, - const_scope: &mut scope, - } - .on_expr(root_expr); + let scope = scopes.new_scope(root); + ExprScopeVisitor { store, scopes: &mut scopes, scope, const_scope: scope } + .on_expr(root_expr); } scopes } @@ -290,11 +282,17 @@ impl ExprScopes { struct ExprScopeVisitor<'a> { store: &'a ExpressionStore, scopes: &'a mut ExprScopes, - scope: &'a mut ScopeId, - const_scope: &'a mut ScopeId, + scope: ScopeId, + const_scope: ScopeId, } impl ExprScopeVisitor<'_> { + fn with_scope(&mut self, scope: ScopeId, f: impl FnOnce(&mut Self)) { + let old_scope = mem::replace(&mut self.scope, scope); + f(self); + self.scope = old_scope; + } + fn visit_block( &mut self, expr: ExprId, @@ -303,49 +301,47 @@ impl ExprScopeVisitor<'_> { tail: Option, label: Option, ) { - let mut scope = self.scopes.new_block_scope(*self.scope, id, label); - let mut const_scope = if id.is_some() { - self.scopes.new_block_scope(*self.const_scope, id, None) - } else { - // We don't need to allocate a new scope, since only items matter to us. - *self.const_scope - }; - // Overwrite the old scope for the block expr, so that every block scope can be found - // via the block itself (important for blocks that only contain items, no expressions). - self.scopes.set_scope(expr, scope); - - let mut visitor = ExprScopeVisitor { - store: self.store, - scopes: self.scopes, - scope: &mut scope, - const_scope: &mut const_scope, - }; - for stmt in statements { - match stmt { - Statement::Let { pat, initializer, else_branch, type_ref } => { - visitor.on_type_opt(*type_ref); - visitor.on_expr_opt(*initializer); - visitor.on_expr_opt(*else_branch); - *visitor.scope = visitor.scopes.new_scope(*visitor.scope); - visitor.scopes.add_pat_bindings(visitor.store, *visitor.scope, *pat); - } - Statement::Expr { expr, has_semi: _ } => visitor.on_expr(*expr), - Statement::Item(Item::MacroDef(macro_id)) => { - *visitor.scope = - visitor.scopes.new_macro_def_scope(*visitor.scope, macro_id.clone()); - *visitor.const_scope = - visitor.scopes.new_macro_def_scope(*visitor.const_scope, macro_id.clone()); + let scope = self.scopes.new_block_scope(self.scope, id, label); + self.with_scope(scope, |this| { + let old_const_scope = if id.is_some() { + let const_scope = this.scopes.new_block_scope(this.const_scope, id, None); + mem::replace(&mut this.const_scope, const_scope) + } else { + // We don't need to allocate a new scope, since only items matter to us. + this.const_scope + }; + // Overwrite the old scope for the block expr, so that every block scope can be found + // via the block itthis (important for blocks that only contain items, no expressions). + this.scopes.set_scope(expr, this.scope); + + for stmt in statements { + match stmt { + Statement::Let { pat, initializer, else_branch, type_ref } => { + this.on_type_opt(*type_ref); + this.on_expr_opt(*initializer); + this.on_expr_opt(*else_branch); + this.scope = this.scopes.new_scope(this.scope); + this.scopes.add_pat_bindings(this.store, this.scope, *pat); + } + Statement::Expr { expr, has_semi: _ } => this.on_expr(*expr), + Statement::Item(Item::MacroDef(macro_id)) => { + this.scope = this.scopes.new_macro_def_scope(this.scope, macro_id.clone()); + this.const_scope = + this.scopes.new_macro_def_scope(this.const_scope, macro_id.clone()); + } + Statement::Item(Item::Other) => (), } - Statement::Item(Item::Other) => (), } - } - visitor.on_expr_opt(tail); + this.on_expr_opt(tail); + + this.const_scope = old_const_scope; + }); } } impl StoreVisitor for ExprScopeVisitor<'_> { fn on_expr(&mut self, expr: ExprId) { - self.scopes.set_scope(expr, *self.scope); + self.scopes.set_scope(expr, self.scope); match &self.store[expr] { Expr::Block { statements, tail, id, label } => { self.visit_block(expr, *id, statements, *tail, *label); @@ -355,99 +351,56 @@ impl StoreVisitor for ExprScopeVisitor<'_> { self.visit_block(expr, *id, statements, *tail, None); } Expr::Loop { body, label, source: _ } => { - let mut scope = self.scopes.new_labeled_scope(*self.scope, *label); - ExprScopeVisitor { - store: self.store, - scopes: self.scopes, - scope: &mut scope, - const_scope: self.const_scope, - } - .on_expr(*body); + let scope = self.scopes.new_labeled_scope(self.scope, *label); + self.with_scope(scope, |this| this.on_expr(*body)); } Expr::Closure { args, arg_types, ret_type, body, capture_by: _, closure_kind: _ } => { arg_types.iter().flatten().for_each(|type_ref| self.on_type(*type_ref)); self.on_type_opt(*ret_type); - let mut scope = self.scopes.new_scope(*self.scope); + let scope = self.scopes.new_scope(self.scope); args.iter().for_each(|arg| self.scopes.add_pat_bindings(self.store, scope, *arg)); - ExprScopeVisitor { - store: self.store, - scopes: self.scopes, - scope: &mut scope, - const_scope: self.const_scope, - } - .on_expr(*body); + self.with_scope(scope, |this| this.on_expr(*body)); } Expr::Match { expr, arms } => { self.on_expr(*expr); for arm in arms.iter() { - let mut scope = self.scopes.new_scope(*self.scope); - self.scopes.add_pat_bindings(self.store, scope, arm.pat); - if let Some(guard) = arm.guard { - scope = self.scopes.new_scope(scope); - ExprScopeVisitor { - store: self.store, - scopes: self.scopes, - scope: &mut scope, - const_scope: self.const_scope, + let scope = self.scopes.new_scope(self.scope); + self.with_scope(scope, |this| { + this.scopes.add_pat_bindings(this.store, scope, arm.pat); + if let Some(guard) = arm.guard { + this.on_expr(guard); } - .on_expr(guard); - } - ExprScopeVisitor { - store: self.store, - scopes: self.scopes, - scope: &mut scope, - const_scope: self.const_scope, - } - .on_expr(arm.expr); + this.on_expr(arm.expr); + }); } } &Expr::If { condition, then_branch, else_branch } => { - let mut then_branch_scope = self.scopes.new_scope(*self.scope); - let mut visitor = ExprScopeVisitor { - store: self.store, - scopes: self.scopes, - scope: &mut then_branch_scope, - const_scope: self.const_scope, - }; - visitor.on_expr(condition); - visitor.on_expr(then_branch); + let then_branch_scope = self.scopes.new_scope(self.scope); + self.with_scope(then_branch_scope, |this| { + this.on_expr(condition); + this.on_expr(then_branch); + }); self.on_expr_opt(else_branch); } &Expr::Let { pat, expr } => { self.on_expr(expr); - *self.scope = self.scopes.new_scope(*self.scope); - self.scopes.add_pat_bindings(self.store, *self.scope, pat); + self.scope = self.scopes.new_scope(self.scope); + self.scopes.add_pat_bindings(self.store, self.scope, pat); } _ => self.store.visit_expr_children(expr, &mut *self), } } fn on_anon_const_expr(&mut self, expr: ExprId) { - let mut scope = *self.const_scope; - ExprScopeVisitor { - store: self.store, - scopes: self.scopes, - scope: &mut scope, - const_scope: self.const_scope, - } - .on_expr(expr); + self.with_scope(self.const_scope, |this| this.on_expr(expr)); } fn on_pat(&mut self, pat: PatId) { - self.store.visit_pat_children(pat, &mut *self); + self.store.visit_pat_children(pat, self); } fn on_type(&mut self, ty: TypeRefId) { - let mut scope = *self.const_scope; - self.store.visit_type_ref_children( - ty, - ExprScopeVisitor { - store: self.store, - scopes: self.scopes, - scope: &mut scope, - const_scope: self.const_scope, - }, - ); + self.with_scope(self.const_scope, |this| self.store.visit_type_ref_children(ty, this)); } } From 889a9405a585b6c8158a269ebdeb28beda1cf251 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 11:14:47 +0530 Subject: [PATCH 54/94] Bump rowan to 0.17.0 --- src/tools/rust-analyzer/Cargo.lock | 4 ++-- src/tools/rust-analyzer/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 7a2e2d493b59a..51d907024c6c9 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2296,9 +2296,9 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rowan" -version = "0.15.19" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2441aaccb50f4267d4f0f58b21e0138e96a449f361ed57a2673a83c9bca0772" +checksum = "14b574c58582fa59fa43a2feb6608b8744184659f08a2e0117e4b8224d95ed61" dependencies = [ "countme", "hashbrown 0.14.5", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 4ef92c5bd2ca3..693a819052634 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -123,7 +123,7 @@ process-wrap = { version = "9.1.0", features = ["std"] } pulldown-cmark-to-cmark = "10.0.4" pulldown-cmark = { version = "0.9.6", default-features = false } rayon = "1.10.0" -rowan = "=0.15.19" +rowan = "=0.17.0" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it salsa = { version = "0.27.0", default-features = false, features = [ From 9c3358aea547ba6b336626499ea4055463c3ddfc Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 11:15:38 +0530 Subject: [PATCH 55/94] Adapt to borrowed Rowan green nodes --- .../crates/syntax/src/ast/node_ext.rs | 35 ++++++------------- .../syntax/src/syntax_editor/edit_algo.rs | 6 ++-- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index 1eb658f4b8d06..00aeb1dfebf8d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -3,7 +3,7 @@ //! //! These methods should only do simple, shallow tasks related to the syntax of the node itself. -use std::{borrow::Cow, fmt, iter::successors}; +use std::{fmt, iter::successors}; use itertools::Itertools; use parser::SyntaxKind; @@ -31,15 +31,9 @@ impl ast::Name { pub fn text(&self) -> TokenText<'_> { text_of_first_token(self.syntax()) } - pub fn text_non_mutable(&self) -> &str { - fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData { - green_ref.children().next().and_then(NodeOrToken::into_token).unwrap() - } - match self.syntax().green() { - Cow::Borrowed(green_ref) => first_token(green_ref).text(), - Cow::Owned(_) => unreachable!(), - } + pub fn text_non_mutable(&self) -> &str { + first_token(self.syntax().green()).text() } } @@ -47,15 +41,9 @@ impl ast::NameRef { pub fn text(&self) -> TokenText<'_> { text_of_first_token(self.syntax()) } - pub fn text_non_mutable(&self) -> &str { - fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData { - green_ref.children().next().and_then(NodeOrToken::into_token).unwrap() - } - match self.syntax().green() { - Cow::Borrowed(green_ref) => first_token(green_ref).text(), - Cow::Owned(_) => unreachable!(), - } + pub fn text_non_mutable(&self) -> &str { + first_token(self.syntax().green()).text() } pub fn as_tuple_field(&self) -> Option { @@ -67,15 +55,12 @@ impl ast::NameRef { } } -fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> { - fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData { - green_ref.children().next().and_then(NodeOrToken::into_token).unwrap() - } +fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData { + green_ref.children().next().and_then(NodeOrToken::into_token).unwrap() +} - match node.green() { - Cow::Borrowed(green_ref) => TokenText::borrowed(first_token(green_ref).text()), - Cow::Owned(green) => TokenText::owned(first_token(&green).to_owned()), - } +fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> { + TokenText::borrowed(first_token(node.green()).text()) } fn into_comma(it: NodeOrToken) -> Option { diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs index d24d9b1334dec..24e7016f5bdee 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -449,7 +449,7 @@ impl TreeState { let parent = parent_path.resolve(&self.root).and_then(SyntaxElement::into_node).unwrap(); let green = rowan::GreenNodeData::splice_children( - parent.green().as_ref(), + parent.green(), deleted.clone(), inserted.into_iter().map(PreparedElement::into_green), ); @@ -466,7 +466,7 @@ impl TreeState { let NodeOrToken::Node(node) = replacement.syntax else { panic!("root node replacement should be a node") }; - self.root = SyntaxNode::new_root(node.green().into_owned()); + self.root = SyntaxNode::new_root(node.green().to_owned()); self.changed.clear(); if track_as_changed { self.changed.push(SyntaxPath { child_indices: Vec::new() }); @@ -543,7 +543,7 @@ struct PreparedElement { impl PreparedElement { fn into_green(self) -> rowan::NodeOrToken { match self.syntax { - SyntaxElement::Node(node) => NodeOrToken::Node(node.green().into_owned()), + SyntaxElement::Node(node) => NodeOrToken::Node(node.green().to_owned()), SyntaxElement::Token(token) => NodeOrToken::Token(token.green().to_owned()), } } From f12a1361a12f29a998f3d4a7441ef8289e3a13c4 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 11:17:20 +0530 Subject: [PATCH 56/94] Remove the obsolete TokenText wrapper --- .../hir-expand/src/builtin/derive_macro.rs | 2 +- .../src/handlers/apply_demorgan.rs | 4 +- .../src/handlers/convert_into_to_from.rs | 2 +- .../extract_struct_from_enum_variant.rs | 23 ++-- .../src/handlers/generate_function.rs | 4 +- .../generate_single_field_struct_from.rs | 16 ++- .../src/handlers/reorder_impl_items.rs | 2 +- .../crates/ide-assists/src/utils.rs | 2 +- .../src/utils/gen_trait_fn_body.rs | 2 +- .../src/completions/attribute.rs | 1 - .../src/completions/attribute/repr.rs | 2 +- .../ide-completion/src/completions/postfix.rs | 2 +- .../crates/ide-db/src/imports/insert_use.rs | 2 +- .../ide-db/src/imports/merge_imports.rs | 10 +- .../crates/ide-db/src/path_transform.rs | 2 +- .../crates/ide-db/src/ra_fixture.rs | 3 +- .../rust-analyzer/crates/ide-db/src/rename.rs | 2 +- .../rust-analyzer/crates/ide-db/src/search.rs | 8 +- .../crates/ide-ssr/src/resolving.rs | 2 +- .../crates/ide/src/inlay_hints/lifetime.rs | 12 +-- .../crates/ide/src/inlay_hints/param_name.rs | 4 +- .../rust-analyzer/crates/span/src/ast_id.rs | 8 +- .../crates/syntax/src/ast/node_ext.rs | 30 ++---- .../rust-analyzer/crates/syntax/src/lib.rs | 2 - .../crates/syntax/src/token_text.rs | 102 ------------------ 25 files changed, 63 insertions(+), 186 deletions(-) delete mode 100644 src/tools/rust-analyzer/crates/syntax/src/token_text.rs diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index 5e85a710e0855..ccd2d6dca29f1 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -412,7 +412,7 @@ fn name_to_token( })?; let span = token_map.span_at(name.syntax().text_range().start()); - let name_token = tt::Ident::new(name.text().as_ref(), span); + let name_token = tt::Ident::new(name.text(), span); Ok(name_token) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index 10262445a2dfe..e2c1048bdb785 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -206,7 +206,7 @@ pub(crate) fn apply_demorgan_iterator( let closure_body = closure_expr.body()?; let op_range = method_call.syntax().text_range(); - let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text().as_str()); + let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text()); acc.add_group( &GroupLabel("Apply De Morgan's law".to_owned()), AssistId::refactor_rewrite("apply_demorgan_iterator"), @@ -216,7 +216,7 @@ pub(crate) fn apply_demorgan_iterator( let editor = builder.make_editor(method_call.syntax()); let make = editor.make(); // replace the method name - let new_name = match name.text().as_str() { + let new_name = match name.text() { "all" => make.name_ref("any"), "any" => make.name_ref("all"), "is_some_and" => make.name_ref("is_none_or"), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs index a01a66e7b1a52..c8de14bed4bb8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs @@ -109,7 +109,7 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_, '_ editor.replace(into_fn_name.syntax(), make.name("from").syntax()); for s in selfs { - match s.text().as_ref() { + match s.text() { "self" => editor.replace(s.syntax(), make.name_ref("val").syntax()), "Self" => { if let Some(path_segment) = diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 5e6e74bc94429..89c5470c160ff 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -241,7 +241,7 @@ fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, b if matches!(token.kind(), T![lifetime_ident]) => { if let Some(lt) = lt.lifetime() - && lt.text().as_str() == token.text() + && lt.text() == token.text() { *tag = true; tagged_one = true; @@ -250,18 +250,15 @@ fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, b } param if matches!(token.kind(), T![ident]) => { if match param { - ast::GenericParam::ConstParam(konst) => konst - .name() - .map(|name| name.text().as_str() == token.text()) - .unwrap_or_default(), - ast::GenericParam::TypeParam(ty) => ty - .name() - .map(|name| name.text().as_str() == token.text()) - .unwrap_or_default(), - ast::GenericParam::LifetimeParam(lt) => lt - .lifetime() - .map(|lt| lt.text().as_str() == token.text()) - .unwrap_or_default(), + ast::GenericParam::ConstParam(konst) => { + konst.name().map(|name| name.text() == token.text()).unwrap_or_default() + } + ast::GenericParam::TypeParam(ty) => { + ty.name().map(|name| name.text() == token.text()).unwrap_or_default() + } + ast::GenericParam::LifetimeParam(lt) => { + lt.lifetime().map(|lt| lt.text() == token.text()).unwrap_or_default() + } } { *tag = true; tagged_one = true; 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 3c3fde80f99e7..3bec992252861 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 @@ -67,7 +67,7 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { return None; } - let fn_name = &*name_ref.text(); + let fn_name = name_ref.text(); let TargetInfo { target_module, adt_info, target, file } = fn_target_info(ctx, path, &call, fn_name)?; @@ -159,7 +159,7 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { 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())? + get_adt_source(ctx, &adt, fn_name.text())? }; let target = get_method_target(ctx, &impl_, &adt)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index d5629e2e7e073..23ce72670332d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -4,14 +4,11 @@ use ide_db::{ RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast_with_factory, imports::import_assets::item_for_path_search, }; -use syntax::syntax_editor::{Position, SyntaxEditor}; -use syntax::{ - TokenText, - ast::{ - self, AstNode, HasAttrs, HasGenericParams, HasName, edit::AstNodeEdit, - syntax_factory::SyntaxFactory, - }, +use syntax::ast::{ + self, AstNode, HasAttrs, HasGenericParams, HasName, edit::AstNodeEdit, + syntax_factory::SyntaxFactory, }; +use syntax::syntax_editor::{Position, SyntaxEditor}; use crate::{ AssistId, @@ -71,8 +68,7 @@ pub(crate) fn generate_single_field_struct_from( return None; } - let main_field_name = - names.as_ref().map_or(TokenText::borrowed("value"), |names| names[main_field_i].text()); + let main_field_name = names.as_ref().map_or("value", |names| names[main_field_i].text()); let main_field_ty = types[main_field_i].clone(); acc.add( @@ -161,7 +157,7 @@ pub(crate) fn generate_single_field_struct_from( fn make_adt_constructor( names: Option<&[ast::Name]>, constructors: Vec>, - main_field_name: &TokenText<'_>, + main_field_name: &str, make: &SyntaxFactory, ) -> ast::Expr { if let Some(names) = names { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs index 658947abe135f..ed5a372e546db 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs @@ -82,7 +82,7 @@ pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_, '_>) ast::AssocItem::MacroCall(_) => None, }; - name.and_then(|n| ranks.get(n.text().as_str().trim_start_matches("r#")).copied()) + name.and_then(|n| ranks.get(n.text().trim_start_matches("r#")).copied()) .unwrap_or(usize::MAX) }) .collect(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 670a030255cfc..ad46c61935f65 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -344,7 +344,7 @@ fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option "is_none", "is_none" => "is_some", "is_ok" => "is_err", diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs index c0ddcb950cbac..277b5bd8dfc5e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -21,7 +21,7 @@ pub(crate) fn gen_trait_fn_body( trait_ref: Option>, ) -> Option { let _ = func.body()?; - match trait_path.segment()?.name_ref()?.text().as_str() { + match trait_path.segment()?.name_ref()?.text() { "Clone" => { stdx::always!(func.name().is_some_and(|name| name.text() == "clone")); gen_clone_impl(make, adt) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index 109ebce01c9af..c2d7cb98cf9a8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -40,7 +40,6 @@ pub(crate) fn complete_known_attribute_input( let path = attribute.path()?; let segments = path.segments().map(|s| s.name_ref()).collect::>>()?; let segments = segments.iter().map(|n| n.text()).collect::>(); - let segments = segments.iter().map(|t| t.as_str()).collect::>(); let tt = attribute.token_tree()?; match segments.as_slice() { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs index 63cddb365e301..73ba847e7e61f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs @@ -23,7 +23,7 @@ pub(super) fn complete_repr( }) .any(|it| { let text = it.text(); - lookup.unwrap_or(label) == text || collides.contains(&text.as_str()) + lookup.unwrap_or(label) == text || collides.contains(&text) }); if repr_already_annotated { continue; 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 5a3a3ac39cb2f..34b53e5e5bfea 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 @@ -365,7 +365,7 @@ fn suggest_receiver_name( match receiver { ast::Expr::PathExpr(path) => { if let Some(name) = path.path().and_then(|it| it.as_single_name_ref()) { - return placeholder(name.text().as_str()); + return placeholder(name.text()); } } ast::Expr::RefExpr(it) => { 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 27e3ed6bdb52e..0235389763080 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 @@ -314,7 +314,7 @@ impl ImportGroup { PathSegmentKind::SelfKw => ImportGroup::ThisModule, PathSegmentKind::SuperKw => ImportGroup::SuperModule, PathSegmentKind::CrateKw => ImportGroup::ThisCrate, - PathSegmentKind::Name(name) => match name.text().as_str() { + PathSegmentKind::Name(name) => match name.text() { "std" => ImportGroup::Std, "core" => ImportGroup::Std, _ => ImportGroup::ExternCrate, 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 59099056f5c5b..f9251458fd026 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 @@ -582,8 +582,8 @@ fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering { (Some(_), None) => Ordering::Greater, (None, Some(_)) => Ordering::Less, (Some(a_name), Some(b_name)) => { - let a_text = a_name.as_str().trim_start_matches("r#"); - let b_text = b_name.as_str().trim_start_matches("r#"); + let a_text = a_name.trim_start_matches("r#"); + let b_text = b_name.trim_start_matches("r#"); version_sort::version_sort(a_text, b_text) } } @@ -614,15 +614,13 @@ fn use_tree_cmp_by_tree_list_glob_or_alias( .name() .as_ref() .map(ast::Name::text) - .as_ref() - .map_or("_", |a_name| a_name.as_str().trim_start_matches("r#")) + .map_or("_", |a_name| a_name.trim_start_matches("r#")) .cmp( b_rename .name() .as_ref() .map(ast::Name::text) - .as_ref() - .map_or("_", |b_name| b_name.as_str().trim_start_matches("r#")), + .map_or("_", |b_name| b_name.trim_start_matches("r#")), ), }, }; 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 101046cf54436..55d602ce2e4f0 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 @@ -651,7 +651,7 @@ fn find_trait_for_assoc_item( }); for name in names { - if assoc_item_name.as_str() == name.as_str() { + if assoc_item_name == name.as_str() { // It is fine to return the first match because in case of // multiple possibilities, the exact trait must be disambiguated // in the definition of trait being implemented, so this search diff --git a/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs b/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs index c8607a8099b91..09a270c143888 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs @@ -102,8 +102,7 @@ impl RaFixtureAnalysis { else { return false; }; - segment1.text_non_mutable() == "rust_analyzer" - && segment2.text_non_mutable() == "rust_fixture" + segment1.text() == "rust_analyzer" && segment2.text() == "rust_fixture" }) }); if !has_rust_fixture_attr { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index b89c2fdf4ad87..775b85c479662 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -734,7 +734,7 @@ fn source_edit_from_def<'db>( // special cases required for renaming fields/locals in Record patterns if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) { if let Some(name_ref) = pat_field.name_ref() { - if new_name.as_str() == name_ref.text().as_str().trim_start_matches("r#") + if new_name.as_str() == name_ref.text().trim_start_matches("r#") && pat.at_token().is_none() { // Foo { field: ref mut local } -> Foo { ref mut field } 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 b688cb188d58d..6a492a54798ce 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -119,13 +119,13 @@ impl FileReferenceNode { _ => None, } } - pub fn text(&self) -> syntax::TokenText<'_> { + pub fn text(&self) -> &str { match self { FileReferenceNode::NameRef(name_ref) => name_ref.text(), FileReferenceNode::Name(name) => name.text(), FileReferenceNode::Lifetime(lifetime) => lifetime.text(), FileReferenceNode::FormatStringEntry(it, range) => { - syntax::TokenText::borrowed(&it.text()[*range - it.syntax().text_range().start()]) + &it.text()[*range - it.syntax().text_range().start()] } } } @@ -751,7 +751,7 @@ impl<'a, 'db> FindUsages<'a, 'db> { insert_type_alias( sema.db, &mut to_process, - name.text().as_str(), + name.text(), def.into(), ); } else { @@ -814,7 +814,7 @@ impl<'a, 'db> FindUsages<'a, 'db> { insert_type_alias( sema.db, &mut to_process, - name.text().as_str(), + name.text(), def.into(), ); } else { diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs index 3dbba0ff2dab4..9d6079202a5b6 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs @@ -155,7 +155,7 @@ impl<'db> Resolver<'_, 'db> { fn path_contains_placeholder(&self, path: &ast::Path) -> bool { if let Some(segment) = path.segment() && let Some(name_ref) = segment.name_ref() - && self.placeholders_by_stand_in.contains_key(name_ref.text().as_str()) + && self.placeholders_by_stand_in.contains_key(name_ref.text()) { return true; } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs index 7a8a6eb84a5fa..89a2d9fa97eba 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs @@ -204,7 +204,7 @@ fn hints_( mut is_trivial: bool, ) -> Option<()> { let is_elided = |lt: &Option| match lt { - Some(lt) => matches!(lt.text().as_str(), "'_"), + Some(lt) => matches!(lt.text(), "'_"), None => true, }; let self_param = self_param.and_then(|it| { @@ -298,12 +298,12 @@ fn hints_( potential_lt_refs.for_each(|(name, ..)| { let name = match name { Some(it) if config.param_names_for_lifetime_elision_hints => { - if let Some(c) = used_names.get_mut(it.text().as_str()) { + if let Some(c) = used_names.get_mut(it.text()) { *c += 1; - format_smolstr!("'{}{c}", it.text().as_str()) + format_smolstr!("'{}{c}", it.text()) } else { - used_names.insert(it.text().as_str().into(), 0); - format_smolstr!("'{}", it.text().as_str()) + used_names.insert(it.text().into(), 0); + format_smolstr!("'{}", it.text()) } } _ => gen_idx_name(), @@ -316,7 +316,7 @@ fn hints_( let output = match potential_lt_refs.as_slice() { [(_, _, lifetime, _), ..] if self_param.is_some() || potential_lt_refs.len() == 1 => { match lifetime { - Some(lt) => match lt.text().as_str() { + Some(lt) => match lt.text() { "'_" => allocated_lifetimes.first().cloned(), "'static" => None, name => Some(name.into()), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index 5da8f2e1624a3..71cc17755288e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -294,7 +294,7 @@ pub(super) fn is_argument_similar_to_param_name( debug_assert!(!argument.is_empty()); debug_assert!(!param_name.is_empty()); let param_name = param_name.split('_'); - let argument = argument.iter().flat_map(|it| it.text_non_mutable().split('_')); + let argument = argument.iter().flat_map(|it| it.text().split('_')); let argument = argument.map(|it| it.strip_prefix("r#").unwrap_or(it)); let prefix_match = zip(argument.clone(), param_name.clone()) @@ -313,7 +313,7 @@ pub(super) fn get_segment_representation( let receiver = method_call_expr.receiver().and_then(|expr| get_segment_representation(&expr)); let name_ref = method_call_expr.name_ref()?; - if INSIGNIFICANT_METHOD_NAMES.contains(&name_ref.text().as_str()) { + if INSIGNIFICANT_METHOD_NAMES.contains(&name_ref.text()) { return receiver; } Some(Either::Left(match receiver { diff --git a/src/tools/rust-analyzer/crates/span/src/ast_id.rs b/src/tools/rust-analyzer/crates/span/src/ast_id.rs index 83a6748c01eaf..369f383dbb935 100644 --- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs +++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs @@ -381,8 +381,8 @@ fn impl_ast_id( let self_ty_name = type_as_name(node.self_ty()); let trait_name = type_as_name(node.trait_()); let data = ImplFileAstId { - self_ty_name: self_ty_name.as_ref().map(|it| it.text_non_mutable()), - trait_name: trait_name.as_ref().map(|it| it.text_non_mutable()), + self_ty_name: self_ty_name.as_ref().map(|it| it.text()), + trait_name: trait_name.as_ref().map(|it| it.text()), }; Some(index_map.new_id(ErasedFileAstIdKind::Impl, data)) } else { @@ -473,7 +473,7 @@ macro_rules! register_has_name_ast_id { $( ast::$ident(node) => { let name = node.$name_method(); - let name = name.as_ref().map_or("", |it| it.text_non_mutable()); + let name = name.as_ref().map_or("", |it| it.text()); let result = ErasedHasNameFileAstId { name, }; @@ -519,7 +519,7 @@ macro_rules! register_assoc_item_ast_id { $( ast::$ident(node) => { let name = $name_callback(node); - let name = name.as_ref().map_or("", |it| it.text_non_mutable()); + let name = name.as_ref().map_or("", |it| it.text()); let properties = ErasedHasNameFileAstId { name, }; diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index 00aeb1dfebf8d..43bca6ed9e63d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -11,7 +11,7 @@ use rowan::{GreenNodeData, GreenTokenData}; use smallvec::{SmallVec, smallvec}; use crate::{ - NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, TokenText, + NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, ast::{ self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds, SyntaxNode, support, @@ -22,30 +22,22 @@ use crate::{ use super::{GenericParam, RangeItem, RangeOp}; impl ast::Lifetime { - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { text_of_first_token(self.syntax()) } } impl ast::Name { - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { text_of_first_token(self.syntax()) } - - pub fn text_non_mutable(&self) -> &str { - first_token(self.syntax().green()).text() - } } impl ast::NameRef { - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { text_of_first_token(self.syntax()) } - pub fn text_non_mutable(&self) -> &str { - first_token(self.syntax().green()).text() - } - pub fn as_tuple_field(&self) -> Option { self.text().parse().ok() } @@ -55,12 +47,12 @@ impl ast::NameRef { } } -fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData { - green_ref.children().next().and_then(NodeOrToken::into_token).unwrap() -} +fn text_of_first_token(node: &SyntaxNode) -> &str { + fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData { + green_ref.children().next().and_then(NodeOrToken::into_token).unwrap() + } -fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> { - TokenText::borrowed(first_token(node.green()).text()) + first_token(node.green()).text() } fn into_comma(it: NodeOrToken) -> Option { @@ -604,7 +596,7 @@ impl NameLike { _ => None, } } - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { match self { NameLike::NameRef(name_ref) => name_ref.text(), NameLike::Name(name) => name.text(), @@ -676,7 +668,7 @@ impl ast::AstNode for NameOrNameRef { } impl NameOrNameRef { - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { match self { NameOrNameRef::Name(name) => name.text(), NameOrNameRef::NameRef(name_ref) => name_ref.text(), diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index 614678536a512..204ebbd2633b3 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -30,7 +30,6 @@ mod syntax_error; mod syntax_node; #[cfg(test)] mod tests; -mod token_text; mod validation; pub mod algo; @@ -54,7 +53,6 @@ pub use crate::{ PreorderWithTokens, RustLanguage, SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, SyntaxToken, SyntaxTreeBuilder, }, - token_text::TokenText, }; pub use parser::{Edition, SyntaxKind, T}; pub use rowan::{ diff --git a/src/tools/rust-analyzer/crates/syntax/src/token_text.rs b/src/tools/rust-analyzer/crates/syntax/src/token_text.rs deleted file mode 100644 index e69deb49ce142..0000000000000 --- a/src/tools/rust-analyzer/crates/syntax/src/token_text.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! Yet another version of owned string, backed by a syntax tree token. - -use std::{cmp::Ordering, fmt, ops}; - -use rowan::GreenToken; -use smol_str::SmolStr; - -pub struct TokenText<'a>(pub(crate) Repr<'a>); - -pub(crate) enum Repr<'a> { - Borrowed(&'a str), - Owned(GreenToken), -} - -impl<'a> TokenText<'a> { - pub fn borrowed(text: &'a str) -> Self { - TokenText(Repr::Borrowed(text)) - } - - pub(crate) fn owned(green: GreenToken) -> Self { - TokenText(Repr::Owned(green)) - } - - pub fn as_str(&self) -> &str { - match &self.0 { - &Repr::Borrowed(it) => it, - Repr::Owned(green) => green.text(), - } - } -} - -impl ops::Deref for TokenText<'_> { - type Target = str; - - fn deref(&self) -> &str { - self.as_str() - } -} -impl AsRef for TokenText<'_> { - fn as_ref(&self) -> &str { - self.as_str() - } -} - -impl From> for String { - fn from(token_text: TokenText<'_>) -> Self { - token_text.as_str().into() - } -} - -impl From> for SmolStr { - fn from(token_text: TokenText<'_>) -> Self { - SmolStr::new(token_text.as_str()) - } -} - -impl PartialEq<&'_ str> for TokenText<'_> { - fn eq(&self, other: &&str) -> bool { - self.as_str() == *other - } -} -impl PartialEq> for &'_ str { - fn eq(&self, other: &TokenText<'_>) -> bool { - other == self - } -} -impl PartialEq for TokenText<'_> { - fn eq(&self, other: &String) -> bool { - self.as_str() == other.as_str() - } -} -impl PartialEq> for String { - fn eq(&self, other: &TokenText<'_>) -> bool { - other == self - } -} -impl PartialEq for TokenText<'_> { - fn eq(&self, other: &TokenText<'_>) -> bool { - self.as_str() == other.as_str() - } -} -impl Eq for TokenText<'_> {} -impl Ord for TokenText<'_> { - fn cmp(&self, other: &Self) -> Ordering { - self.as_str().cmp(other.as_str()) - } -} -impl PartialOrd for TokenText<'_> { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl fmt::Display for TokenText<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.as_str(), f) - } -} -impl fmt::Debug for TokenText<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.as_str(), f) - } -} From 29133dfb934d92fc5cdfadea05d6d8063b154e89 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 2 Aug 2026 09:57:10 +0530 Subject: [PATCH 57/94] Fix tokenText removal fallout --- .../rust-analyzer/crates/hir-def/src/attrs.rs | 2 +- .../crates/hir-def/src/expr_store/lower.rs | 8 ++++---- .../crates/hir-def/src/expr_store/lower/asm.rs | 4 ++-- .../hir-def/src/expr_store/lower/generics.rs | 2 +- .../hir-expand/src/builtin/derive_macro.rs | 16 ++++++++-------- .../rust-analyzer/crates/hir-expand/src/name.rs | 4 ++-- .../crates/hir/src/source_analyzer.rs | 6 +++--- .../src/handlers/convert_closure_to_fn.rs | 2 +- .../src/handlers/convert_match_to_let_else.rs | 3 +-- .../src/handlers/convert_range_for_to_while.rs | 2 +- .../convert_tuple_struct_to_named_struct.rs | 4 ++-- .../ide-assists/src/handlers/extract_function.rs | 4 ++-- .../handlers/extract_struct_from_enum_variant.rs | 2 +- .../src/handlers/extract_type_alias.rs | 9 ++++----- .../src/handlers/generate_blanket_trait_impl.rs | 4 ++-- .../generate_default_from_enum_variant.rs | 2 +- .../src/handlers/generate_enum_is_method.rs | 2 +- .../handlers/generate_enum_projection_method.rs | 2 +- .../src/handlers/generate_enum_variant.rs | 2 +- .../src/handlers/generate_function.rs | 2 +- .../src/handlers/generate_getter_or_setter.rs | 4 ++-- .../ide-assists/src/handlers/generate_impl.rs | 2 +- .../src/handlers/generate_mut_trait_impl.rs | 2 +- .../generate_single_field_struct_from.rs | 8 ++++---- .../src/handlers/generate_trait_from_impl.rs | 4 ++-- .../src/handlers/inline_type_alias.rs | 2 +- .../ide-assists/src/handlers/merge_match_arms.rs | 2 +- .../src/handlers/replace_method_eager_lazy.rs | 4 ++-- .../crates/ide-assists/src/utils.rs | 4 ++-- .../crates/ide-db/src/imports/import_assets.rs | 6 +++--- .../crates/ide-db/src/path_transform.rs | 2 +- .../rust-analyzer/crates/ide-db/src/rename.rs | 4 ++-- .../rust-analyzer/crates/ide/src/doc_links.rs | 2 +- .../crates/ide/src/file_structure.rs | 2 +- .../crates/ide/src/inlay_hints/param_name.rs | 6 +++--- .../crates/ide/src/navigation_target.rs | 2 +- .../rust-analyzer/crates/syntax/src/ast/edit.rs | 2 +- 37 files changed, 69 insertions(+), 71 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs index d55509e2f0884..c330b374f71f9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -201,7 +201,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow< let segment4 = segment4.and_then(|it| it.segment()?.name_ref()); segment1.text() == "test" && segment3.is_none_or(|it| it.text() == "prelude") - && segment4.is_none_or(|it| matches!(&*it.text(), "core" | "std")) + && segment4.is_none_or(|it| matches!(it.text(), "core" | "std")) }); if is_test { attr_flags.insert(AttrFlags::IS_TEST); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index df4fc6e531466..aaca830088e33 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -665,7 +665,7 @@ impl<'db> ExprCollector<'db> { lifetime: ast::Lifetime, ) -> LifetimeRefId { // FIXME: Keyword check? - let lifetime_ref = match &*lifetime.text() { + let lifetime_ref = match lifetime.text() { "" | "'" => LifetimeRef::Error, "'static" => LifetimeRef::Static, "'_" => LifetimeRef::Placeholder, @@ -1295,7 +1295,7 @@ impl<'db> ExprCollector<'db> { match binder.generic_param_list() { Some(gpl) => gpl .lifetime_params() - .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(<.text()))) + .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(lt.text()))) .collect(), None => ThinVec::default(), } @@ -3175,7 +3175,7 @@ impl<'db> ExprCollector<'db> { name: ast_label .lifetime() .as_ref() - .map_or_else(Name::missing, |lt| Name::new_lifetime(<.text())), + .map_or_else(Name::missing, |lt| Name::new_lifetime(lt.text())), }; self.alloc_label(label, AstPtr::new(&ast_label)) } @@ -3195,7 +3195,7 @@ impl<'db> ExprCollector<'db> { (hygiene_id.syntax_context().parent(self.db), expansion.def) }) }; - let name = Name::new_lifetime(&lifetime.text()); + let name = Name::new_lifetime(lifetime.text()); for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() { match &rib.kind { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs index 63a0594f74c1b..fb0a5b0bf7a71 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs @@ -39,7 +39,7 @@ impl ExprCollector<'_> { Some(InlineAsmRegOrRegClass::Reg(Symbol::intern(string.text()))) } else { reg.name_ref().map(|name_ref| { - InlineAsmRegOrRegClass::RegClass(Symbol::intern(&name_ref.text())) + InlineAsmRegOrRegClass::RegClass(Symbol::intern(name_ref.text())) }) } }; @@ -69,7 +69,7 @@ impl ExprCollector<'_> { continue; } ast::AsmPiece::AsmOperandNamed(op) => { - let name = op.name().map(|name| Symbol::intern(&name.text())); + let name = op.name().map(|name| Symbol::intern(name.text())); if let Some(name) = &name { named_args.insert(name.clone(), slot); named_pos.insert(slot, name.clone()); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs index ce6e73670cba4..65877fb627f24 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs @@ -181,7 +181,7 @@ impl GenericParamsCollector { .map(|lifetime_param| { lifetime_param .lifetime() - .map_or_else(Name::missing, |lt| Name::new_lifetime(<.text())) + .map_or_else(Name::missing, |lt| Name::new_lifetime(lt.text())) }) .collect() }); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index ccd2d6dca29f1..63e57647283cf 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -1184,7 +1184,7 @@ fn coerce_pointee_expand( let new_bounds = bounds.bounds().filter_map(|bound| { let new_bound = substitute_type_bound( bound.clone(), - &pointee_param_name.text(), + pointee_param_name.text(), ADDED_PARAM, ); @@ -1197,7 +1197,7 @@ fn coerce_pointee_expand( let new_bounds_target = if is_pointee { make.name_ref(ADDED_PARAM) } else { - make.name_ref(¶m_name.text()) + make.name_ref(param_name.text()) }; new_predicates.push(make.where_pred( Either::Right( @@ -1240,12 +1240,12 @@ fn coerce_pointee_expand( // If the target type references the pointee, duplicate the bound as whole. // Otherwise, duplicate only bounds that mention the pointee. if let Some(predicate_with_substituted_target) = - substitute_where_pred(&predicate, &pointee_param_name.text(), ADDED_PARAM) + substitute_where_pred(&predicate, pointee_param_name.text(), ADDED_PARAM) { new_predicates.push(predicate_with_substituted_target); } else if let Some(bounds) = predicate.type_bound_list() { let new_bounds = bounds.bounds().filter_map(|bound| { - substitute_type_bound(bound, &pointee_param_name.text(), ADDED_PARAM) + substitute_type_bound(bound, pointee_param_name.text(), ADDED_PARAM) }); new_predicates.push(make.where_pred(Either::Right(pred_target), new_bounds)); } @@ -1259,7 +1259,7 @@ fn coerce_pointee_expand( new_predicates.push( make.where_pred( Either::Right(make.ty_path_from_segments( - [make.path_segment(make.name_ref(&pointee_param_name.text()))], + [make.path_segment(make.name_ref(pointee_param_name.text()))], false, )), [make.type_bound( @@ -1294,7 +1294,7 @@ fn coerce_pointee_expand( .filter_map(|param| { Some(match param { ast::GenericParam::ConstParam(param) => { - ast::GenericArg::ConstArg(make.expr_const_value(¶m.name()?.text())) + ast::GenericArg::ConstArg(make.expr_const_value(param.name()?.text())) } ast::GenericParam::LifetimeParam(param) => { make.lifetime_arg(param.lifetime()?).into() @@ -1303,7 +1303,7 @@ fn coerce_pointee_expand( let name = if pointee_param_idx == type_param_idx { make.name_ref(ADDED_PARAM) } else { - make.name_ref(¶m.name()?.text()) + make.name_ref(param.name()?.text()) }; type_param_idx += 1; make.type_arg(make.ty_path_from_segments([make.path_segment(name)], false)) @@ -1314,7 +1314,7 @@ fn coerce_pointee_expand( make.path_from_segments( [make.generic_ty_path_segment( - make.name_ref(&struct_name.text()), + make.name_ref(struct_name.text()), self_params_for_traits, )], false, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index d91b0f378e191..7968adabbccf2 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -246,14 +246,14 @@ impl AsName for ast::NameRef { fn as_name(&self) -> Name { match self.as_tuple_field() { Some(idx) => Name::new_tuple_field(idx), - None => Name::new_root(&self.text()), + None => Name::new_root(self.text()), } } } impl AsName for ast::Name { fn as_name(&self) -> Name { - Name::new_root(&self.text()) + Name::new_root(self.text()) } } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index e80567641baf3..209091683a01b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -1320,7 +1320,7 @@ impl<'db> SourceAnalyzer<'db> { .first_segment() .and_then(|it| it.name_ref()) .and_then(|name_ref| { - ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) + ToolModule::by_name(db, self.resolver.krate().into(), name_ref.text()) .map(PathResolution::ToolModule) }) .map(|it| (it, None)), @@ -1361,7 +1361,7 @@ impl<'db> SourceAnalyzer<'db> { // in this case we have to check for inert/builtin attributes and tools and prioritize // resolution of attributes over other namespaces if let Some(name_ref) = path.as_single_name_ref() { - let builtin = BuiltinAttr::builtin(&name_ref.text()); + let builtin = BuiltinAttr::builtin(name_ref.text()); if builtin.is_some() { return builtin.map(|it| (PathResolution::BuiltinAttr(it), None)); } @@ -1411,7 +1411,7 @@ impl<'db> SourceAnalyzer<'db> { .first_segment() .and_then(|it| it.name_ref()) .and_then(|name_ref| { - ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) + ToolModule::by_name(db, self.resolver.krate().into(), name_ref.text()) .map(PathResolution::ToolModule) }) .map(|it| (it, None)), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index c9f5e0a4fbede..83effa11820b7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -507,7 +507,7 @@ fn wrap_capture_in_deref_if_needed( capture_kind: CaptureKind, is_ref: bool, ) -> ast::Expr { - let capture_name = make.expr_path(make.path_from_text(&capture_name.text())); + let capture_name = make.expr_path(make.path_from_text(capture_name.text())); if capture_kind == CaptureKind::Move || is_ref { return capture_name; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index 9dffdf3f367c4..db084c6ea21d7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -148,8 +148,7 @@ fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> Syn if let Some(name_ref) = record_pat_field.field_name() { editor.replace( record_pat_field.syntax(), - make.record_pat_field(make.name_ref(&name_ref.text()), binding.clone()) - .syntax(), + make.record_pat_field(make.name_ref(name_ref.text()), binding.clone()).syntax(), ); } } else { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs index 7026b5bafdc7c..ae8f626c5da8a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs @@ -74,7 +74,7 @@ pub(crate) fn convert_range_for_to_while( let mut elements = vec![]; - let var_expr = make.expr_path(make.ident_path(&name.text())); + let var_expr = make.expr_path(make.ident_path(name.text())); let op = ast::BinaryOp::CmpOp(ast::CmpOp::Ord { ordering: ast::Ordering::Less, strict: !inclusive, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index eb74e9107581b..0bb9bf12b12cc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -194,7 +194,7 @@ fn process_struct_name_reference( let range = ctx.sema.original_range_opt(pat.syntax())?.range; let place = cover_edit_range(source.syntax(), range); let elements = vec![ - make.name_ref(&name.text()).syntax().clone().into(), + make.name_ref(name.text()).syntax().clone().into(), make.token(T![:]).into(), make.whitespace(" ").into(), ]; @@ -237,7 +237,7 @@ fn process_struct_name_reference( let range = ctx.sema.original_range_opt(expr.syntax())?.range; let place = cover_edit_range(source.syntax(), range); let elements = vec![ - make.name_ref(&name.text()).syntax().clone().into(), + make.name_ref(name.text()).syntax().clone().into(), make.token(T![:]).into(), make.whitespace(" ").into(), ]; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index c2eb49dde5695..46333ed726388 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -934,7 +934,7 @@ impl FunctionBody { }; // FIXME: make trait arguments - let trait_name = trait_name.map(|name| make.ty_path(make.ident_path(&name.text())).into()); + let trait_name = trait_name.map(|name| make.ty_path(make.ident_path(name.text())).into()); let parent = self.parent()?; let parents = generic_parents(&parent); @@ -1561,7 +1561,7 @@ fn format_function<'db>( old_indent: IndentLevel, make: &SyntaxFactory, ) -> ast::Fn { - let fun_name = make.name(&fun.name.text()); + let fun_name = make.name(fun.name.text()); let params = fun.make_param_list(make, ctx, module, fun.mods.edition); let ret_ty = fun.make_ret_ty(make, ctx, module); let body = make_body(make, ctx, old_indent, fun); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 89c5470c160ff..c1ac4f1724893 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -335,7 +335,7 @@ fn update_variant( // FIXME: replace with a `ast::make` constructor let ty = match generic_args { Some(generic_args) => make.ty(&format!("{name}{generic_args}")), - None => make.ty(&name.text()), + None => make.ty(name.text()), }; // change from a record to a tuple field list diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs index 329f8325b4c12..a378256b598aa 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -145,7 +145,7 @@ fn collect_used_generics<'gp>( .filter_map(|it| match it { ast::GenericArg::LifetimeArg(lt) => { let lt = lt.lifetime()?; - known_generics.iter().find(find_lifetime(<.text())) + known_generics.iter().find(find_lifetime(lt.text())) } _ => None, }), @@ -157,7 +157,7 @@ fn collect_used_generics<'gp>( generics.extend( it.bounds() .filter_map(|it| it.lifetime()) - .filter_map(|lt| known_generics.iter().find(find_lifetime(<.text()))), + .filter_map(|lt| known_generics.iter().find(find_lifetime(lt.text()))), ); } } @@ -166,13 +166,12 @@ fn collect_used_generics<'gp>( generics.extend( it.bounds() .filter_map(|it| it.lifetime()) - .filter_map(|lt| known_generics.iter().find(find_lifetime(<.text()))), + .filter_map(|lt| known_generics.iter().find(find_lifetime(lt.text()))), ); } } ast::Type::RefType(ref_) => generics.extend( - ref_.lifetime() - .and_then(|lt| known_generics.iter().find(find_lifetime(<.text()))), + ref_.lifetime().and_then(|lt| known_generics.iter().find(find_lifetime(lt.text()))), ), ast::Type::ArrayType(ar) => { if let Some(ast::Expr::PathExpr(p)) = ar.const_arg().and_then(|x| x.expr()) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index acd98aed00cee..738f461a1f2fd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -75,12 +75,12 @@ pub(crate) fn generate_blanket_trait_impl( |builder| { let editor = builder.make_editor(traitd.syntax()); let make = editor.make(); - let namety = make.ty_path(make.path_from_text(&name.text())); + let namety = make.ty_path(make.path_from_text(name.text())); let trait_where_clause = traitd.where_clause().map(|it| it.reset_indent()); let bounds = traitd.type_bound_list().and_then(|list| exclude_sized(make, list)); let is_unsafe = traitd.unsafe_token().is_some(); let thisname = this_name(make, &traitd); - let thisty = make.ty_path(make.path_from_text(&thisname.text())); + let thisty = make.ty_path(make.path_from_text(thisname.text())); let indent = traitd.indent_level(); let gendecl = make.generic_param_list([GenericParam::TypeParam(make.type_param( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs index 713d6a3fb708c..07c191b0eb666 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs @@ -76,7 +76,7 @@ pub(crate) fn generate_default_from_enum_variant( fn default_impl(variant_name: ast::Name, adt: &ast::Adt, make: &SyntaxFactory) -> ast::Impl { let impl_ = utils::generate_trait_impl_intransitive(make, adt, make.ty("Default")); - let fn_ = default_fn(&variant_name.text(), make); + let fn_ = default_fn(variant_name.text(), make); let (impl_editor, impl_) = SyntaxEditor::with_ast_node(&impl_); impl_ diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs index 53e77b49474c4..5e2ee772b9a51 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs @@ -141,7 +141,7 @@ impl Method { }; let variant_name = variant.name()?; - let fn_name = format!("is_{}", to_lower_snake_case(&variant_name.text())); + let fn_name = format!("is_{}", to_lower_snake_case(variant_name.text())); Some(Method { pattern_suffix, fn_name, variant_name }) } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs index 8a194ae02bff0..479143c133353 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs @@ -219,7 +219,7 @@ impl Method { fn new(variant: &ast::Variant, fn_name_prefix: &str) -> Option { use itertools::Itertools as _; let variant_name = variant.name()?; - let fn_name = format!("{fn_name_prefix}_{}", to_lower_snake_case(&variant_name.text())); + let fn_name = format!("{fn_name_prefix}_{}", to_lower_snake_case(variant_name.text())); match variant.kind() { ast::StructKind::Record(record) => { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs index fb43e3eaa37e0..73837f486cb30 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs @@ -62,7 +62,7 @@ pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_, ' let editor = builder.make_editor(enum_node.syntax()); let make = editor.make(); let field_list = parent.make_field_list(ctx, make); - let variant = make.variant(None, make.name(&name_ref.text()), field_list, None); + let variant = make.variant(None, make.name(name_ref.text()), field_list, None); if let Some(it) = enum_node.variant_list() { it.add_variant(&editor, &variant); } 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 3bec992252861..13096c6efc37a 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 @@ -269,7 +269,7 @@ impl FunctionBuilder { // If generated function has the name "new" and is an associated function, we generate fn body // as a constructor and assume a "Self" return type. if let Some(body) = - make_fn_body_as_new_function(make, ctx, &fn_name.text(), adt_info, target_edition) + make_fn_body_as_new_function(make, ctx, fn_name.text(), adt_info, target_edition) { ret_type = Some(make.ret_type(make.ty_path(make.ident_path("Self")).into())); should_focus_return_type = false; 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 7e5d5cec71bc5..b21e60876296f 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 @@ -225,7 +225,7 @@ fn generate_getter_from_info( ( make.ty_ref(record_field_info.field_ty.clone(), true), make.expr_ref( - make.expr_field(self_expr, &record_field_info.field_name.text()).into(), + make.expr_field(self_expr, record_field_info.field_name.text()).into(), true, ), ) @@ -250,7 +250,7 @@ fn generate_getter_from_info( make.expr_ref( make.expr_field( make.expr_path(make.ident_path("self")), - &record_field_info.field_name.text(), + record_field_info.field_name.text(), ) .into(), false, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index ab0eb56fcf19d..ecff6267bb995 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -185,7 +185,7 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_, '_> None, None, false, - make.ty(&name.text()), + make.ty(name.text()), make.ty_placeholder(), None, None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index fd095dd9b2aab..6858b62f8d22d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -210,7 +210,7 @@ fn get_trait_mut(apply_trait: &hir::Trait, famous: FamousDefs<'_, '_>) -> Option } fn process_method_name(name: ast::Name) -> Option<(ast::Name, &'static str)> { - let new_name = match &*name.text() { + let new_name = match name.text() { "index" => "index_mut", "as_ref" => "as_mut", "borrow" => "borrow_mut", diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 23ce72670332d..242712ff2ed6d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -88,10 +88,10 @@ pub(crate) fn generate_single_field_struct_from( false, )); - let ty = make.ty(&strukt_name.text()); + let ty = make.ty(strukt_name.text()); let constructor = - make_adt_constructor(names.as_deref(), constructors, &main_field_name, make); + make_adt_constructor(names.as_deref(), constructors, main_field_name, make); let body = make.block_expr([], Some(constructor)); let fn_ = make @@ -104,7 +104,7 @@ pub(crate) fn generate_single_field_struct_from( make.param_list( None, [make.param( - make.path_pat(make.path_from_text(&main_field_name)), + make.path_pat(make.path_from_text(main_field_name)), main_field_ty, )], ), @@ -162,7 +162,7 @@ fn make_adt_constructor( ) -> ast::Expr { if let Some(names) = names { let fields = make.record_expr_field_list(names.iter().zip(constructors).map( - |(name, initializer)| make.record_expr_field(make.name_ref(&name.text()), initializer), + |(name, initializer)| make.record_expr_field(make.name_ref(name.text()), initializer), )); make.record_expr(make.path_from_text("Self"), fields).into() } else { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index 12afd9ae6affa..354447cf3356e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -117,7 +117,7 @@ pub(crate) fn generate_trait_from_impl( let params = used_params(&impl_ast, make, ctx); let trait_ast = make.trait_( false, - &trait_name(&impl_assoc_items, make).text(), + trait_name(&impl_assoc_items, make).text(), params.clone(), impl_ast.where_clause(), trait_items, @@ -204,7 +204,7 @@ fn trait_name(items: &ast::AssocItemList, make: &SyntaxFactory) -> ast::Name { fn_names .next() .and_then(|name| { - fn_names.next().is_none().then(|| make.name(&stdx::to_camel_case(&name.text()))) + fn_names.next().is_none().then(|| make.name(&stdx::to_camel_case(name.text()))) }) .unwrap_or_else(|| make.name("NewTrait")) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index bb76e2743c377..f5d5400404cf0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -140,7 +140,7 @@ pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_, '_>) let src = adt.source(ctx.db())?.value; let name = src.name()?; let generic_params = src.generic_param_list(); - let name_ref = make.name_ref(&name.text()); + let name_ref = make.name_ref(name.text()); let segment = match generic_params { Some(params) => { make.path_segment_generics(name_ref, params.to_generic_args(&make)) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs index f41769150c042..5060886cfa9ce 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs @@ -165,7 +165,7 @@ fn get_arm_types<'db>( { let pat_type = ctx.sema.type_of_binding_in_pat(ident_pat); - map.insert(name.text().to_string(), pat_type); + map.insert(name.text().to_owned(), pat_type); } } _ => (), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 17ee8597c1020..a414db0a6c8d0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -39,7 +39,7 @@ pub(crate) fn replace_with_lazy_method( let (_, receiver_ty) = callable.receiver_param(ctx.sema.db)?; let n_params = callable.n_params() + 1; - let method_name_lazy = lazy_method_name(&method_name.text()); + let method_name_lazy = lazy_method_name(method_name.text()); receiver_ty.iterate_method_candidates_with_traits( ctx.sema.db, @@ -156,7 +156,7 @@ pub(crate) fn replace_with_eager_method( } let method_name_text = method_name.text(); - let method_name_eager = eager_method_name(&method_name_text)?; + let method_name_eager = eager_method_name(method_name_text)?; receiver_ty.iterate_method_candidates_with_traits( ctx.sema.db, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index ad46c61935f65..388aac19b40e8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -550,7 +550,7 @@ fn has_any_fn(imp: &ast::Impl, names: &[String]) -> bool { for item in il.assoc_items() { if let ast::AssocItem::Fn(f) = item && let Some(name) = f.name() - && names.iter().any(|n| n.eq_ignore_ascii_case(&name.text())) + && names.iter().any(|n| n.eq_ignore_ascii_case(name.text())) { return true; } @@ -664,7 +664,7 @@ fn generate_impl_inner( .zip(generic_params.as_ref()) .and_then(|(trait_, params)| generic_param_associated_bounds(make, adt, trait_, params)); - let ty: ast::Type = make.ty_path(make.ident_path(&adt.name().unwrap().text())).into(); + let ty: ast::Type = make.ty_path(make.ident_path(adt.name().unwrap().text())).into(); let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))); match trait_ { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index f5dff47acf9ab..422648c8d6c55 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -979,7 +979,7 @@ impl<'db> ImportCandidate<'db> { return None; } let after = std::iter::successors(path.parent_path(), |it| it.parent_path()) - .map(|seg| seg.segment()?.name_ref().map(|name| Name::new_root(&name.text()))) + .map(|seg| seg.segment()?.name_ref().map(|name| Name::new_root(name.text()))) .collect::>()?; path_import_candidate( sema, @@ -993,7 +993,7 @@ impl<'db> ImportCandidate<'db> { fn for_name(sema: &Semantics<'db, RootDatabase>, name: &ast::Name) -> Option { if sema .scope(name.syntax())? - .speculative_resolve(&make::ext::ident_path(&name.text())) + .speculative_resolve(&make::ext::ident_path(name.text())) .is_some() { return None; @@ -1033,7 +1033,7 @@ fn path_import_candidate<'db>( if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) { let qualifier = qualifier .segments() - .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text()))) + .map(|seg| seg.name_ref().map(|name| Name::new_root(name.text()))) .collect::>>()?; ImportCandidate::Path(PathImportCandidate { qualifier, 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 55d602ce2e4f0..ff32badd7f14a 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 @@ -536,7 +536,7 @@ impl Ctx<'_> { let name = ident_pat.name()?; let make = editor.make(); - let temp_path = make.path_from_text(&name.text()); + let temp_path = make.path_from_text(name.text()); let resolution = self.source_scope.speculative_resolve(&temp_path)?; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index 775b85c479662..16224ae5ee0b2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -459,7 +459,7 @@ fn rename_field_constructors( }; expr.record_expr_field_list()?.fields().find_map(|record_field| { if record_field.name_ref().is_none() - && Name::new_root(&record_field.field_name()?.text()) == old_name + && Name::new_root(record_field.field_name()?.text()) == old_name && let ast::Expr::PathExpr(field_name) = record_field.expr()? { field_name.path() @@ -747,7 +747,7 @@ fn source_edit_from_def<'db>( .text_range() .cover_offset(pat.syntax().text_range().start()), ); - edit.replace(name_range, name_ref.text().to_string()); + edit.replace(name_range, name_ref.text().to_owned()); } else { // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 } // Foo { field: ref mut local } -> Foo { field: ref mut new_name } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index 70d05cd3b5dcc..de8cf971da0eb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -282,7 +282,7 @@ pub(crate) fn token_as_doc_comment(doc_token: &SyntaxToken) -> Option TextSize::try_from(comment.prefix().len()).ok(), ast::String(string) => { doc_token.parent_ancestors().find_map(ast::Attr::cast).filter(|attr| attr.simple_name().as_deref() == Some("doc"))?; - if doc_token.parent_ancestors().find_map(ast::MacroCall::cast).filter(|mac| mac.path().and_then(|p| p.segment()?.name_ref()).as_ref().map(|n| n.text()).as_deref() == Some("include_str")).is_some() { + if doc_token.parent_ancestors().find_map(ast::MacroCall::cast).filter(|mac| mac.path().and_then(|p| p.segment()?.name_ref()).as_ref().map(|n| n.text()) == Some("include_str")).is_some() { return None; } string.open_quote_text_range().map(|it| it.len()) diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index 21254fc4d6a22..1a85342dc905c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -106,7 +106,7 @@ fn structure_node(node: &SyntaxNode, config: &FileStructureConfig) -> Option bool { (|| match sema.resolve_path(path)? { hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => { - Some(to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name) + Some(to_lower_snake_case(path.segment()?.name_ref()?.text()) == param_name) } hir::PathResolution::Def(hir::ModuleDef::Function(_) | hir::ModuleDef::EnumVariant(_)) => { - if to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name { + if to_lower_snake_case(path.segment()?.name_ref()?.text()) == param_name { return Some(true); } let qual = path.qualifier()?; match sema.resolve_path(&qual)? { hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => { - Some(to_lower_snake_case(&qual.segment()?.name_ref()?.text()) == param_name) + Some(to_lower_snake_case(qual.segment()?.name_ref()?.text()) == param_name) } _ => None, } diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 125b2f495acca..c3d620a35700b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -190,7 +190,7 @@ impl NavigationTarget { kind: SymbolKind, ) -> UpmappingResult { let name = - value.name().map(|it| Symbol::intern(&it.text())).unwrap_or_else(|| sym::underscore); + value.name().map(|it| Symbol::intern(it.text())).unwrap_or_else(|| sym::underscore); orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( |(FileRange { file_id, range: full_range }, focus_range)| { diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs index 080f9a7c6b175..852b13fc7a3d2 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs @@ -360,7 +360,7 @@ impl ast::RecordExprField { // shorthand `{ x }` → expand to `{ x: expr }` let new_field = editor .make() - .record_expr_field(editor.make().name_ref(&name_ref.text()), Some(expr)); + .record_expr_field(editor.make().name_ref(name_ref.text()), Some(expr)); editor.replace(self.syntax(), new_field.syntax()); } } From fa05017d11beaa0fe5178813a3b45d449255f570 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 2 Aug 2026 10:01:05 +0530 Subject: [PATCH 58/94] Remove absolute versioning in rowan import --- src/tools/rust-analyzer/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 693a819052634..a56d9770f5b3e 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -123,7 +123,7 @@ process-wrap = { version = "9.1.0", features = ["std"] } pulldown-cmark-to-cmark = "10.0.4" pulldown-cmark = { version = "0.9.6", default-features = false } rayon = "1.10.0" -rowan = "=0.17.0" +rowan = "0.17.0" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it salsa = { version = "0.27.0", default-features = false, features = [ From b1ea7ced1a399003afb795e49a21f8b58f125bd9 Mon Sep 17 00:00:00 2001 From: Musteab Date: Sun, 2 Aug 2026 16:03:56 +0800 Subject: [PATCH 59/94] Capture the components array and check it for rust-analyzer --- .../rust-analyzer/editors/code/src/bootstrap.ts | 13 ++++++++----- .../editors/code/tests/unit/bootstrap.test.ts | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts index 98c2b359bf2bc..440f21cbe3c50 100644 --- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -176,13 +176,16 @@ async function fileExists(uri: vscode.Uri) { ); } -// Matches a `components` array that lists `rust-analyzer`. The elements are matched with -// `[^\]]` rather than `.` so that the array may be spread over several lines, which is just -// as valid TOML as keeping it on one. TOML strings come in both quote flavours. -const RA_COMPONENT_RE = /components\s*=\s*\[[^\]]*["']rust-analyzer["'][^\]]*\]/; +// Captures the elements of a `components` array. They are matched with `[^\]]` rather than `.` +// so that the array may be spread over several lines, which is just as valid TOML as keeping it +// on one, while still stopping at the end of the array. +const COMPONENTS_RE = /components\s*=\s*\[(?[^\]]*)\]/; +// TOML strings come in both quote flavours. +const RA_COMPONENT_RE = /["']rust-analyzer["']/; function declaresRaComponent(toolchainFileContents: string): boolean { - return RA_COMPONENT_RE.test(toolchainFileContents); + const components = toolchainFileContents.match(COMPONENTS_RE)?.groups?.["components"]; + return components !== undefined && RA_COMPONENT_RE.test(components); } async function hasToolchainFileWithRaDeclared(uri: vscode.Uri): Promise { diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts index 8d348b9ccbd5f..259428da41dbf 100644 --- a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts +++ b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts @@ -158,7 +158,7 @@ channel = "1.88" components = [ "cargo", ] -path = "/opt/rust-analyzer" +# add "rust-analyzer" here to use the matching server `, ), ); From 9cebc266edcab0a0f8679a0bd64864a01171740f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 2 Aug 2026 20:00:57 +0800 Subject: [PATCH 60/94] fix: add reference for same name param coerce matches Example --- ```rust fn ref_arg(x: &[i32]) {} fn foo(x: [i32; 2]) { ref_ar$0 } ``` **Before this PR** ```rust fn ref_arg(x: &[i32]) {} fn foo(x: [i32; 2]) { ref_arg(${1:x});$0 } ``` **After this PR** ```rust fn ref_arg(x: &[i32]) {} fn foo(x: [i32; 2]) { ref_arg(${1:&x});$0 } ``` --- .../ide-completion/src/render/function.rs | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 4f70a90affbdf..eb6331b68e101 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -288,14 +288,17 @@ pub(super) fn add_call_parens<'b>( } fn ref_of_param(ctx: &CompletionContext<'_, '_>, arg: &str, ty: &hir::Type<'_>) -> &'static str { - if let Some(derefed_ty) = ty.as_reference_inner() { + if ty.is_reference() { + let mutability = hir::Mutability::from_mutable(ty.is_mutable_reference()); + let ref_prefix = if mutability.is_mut() { "&mut " } else { "&" }; + for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) { if name.as_str() == arg { - return if local.ty(ctx.db) == derefed_ty { - if ty.is_mutable_reference() { "&mut " } else { "&" } - } else { - "" - }; + let local_ty = local.ty(ctx.db).instantiate_with_errors(); + let added_ref = local_ty.add_reference(ctx.db, mutability); + let needs_ref = + !local_ty.could_coerce_to(ctx.db, ty) && added_ref.could_coerce_to(ctx.db, ty); + return if needs_ref { ref_prefix } else { "" }; } } } @@ -473,7 +476,7 @@ fn bar(s: &S) { r#" struct S {} impl S { - fn foo(&self, x: i32) { + fn foo(&self, x: i32, y: &i32) { $0 } } @@ -481,8 +484,8 @@ impl S { r#" struct S {} impl S { - fn foo(&self, x: i32) { - self.foo(${1:x});$0 + fn foo(&self, x: i32, y: &i32) { + self.foo(${1:x}, ${2:y});$0 } } "#, @@ -561,6 +564,24 @@ fn main() { let x = Foo {}; ref_arg(${1:&x});$0 } +"#, + ); + check_edit( + "ref_arg", + r#" +//- minicore: coerce_unsized +fn ref_arg(x: &[i32]) {} +fn main() { + let x = [2]; + ref_ar$0 +} +"#, + r#" +fn ref_arg(x: &[i32]) {} +fn main() { + let x = [2]; + ref_arg(${1:&x});$0 +} "#, ); } From 08e0d56015c55cf815db93be20c9db7b32b07a03 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 2 Aug 2026 20:07:54 +0800 Subject: [PATCH 61/94] Use as_reference() api --- .../rust-analyzer/crates/ide-completion/src/render/function.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index eb6331b68e101..b3ec60d4fafb7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -288,8 +288,7 @@ pub(super) fn add_call_parens<'b>( } fn ref_of_param(ctx: &CompletionContext<'_, '_>, arg: &str, ty: &hir::Type<'_>) -> &'static str { - if ty.is_reference() { - let mutability = hir::Mutability::from_mutable(ty.is_mutable_reference()); + if let Some((_, mutability)) = ty.as_reference() { let ref_prefix = if mutability.is_mut() { "&mut " } else { "&" }; for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) { From 95f1a80a1669e15babdc0bc5bc5586b57d8d783d Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 2 Aug 2026 21:20:47 +0800 Subject: [PATCH 62/94] fix: parse postfix range inside closure in access Example --- **Before this PR** ```text EXPR_STMT CLOSURE_EXPR PARAM_LIST PIPE "|" PIPE "|" RANGE_EXPR LITERAL INT_NUMBER "1" DOT2 ".." WHITESPACE " " ERROR DOT "." EXPR_STMT CALL_EXPR PATH_EXPR PATH PATH_SEGMENT NAME_REF IDENT "method" ``` **After this PR** ```rust METHOD_CALL_EXPR CLOSURE_EXPR PARAM_LIST PIPE "|" PIPE "|" RANGE_EXPR LITERAL INT_NUMBER "1" DOT2 ".." WHITESPACE " " DOT "." NAME_REF IDENT "method" ``` --- .../crates/parser/src/grammar/expressions.rs | 11 +++- .../parser/test_data/generated/runner.rs | 4 ++ .../ok/closure_postfix_range_method_call.rast | 53 +++++++++++++++++++ .../ok/closure_postfix_range_method_call.rs | 4 ++ 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rs diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index 3f341c2ab846e..9f3de3b921c6e 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -299,8 +299,16 @@ fn expr_bp( // match 1.. { _ => () }; // match a.b()..S { _ => () }; // } + + // test closure_postfix_range_method_call + // fn foo() { + // || 1.. .method(); + // || 1.. .field; + // } + let has_access_after = p.at(T![.]) && p.nth_at(1, SyntaxKind::IDENT); + let struct_forbidden = r.forbid_structs && p.at(T!['{']); let has_trailing_expression = - p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(T!['{'])); + p.at_ts(EXPR_FIRST) && !has_access_after && !struct_forbidden; if !has_trailing_expression { // no RHS lhs = m.complete(p, RANGE_EXPR); @@ -382,6 +390,7 @@ fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLik // } let has_access_after = p.at(T![.]) && p.nth_at(1, SyntaxKind::IDENT); let struct_forbidden = r.forbid_structs && p.at(T!['{']); + // NOTE: Similar logic `is_range` flag in expr_bp() if p.at_ts(EXPR_FIRST) && !has_access_after && !struct_forbidden { expr_bp(p, None, r, 2); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index 8104d28bafdf0..22b5684581252 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -121,6 +121,10 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/closure_params.rs"); } #[test] + fn closure_postfix_range_method_call() { + run_and_expect_no_errors("test_data/parser/inline/ok/closure_postfix_range_method_call.rs"); + } + #[test] fn closure_range_method_call() { run_and_expect_no_errors("test_data/parser/inline/ok/closure_range_method_call.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rast new file mode 100644 index 0000000000000..555d312e33897 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rast @@ -0,0 +1,53 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + METHOD_CALL_EXPR + CLOSURE_EXPR + PARAM_LIST + PIPE "|" + PIPE "|" + WHITESPACE " " + RANGE_EXPR + LITERAL + INT_NUMBER "1" + DOT2 ".." + WHITESPACE " " + DOT "." + NAME_REF + IDENT "method" + ARG_LIST + L_PAREN "(" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + FIELD_EXPR + CLOSURE_EXPR + PARAM_LIST + PIPE "|" + PIPE "|" + WHITESPACE " " + RANGE_EXPR + LITERAL + INT_NUMBER "1" + DOT2 ".." + WHITESPACE " " + DOT "." + NAME_REF + IDENT "field" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rs new file mode 100644 index 0000000000000..71429be2caad5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rs @@ -0,0 +1,4 @@ +fn foo() { + || 1.. .method(); + || 1.. .field; +} From 95b4e46e02f4003130165a3843f9b110f4cac07f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 2 Aug 2026 22:42:49 +0800 Subject: [PATCH 63/94] minor: variant eval error use source instead of node debug Example --- ```rust enum E { A$0 = {} } ``` **Before this PR** ```rust A = BlockExpr(BlockExpr { syntax: BLOCK_EXPR@29..31 }) ``` **After this PR** ```rust A = {} ``` --- .../crates/ide/src/hover/render.rs | 2 +- .../crates/ide/src/hover/tests.rs | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index f26b99292929e..f70783fd3c0c4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -509,7 +509,7 @@ pub(super) fn definition( Some(if it >= 10 { format!("{it} ({it:#X})") } else { format!("{it}") }) } Err(err) => { - let res = it.value(db).map(|it| format!("{it:?}")); + let res = it.value(db).map(|it| it.to_string()); if env::var_os("RA_DEV").is_some() { let res = res.as_deref().unwrap_or(""); Some(format!( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 89f1cf2fc1e11..baa95e741df3b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -5620,6 +5620,30 @@ enum E { This is a doc "#]], ); + // const eval failed test + check( + r#" +#[repr(u8)] +enum E { + A$0 = {}, +} +"#, + expect![[r#" + *A* + + ```rust + ra_test_fixture::E + ``` + + ```rust + A = {} + ``` + + --- + + size = 1, align = 1, no Drop + "#]], + ); } #[test] From dc281b4ad111ceab31f826faab21986012b4f7e7 Mon Sep 17 00:00:00 2001 From: George Ogden Date: Sat, 1 Aug 2026 14:43:11 +0100 Subject: [PATCH 64/94] Correctly handle unlinked module edge cases Escape keywords when used as module names Display modules with non-identifier names using #[path = "..."] syntax --- src/tools/rust-analyzer/Cargo.lock | 1 + .../src/handlers/unlinked_file.rs | 124 ++++++++++++++++-- .../crates/ide-diagnostics/src/lib.rs | 4 +- .../rust-analyzer/crates/syntax/Cargo.toml | 1 + .../rust-analyzer/crates/syntax/src/lib.rs | 4 + .../rust-analyzer/crates/syntax/src/utils.rs | 19 +++ 6 files changed, 142 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 7a2e2d493b59a..fd0790f4a484b 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2714,6 +2714,7 @@ dependencies = [ "expect-test", "itertools 0.15.0", "parser", + "ra-ap-rustc_lexer", "rayon", "rowan", "rustc-hash 2.1.2", diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs index dc6ae6f08ba5e..376fc4fbdf78d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -3,14 +3,15 @@ use std::iter; use hir::crate_def_map; -use hir::{InFile, ModuleSource}; +use hir::{EditionedFileId, InFile, ModuleSource}; use ide_db::text_edit::TextEdit; use ide_db::{FileId, FileRange, base_db::SourceDatabase, source_change::SourceChange}; use ide_db::{base_db, line_index}; use paths::Utf8Component; use syntax::{ - AstNode, TextRange, + AstNode, Edition, TextRange, ast::{self, HasModuleItem, HasName, edit::IndentLevel}, + utils::{is_identifier, is_raw_identifier}, }; use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity, fix}; @@ -22,10 +23,11 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity, fi pub(crate) fn unlinked_file( ctx: &DiagnosticsContext<'_, '_>, acc: &mut Vec, - file_id: FileId, + editioned_file_id: EditionedFileId, ) { + let file_id = editioned_file_id.file_id(ctx.sema.db); let mut range = TextRange::up_to(line_index(ctx.sema.db, file_id).len()); - let fixes = fixes(ctx, file_id, range); + let fixes = fixes(ctx, editioned_file_id, range); // FIXME: This is a hack for the vscode extension to notice whether there is an autofix or not before having to resolve diagnostics. // This is to prevent project linking popups from appearing when there is an autofix. https://github.com/rust-lang/rust-analyzer/issues/14523 let message = if fixes.is_none() { @@ -74,13 +76,15 @@ pub(crate) fn unlinked_file( fn fixes( ctx: &DiagnosticsContext<'_, '_>, - file_id: FileId, + editioned_file_id: EditionedFileId, trigger_range: TextRange, ) -> Option> { // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file, // suggest that as a fix. let db = ctx.sema.db; + let file_id = editioned_file_id.file_id(db); + let edition = editioned_file_id.edition(db); let source_root = ctx.sema.db.file_source_root(file_id).source_root_id(db); let source_root = ctx.sema.db.source_root(source_root).source_root(db); @@ -136,6 +140,7 @@ fn fixes( return make_fixes( parent_file_id.file_id(ctx.sema.db), source, + edition, &module_name, trigger_range, ); @@ -169,6 +174,7 @@ fn fixes( return make_fixes( parent_id, module.definition_source(ctx.sema.db).value, + edition, &module_name, trigger_range, ); @@ -193,6 +199,7 @@ fn fixes( return make_fixes( parent_file_id.file_id(ctx.sema.db), source, + edition, &module_name, trigger_range, ); @@ -202,9 +209,26 @@ fn fixes( None } +/// Convert a module name along with its visibility to code. +/// In most cases, this just adds the visibility and keyword beforehand, +/// but the exceptions are non-identifiers and keywords. +fn format_mod_name(mod_name: &str, visibility_and_keyword: &str, edition: Edition) -> String { + if is_identifier(mod_name, edition) { + format!("{visibility_and_keyword} {mod_name};") + } else { + if is_raw_identifier(mod_name, edition) { + format!("{visibility_and_keyword} r#{mod_name};") + } else { + let file_name = format!("{mod_name}.rs"); + format!("#[path = {file_name:?}]\n{visibility_and_keyword} mod_name;") + } + } +} + fn make_fixes( parent_file_id: FileId, source: ModuleSource, + edition: Edition, new_mod_name: &str, trigger_range: TextRange, ) -> Option> { @@ -212,9 +236,9 @@ fn make_fixes( matches!(item, ast::Item::Module(m) if m.item_list().is_none()) } - let mod_decl = format!("mod {new_mod_name};"); - let pub_mod_decl = format!("pub mod {new_mod_name};"); - let pub_crate_mod_decl = format!("pub(crate) mod {new_mod_name};"); + let mod_decl = format_mod_name(new_mod_name, "mod", edition); + let pub_mod_decl = format_mod_name(new_mod_name, "pub mod", edition); + let pub_crate_mod_decl = format_mod_name(new_mod_name, "pub(crate) mod", edition); let mut mod_decl_builder = TextEdit::builder(); let mut pub_mod_decl_builder = TextEdit::builder(); @@ -545,6 +569,90 @@ mod bar { //- /main.rs include!("bar/foo/mod.rs"); //- /bar/foo/mod.rs +"#, + ); + } + + #[test] + fn unlinked_file_with_strict_keyword_move() { + check_fix( + r#" +//- /main.rs +//- /move.rs +$0 +"#, + r#" +mod r#move; +"#, + ); + } + + #[test] + fn unlinked_file_with_weak_keyword_safe() { + check_fix( + r#" +//- /main.rs +//- /safe.rs +$0 +"#, + r#" +mod safe; +"#, + ); + } + + #[test] + fn unlinked_file_with_reserved_keyword_abstract() { + check_fix( + r#" +//- /main.rs +//- /abstract.rs +$0 +"#, + r#" +mod r#abstract; +"#, + ); + } + + #[test] + fn unlinked_file_with_unescaped_keyword_crate() { + check_fix( + r#" +//- /main.rs +//- /crate.rs +$0 +"#, + r#"#[path = "crate.rs"] +mod mod_name; +"#, + ); + } + + #[test] + fn unlinked_invalid_symbol_in_module_name() { + check_fix( + r#" +//- /main.rs +//- /my-file.rs +$0 +"#, + r#"#[path = "my-file.rs"] +mod mod_name; +"#, + ); + } + + #[test] + fn unlinked_numeric_module_name() { + check_fix( + r#" +//- /main.rs +//- /0000.rs +$0 +"#, + r#"#[path = "0000.rs"] +mod mod_name; "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 26e4a84d2ee35..d4d36e87f4709 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -435,9 +435,7 @@ pub fn semantic_diagnostics( m.diagnostics(db, &mut diags, config.style_lints); } } - None => { - handlers::unlinked_file::unlinked_file(&ctx, &mut res, editioned_file_id.file_id(db)) - } + None => handlers::unlinked_file::unlinked_file(&ctx, &mut res, editioned_file_id), } for diag in diags { diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index e65836ed8dcb4..a9df1acdae9a7 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -15,6 +15,7 @@ doctest = false [dependencies] either.workspace = true itertools.workspace = true +ra-ap-rustc_lexer.workspace = true rowan.workspace = true rustc-hash.workspace = true rustc-literal-escaper.workspace = true diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index 614678536a512..548b7ac909963 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -21,8 +21,12 @@ #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_lexer as rustc_lexer; #[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_lexer; mod parsing; mod ptr; diff --git a/src/tools/rust-analyzer/crates/syntax/src/utils.rs b/src/tools/rust-analyzer/crates/syntax/src/utils.rs index d1f60f0b71bcc..9538ae2b90792 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/utils.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/utils.rs @@ -1,6 +1,25 @@ //! A set of utils methods to reuse on other abstraction levels use crate::SyntaxKind; +use rustc_lexer; + +#[inline] +/// Checks that the name is an identifier. +/// This also means that it is not a strict keyword. +/// But it may be a weak keyword. +pub fn is_identifier(name: &str, edition: parser::Edition) -> bool { + if rustc_lexer::is_ident(name) { + if let Some(syntax_kind) = SyntaxKind::from_keyword(name, edition) + && syntax_kind.is_strict_keyword(edition) + { + false + } else { + true + } + } else { + false + } +} #[inline] pub fn is_raw_identifier(name: &str, edition: parser::Edition) -> bool { From c1dcbfc504cf6812015733e50700b28f301f789d Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 3 Aug 2026 03:58:17 +0800 Subject: [PATCH 65/94] Move instantiate_with_errors from local_ty into param --- .../crates/ide-completion/src/render/function.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index b3ec60d4fafb7..4698a9cd4a0cd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -233,7 +233,8 @@ pub(super) fn add_call_parens<'b>( Some(n) => { let smol_str = n.display_no_db(ctx.edition).to_smolstr(); let text = smol_str.as_str().trim_start_matches('_'); - let ref_ = ref_of_param(ctx, text, param.ty()); + let ref_ = + ref_of_param(ctx, text, ¶m.ty().instantiate_with_errors()); f(&format_args!("${{{}:{ref_}{text}}}", index + offset)) } None => { @@ -293,7 +294,7 @@ fn ref_of_param(ctx: &CompletionContext<'_, '_>, arg: &str, ty: &hir::Type<'_>) for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) { if name.as_str() == arg { - let local_ty = local.ty(ctx.db).instantiate_with_errors(); + let local_ty = local.ty(ctx.db); let added_ref = local_ty.add_reference(ctx.db, mutability); let needs_ref = !local_ty.could_coerce_to(ctx.db, ty) && added_ref.could_coerce_to(ctx.db, ty); From 81b74457d8d2c51ce00fd6e8d509474ffafcf843 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 31 Jul 2026 17:02:43 +1000 Subject: [PATCH 66/94] Replace some older path-handling tests with snapshot tests This is not meant as a 1:1 migration. Think of it as removing some older tests of questionable value, then using them as inspiration for some new snapshot-test cases. --- src/bootstrap/src/core/builder/cli_paths.rs | 8 ---- ...st_library_core_and_alloc_and_stdarch.snap | 11 +++++ .../x_test_src_tools_miri_and_cargo_miri.snap | 10 ++++ .../src/core/builder/cli_paths/tests.rs | 2 + src/bootstrap/src/core/builder/tests.rs | 46 ------------------- 5 files changed, 23 insertions(+), 54 deletions(-) create mode 100644 src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_library_core_and_alloc_and_stdarch.snap create mode 100644 src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_src_tools_miri_and_cargo_miri.snap diff --git a/src/bootstrap/src/core/builder/cli_paths.rs b/src/bootstrap/src/core/builder/cli_paths.rs index 33bddabda0c70..55627dcf5eea8 100644 --- a/src/bootstrap/src/core/builder/cli_paths.rs +++ b/src/bootstrap/src/core/builder/cli_paths.rs @@ -73,14 +73,6 @@ pub(crate) struct CLIStepPath { pub(crate) will_be_executed: bool, } -#[cfg(test)] -impl CLIStepPath { - pub(crate) fn will_be_executed(mut self, will_be_executed: bool) -> Self { - self.will_be_executed = will_be_executed; - self - } -} - impl Debug for CLIStepPath { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.path.display()) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_library_core_and_alloc_and_stdarch.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_library_core_and_alloc_and_stdarch.snap new file mode 100644 index 0000000000000..2664ab7a404cc --- /dev/null +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_library_core_and_alloc_and_stdarch.snap @@ -0,0 +1,11 @@ +--- +source: src/bootstrap/src/core/builder/cli_paths/tests.rs +expression: test library/core library/alloc library/stdarch +--- +[Test] test::Crate + targets: [aarch64-unknown-linux-gnu] + - Set({library/alloc}) + - Set({library/core}) +[Test] test::StdarchVerify + targets: [x86_64-unknown-linux-gnu] + - Set({library/stdarch/crates/stdarch-verify}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_src_tools_miri_and_cargo_miri.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_src_tools_miri_and_cargo_miri.snap new file mode 100644 index 0000000000000..3698b48610b41 --- /dev/null +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_src_tools_miri_and_cargo_miri.snap @@ -0,0 +1,10 @@ +--- +source: src/bootstrap/src/core/builder/cli_paths/tests.rs +expression: test src/tools/miri src/tools/miri/cargo-miri +--- +[Test] test::Miri + targets: [aarch64-unknown-linux-gnu] + - Set({src/tools/miri}) +[Test] test::CargoMiri + targets: [aarch64-unknown-linux-gnu] + - Set({src/tools/miri/cargo-miri}) diff --git a/src/bootstrap/src/core/builder/cli_paths/tests.rs b/src/bootstrap/src/core/builder/cli_paths/tests.rs index 45655b71e611a..5be01f4616e5e 100644 --- a/src/bootstrap/src/core/builder/cli_paths/tests.rs +++ b/src/bootstrap/src/core/builder/cli_paths/tests.rs @@ -165,6 +165,7 @@ declare_tests!( (x_test_coverage_skip_coverage_run, "test coverage --skip=coverage-run"), (x_test_debuginfo, "test debuginfo"), (x_test_library, "test library"), + (x_test_library_core_and_alloc_and_stdarch, "test library/core library/alloc library/stdarch"), (x_test_librustdoc, "test librustdoc"), (x_test_librustdoc_rustdoc, "test librustdoc rustdoc"), (x_test_librustdoc_rustdoc_html, "test librustdoc rustdoc-html"), @@ -178,6 +179,7 @@ declare_tests!( (x_test_skip_tests_coverage, "test --skip=tests/coverage"), // From `src/ci/docker/scripts/stage_2_test_set2.sh`. (x_test_skip_tests_etc, "test --skip=tests --skip=library --skip=tidyselftest"), + (x_test_src_tools_miri_and_cargo_miri, "test src/tools/miri src/tools/miri/cargo-miri"), (x_test_tests, "test tests"), (x_test_tests_skip_coverage, "test tests --skip=coverage"), (x_test_tests_ui, "test tests/ui"), diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index f56de744883b8..eedd565539c18 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -50,52 +50,6 @@ fn test_invalid() { check_cli(["test", "library/std", "x"]); } -#[test] -fn test_intersection() { - let set = |paths: &[&str]| { - PathSet::Set(paths.into_iter().map(|p| TaskPath { path: p.into() }).collect()) - }; - let library_set = set(&["library/core", "library/alloc", "library/std"]); - let mut command_paths = vec![ - CLIStepPath::from(PathBuf::from("library/core")), - CLIStepPath::from(PathBuf::from("library/alloc")), - CLIStepPath::from(PathBuf::from("library/stdarch")), - ]; - let subset = library_set.intersection_removing_matches(&mut command_paths); - assert_eq!(subset, set(&["library/core", "library/alloc"]),); - assert_eq!( - command_paths, - vec![ - CLIStepPath::from(PathBuf::from("library/core")).will_be_executed(true), - CLIStepPath::from(PathBuf::from("library/alloc")).will_be_executed(true), - CLIStepPath::from(PathBuf::from("library/stdarch")).will_be_executed(false), - ] - ); -} - -#[test] -fn test_resolve_parent_and_subpaths() { - let set = |paths: &[&str]| { - PathSet::Set(paths.into_iter().map(|p| TaskPath { path: p.into() }).collect()) - }; - - let mut command_paths = vec![ - CLIStepPath::from(PathBuf::from("src/tools/miri")), - CLIStepPath::from(PathBuf::from("src/tools/miri/cargo-miri")), - ]; - - let library_set = set(&["src/tools/miri", "src/tools/miri/cargo-miri"]); - library_set.intersection_removing_matches(&mut command_paths); - - assert_eq!( - command_paths, - vec![ - CLIStepPath::from(PathBuf::from("src/tools/miri")).will_be_executed(true), - CLIStepPath::from(PathBuf::from("src/tools/miri/cargo-miri")).will_be_executed(true), - ] - ); -} - #[test] fn validate_path_remap() { let build = Build::new(configure("test", &[TEST_TRIPLE_1], &[TEST_TRIPLE_1])); From 122881b73e856a6a8d2ca26f331d53e4e5a213ae Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 3 Aug 2026 17:33:09 +1000 Subject: [PATCH 67/94] Snapshot test for `./x test rustdoc --skip=rustdoc` For this step, skipping "rustdoc" should prevent `src/librustdoc` from being tested. --- .../cli_paths/snapshots/x_test_rustdoc_skip_rustdoc.snap | 5 +++++ src/bootstrap/src/core/builder/cli_paths/tests.rs | 1 + 2 files changed, 6 insertions(+) create mode 100644 src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_rustdoc_skip_rustdoc.snap diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_rustdoc_skip_rustdoc.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_rustdoc_skip_rustdoc.snap new file mode 100644 index 0000000000000..f7e480936ebb7 --- /dev/null +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_rustdoc_skip_rustdoc.snap @@ -0,0 +1,5 @@ +--- +source: src/bootstrap/src/core/builder/cli_paths/tests.rs +expression: test rustdoc --skip=rustdoc +--- + diff --git a/src/bootstrap/src/core/builder/cli_paths/tests.rs b/src/bootstrap/src/core/builder/cli_paths/tests.rs index 5be01f4616e5e..6b4bc4550435a 100644 --- a/src/bootstrap/src/core/builder/cli_paths/tests.rs +++ b/src/bootstrap/src/core/builder/cli_paths/tests.rs @@ -171,6 +171,7 @@ declare_tests!( (x_test_librustdoc_rustdoc_html, "test librustdoc rustdoc-html"), (x_test_rustdoc, "test rustdoc"), (x_test_rustdoc_html, "test rustdoc-html"), + (x_test_rustdoc_skip_rustdoc, "test rustdoc --skip=rustdoc"), (x_test_semver_check, "test std-semver-check"), (x_test_skip_coverage, "test --skip=coverage"), (x_test_skip_coverage_map, "test --skip=coverage-map"), From d4e34c90981e816ce547600fd7da25dd9229dad8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 9 Jul 2026 15:10:56 +0200 Subject: [PATCH 68/94] Do not take `doc(cfg())` into account when filtering doctests --- src/librustdoc/clean/cfg.rs | 4 +- src/librustdoc/doctest/rust.rs | 132 +++++++++++++++++++-------------- 2 files changed, 80 insertions(+), 56 deletions(-) diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 74a04b4451040..04dd0eaf22899 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -30,7 +30,7 @@ mod tests; // Because `CfgEntry` includes `Span`, we must NEVER use `==`/`!=` operators on `Cfg` and instead // use `is_equivalent_to`. #[cfg_attr(test, derive(PartialEq))] -pub(crate) struct Cfg(CfgEntry); +pub(crate) struct Cfg(pub(crate) CfgEntry); // Similar to `hir::DocCfgHideShow` but allows to handle both `show` and `hide` as with the `except` // field in `Any` variant. @@ -744,7 +744,7 @@ pub(crate) struct CfgInfo { hidden_cfg: FxHashMap, /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while /// taking into account the `hidden_cfg` information. - current_cfg: Cfg, + pub(crate) current_cfg: Cfg, /// Whether the `doc(auto_cfg())` feature is enabled or not at this point. auto_cfg_active: bool, /// If the parent item used `doc(cfg(...))`, then we don't want to overwrite `current_cfg`, diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index d89fb2ae1767b..13f705f9c3f47 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -6,17 +6,18 @@ use std::sync::Arc; use proc_macro2::{TokenStream, TokenTree}; use rustc_attr_parsing::eval_config_entry; -use rustc_hir::attrs::AttributeKind; +use rustc_hir::attrs::{AttributeKind, CfgEntry}; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; -use rustc_hir::{self as hir, Attribute, CRATE_HIR_ID, intravisit}; +use rustc_hir::{self as hir, CRATE_HIR_ID, intravisit}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_resolve::rustdoc::span_of_fragments; use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, DUMMY_SP, FileName, Pos, Span}; +use rustc_span::{BytePos, DUMMY_SP, FileName, Pos, Span, sym}; use super::{DocTestVisitor, ScrapedDocTest}; -use crate::clean::{Attributes, CfgInfo, extract_cfg_from_attrs}; +use crate::clean::cfg::Cfg; +use crate::clean::{Attributes, CfgInfo}; use crate::html::markdown::{self, CodeLineMapping, ErrorCodes, LangString, MdRelLine}; struct RustCollector { @@ -118,58 +119,73 @@ impl HirCollector<'_> { sp: Span, nested: F, ) { - let ast_attrs = self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id(def_id)); - if let Some(ref cfg) = - extract_cfg_from_attrs(ast_attrs.iter(), self.tcx, &mut CfgInfo::default()) - && !eval_config_entry(&self.tcx.sess, cfg.inner()).as_bool() - { - return; - } + let hir_attrs = self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id(def_id)); + + let mut cfg_info = CfgInfo::default(); + let mut found_features = 0; let source_map = self.tcx.sess.source_map(); - // Try collecting `#[doc(test(attr(...)))]` let old_global_crate_attrs_len = self.collector.global_crate_attrs.len(); - for attr in ast_attrs { - let Attribute::Parsed(AttributeKind::Doc(d)) = attr else { continue }; - for attr_span in &d.test_attrs { - // FIXME: This is ugly, remove when `test_attrs` has been ported to new attribute API. - if let Ok(snippet) = source_map.span_to_snippet(*attr_span) - && let Ok(stream) = TokenStream::from_str(&snippet) - { - let mut iter = stream.into_iter().peekable(); - while let Some(token) = iter.next() { - if let TokenTree::Ident(i) = token { - let i = i.to_string(); - let peek = iter.peek(); - // From this ident, we can have things like: - // - // * Group: `allow(...)` - // * Name/value: `crate_name = "..."` - // * Tokens: `html_no_url` - // - // So we peek next element to know what case we are in. - match peek { - Some(TokenTree::Group(g)) => { - let g = g.to_string(); - iter.next(); - // Add the additional attributes to the global_crate_attrs vector - self.collector.global_crate_attrs.push(format!("{i}{g}")); - } - // If next item is `=`, it means it's a name value so we will need - // to get the value as well. - Some(TokenTree::Punct(p)) if p.as_char() == '=' => { - let p = p.to_string(); - iter.next(); - if let Some(last) = iter.next() { - // Add the additional attributes to the global_crate_attrs vector - self.collector - .global_crate_attrs - .push(format!("{i}{p}{last}")); + // This loop does two things: + // + // 1. Collect `#[target_feature(...)]`. + // 2. Collect `#[doc(test(attr(...)))]`. + for attr in hir_attrs.iter() { + let hir::Attribute::Parsed(attr) = attr else { continue }; + if let AttributeKind::TargetFeature { features, .. } = attr { + for (feature, _) in features { + found_features += 1; + cfg_info.current_cfg &= Cfg(CfgEntry::NameValue { + name: sym::target_feature, + value: Some(*feature), + span: DUMMY_SP, + }); + } + } else if let AttributeKind::Doc(d) = attr { + for attr_span in &d.test_attrs { + // FIXME: This is ugly, remove when `test_attrs` has been ported to new + // attribute API. + if let Ok(snippet) = source_map.span_to_snippet(*attr_span) + && let Ok(stream) = TokenStream::from_str(&snippet) + { + let mut iter = stream.into_iter().peekable(); + while let Some(token) = iter.next() { + if let TokenTree::Ident(i) = token { + let i = i.to_string(); + let peek = iter.peek(); + // From this ident, we can have things like: + // + // * Group: `allow(...)` + // * Name/value: `crate_name = "..."` + // * Tokens: `html_no_url` + // + // So we peek next element to know what case we are in. + match peek { + Some(TokenTree::Group(g)) => { + let g = g.to_string(); + iter.next(); + // Add the additional attributes to the `global_crate_attrs` + // vector + self.collector.global_crate_attrs.push(format!("{i}{g}")); + } + // If next item is `=`, it means it's a name value so we will + // need to get the value as well. + Some(TokenTree::Punct(p)) if p.as_char() == '=' => { + let p = p.to_string(); + iter.next(); + if let Some(last) = iter.next() { + // Add the additional attributes to the + // `global_crate_attrs` vector. + self.collector + .global_crate_attrs + .push(format!("{i}{p}{last}")); + } + } + _ => { + // Add the additional attributes to the `global_crate_attrs` + // vector. + self.collector.global_crate_attrs.push(i.to_string()); } - } - _ => { - // Add the additional attributes to the global_crate_attrs vector - self.collector.global_crate_attrs.push(i.to_string()); } } } @@ -178,6 +194,14 @@ impl HirCollector<'_> { } } + // We only look at the `target_feature` attributes as the `cfg` attributes have already been + // applied at this point, so no need to take them into account again. + if found_features != 0 + && !eval_config_entry(&self.tcx.sess, &cfg_info.current_cfg.inner()).as_bool() + { + return; + } + let mut has_name = false; if let Some(name) = name { self.collector.cur_path.push(name); @@ -186,7 +210,7 @@ impl HirCollector<'_> { // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with // anything else, this will combine them for us. - let attrs = Attributes::from_hir(ast_attrs); + let attrs = Attributes::from_hir(hir_attrs); if let Some(doc) = attrs.opt_doc_value() { let span = span_of_fragments(&attrs.doc_strings).unwrap_or(sp); self.collector.position = if span.edition().at_least_rust_2024() { @@ -194,7 +218,7 @@ impl HirCollector<'_> { } else { // this span affects filesystem path resolution, // so we need to keep it the same as it was previously - ast_attrs + hir_attrs .iter() .find(|attr| attr.doc_str().is_some()) .map(|attr| { From 7bb6021fe8b621066081468853c7e67c34635327 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 9 Jul 2026 15:11:27 +0200 Subject: [PATCH 69/94] Add regression test for `doc(cfg())` doctest (non-)filtering --- .../rustdoc-filter-doc_cfg-doctest/foo.rs | 15 ++++++++++ .../rustdoc-filter-doc_cfg-doctest/rmake.rs | 30 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tests/run-make/rustdoc-filter-doc_cfg-doctest/foo.rs create mode 100644 tests/run-make/rustdoc-filter-doc_cfg-doctest/rmake.rs diff --git a/tests/run-make/rustdoc-filter-doc_cfg-doctest/foo.rs b/tests/run-make/rustdoc-filter-doc_cfg-doctest/foo.rs new file mode 100644 index 0000000000000..76f9f463f4f4d --- /dev/null +++ b/tests/run-make/rustdoc-filter-doc_cfg-doctest/foo.rs @@ -0,0 +1,15 @@ +#![feature(doc_cfg)] + +/// ``` +/// assert!(true); +/// ``` +#[doc(cfg(spec))] +fn f() {} + +#[doc(cfg(false))] +mod dummy { + /// ``` + /// assert!(true); + /// ``` + fn f2() {} +} diff --git a/tests/run-make/rustdoc-filter-doc_cfg-doctest/rmake.rs b/tests/run-make/rustdoc-filter-doc_cfg-doctest/rmake.rs new file mode 100644 index 0000000000000..942f23964f4d3 --- /dev/null +++ b/tests/run-make/rustdoc-filter-doc_cfg-doctest/rmake.rs @@ -0,0 +1,30 @@ +//! Regression test to ensure that `doc(cfg())` has no impact on the filtered-out doctests. +//! +//! Regression test for . + +//@ ignore-cross-compile + +use run_make_support::rustdoc; + +fn check_rustdoc_test_output(edition: &str) { + let out = rustdoc().input("foo.rs").edition(edition).arg("--test").run().stdout_utf8(); + + // There should be two tests run. + assert!(out.contains("running 2 test"), "Failed with edition {edition}"); + // They should be in `foo.rs`. + assert!(out.contains("test foo.rs - f (line 3) ... ok"), "Failed with edition {edition}"); + assert!( + out.contains("test foo.rs - dummy::f2 (line 11) ... ok"), + "Failed with edition {edition}" + ); + // We double-check that the test was run (successfully). + assert!( + out.contains("test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out;"), + "Failed with edition {edition}", + ); +} + +fn main() { + check_rustdoc_test_output("2015"); + check_rustdoc_test_output("2024"); +} From bfc0d0d1230c37f72f23d7d6894cb315431b3c53 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 9 Jul 2026 16:51:56 +0200 Subject: [PATCH 70/94] Ignore std doctests if not run on the right OS --- library/core/src/os/darwin/objc.rs | 6 ++- library/std/src/os/wasi/mod.rs | 3 +- library/std/src/os/windows/ffi.rs | 6 ++- library/std/src/os/windows/fs.rs | 48 ++++++++++++------- library/std/src/os/windows/io/handle.rs | 3 +- library/std/src/os/windows/mod.rs | 3 +- library/std/src/os/windows/net/addr.rs | 12 +++-- library/std/src/os/windows/net/listener.rs | 30 ++++++++---- library/std/src/os/windows/net/stream.rs | 45 +++++++++++------ library/std/src/os/windows/process.rs | 11 +++-- .../crates/core_arch/src/amdgpu/mod.rs | 8 ++-- .../stdarch/crates/core_arch/src/nvptx/mod.rs | 5 +- .../stdarch/crates/core_arch/src/x86/mod.rs | 36 +++++++++++--- 13 files changed, 149 insertions(+), 67 deletions(-) diff --git a/library/core/src/os/darwin/objc.rs b/library/core/src/os/darwin/objc.rs index df3aab867e83d..7be07891085a3 100644 --- a/library/core/src/os/darwin/objc.rs +++ b/library/core/src/os/darwin/objc.rs @@ -67,7 +67,8 @@ pub type SEL = *mut objc_selector; /// /// # Example /// -/// ```no_run +#[cfg_attr(target_os = "macos", doc = "```no_run")] +#[cfg_attr(not(target_os = "macos"), doc = "```ignore (needs macos)")] /// #![feature(darwin_objc)] /// use core::os::darwin::objc; /// @@ -93,7 +94,8 @@ pub macro class($classname:expr) {{ /// /// # Examples /// -/// ```no_run +#[cfg_attr(target_os = "macos", doc = "```no_run")] +#[cfg_attr(not(target_os = "macos"), doc = "```ignore (needs macos)")] /// #![feature(darwin_objc)] /// use core::os::darwin::objc; /// diff --git a/library/std/src/os/wasi/mod.rs b/library/std/src/os/wasi/mod.rs index 2ee6aa4660094..1db9ec906726f 100644 --- a/library/std/src/os/wasi/mod.rs +++ b/library/std/src/os/wasi/mod.rs @@ -11,7 +11,8 @@ //! //! # Examples //! -//! ```no_run +#![cfg_attr(target_os = "wasi", doc = "```no_run")] +#![cfg_attr(not(target_os = "wasi"), doc = "```ignore (needs wasi)")] //! use std::fs::File; //! use std::os::wasi::prelude::*; //! diff --git a/library/std/src/os/windows/ffi.rs b/library/std/src/os/windows/ffi.rs index ed933975bd5a5..3cda3e25fb544 100644 --- a/library/std/src/os/windows/ffi.rs +++ b/library/std/src/os/windows/ffi.rs @@ -72,7 +72,8 @@ pub impl(self) trait OsStringExt { /// /// # Examples /// - /// ``` + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// use std::ffi::OsString; /// use std::os::windows::prelude::*; /// @@ -104,7 +105,8 @@ pub impl(self) trait OsStrExt { /// /// # Examples /// - /// ``` + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// use std::ffi::OsString; /// use std::os::windows::prelude::*; /// diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index dfa9236a7e428..7b4f5e7a40055 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -31,7 +31,8 @@ pub trait FileExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// use std::io; /// use std::fs::File; /// use std::os::windows::prelude::*; @@ -59,7 +60,8 @@ pub trait FileExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(core_io_borrowed_buf)] /// #![feature(read_buf_at)] /// @@ -104,7 +106,8 @@ pub trait FileExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// use std::fs::File; /// use std::os::windows::prelude::*; /// @@ -151,7 +154,8 @@ pub trait OpenOptionsExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// use std::fs::OpenOptions; /// use std::os::windows::prelude::*; /// @@ -176,7 +180,8 @@ pub trait OpenOptionsExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// use std::fs::OpenOptions; /// use std::os::windows::prelude::*; /// @@ -202,7 +207,8 @@ pub trait OpenOptionsExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// # #![allow(unexpected_cfgs)] /// # #[cfg(for_demonstration_only)] /// extern crate winapi; @@ -240,7 +246,8 @@ pub trait OpenOptionsExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// # #![allow(unexpected_cfgs)] /// # #[cfg(for_demonstration_only)] /// extern crate winapi; @@ -282,7 +289,8 @@ pub trait OpenOptionsExt { /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// # #![allow(unexpected_cfgs)] /// # #[cfg(for_demonstration_only)] /// extern crate winapi; @@ -377,7 +385,8 @@ impl OpenOptionsExt2 for OpenOptions { /// /// # Example /// -/// ```no_run +#[cfg_attr(windows, doc = "```no_run")] +#[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_permissions_ext)] /// use std::fs::Permissions; /// use std::os::windows::fs::PermissionsExt; @@ -440,7 +449,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// use std::io; /// use std::fs; /// use std::os::windows::prelude::*; @@ -470,7 +480,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// use std::io; /// use std::fs; /// use std::os::windows::prelude::*; @@ -505,7 +516,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// use std::io; /// use std::fs; /// use std::os::windows::prelude::*; @@ -538,7 +550,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// use std::io; /// use std::fs; /// use std::os::windows::prelude::*; @@ -561,7 +574,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// use std::io; /// use std::fs; /// use std::os::windows::prelude::*; @@ -700,7 +714,8 @@ impl FileTimesExt for fs::FileTimes { /// /// # Examples /// -/// ```no_run +#[cfg_attr(windows, doc = "```no_run")] +#[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// use std::os::windows::fs; /// /// fn main() -> std::io::Result<()> { @@ -739,7 +754,8 @@ pub fn symlink_file, Q: AsRef>(original: P, link: Q) -> io: /// /// # Examples /// -/// ```no_run +#[cfg_attr(windows, doc = "```no_run")] +#[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// use std::os::windows::fs; /// /// fn main() -> std::io::Result<()> { diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index e58f94253bdf7..29697bbb5f8fc 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -424,7 +424,8 @@ pub trait AsHandle { /// /// # Example /// - /// ```rust,no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// use std::fs::File; /// # use std::io; /// use std::os::windows::io::{AsHandle, BorrowedHandle}; diff --git a/library/std/src/os/windows/mod.rs b/library/std/src/os/windows/mod.rs index 53c33d17a9f65..a7e032dbf4d4d 100644 --- a/library/std/src/os/windows/mod.rs +++ b/library/std/src/os/windows/mod.rs @@ -8,7 +8,8 @@ //! //! # Examples //! -//! ```no_run +#![cfg_attr(windows, doc = "```no_run")] +#![cfg_attr(not(windows), doc = "```ignore (needs windows)")] //! use std::fs::File; //! use std::os::windows::prelude::*; //! diff --git a/library/std/src/os/windows/net/addr.rs b/library/std/src/os/windows/net/addr.rs index ef2263edcf617..c330432039a8f 100644 --- a/library/std/src/os/windows/net/addr.rs +++ b/library/std/src/os/windows/net/addr.rs @@ -79,7 +79,8 @@ impl SocketAddr { /// /// With a pathname: /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixListener; /// use std::path::Path; @@ -104,7 +105,8 @@ impl SocketAddr { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::SocketAddr; /// use std::path::Path; @@ -118,7 +120,8 @@ impl SocketAddr { /// /// Creating a `SocketAddr` with a NULL byte results in an error. /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::SocketAddr; /// @@ -151,7 +154,8 @@ impl SocketAddr { /// /// A named address: /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixListener; /// diff --git a/library/std/src/os/windows/net/listener.rs b/library/std/src/os/windows/net/listener.rs index 345cfe8d22ba9..19f5254e08bf9 100644 --- a/library/std/src/os/windows/net/listener.rs +++ b/library/std/src/os/windows/net/listener.rs @@ -16,7 +16,8 @@ use crate::{fmt, io}; /// /// # Examples /// -/// ```no_run +#[cfg_attr(windows, doc = "```no_run")] +#[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::thread; /// use std::os::windows::net::{UnixStream, UnixListener}; @@ -61,7 +62,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixListener; /// @@ -84,7 +86,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::{UnixListener}; /// @@ -122,7 +125,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixListener; /// @@ -148,7 +152,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixListener; /// @@ -170,7 +175,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixListener; /// @@ -194,7 +200,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixListener; /// @@ -212,7 +219,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixListener; /// @@ -236,7 +244,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::thread; /// use std::os::windows::net::{UnixStream, UnixListener}; @@ -272,7 +281,8 @@ impl UnixListener { /// /// # Examples /// -/// ```no_run +#[cfg_attr(windows, doc = "```no_run")] +#[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::thread; /// use std::os::windows::net::{UnixStream, UnixListener}; diff --git a/library/std/src/os/windows/net/stream.rs b/library/std/src/os/windows/net/stream.rs index f2d0f7c09e9f1..c0f32e75411e9 100644 --- a/library/std/src/os/windows/net/stream.rs +++ b/library/std/src/os/windows/net/stream.rs @@ -21,7 +21,8 @@ use crate::{fmt, io}; /// /// # Examples /// -/// ```no_run +#[cfg_attr(windows, doc = "```no_run")] +#[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixStream; /// use std::io::prelude::*; @@ -54,7 +55,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixStream; /// @@ -77,7 +79,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::{UnixListener, UnixStream}; /// @@ -112,7 +115,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixStream; /// @@ -130,7 +134,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixStream; /// @@ -148,7 +153,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixStream; /// use std::time::Duration; @@ -168,7 +174,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixStream; /// @@ -192,7 +199,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixStream; /// use std::time::Duration; @@ -207,7 +215,8 @@ impl UnixStream { /// An [`Err`] is returned if the zero [`Duration`] is passed to this /// method: /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::io; /// use std::os::windows::net::UnixStream; @@ -235,7 +244,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixStream; /// use std::time::Duration; @@ -251,7 +261,8 @@ impl UnixStream { /// An [`Err`] is returned if the zero [`Duration`] is passed to this /// method: /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::io; /// use std::os::windows::net::UnixStream; @@ -277,7 +288,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixStream; /// use std::net::Shutdown; @@ -296,7 +308,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixStream; /// @@ -321,7 +334,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixStream; /// @@ -339,7 +353,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_unix_domain_sockets)] /// use std::os::windows::net::UnixStream; /// use std::time::Duration; diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 3332714ae4bb7..41dcb70c59c9f 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -273,7 +273,8 @@ pub impl(self) trait CommandExt { /// /// # Example /// - /// ``` + #[cfg_attr(windows, doc = "```")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_process_extensions_async_pipes)] /// use std::os::windows::process::CommandExt; /// use std::process::{Command, Stdio}; @@ -304,7 +305,8 @@ pub impl(self) trait CommandExt { /// /// # Example /// - /// ``` + #[cfg_attr(windows, doc = "```")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(windows_process_extensions_raw_attribute)] /// use std::os::windows::io::AsRawHandle; /// use std::os::windows::process::{CommandExt, ProcThreadAttributeList}; @@ -563,8 +565,9 @@ impl<'a> ProcThreadAttributeListBuilder<'a> { /// /// # Example /// - #[cfg_attr(target_vendor = "win7", doc = "```no_run")] - #[cfg_attr(not(target_vendor = "win7"), doc = "```")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] + #[cfg_attr(all(windows, target_vendor = "win7"), doc = "```no_run")] + #[cfg_attr(all(windows, not(target_vendor = "win7")), doc = "```")] /// #![feature(windows_process_extensions_raw_attribute)] /// use std::ffi::c_void; /// use std::os::windows::process::{CommandExt, ProcThreadAttributeList}; diff --git a/library/stdarch/crates/core_arch/src/amdgpu/mod.rs b/library/stdarch/crates/core_arch/src/amdgpu/mod.rs index 374f582696947..91dfbd1a16c0f 100644 --- a/library/stdarch/crates/core_arch/src/amdgpu/mod.rs +++ b/library/stdarch/crates/core_arch/src/amdgpu/mod.rs @@ -351,13 +351,13 @@ pub unsafe fn sched_barrier() { /// Combining multiple `sched_group_barrier` intrinsics enables an ordering of specific instruction types during instruction scheduling. /// For example, the following enforces a sequence of 1 VMEM read, followed by 1 VALU instruction, followed by 5 MFMA instructions. /// -/// ```rust +/// ```ignore (only available on AMD) /// // 1 VMEM read -/// sched_group_barrier::<32, 1, 0>() +/// sched_group_barrier::<32, 1, 0>(); /// // 1 VALU -/// sched_group_barrier::<2, 1, 0>() +/// sched_group_barrier::<2, 1, 0>(); /// // 5 MFMA -/// sched_group_barrier::<8, 5, 0>() +/// sched_group_barrier::<8, 5, 0>(); /// ``` /// #[doc = include_str!("intrinsic_is_convergent.md")] diff --git a/library/stdarch/crates/core_arch/src/nvptx/mod.rs b/library/stdarch/crates/core_arch/src/nvptx/mod.rs index d22f3a25bf70e..53d53d1e1ef60 100644 --- a/library/stdarch/crates/core_arch/src/nvptx/mod.rs +++ b/library/stdarch/crates/core_arch/src/nvptx/mod.rs @@ -157,10 +157,13 @@ unsafe extern "C" { /// * `format`: A pointer to the format specifier input (uses common `printf` format). /// * `valist`: A pointer to the valist input. /// - /// ``` + /// ```ignore (available only for nvptx) + /// # use std::mem::transmute; /// #[repr(C)] /// struct PrintArgs(f32, f32, f32, i32); /// + /// let a = 0.1f32; + /// let b = 0.2f32; /// vprintf( /// "int(%f + %f) = int(%f) = %d\n".as_ptr(), /// transmute(&PrintArgs(a, b, a + b, (a + b) as i32)), diff --git a/library/stdarch/crates/core_arch/src/x86/mod.rs b/library/stdarch/crates/core_arch/src/x86/mod.rs index fbf1002eab8ba..589efbcf872d5 100644 --- a/library/stdarch/crates/core_arch/src/x86/mod.rs +++ b/library/stdarch/crates/core_arch/src/x86/mod.rs @@ -39,7 +39,11 @@ types! { /// /// # Examples /// - /// ``` + #[cfg_attr(any(target_arch = "x86", target_arch = "x86_64"), doc = "```")] + #[cfg_attr( + not(any(target_arch = "x86", target_arch = "x86_64")), + doc = "```ignore (only works on x86 targets)", + )] /// #[cfg(target_arch = "x86")] /// use std::arch::x86::*; /// #[cfg(target_arch = "x86_64")] @@ -82,7 +86,11 @@ types! { /// /// # Examples /// - /// ``` + #[cfg_attr(any(target_arch = "x86", target_arch = "x86_64"), doc = "```")] + #[cfg_attr( + not(any(target_arch = "x86", target_arch = "x86_64")), + doc = "```ignore (only works on x86 targets)", + )] /// #[cfg(target_arch = "x86")] /// use std::arch::x86::*; /// #[cfg(target_arch = "x86_64")] @@ -125,7 +133,11 @@ types! { /// /// # Examples /// - /// ``` + #[cfg_attr(any(target_arch = "x86", target_arch = "x86_64"), doc = "```")] + #[cfg_attr( + not(any(target_arch = "x86", target_arch = "x86_64")), + doc = "```ignore (only works on x86 targets)", + )] /// #[cfg(target_arch = "x86")] /// use std::arch::x86::*; /// #[cfg(target_arch = "x86_64")] @@ -172,7 +184,11 @@ types! { /// /// # Examples /// - /// ``` + #[cfg_attr(any(target_arch = "x86", target_arch = "x86_64"), doc = "```")] + #[cfg_attr( + not(any(target_arch = "x86", target_arch = "x86_64")), + doc = "```ignore (only works on x86 targets)", + )] /// #[cfg(target_arch = "x86")] /// use std::arch::x86::*; /// #[cfg(target_arch = "x86_64")] @@ -215,7 +231,11 @@ types! { /// /// # Examples /// - /// ``` + #[cfg_attr(any(target_arch = "x86", target_arch = "x86_64"), doc = "```")] + #[cfg_attr( + not(any(target_arch = "x86", target_arch = "x86_64")), + doc = "```ignore (only works on x86 targets)", + )] /// #[cfg(target_arch = "x86")] /// use std::arch::x86::*; /// #[cfg(target_arch = "x86_64")] @@ -258,7 +278,11 @@ types! { /// /// # Examples /// - /// ``` + #[cfg_attr(any(target_arch = "x86", target_arch = "x86_64"), doc = "```")] + #[cfg_attr( + not(any(target_arch = "x86", target_arch = "x86_64")), + doc = "```ignore (only works on x86 targets)", + )] /// #[cfg(target_arch = "x86")] /// use std::arch::x86::*; /// #[cfg(target_arch = "x86_64")] From 20cf32e136f8855c24a2110eb93a8d77c1ccf13e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 3 Aug 2026 12:02:35 +0200 Subject: [PATCH 71/94] Fix new windows doc code example --- library/std/src/os/windows/io/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/std/src/os/windows/io/mod.rs b/library/std/src/os/windows/io/mod.rs index db0ec8f2fbb2e..bf0605aa08a95 100644 --- a/library/std/src/os/windows/io/mod.rs +++ b/library/std/src/os/windows/io/mod.rs @@ -83,7 +83,8 @@ pub impl(self) trait StdioExt { /// (e.g. C stdio) or libraries that acquire a clone of the file handle /// will not be aware of this change. /// - /// ``` + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] /// #![feature(stdio_swap)] /// use std::io::{self, Read, Write}; /// use std::os::windows::io::StdioExt; From 6c07a43fb66ee2329540bd82762179dc2f7d7eaa Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 31 Jul 2026 16:28:27 +1000 Subject: [PATCH 72/94] Don't produce mutated/filtered PathSets during command-line matching --- src/bootstrap/src/core/build_steps/check.rs | 2 +- src/bootstrap/src/core/build_steps/test.rs | 2 +- src/bootstrap/src/core/build_steps/tool.rs | 2 +- src/bootstrap/src/core/builder/cli_paths.rs | 2 +- .../cli_paths/snapshots/x_build_rustdoc.snap | 2 +- .../cli_paths/snapshots/x_check_rustdoc.snap | 2 +- .../snapshots/x_test_librustdoc.snap | 2 +- .../x_test_librustdoc_rustdoc_html.snap | 2 +- .../cli_paths/snapshots/x_test_rustdoc.snap | 2 +- src/bootstrap/src/core/builder/mod.rs | 45 ++++++++----------- 10 files changed, 28 insertions(+), 35 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 2fc72a42b0e54..3c4815993b786 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -699,7 +699,7 @@ macro_rules! tool_check_step { const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.selectors(&[$path $(, $alt_path )*]) + run.multi_path(&[$path $(, $alt_path )*]) } fn is_default_step(_builder: &Builder<'_>) -> bool { diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index eed0481bb5255..3015d5a83db8d 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3519,7 +3519,7 @@ impl CommandLineStep for CrateRustdoc { const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.selectors(&["src/librustdoc", "src/tools/rustdoc"]) + run.multi_path(&["src/librustdoc", "src/tools/rustdoc"]) } fn is_default_step(_builder: &Builder<'_>) -> bool { diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 1941566e2a0a2..85ff186626498 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -704,7 +704,7 @@ impl CommandLineStep for Rustdoc { const IS_HOST: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.selectors(&["src/tools/rustdoc", "src/librustdoc"]) + run.multi_path(&["src/tools/rustdoc", "src/librustdoc"]) } fn is_default_step(_builder: &Builder<'_>) -> bool { diff --git a/src/bootstrap/src/core/builder/cli_paths.rs b/src/bootstrap/src/core/builder/cli_paths.rs index 55627dcf5eea8..51a7ddaf62052 100644 --- a/src/bootstrap/src/core/builder/cli_paths.rs +++ b/src/bootstrap/src/core/builder/cli_paths.rs @@ -195,7 +195,7 @@ pub(crate) fn match_paths_to_steps_and_run( let mut steps_to_run = vec![]; for StepExtra { desc, should_run } in &steps { - let pathsets = should_run.pathset_for_paths_removing_matches(&mut paths); + let pathsets = should_run.pathsets_for_paths_flagging_matches(&mut paths); // This value is used for sorting the step execution order. // By default, `usize::MAX` is used as the index for steps to assign them the lowest priority. diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_rustdoc.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_rustdoc.snap index b20902dceda9f..52e76520bd72c 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_rustdoc.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_rustdoc.snap @@ -4,4 +4,4 @@ expression: build rustdoc --- [Build] tool::Rustdoc targets: [x86_64-unknown-linux-gnu] - - Set({src/tools/rustdoc}) + - Set({src/librustdoc, src/tools/rustdoc}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_rustdoc.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_rustdoc.snap index 5814ba8d3b290..e179d75a59724 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_rustdoc.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_rustdoc.snap @@ -4,4 +4,4 @@ expression: check rustdoc --- [Check] check::Rustdoc targets: [x86_64-unknown-linux-gnu] - - Set({src/tools/rustdoc}) + - Set({src/librustdoc, src/tools/rustdoc}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_librustdoc.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_librustdoc.snap index 611d6c0f2e8bf..2b9967ddca71c 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_librustdoc.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_librustdoc.snap @@ -4,4 +4,4 @@ expression: test librustdoc --- [Test] test::CrateRustdoc targets: [x86_64-unknown-linux-gnu] - - Set({src/librustdoc}) + - Set({src/librustdoc, src/tools/rustdoc}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_librustdoc_rustdoc_html.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_librustdoc_rustdoc_html.snap index ef171fd423343..e1ba1514cd57b 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_librustdoc_rustdoc_html.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_librustdoc_rustdoc_html.snap @@ -4,7 +4,7 @@ expression: test librustdoc rustdoc-html --- [Test] test::CrateRustdoc targets: [x86_64-unknown-linux-gnu] - - Set({src/librustdoc}) + - Set({src/librustdoc, src/tools/rustdoc}) [Test] test::RustdocHtml targets: [x86_64-unknown-linux-gnu] - Suite(tests/rustdoc-html) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_rustdoc.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_rustdoc.snap index 0872c63dbe1f1..c99568bc5c3ce 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_rustdoc.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_rustdoc.snap @@ -4,7 +4,7 @@ expression: test rustdoc --- [Test] test::CrateRustdoc targets: [x86_64-unknown-linux-gnu] - - Set({src/tools/rustdoc}) + - Set({src/librustdoc, src/tools/rustdoc}) [Test] test::RustdocBook targets: [x86_64-unknown-linux-gnu] - Set({src/doc/rustdoc}) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 030dff229e9fb..051e01a0a6666 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -393,10 +393,6 @@ pub enum PathSet { } impl PathSet { - fn empty() -> PathSet { - PathSet::Set(BTreeSet::new()) - } - fn one>(path: P) -> PathSet { let mut set = BTreeSet::new(); set.insert(TaskPath { path: path.into() }); @@ -416,33 +412,31 @@ impl PathSet { p.path.ends_with(needle) || p.path.starts_with(needle) } - /// Return all `TaskPath`s in `Self` that contain any of the `needles`, removing the - /// matched needles. - /// - /// This is used for `StepDescription::krate`, which passes all matching crates at once to - /// `Step::make_run`, rather than calling it many times with a single crate. - /// See `tests.rs` for examples. - fn intersection_removing_matches(&self, needles: &mut [CLIStepPath]) -> PathSet { - let mut check = |p| { + /// Returns true if self is matched by any of the command-line selectors, + /// and mutates those selectors to flag them as will-be-executed. + fn match_and_flag_selectors(&self, selectors: &mut [CLIStepPath]) -> bool { + let mut check_and_flag = |p| { let mut result = false; - for n in needles.iter_mut() { - let matched = Self::check(p, &n.path); + for selector in selectors.iter_mut() { + let matched = Self::check(p, &selector.path); if matched { - n.will_be_executed = true; + selector.will_be_executed = true; result = true; } } result }; + match self { - PathSet::Set(set) => PathSet::Set(set.iter().filter(|&p| check(p)).cloned().collect()), - PathSet::Suite(suite) => { - if check(suite) { - self.clone() - } else { - PathSet::empty() + PathSet::Set(set) => { + // Flag all matching selectors, not just the first match. + let mut matched = false; + for p in set { + matched |= check_and_flag(p); } + matched } + PathSet::Suite(suite) => check_and_flag(suite), } } @@ -607,7 +601,7 @@ impl<'a> ShouldRun<'a> { } /// Multiple on-disk paths that should select the same unit of work. - pub fn selectors(mut self, paths: &[&str]) -> Self { + pub fn multi_path(mut self, paths: &[&str]) -> Self { let mut set = BTreeSet::new(); for path in paths { self.assert_valid_path(path); @@ -639,12 +633,11 @@ impl<'a> ShouldRun<'a> { /// /// The reason we return PathSet instead of PathBuf is to allow for aliases that mean the same thing /// (for now, just `all_krates` and `paths`, but we may want to add an `aliases` function in the future?) - fn pathset_for_paths_removing_matches(&self, paths: &mut [CLIStepPath]) -> Vec { + fn pathsets_for_paths_flagging_matches(&self, paths: &mut [CLIStepPath]) -> Vec { let mut sets = vec![]; for pathset in &self.paths { - let subset = pathset.intersection_removing_matches(paths); - if subset != PathSet::empty() { - sets.push(subset); + if pathset.match_and_flag_selectors(paths) { + sets.push(pathset.clone()); } } sets From 5aff738d05e0f92fb7388c93818da87dd06ff70d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 30 Jul 2026 23:24:20 +0200 Subject: [PATCH 73/94] Derive the allocator used by tools from rustc's allocator --- compiler/rustc/Cargo.toml | 2 +- compiler/rustc/src/main.rs | 17 +++++--------- compiler/rustc_driver_impl/Cargo.toml | 1 + compiler/rustc_driver_impl/src/allocator.rs | 24 +++++++++++++++++++ compiler/rustc_driver_impl/src/lib.rs | 1 + src/bootstrap/src/core/build_steps/tool.rs | 26 ++------------------- src/librustdoc/Cargo.toml | 1 - src/librustdoc/lib.rs | 9 ------- src/tools/clippy/Cargo.toml | 1 - src/tools/clippy/src/driver.rs | 10 ++------ src/tools/miri/Cargo.toml | 1 - src/tools/miri/src/bin/miri.rs | 15 ++---------- src/tools/rustdoc/Cargo.toml | 4 +++- src/tools/rustdoc/main.rs | 5 ++++ 14 files changed, 47 insertions(+), 70 deletions(-) create mode 100644 compiler/rustc_driver_impl/src/allocator.rs diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index e3e94e440f694..19a9a7913c9ee 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -35,7 +35,7 @@ features = ['override_allocator_on_supported_platforms'] [features] # tidy-alphabetical-start check_only = ['rustc_driver_impl/check_only'] -jemalloc = ['dep:tikv-jemalloc-sys'] +jemalloc = ['dep:tikv-jemalloc-sys', 'rustc_driver_impl/jemalloc'] llvm = ['rustc_driver_impl/llvm'] llvm_offload = ['rustc_driver_impl/llvm_offload'] max_level_info = ['rustc_driver_impl/max_level_info'] diff --git a/compiler/rustc/src/main.rs b/compiler/rustc/src/main.rs index 30d64b05cfde9..17f768f6dc54a 100644 --- a/compiler/rustc/src/main.rs +++ b/compiler/rustc/src/main.rs @@ -7,11 +7,8 @@ use std::process::ExitCode; // A note about jemalloc: rustc uses jemalloc when built for CI and // distribution. The obvious way to do this is with the `#[global_allocator]` -// mechanism. However, for complicated reasons (see -// https://github.com/rust-lang/rust/pull/81782#issuecomment-784438001 for some -// details) that mechanism doesn't work here. Also, we'd like to use a -// consistent allocator across the rustc <-> llvm boundary, and -// `#[global_allocator]` wouldn't provide that. +// mechanism. However, that would not affect LLVM's C / C++ allocations and we also want +// to use a single allocator in the process to reduce memory usage. // // Instead, we use a lower-level mechanism, namely the // `"override_allocator_on_supported_platforms"` Cargo feature of jemalloc-sys. @@ -20,15 +17,14 @@ use std::process::ExitCode; // of `malloc`, `free`, etc.. This means that Rust's `System` allocator, which // calls `libc::malloc()` et al., is actually calling into jemalloc. // +// This override happens for the entire process, ensuring that there's no mixup +// of C allocators across dylibs / binaries, notably the rustc <-> llvm boundary. +// // A consequence of not using `GlobalAlloc` (and the `tikv-jemallocator` crate // provides an impl of that trait, which is called `Jemalloc`) is that we // cannot use the sized deallocation APIs (`sdallocx`) that jemalloc provides. // It's unclear how much performance is lost because of this. // -// NOTE: Even though Cargo passes `--extern` with `tikv_jemalloc_sys`, we still need to `use` the -// crate for the compiler to see the `#[used]`, see https://github.com/rust-lang/rust/issues/64402. -// This is similarly required if we used a crate with `#[global_allocator]`. -// // NOTE: if you are reading this comment because you want to set a custom `global_allocator` for // benchmarking, consider using the benchmarks in the `rustc-perf` collector suite instead: // https://github.com/rust-lang/rustc-perf/blob/master/collector/README.md#profiling @@ -37,8 +33,7 @@ use std::process::ExitCode; // to compare their performance, see // https://github.com/rust-lang/rust/commit/b90cfc887c31c3e7a9e6d462e2464db1fe506175#diff-43914724af6e464c1da2171e4a9b6c7e607d5bc1203fa95c0ab85be4122605ef // for an example of how to do so. -#[cfg(feature = "jemalloc")] -use tikv_jemalloc_sys as _; +rustc_driver::override_c_allocator_in_binary!(); fn main() -> ExitCode { rustc_driver::main() diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index c7d3e4fae3fc5..2e3002b4b6115 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -57,6 +57,7 @@ ctrlc = "3.4.4" [features] # tidy-alphabetical-start check_only = ['rustc_interface/check_only'] +jemalloc = [] llvm = ['rustc_interface/llvm'] llvm_offload = ['rustc_interface/llvm_offload'] max_level_info = ['rustc_log/max_level_info'] diff --git a/compiler/rustc_driver_impl/src/allocator.rs b/compiler/rustc_driver_impl/src/allocator.rs new file mode 100644 index 0000000000000..767f714a6caef --- /dev/null +++ b/compiler/rustc_driver_impl/src/allocator.rs @@ -0,0 +1,24 @@ +/// This macro overrides the C allocator (i.e., `malloc`) in final binaries by linking +/// jemalloc with the override feature enabled. The C allocator is used by the default +/// Rust allocator (`alloc::System`) on Unix targets but not on Windows targets. +#[cfg(feature = "jemalloc")] +#[macro_export] +macro_rules! override_c_allocator_in_binary { + () => { + // NOTE: even though Cargo passes `--extern` for this in rustc-main, the crate still has + // to be named for the compiler to see the `#[used]` inside it, see + // . + // + // FIXME(madsmtm): for the rustc-private tools this is loaded from the sysroot that was + // built with the other `rustc` crates, instead of via Cargo as you'd normally do. This is + // currently needed for LTO due to . + extern crate tikv_jemalloc_sys as _; + }; +} + +/// This macro does nothing when no allocator features are enabled. +#[cfg(not(feature = "jemalloc"))] +#[macro_export] +macro_rules! override_c_allocator_in_binary { + () => {}; +} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 7b4c5626a79e1..8eb1017e6fa67 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -82,6 +82,7 @@ macro do_not_use_safe_print($($t:tt)*) { #[allow(unused_imports)] use {do_not_use_print as print, do_not_use_print as println}; +mod allocator; pub mod args; pub mod pretty; #[macro_use] diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 1941566e2a0a2..2322dea6dfe1e 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -765,11 +765,7 @@ impl CommandLineStep for Rustdoc { // they'll be linked to those libraries). As such, don't explicitly `ensure` any additional // libraries here. The intuition here is that If we've built a compiler, we should be able // to build rustdoc. - // let mut extra_features = Vec::new(); - if let Some(allocator_feature_name) = builder.config.allocator(target).feature_name() { - extra_features.push(allocator_feature_name.to_string()); - } if !builder.config.rust_debug_logging { extra_features.push("max_level_info".to_string()) } @@ -1428,7 +1424,6 @@ macro_rules! tool_rustc_extended { tool_name: $tool_name:expr, stable: $stable:expr $( , add_bins_to_sysroot: $add_bins_to_sysroot:expr )? - $( , add_features: $add_features:expr )? $( , cargo_args: $cargo_args:expr )? $( , )? } @@ -1479,7 +1474,6 @@ macro_rules! tool_rustc_extended { $tool_name, $path, None $( .or(Some(&$add_bins_to_sysroot)) )?, - None $( .or(Some($add_features)) )?, None $( .or(Some($cargo_args)) )?, ) } @@ -1524,15 +1518,9 @@ fn build_extended_rustc_tool( tool_name: &'static str, path: &'static str, add_bins_to_sysroot: Option<&[&str]>, - add_features: Option, TargetSelection, &mut Vec)>, cargo_args: Option<&[&'static str]>, ) -> ToolBuildResult { let target = compilers.target(); - let mut extra_features = Vec::new(); - if let Some(func) = add_features { - func(builder, target, &mut extra_features); - } - let build_compiler = compilers.build_compiler; let ToolBuildResult { tool_path, .. } = builder.ensure(ToolBuild { build_compiler, @@ -1540,7 +1528,7 @@ fn build_extended_rustc_tool( tool: tool_name, mode: Mode::ToolRustcPrivate, path, - extra_features, + extra_features: Vec::new(), source_type: SourceType::InTree, allow_features: "", cargo_args: cargo_args.unwrap_or_default().iter().map(|s| String::from(*s)).collect(), @@ -1583,23 +1571,13 @@ tool_rustc_extended!(Clippy { path: "src/tools/clippy", tool_name: "clippy-driver", stable: true, - add_bins_to_sysroot: ["clippy-driver"], - add_features: |builder, target, features| { - if let Some(allocator_feature_name) = builder.config.allocator(target).feature_name() { - features.push(allocator_feature_name.to_string()); - } - } + add_bins_to_sysroot: ["clippy-driver"] }); tool_rustc_extended!(Miri { path: "src/tools/miri", tool_name: "miri", stable: false, add_bins_to_sysroot: ["miri"], - add_features: |builder, target, features| { - if let Some(allocator_feature_name) = builder.config.allocator(target).feature_name() { - features.push(allocator_feature_name.to_string()); - } - }, // Always compile also tests when building miri. Otherwise feature unification can cause rebuilds between building and testing miri. cargo_args: &["--all-targets"], }); diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 1fcc29bf92d93..2b11512fadd4a 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -43,7 +43,6 @@ minifier = { version = "0.4.0", default-features = false } expect-test = "1.4.0" [features] -jemalloc = [] max_level_info = ["tracing/max_level_info", "tracing/release_max_level_info"] [package.metadata.rust-analyzer] diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 0fadd78fd30b1..8bf0d7160db8e 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -58,15 +58,6 @@ extern crate rustc_target; extern crate rustc_trait_selection; extern crate test; -/// See docs in https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc/src/main.rs -/// and https://github.com/rust-lang/rust/pull/146627 for why we need this. -/// -/// FIXME(madsmtm): This is loaded from the sysroot that was built with the other `rustc` crates -/// above, instead of via Cargo as you'd normally do. This is currently needed for LTO due to -/// https://github.com/rust-lang/cc-rs/issues/1613. -#[cfg(feature = "jemalloc")] -extern crate tikv_jemalloc_sys as _; - use std::env::{self, VarError}; use std::io::{self, IsTerminal}; use std::path::Path; diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 2831f0f5dc5e8..e687da25a8f78 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -56,7 +56,6 @@ rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } [features] integration = ["dep:tempfile"] internal = ["dep:clippy_lints_internal", "dep:tempfile"] -jemalloc = [] [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index b73ddc3ae12c8..ed217bc7c3f40 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -11,14 +11,8 @@ extern crate rustc_interface; extern crate rustc_session; extern crate rustc_span; -/// See docs in -/// and for why we need this. -/// -/// FIXME(madsmtm): This is loaded from the sysroot that was built with the other `rustc` crates -/// above, instead of via Cargo as you'd normally do. This is currently needed for LTO due to -/// . -#[cfg(feature = "jemalloc")] -extern crate tikv_jemalloc_sys as _; +// Override the C allocator in the same way that the `rustc` binary would do. +rustc_driver::override_c_allocator_in_binary!(); use clippy_utils::sym; use declare_clippy_lint::LintListBuilder; diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index a08e9210028d6..e0547a4539612 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -67,7 +67,6 @@ stack-cache = [] expensive-consistency-checks = ["stack-cache"] tracing = ["serde_json"] native-lib = ["dep:libffi", "dep:libloading", "dep:capstone", "dep:ipc-channel", "dep:nix", "dep:serde"] -jemalloc = [] check_only = ["libffi?/check_only", "capstone?/check_only", "genmc-sys?/check_only"] [lints.rust.unexpected_cfgs] diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 641d37e16f1b9..5a5acc53de766 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -16,19 +16,8 @@ extern crate rustc_log; extern crate rustc_middle; extern crate rustc_session; -/// See docs in https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc/src/main.rs -/// and https://github.com/rust-lang/rust/pull/146627 for why we need this. -/// -/// FIXME(madsmtm): This is loaded from the sysroot that was built with the other `rustc` crates -/// above, instead of via Cargo as you'd normally do. This is currently needed for LTO due to -/// https://github.com/rust-lang/cc-rs/issues/1613. -#[cfg(feature = "jemalloc")] -// Make sure `--all-features` works: only Linux and macOS actually use jemalloc, and not on arm32. -#[cfg(all( - any(target_os = "linux", target_os = "macos"), - any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"), -))] -extern crate tikv_jemalloc_sys as _; +// Override the C allocator in the same way that the `rustc` binary would do. +rustc_driver::override_c_allocator_in_binary!(); mod log; diff --git a/src/tools/rustdoc/Cargo.toml b/src/tools/rustdoc/Cargo.toml index 6b0491ef47a84..681256665d688 100644 --- a/src/tools/rustdoc/Cargo.toml +++ b/src/tools/rustdoc/Cargo.toml @@ -14,5 +14,7 @@ path = "main.rs" rustdoc = { path = "../../librustdoc" } [features] -jemalloc = ['rustdoc/jemalloc'] max_level_info = ["rustdoc/max_level_info"] + +[package.metadata.rust-analyzer] +rustc_private = true diff --git a/src/tools/rustdoc/main.rs b/src/tools/rustdoc/main.rs index a35bcf9f547cb..15fd67a503830 100644 --- a/src/tools/rustdoc/main.rs +++ b/src/tools/rustdoc/main.rs @@ -1,8 +1,13 @@ // We need this feature as it changes `dylib` linking behavior and allows us to link to `rustc_driver`. #![feature(rustc_private)] +extern crate rustc_driver; + use std::process::ExitCode; +// Override the C allocator in the same way that the `rustc` binary would do. +rustc_driver::override_c_allocator_in_binary!(); + fn main() -> ExitCode { rustdoc::main() } From 25f20f56d309e6a100a103059c952de860b39757 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 2 Aug 2026 08:57:31 +0100 Subject: [PATCH 74/94] Add a regression test for casting a `fn` item with an illegal `self` parameter to a trait object --- .../typeck/self-param-in-fn-item-cast-to-fn-trait.rs | 7 +++++++ .../self-param-in-fn-item-cast-to-fn-trait.stderr | 10 ++++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/ui/typeck/self-param-in-fn-item-cast-to-fn-trait.rs create mode 100644 tests/ui/typeck/self-param-in-fn-item-cast-to-fn-trait.stderr diff --git a/tests/ui/typeck/self-param-in-fn-item-cast-to-fn-trait.rs b/tests/ui/typeck/self-param-in-fn-item-cast-to-fn-trait.rs new file mode 100644 index 0000000000000..3c9e98e2abbb4 --- /dev/null +++ b/tests/ui/typeck/self-param-in-fn-item-cast-to-fn-trait.rs @@ -0,0 +1,7 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/111411. + +pub fn main() { + fn baz(&self) {} + //~^ ERROR `self` parameter is only allowed in associated functions + let _ = &baz as &dyn Fn(i32); +} diff --git a/tests/ui/typeck/self-param-in-fn-item-cast-to-fn-trait.stderr b/tests/ui/typeck/self-param-in-fn-item-cast-to-fn-trait.stderr new file mode 100644 index 0000000000000..f6c2479e1f8e9 --- /dev/null +++ b/tests/ui/typeck/self-param-in-fn-item-cast-to-fn-trait.stderr @@ -0,0 +1,10 @@ +error: `self` parameter is only allowed in associated functions + --> $DIR/self-param-in-fn-item-cast-to-fn-trait.rs:4:12 + | +LL | fn baz(&self) {} + | ^^^^^ not semantically valid as function parameter + | + = note: associated functions are those in `impl` or `trait` definitions + +error: aborting due to 1 previous error + From cfafd1587ceb6f325e369e76a57fc49635062fc5 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 2 Aug 2026 08:59:54 +0100 Subject: [PATCH 75/94] Add a regression test for a trait alias mentioning `Self` in a trait object --- .../trait-alias-with-self-in-dyn-object.rs | 10 +++++++ ...trait-alias-with-self-in-dyn-object.stderr | 26 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/ui/traits/alias/trait-alias-with-self-in-dyn-object.rs create mode 100644 tests/ui/traits/alias/trait-alias-with-self-in-dyn-object.stderr diff --git a/tests/ui/traits/alias/trait-alias-with-self-in-dyn-object.rs b/tests/ui/traits/alias/trait-alias-with-self-in-dyn-object.rs new file mode 100644 index 0000000000000..aa27e41a94187 --- /dev/null +++ b/tests/ui/traits/alias/trait-alias-with-self-in-dyn-object.rs @@ -0,0 +1,10 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/138891. + +#![feature(trait_alias)] +trait F = Fn() -> Self; + +fn _f3(a: dyn F) {} +//~^ ERROR trait alias takes 0 generic arguments but 1 generic argument was supplied +//~| ERROR associated type binding in trait object type mentions `Self` + +fn main() {} diff --git a/tests/ui/traits/alias/trait-alias-with-self-in-dyn-object.stderr b/tests/ui/traits/alias/trait-alias-with-self-in-dyn-object.stderr new file mode 100644 index 0000000000000..ef12c36448262 --- /dev/null +++ b/tests/ui/traits/alias/trait-alias-with-self-in-dyn-object.stderr @@ -0,0 +1,26 @@ +error[E0107]: trait alias takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/trait-alias-with-self-in-dyn-object.rs:6:20 + | +LL | fn _f3(a: dyn F) {} + | ^----- help: remove the unnecessary generics + | | + | expected 0 generic arguments + | +note: trait alias defined here, with 0 generic parameters + --> $DIR/trait-alias-with-self-in-dyn-object.rs:4:7 + | +LL | trait F = Fn() -> Self; + | ^ + +error: associated type binding in trait object type mentions `Self` + --> $DIR/trait-alias-with-self-in-dyn-object.rs:6:16 + | +LL | trait F = Fn() -> Self; + | ---- this binding mentions `Self` +LL | +LL | fn _f3(a: dyn F) {} + | ^^^^^^^^^^ contains a mention of `Self` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0107`. From 17446a6ad8a34d9c1bc102754f9514a65c27f84c Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 2 Aug 2026 09:03:35 +0100 Subject: [PATCH 76/94] Add a regression test for the layout of an unsafe binder over an opaque type --- .../unsafe-binder-opaque-type-layout.rs | 17 ++++++++++++ .../unsafe-binder-opaque-type-layout.stderr | 26 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/ui/transmutability/unsafe-binder-opaque-type-layout.rs create mode 100644 tests/ui/transmutability/unsafe-binder-opaque-type-layout.stderr diff --git a/tests/ui/transmutability/unsafe-binder-opaque-type-layout.rs b/tests/ui/transmutability/unsafe-binder-opaque-type-layout.rs new file mode 100644 index 0000000000000..e456e88e4a383 --- /dev/null +++ b/tests/ui/transmutability/unsafe-binder-opaque-type-layout.rs @@ -0,0 +1,17 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/141400. + +#![feature(unsafe_binders)] +#![feature(transmutability)] +#![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] + +trait OpaqueTrait {} +type OpaqueType = unsafe<> impl OpaqueTrait; +//~^ ERROR the trait bound `OpaqueType::{opaque#0}: Copy` is not satisfied +//~| ERROR unconstrained opaque type +trait AnotherTrait {} +impl> AnotherTrait for T {} +impl AnotherTrait for OpaqueType {} +//~^ ERROR conflicting implementations of trait `AnotherTrait` + +pub fn main() {} diff --git a/tests/ui/transmutability/unsafe-binder-opaque-type-layout.stderr b/tests/ui/transmutability/unsafe-binder-opaque-type-layout.stderr new file mode 100644 index 0000000000000..d2bfbfbf3a095 --- /dev/null +++ b/tests/ui/transmutability/unsafe-binder-opaque-type-layout.stderr @@ -0,0 +1,26 @@ +error[E0277]: the trait bound `OpaqueType::{opaque#0}: Copy` is not satisfied + --> $DIR/unsafe-binder-opaque-type-layout.rs:9:19 + | +LL | type OpaqueType = unsafe<> impl OpaqueTrait; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `OpaqueType::{opaque#0}` + +error[E0119]: conflicting implementations of trait `AnotherTrait` for type `unsafe<> _` + --> $DIR/unsafe-binder-opaque-type-layout.rs:14:1 + | +LL | impl> AnotherTrait for T {} + | ------------------------------------------------------- first implementation here +LL | impl AnotherTrait for OpaqueType {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `unsafe<> _` + +error: unconstrained opaque type + --> $DIR/unsafe-binder-opaque-type-layout.rs:9:28 + | +LL | type OpaqueType = unsafe<> impl OpaqueTrait; + | ^^^^^^^^^^^^^^^^ + | + = note: `OpaqueType` must be used in combination with a concrete type within the same crate + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0119, E0277. +For more information about an error, try `rustc --explain E0119`. From 6f4fdb0022a29e2cedd3b30352eefab7d6cbd931 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 2 Aug 2026 09:24:51 +0100 Subject: [PATCH 77/94] Add a regression test for transmuting a `usize` to a `rust-call` fn pointer --- .../transmute-usize-to-rust-call-fn-ptr.rs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/ui/unboxed-closures/transmute-usize-to-rust-call-fn-ptr.rs diff --git a/tests/ui/unboxed-closures/transmute-usize-to-rust-call-fn-ptr.rs b/tests/ui/unboxed-closures/transmute-usize-to-rust-call-fn-ptr.rs new file mode 100644 index 0000000000000..03507d22e8643 --- /dev/null +++ b/tests/ui/unboxed-closures/transmute-usize-to-rust-call-fn-ptr.rs @@ -0,0 +1,9 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/114665. + +//@ build-pass +//@ compile-flags: -Zmir-opt-level=0 + +#![feature(unboxed_closures)] +fn main() { + unsafe { std::mem::transmute::(5); } +} From a0efa72c2c1dd4af2d2ab2d9f6ec3cf055772de8 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 2 Aug 2026 09:26:38 +0100 Subject: [PATCH 78/94] Add a regression test for MIR validation of a nested type-alias `impl Trait` --- .../nested-tait-inline-mir-validation.rs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/ui/type-alias-impl-trait/nested-tait-inline-mir-validation.rs diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inline-mir-validation.rs b/tests/ui/type-alias-impl-trait/nested-tait-inline-mir-validation.rs new file mode 100644 index 0000000000000..f70a08e2eae78 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/nested-tait-inline-mir-validation.rs @@ -0,0 +1,35 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/118478. + +//@ check-pass +//@ compile-flags: -Zmir-opt-level=3 + +#![feature(type_alias_impl_trait)] +#![crate_type = "lib"] +pub trait Tr { + fn get(&self) -> u32; +} + +impl Tr for (u32,) { + #[inline] + fn get(&self) -> u32 { self.0 } +} + +pub fn tr1() -> impl Tr { + (32,) +} + +pub fn tr2() -> impl Tr { + struct Inner { + x: X, + } + type X = impl Tr; + impl Tr for Inner { + fn get(&self) -> u32 { + self.x.get() + } + } + + Inner { + x: tr1(), + } +} From 62505193440d9886d1eda13d1166de4fd4b8b0be Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 2 Aug 2026 09:35:38 +0100 Subject: [PATCH 79/94] Restore a crash test for a higher-ranked generic associated type The test was removed on the assumption that it no longer crashed the compiler, but it still does, both on the nightly the issue was reported against and on current master. The assertion has since moved out of the debug info code and into `normalize_erasing_regions`, but it is the same one. --- tests/crashes/129372.rs | 53 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/crashes/129372.rs diff --git a/tests/crashes/129372.rs b/tests/crashes/129372.rs new file mode 100644 index 0000000000000..824e6fbfc4dd0 --- /dev/null +++ b/tests/crashes/129372.rs @@ -0,0 +1,53 @@ +//@ known-bug: #129372 +//@ compile-flags: -Cdebuginfo=2 -Copt-level=0 +//@ ignore-backends: gcc + +pub struct Wrapper(T); +struct Struct; + +pub trait TraitA { + type AssocA<'t>; +} +pub trait TraitB { + type AssocB; +} + +pub fn helper(v: impl MethodTrait) { + let _local_that_causes_ice = v.method(); +} + +pub fn main() { + helper(Wrapper(Struct)); +} + +pub trait MethodTrait { + type Assoc<'a>; + + fn method(self) -> impl for<'a> FnMut(&'a ()) -> Self::Assoc<'a>; +} + +impl MethodTrait for T +where + ::AssocB: TraitA, +{ + type Assoc<'a> = ::AssocA<'a>; + + fn method(self) -> impl for<'a> FnMut(&'a ()) -> Self::Assoc<'a> { + move |_| loop {} + } +} + +impl TraitB for Wrapper +where + B: TraitB, +{ + type AssocB = T; +} + +impl TraitB for Struct { + type AssocB = Struct; +} + +impl TraitA for Struct { + type AssocA<'t> = Self; +} From 28b62fa30e5c8af0e700c711aa44259de555b090 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 2 Aug 2026 09:38:46 +0100 Subject: [PATCH 80/94] Add a regression test for a malformed `impl Trait` in an associated type with a `const` parameter --- ...med-tait-impl-with-const-param.incr.stderr | 27 +++++++++++++++++++ ...d-tait-impl-with-const-param.normal.stderr | 27 +++++++++++++++++++ .../malformed-tait-impl-with-const-param.rs | 24 +++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 tests/ui/impl-trait/malformed-tait-impl-with-const-param.incr.stderr create mode 100644 tests/ui/impl-trait/malformed-tait-impl-with-const-param.normal.stderr create mode 100644 tests/ui/impl-trait/malformed-tait-impl-with-const-param.rs diff --git a/tests/ui/impl-trait/malformed-tait-impl-with-const-param.incr.stderr b/tests/ui/impl-trait/malformed-tait-impl-with-const-param.incr.stderr new file mode 100644 index 0000000000000..f940a1e5011cf --- /dev/null +++ b/tests/ui/impl-trait/malformed-tait-impl-with-const-param.incr.stderr @@ -0,0 +1,27 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/malformed-tait-impl-with-const-param.rs:15:32 + | +LL | impl Trait for &'a () { + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | impl<'a, const B: Word> Trait for &'a () { + | +++ + +error[E0407]: method `constrain` is not a member of trait `Trait` + --> $DIR/malformed-tait-impl-with-const-param.rs:20:5 + | +LL | fn constrain(self) -> (Self::Opaque1,) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `Trait` + +error[E0425]: cannot find type `Word` in this scope + --> $DIR/malformed-tait-impl-with-const-param.rs:15:15 + | +LL | impl Trait for &'a () { + | ^^^^ not found in this scope + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0261, E0407, E0425. +For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/impl-trait/malformed-tait-impl-with-const-param.normal.stderr b/tests/ui/impl-trait/malformed-tait-impl-with-const-param.normal.stderr new file mode 100644 index 0000000000000..f940a1e5011cf --- /dev/null +++ b/tests/ui/impl-trait/malformed-tait-impl-with-const-param.normal.stderr @@ -0,0 +1,27 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/malformed-tait-impl-with-const-param.rs:15:32 + | +LL | impl Trait for &'a () { + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | impl<'a, const B: Word> Trait for &'a () { + | +++ + +error[E0407]: method `constrain` is not a member of trait `Trait` + --> $DIR/malformed-tait-impl-with-const-param.rs:20:5 + | +LL | fn constrain(self) -> (Self::Opaque1,) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `Trait` + +error[E0425]: cannot find type `Word` in this scope + --> $DIR/malformed-tait-impl-with-const-param.rs:15:15 + | +LL | impl Trait for &'a () { + | ^^^^ not found in this scope + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0261, E0407, E0425. +For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/impl-trait/malformed-tait-impl-with-const-param.rs b/tests/ui/impl-trait/malformed-tait-impl-with-const-param.rs new file mode 100644 index 0000000000000..d5374fc99f71a --- /dev/null +++ b/tests/ui/impl-trait/malformed-tait-impl-with-const-param.rs @@ -0,0 +1,24 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/122214. +//! +//! The reported ICE only happened under incremental compilation, where the const inference +//! variable reached `HashStable`, but this used to crash without it as well, so check both. + +//@ revisions: normal incr +//@[incr] incremental + +#![feature(impl_trait_in_assoc_type, const_precise_live_drops)] + +trait Trait { + type Opaque1; +} + +impl Trait for &'a () { + //~^ ERROR use of undeclared lifetime name `'a` + //~| ERROR cannot find type `Word` in this scope + type Opaque1 = impl Sized; + + fn constrain(self) -> (Self::Opaque1,) {} + //~^ ERROR method `constrain` is not a member of trait `Trait` +} + +fn main() {} From 3d3bf1315c369d0f0cb09da797162e40d6508a72 Mon Sep 17 00:00:00 2001 From: fereidani Date: Tue, 4 Aug 2026 06:38:50 +0330 Subject: [PATCH 81/94] perf: single-pass for lower/upper case conversion --- library/alloc/src/slice.rs | 8 ++------ library/alloc/src/str.rs | 14 ++++++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index da9b40c2c3ce0..e6b540f093ba5 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -639,9 +639,7 @@ impl [u8] { #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[inline] pub fn to_ascii_uppercase(&self) -> Vec { - let mut me = self.to_vec(); - me.make_ascii_uppercase(); - me + self.iter().map(|b| b.to_ascii_uppercase()).collect() } /// Returns a vector containing a copy of this slice where each byte @@ -660,9 +658,7 @@ impl [u8] { #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[inline] pub fn to_ascii_lowercase(&self) -> Vec { - let mut me = self.to_vec(); - me.make_ascii_lowercase(); - me + self.iter().map(|b| b.to_ascii_lowercase()).collect() } } diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index fd0d66cf028b3..ee9e82368899f 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -844,9 +844,10 @@ impl str { #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[inline] pub fn to_ascii_uppercase(&self) -> String { - let mut s = self.to_owned(); - s.make_ascii_uppercase(); - s + let bytes = self.as_bytes().to_ascii_uppercase(); + // SAFETY: ASCII case conversion only maps a-z to A-Z and leaves + // all other bytes unchanged as valid UTF-8 + unsafe { String::from_utf8_unchecked(bytes) } } /// Returns a copy of this string where each character is mapped to its @@ -876,9 +877,10 @@ impl str { #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[inline] pub fn to_ascii_lowercase(&self) -> String { - let mut s = self.to_owned(); - s.make_ascii_lowercase(); - s + let bytes = self.as_bytes().to_ascii_lowercase(); + // SAFETY: ASCII case conversion only maps A-Z to a-z and leaves + // all other bytes unchanged as valid UTF-8 + unsafe { String::from_utf8_unchecked(bytes) } } } From 2cba42ad7b2339f897d665c6a7b9568249695545 Mon Sep 17 00:00:00 2001 From: Adwin White Date: Tue, 4 Aug 2026 15:35:27 +0800 Subject: [PATCH 82/94] remove unused source file --- .../src/infer/outlives/for_liveness.rs | 117 ------------------ 1 file changed, 117 deletions(-) delete mode 100644 compiler/rustc_infer/src/infer/outlives/for_liveness.rs diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs deleted file mode 100644 index 1ad0c5d192cf1..0000000000000 --- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs +++ /dev/null @@ -1,117 +0,0 @@ -use rustc_middle::ty::{ - self, Flags, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, - Unnormalized, -}; - -use crate::infer::outlives::test_type_match; -use crate::infer::region_constraints::VerifyIfEq; - -/// Visits free regions in the type that are relevant for liveness computation. -/// These regions are passed to `OP`. -/// -/// Specifically, we visit all of the regions of types recursively, except if -/// the type is an alias, we look at the outlives bounds in the param-env -/// and alias's item bounds. If there is a unique outlives bound, then visit -/// that instead. If there is not a unique but there is a `'static` outlives -/// bound, then don't visit anything. Otherwise, walk through the opaque's -/// regions structurally. -pub struct FreeRegionsVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> { - pub tcx: TyCtxt<'tcx>, - pub param_env: ty::ParamEnv<'tcx>, - pub op: OP, -} - -impl<'tcx, OP> TypeVisitor> for FreeRegionsVisitor<'tcx, OP> -where - OP: FnMut(ty::Region<'tcx>), -{ - fn visit_region(&mut self, r: ty::Region<'tcx>) { - match r.kind() { - // ignore bound regions, keep visiting - ty::ReBound(_, _) => {} - _ => (self.op)(r), - } - } - - fn visit_ty(&mut self, ty: Ty<'tcx>) { - // We're only interested in types involving regions - if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { - return; - } - - // FIXME: Don't consider alias bounds on types that have escaping bound - // vars. See #117455. - if ty.has_escaping_bound_vars() { - return ty.super_visit_with(self); - } - - match *ty.kind() { - // We can prove that an alias is live two ways: - // 1. All the components are live. - // - // 2. There is a known outlives bound or where-clause, and that - // region is live. - // - // We search through the item bounds and where clauses for - // either `'static` or a unique outlives region, and if one is - // found, we just need to prove that that region is still live. - // If one is not found, then we continue to walk through the alias. - ty::Alias(is_rigid, alias_ty @ ty::AliasTy { kind, args, .. }) => { - let tcx = self.tcx; - let param_env = self.param_env; - let def_id = match kind { - ty::AliasTyKind::Projection { def_id } - | ty::AliasTyKind::Inherent { def_id } - | ty::AliasTyKind::Opaque { def_id } - | ty::AliasTyKind::Free { def_id } => def_id, - }; - let outlives_bounds: Vec<_> = tcx - .item_bounds(def_id) - .iter_instantiated(tcx, args) - .map(Unnormalized::skip_norm_wip) - .chain(param_env.caller_bounds()) - .filter_map(|clause| { - let outlives = clause.as_type_outlives_clause()?; - if let Some(outlives) = outlives.no_bound_vars() - && outlives.0 == ty - { - Some(outlives.1) - } else { - test_type_match::extract_verify_if_eq( - tcx, - &outlives.map_bound(|ty::OutlivesPredicate(ty, bound)| { - VerifyIfEq { ty, bound } - }), - alias_ty.to_ty(tcx, is_rigid), - ) - } - }) - .collect(); - // If we find `'static`, then we know the alias doesn't capture *any* regions. - // Otherwise, all of the outlives regions should be equal -- if they're not, - // we don't really know how to proceed, so we continue recursing through the - // alias. - if outlives_bounds.contains(&tcx.lifetimes.re_static) { - // no - } else if let Some(r) = outlives_bounds.first() - && outlives_bounds[1..].iter().all(|other_r| other_r == r) - { - assert!(r.type_flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS)); - r.visit_with(self); - } else { - // Skip lifetime parameters that are not captured, since they do - // not need to be live. - let variances = tcx.opt_alias_variances(kind); - - for (idx, s) in args.iter().enumerate() { - if variances.map(|variances| variances[idx]) != Some(ty::Bivariant) { - s.visit_with(self); - } - } - } - } - - _ => ty.super_visit_with(self), - } - } -} From c31611589e3ee317c667737d387ed1c1e8446247 Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Tue, 4 Aug 2026 09:36:53 +0200 Subject: [PATCH 83/94] normalize in relations, not generalize, when relating infer with alias --- .../src/type_check/relate_tys.rs | 17 +++- .../src/infer/relate/generalize.rs | 91 +++++-------------- .../rustc_infer/src/infer/relate/lattice.rs | 19 +++- .../src/infer/relate/type_relating.rs | 4 - compiler/rustc_type_ir/src/relate/combine.rs | 88 +++++------------- .../src/relate/solver_relating.rs | 23 ++++- .../next-solver/borrowck-normalization.rs | 23 +++++ 7 files changed, 111 insertions(+), 154 deletions(-) create mode 100644 tests/ui/traits/next-solver/borrowck-normalization.rs diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index d821ada3c2f57..e99a757b76305 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -379,6 +379,19 @@ impl<'b, 'tcx> TypeRelation> for NllTypeRelating<'_, 'b, 'tcx> { ); } + (&ty::Alias(ty::IsRigid::No, _), _) | (_, &ty::Alias(ty::IsRigid::No, _)) + if infcx.next_trait_solver() => + { + // NOTE(khyperia): If this turns out to be possible, either the caller should + // normalize the alias, or we should normalize the alias here. See the PR that + // introduced this comment for how to do so, which normalizes aliases in other + // relations. + span_bug!( + self.span(), + "it should not be possible to encounter unnormalized aliases in borrowck" + ); + } + (&ty::Infer(ty::TyVar(a_vid)), _) => { infcx.instantiate_ty_var(self, true, a_vid, self.ambient_variance, b)? } @@ -590,8 +603,4 @@ impl<'b, 'tcx> PredicateEmittingRelation> for NllTypeRelating<'_ }, ); } - - fn ambient_variance(&self) -> ty::Variance { - self.ambient_variance - } } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 36eb4c9f59370..c647226106d91 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -161,81 +161,32 @@ impl<'tcx> InferCtxt<'tcx> { let Some(source_alias) = source_term.to_alias_term() else { bug!("generalized `{source_term:?} to infer, not an alias"); }; - if self.next_trait_solver() { - if let Some(generalized_ty) = generalized_term.as_type() { - match instantiation_variance { - ty::Invariant => relation.register_predicates([ty::ProjectionPredicate { - projection_term: source_alias.into(), - term: generalized_ty.into(), - }]), - ty::Covariant => { - // Generate a new var, then do: - // `source_alias == ?A && ?A <: generalized_ty` - let new_var = self.next_ty_var(relation.span()); - relation.register_predicates([ - ty::PredicateKind::Subtype(ty::SubtypePredicate { - a_is_expected: !target_is_expected, - a: new_var, - b: generalized_ty, - }), - ty::PredicateKind::Clause(ty::ClauseKind::Projection( - ty::ProjectionPredicate { - projection_term: source_alias.into(), - term: new_var.into(), - }, - )), - ]); - } - ty::Contravariant => { - // a :> b is b <: a - let new_var = self.next_ty_var(relation.span()); - relation.register_predicates([ - ty::PredicateKind::Subtype(ty::SubtypePredicate { - a_is_expected: target_is_expected, - a: generalized_ty, - b: new_var, - }), - ty::PredicateKind::Clause(ty::ClauseKind::Projection( - ty::ProjectionPredicate { - projection_term: source_alias.into(), - term: new_var.into(), - }, - )), - ]); - } - ty::Bivariant => unreachable!("bivariant generalization"), - } - } else { - debug_assert_eq!(instantiation_variance, ty::Variance::Invariant); + assert!( + !self.next_trait_solver(), + "nonrigid aliases should be handled in relations, not here" + ); + match source_alias.kind { + ty::AliasTermKind::ProjectionTy { .. } + | ty::AliasTermKind::ProjectionConst { .. } => { + // FIXME: This does not handle subtyping correctly, we could + // instead create a new inference variable `?normalized_source`, emitting + // `Projection(normalized_source, ?ty_normalized)` and + // `?normalized_source <: generalized_term`. relation.register_predicates([ty::ProjectionPredicate { projection_term: source_alias, term: generalized_term, }]); } - } else { - match source_alias.kind { - ty::AliasTermKind::ProjectionTy { .. } - | ty::AliasTermKind::ProjectionConst { .. } => { - // FIXME: This does not handle subtyping correctly, we could - // instead create a new inference variable `?normalized_source`, emitting - // `Projection(normalized_source, ?ty_normalized)` and - // `?normalized_source <: generalized_term`. - relation.register_predicates([ty::ProjectionPredicate { - projection_term: source_alias, - term: generalized_term, - }]); - } - // The old solver only accepts projection predicates for associated types. - ty::AliasTermKind::InherentTy { .. } - | ty::AliasTermKind::FreeTy { .. } - | ty::AliasTermKind::OpaqueTy { .. } => { - return Err(TypeError::CyclicTy(source_term.expect_type())); - } - ty::AliasTermKind::InherentConst { .. } - | ty::AliasTermKind::FreeConst { .. } - | ty::AliasTermKind::AnonConst { .. } => { - return Err(TypeError::CyclicConst(source_term.expect_const())); - } + // The old solver only accepts projection predicates for associated types. + ty::AliasTermKind::InherentTy { .. } + | ty::AliasTermKind::FreeTy { .. } + | ty::AliasTermKind::OpaqueTy { .. } => { + return Err(TypeError::CyclicTy(source_term.expect_type())); + } + ty::AliasTermKind::InherentConst { .. } + | ty::AliasTermKind::FreeConst { .. } + | ty::AliasTermKind::AnonConst { .. } => { + return Err(TypeError::CyclicConst(source_term.expect_const())); } } } else { diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index d8baaf01c87d3..6a9db2b6546a8 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -18,6 +18,7 @@ //! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) use rustc_hir::def_id::DefId; +use rustc_middle::span_bug; use rustc_middle::traits::solve::Goal; use rustc_middle::ty::relate::combine::{combine_ty_args, super_combine_consts, super_combine_tys}; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; @@ -159,6 +160,19 @@ impl<'tcx> TypeRelation> for LatticeOp<'_, 'tcx> { Ok(v) } + (&ty::Alias(ty::IsRigid::No, _), _) | (_, &ty::Alias(ty::IsRigid::No, _)) + if infcx.next_trait_solver() => + { + // NOTE(khyperia): If this turns out to be possible, either the caller should + // normalize the alias, or we should normalize the alias here. See the PR that + // introduced this comment for how to do so, which normalizes aliases in other + // relations. + span_bug!( + self.span(), + "it should not be possible to encounter unnormalized aliases in lattice relation" + ); + } + ( &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: a_def_id }, .. }), &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: b_def_id }, .. }), @@ -285,9 +299,4 @@ impl<'tcx> PredicateEmittingRelation> for LatticeOp<'_, 'tcx> { ) })) } - - fn ambient_variance(&self) -> ty::Variance { - // FIXME(deferred_projection_equality): This isn't right, I think? - ty::Variance::Invariant - } } diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index e9541592e4e03..7c8e263db5582 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -379,8 +379,4 @@ impl<'tcx> PredicateEmittingRelation> for TypeRelating<'_, 'tcx> ) })) } - - fn ambient_variance(&self) -> ty::Variance { - self.ambient_variance - } } diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 66eb27074ee30..4c0fe9cd25724 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -31,8 +31,6 @@ where &mut self, obligations: impl IntoIterator>, ); - - fn ambient_variance(&self) -> ty::Variance; } pub fn super_combine_tys( @@ -107,60 +105,10 @@ where { panic!("We do not expect to encounter `Fresh` variables in the new solver") } - - (ty::Alias(ty::IsRigid::No, alias), _) | (_, ty::Alias(ty::IsRigid::No, alias)) + (ty::Alias(ty::IsRigid::No, _), _) | (_, ty::Alias(ty::IsRigid::No, _)) if infcx.next_trait_solver() => { - // If both sides are aliases, arbitrarily do the LHS first - let terms_are_inverted = !matches!(a.kind(), ty::Alias(ty::IsRigid::No, _)); - let other = if terms_are_inverted { a } else { b }; - match (relation.ambient_variance(), terms_are_inverted) { - (ty::Invariant, _) => relation.register_predicates([ty::ProjectionPredicate { - projection_term: alias.into(), - term: other.into(), - }]), - (ty::Covariant, false) | (ty::Contravariant, true) => { - // Generate a new var to represent `alias <: other` - // with `alias == ?A && ?A <: other` - let new_var = infcx.next_ty_infer(); - relation.register_predicates([ - ty::PredicateKind::Clause(ty::ClauseKind::Projection( - ty::ProjectionPredicate { - projection_term: alias.into(), - term: new_var.into(), - }, - )), - ty::PredicateKind::Subtype(ty::SubtypePredicate { - a_is_expected: !terms_are_inverted, - a: new_var, - b: other, - }), - ]); - } - (ty::Contravariant, false) | (ty::Covariant, true) => { - // a :> b is b <: a - let new_var = infcx.next_ty_infer(); - relation.register_predicates([ - ty::PredicateKind::Clause(ty::ClauseKind::Projection( - ty::ProjectionPredicate { - projection_term: alias.into(), - term: new_var.into(), - }, - )), - ty::PredicateKind::Subtype(ty::SubtypePredicate { - a_is_expected: terms_are_inverted, - a: other, - b: new_var, - }), - ]); - } - (ty::Bivariant, _) => { - unreachable!( - "cannot handle bivariant aliases in register_projection_with_variance" - ) - } - } - Ok(a) + panic!("non-rigid aliases should be handled in the caller of super_combine_tys") } // All other cases of inference are errors @@ -231,6 +179,21 @@ where ) } + (ty::ConstKind::Alias(ty::IsRigid::No, alias), _) if infcx.next_trait_solver() => { + relation.register_predicates([ty::ProjectionPredicate { + projection_term: alias.into(), + term: b.into(), + }]); + Ok(b) + } + (_, ty::ConstKind::Alias(ty::IsRigid::No, alias)) if infcx.next_trait_solver() => { + relation.register_predicates([ty::ProjectionPredicate { + projection_term: alias.into(), + term: a.into(), + }]); + Ok(b) + } + (ty::ConstKind::Infer(ty::InferConst::Var(vid)), _) => { infcx.instantiate_const_var(relation, true, vid, b)?; Ok(b) @@ -241,20 +204,11 @@ where Ok(a) } - (ty::ConstKind::Alias(ty::IsRigid::No, alias), _) - | (_, ty::ConstKind::Alias(ty::IsRigid::No, alias)) - if (infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver()) => + (ty::ConstKind::Alias(ty::IsRigid::No, _), _) + | (_, ty::ConstKind::Alias(ty::IsRigid::No, _)) + if infcx.cx().features().generic_const_exprs() => { - if infcx.next_trait_solver() { - let other = if matches!(a.kind(), ty::ConstKind::Alias(..)) { b } else { a }; - relation.register_predicates([ty::ProjectionPredicate { - projection_term: alias.into(), - term: other.into(), - }]) - } else { - relation.register_predicates([ty::PredicateKind::ConstEquate(a, b)]); - } - + relation.register_predicates([ty::PredicateKind::ConstEquate(a, b)]); Ok(b) } diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index 930eaa965dfcf..1d28ff15f13c7 100644 --- a/compiler/rustc_type_ir/src/relate/solver_relating.rs +++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs @@ -193,6 +193,25 @@ where } } + (ty::Alias(ty::IsRigid::No, alias), _) if infcx.next_trait_solver() => { + let new_var = infcx.next_ty_infer(); + self.goals.push(Goal::new( + self.cx(), + self.param_env, + ty::ProjectionPredicate { projection_term: alias.into(), term: new_var.into() }, + )); + self.tys(new_var, b)?; + } + (_, ty::Alias(ty::IsRigid::No, alias)) if infcx.next_trait_solver() => { + let new_var = infcx.next_ty_infer(); + self.goals.push(Goal::new( + self.cx(), + self.param_env, + ty::ProjectionPredicate { projection_term: alias.into(), term: new_var.into() }, + )); + self.tys(a, new_var)?; + } + (ty::Infer(ty::TyVar(a_vid)), _) => { infcx.instantiate_ty_var(self, true, a_vid, self.ambient_variance, b)?; } @@ -342,8 +361,4 @@ where fn register_goals(&mut self, obligations: impl IntoIterator>) { self.goals.extend(obligations); } - - fn ambient_variance(&self) -> ty::Variance { - self.ambient_variance - } } diff --git a/tests/ui/traits/next-solver/borrowck-normalization.rs b/tests/ui/traits/next-solver/borrowck-normalization.rs new file mode 100644 index 0000000000000..1bba72867a4fb --- /dev/null +++ b/tests/ui/traits/next-solver/borrowck-normalization.rs @@ -0,0 +1,23 @@ +//@ check-pass +//@ compile-flags: -Znext-solver +//! https://github.com/rust-lang/rust/pull/160443 reworked how normalization works in type +//! relations. Initially, that PR left out normalization in NllTypeRelating, as it seemed to be +//! unused. However, upon doing a stage 2 build with -Znext-solver, turns out it *is* used. This is +//! the extracted case from the failing crate, to have it as a proper test instead of just failing +//! to compile stage 2. + +trait Trait { + type Item; +} + +struct Struct { + item: I::Item, +} + +impl Struct { + fn func(self) { + let Self { item } = self; + } +} + +fn main() {} From 3dc2ef80d1415d8990fe8c076e51b4249a3137a2 Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Tue, 4 Aug 2026 10:18:04 +0200 Subject: [PATCH 84/94] normalize the field ty in relate_type_and_user_type --- compiler/rustc_borrowck/src/type_check/mod.rs | 6 +-- compiler/rustc_middle/src/mir/statement.rs | 37 ++++++++++--------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 10e20ac06864f..f0a2326747090 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -479,10 +479,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let projected_ty = curr_projected_ty.projection_ty_core( tcx, proj, - |ty| self.normalize(ty::Unnormalized::new_wip(ty), locations), - |ty, variant_index, field, ()| { - PlaceTy::field_ty(tcx, ty, variant_index, field).skip_norm_wip() - }, + |ty| self.normalize(ty, locations), + |()| None, |_| unreachable!(), ); curr_projected_ty = projected_ty; diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 17eeb7c3c12aa..99e76baad9275 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -190,22 +190,21 @@ impl<'tcx> PlaceTy<'tcx> { tcx: TyCtxt<'tcx>, elem: ProjectionElem>, ) -> PlaceTy<'tcx> { - self.projection_ty_core(tcx, &elem, |ty| ty, |_, _, _, ty| ty, |ty| ty) + self.projection_ty_core(tcx, &elem, |ty| ty.skip_norm_wip(), |ty| Some(ty), |ty| ty) } /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })` /// projects `place_ty` onto `elem`, returning the appropriate /// `Ty` or downcast variant corresponding to that projection. - /// The `handle_field` callback must map a `FieldIdx` to its `Ty`, - /// (which should be trivial when `T` = `Ty`). + /// `trivial_field_ty` is used for when `T` = `Ty`, otherwise, + /// `PlaceTy::field_ty` is used to map a `FieldIdx` to its `Ty`. pub fn projection_ty_core( self, tcx: TyCtxt<'tcx>, elem: &ProjectionElem, - // FIXME(#155345): This should take `Unnormalized` as input and only - // normalize when actually required. - mut structurally_normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, - mut handle_field: impl FnMut(Ty<'tcx>, Option, FieldIdx, T) -> Ty<'tcx>, + // FIXME(#155345): This should only normalize when actually required. + mut normalize: impl FnMut(Unnormalized<'tcx, Ty<'tcx>>) -> Ty<'tcx>, + trivial_field_ty: impl Fn(T) -> Option>, mut handle_opaque_cast_and_subtype: impl FnMut(T) -> Ty<'tcx>, ) -> PlaceTy<'tcx> where @@ -217,16 +216,17 @@ impl<'tcx> PlaceTy<'tcx> { } let answer = match *elem { ProjectionElem::Deref => { - let ty = structurally_normalize(self.ty).builtin_deref(true).unwrap_or_else(|| { - bug!("deref projection of non-dereferenceable ty {:?}", self) - }); + let ty = + normalize(Unnormalized::new_wip(self.ty)).builtin_deref(true).unwrap_or_else( + || bug!("deref projection of non-dereferenceable ty {:?}", self), + ); PlaceTy::from_ty(ty) } ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => { - PlaceTy::from_ty(structurally_normalize(self.ty).builtin_index().unwrap()) + PlaceTy::from_ty(normalize(Unnormalized::new_wip(self.ty)).builtin_index().unwrap()) } ProjectionElem::Subslice { from, to, from_end } => { - PlaceTy::from_ty(match structurally_normalize(self.ty).kind() { + PlaceTy::from_ty(match normalize(Unnormalized::new_wip(self.ty)).kind() { ty::Slice(..) => self.ty, ty::Array(inner, _) if !from_end => Ty::new_array(tcx, *inner, to - from), ty::Array(inner, size) if from_end => { @@ -242,12 +242,13 @@ impl<'tcx> PlaceTy<'tcx> { ProjectionElem::Downcast(_name, index) => { PlaceTy { ty: self.ty, variant_index: Some(index) } } - ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field( - structurally_normalize(self.ty), - self.variant_index, - f, - fty, - )), + ProjectionElem::Field(f, fty) => PlaceTy::from_ty(match trivial_field_ty(fty) { + Some(ty) => ty, + None => { + let self_ty = normalize(Unnormalized::new_wip(self.ty)); + normalize(PlaceTy::field_ty(tcx, self_ty, self.variant_index, f)) + } + }), ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast_and_subtype(ty)), // FIXME(unsafe_binders): Rename `handle_opaque_cast_and_subtype` to be more general. From c7fc5f264663b2992cdb61acecbf256a2955828e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 16 Jul 2026 21:39:30 +0200 Subject: [PATCH 85/94] Add reflection method for field names --- .../src/const_eval/machine.rs | 27 ++++++++++++++++--- .../rustc_hir_analysis/src/check/intrinsic.rs | 2 ++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/mod.rs | 10 +++++++ library/core/src/mem/type_info.rs | 22 +++++++++++++++ library/coretests/tests/mem/type_info.rs | 1 + 6 files changed, 60 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index c0756a25db8b1..1178ac8a03959 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -21,9 +21,9 @@ use super::error::*; use crate::diagnostics::{LongRunning, LongRunningWarn}; use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, - GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, RetagMode, - Scalar, compile_time_machine, ensure_monomorphic_enough, err_inval, interp_ok, throw_exhaust, - throw_inval, throw_ub, throw_ub_format, throw_unsup, throw_unsup_format, + GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, + RetagMode, Scalar, compile_time_machine, ensure_monomorphic_enough, err_inval, interp_ok, + throw_exhaust, throw_inval, throw_ub, throw_ub_format, throw_unsup, throw_unsup_format, type_implements_dyn_trait, }; @@ -712,6 +712,27 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_scalar(Scalar::from_target_usize(offset, ecx), dest)?; } + sym::field_representing_type_name => { + let frt_ty = ecx.read_type_id(&args[0])?; + + let field_name = if let ty::Adt(def, args) = frt_ty.kind() + && let Some(FieldInfo { name, .. }) = + def.field_representing_type_info(ecx.tcx.tcx, args) + { + name + } else { + span_bug!(ecx.cur_span(), "expected field representing type, got {frt_ty}") + }; + let ptr = ecx.allocate_bytes_dedup(field_name.as_str().as_bytes())?; + ecx.write_immediate( + Immediate::ScalarPair( + Scalar::from_pointer(ptr, ecx), + Scalar::from_target_usize(field_name.as_str().len() as u64, ecx), + ), + dest, + )?; + } + sym::field_representing_type_actual_type_id => { let frt_ty = ecx.read_type_id(&args[0])?; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index cb6bf2cc7d623..3256ce3552265 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -115,6 +115,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::fdiv_algebraic | sym::field_offset | sym::field_representing_type_actual_type_id + | sym::field_representing_type_name | sym::floorf16 | sym::floorf32 | sym::floorf64 @@ -349,6 +350,7 @@ pub(crate) fn check_intrinsic_type( tcx.type_of(tcx.lang_items().type_struct().unwrap()).no_bound_vars().unwrap(), ), sym::field_representing_type_actual_type_id => (0, 0, vec![type_id_ty()], type_id_ty()), + sym::field_representing_type_name => (0, 0, vec![type_id_ty()], Ty::new_static_str(tcx)), sym::offload => ( 3, 0, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index fd555e6d97fd8..2275dcdb87cbb 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -970,6 +970,7 @@ symbols! { field_projections, field_representing_type, field_representing_type_actual_type_id, + field_representing_type_name, field_representing_type_raw, field_type, fields, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 87963bcce8ebb..3fd4d9c1bfb59 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3178,6 +3178,16 @@ pub fn field_representing_type_actual_type_id( _frt_type_id: crate::any::TypeId, ) -> crate::any::TypeId; +/// Gets the name of the field represented by the [`FieldRepresentingType`]'s `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::mem::type_info::FieldId::name`]. +/// +/// [`FieldRepresentingType`]: crate::field::FieldRepresentingType +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn field_representing_type_name(_frt_type_id: crate::any::TypeId) -> &'static str; + /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 4c29daed20228..cc35096c145c6 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -596,4 +596,26 @@ impl FieldId { pub fn type_id(self) -> TypeId { intrinsics::field_representing_type_actual_type_id(self.frt_type_id) } + + /// Returns the name of the field. + /// + /// ``` + /// #![feature(type_info)] + /// use std::any::TypeId; + /// + /// struct Point { + /// x: u32, + /// y: u32, + /// } + /// assert_eq!( + /// const { TypeId::of::().field(0, 0).name() }, + /// "x", + /// ); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn name(self) -> &'static str { + intrinsics::field_representing_type_name(self.frt_type_id) + } } diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index 9a37a2ba0db5f..98d15c379b833 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -255,6 +255,7 @@ fn test_enums() { assert!(ty_id.field(0, 0).type_id() == TypeId::of::()); assert!(ty_id.field(2, 0).type_id() == TypeId::of::<()>()); assert!(ty_id.field(2, 1).type_id() == TypeId::of::<&str>()); + assert!(ty_id.field(2, 1).name() == "b"); } const { From 20d5e784afdb77ca4057156c1fbfc27f0f7dffa5 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 16 Jul 2026 22:07:59 +0200 Subject: [PATCH 86/94] Add reflection method for field offsets --- .../src/const_eval/machine.rs | 20 +++++++++++++++++ .../rustc_hir_analysis/src/check/intrinsic.rs | 2 ++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/mod.rs | 10 +++++++++ library/core/src/mem/type_info.rs | 22 +++++++++++++++++++ 5 files changed, 55 insertions(+) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 1178ac8a03959..cb2016e515bc4 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -733,6 +733,26 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { )?; } + sym::field_representing_type_offset => { + let frt_ty = ecx.read_type_id(&args[0])?; + + let (ty, variant, field) = if let ty::Adt(def, args) = frt_ty.kind() + && let Some(FieldInfo { base, variant_idx, field_idx, .. }) = + def.field_representing_type_info(ecx.tcx.tcx, args) + { + (base, variant_idx, field_idx) + } else { + span_bug!(ecx.cur_span(), "expected field representing type, got {frt_ty}") + }; + let layout = ecx.layout_of(ty)?; + let cx = ty::layout::LayoutCx::new(ecx.tcx.tcx, ecx.typing_env()); + + let layout = layout.for_variant(&cx, variant); + let offset = layout.fields.offset(field.index()).bytes(); + + ecx.write_scalar(Scalar::from_target_usize(offset, ecx), dest)?; + } + sym::field_representing_type_actual_type_id => { let frt_ty = ecx.read_type_id(&args[0])?; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 3256ce3552265..a21114974f2c0 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -116,6 +116,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::field_offset | sym::field_representing_type_actual_type_id | sym::field_representing_type_name + | sym::field_representing_type_offset | sym::floorf16 | sym::floorf32 | sym::floorf64 @@ -351,6 +352,7 @@ pub(crate) fn check_intrinsic_type( ), sym::field_representing_type_actual_type_id => (0, 0, vec![type_id_ty()], type_id_ty()), sym::field_representing_type_name => (0, 0, vec![type_id_ty()], Ty::new_static_str(tcx)), + sym::field_representing_type_offset => (0, 0, vec![type_id_ty()], tcx.types.usize), sym::offload => ( 3, 0, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2275dcdb87cbb..41b505a1df9be 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -971,6 +971,7 @@ symbols! { field_representing_type, field_representing_type_actual_type_id, field_representing_type_name, + field_representing_type_offset, field_representing_type_raw, field_type, fields, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 3fd4d9c1bfb59..2f6677349ae2a 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3188,6 +3188,16 @@ pub fn field_representing_type_actual_type_id( #[rustc_comptime] pub fn field_representing_type_name(_frt_type_id: crate::any::TypeId) -> &'static str; +/// Gets the name of the field represented by the [`FieldRepresentingType`]'s `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::mem::type_info::FieldId::name`]. +/// +/// [`FieldRepresentingType`]: crate::field::FieldRepresentingType +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn field_representing_type_offset(_frt_type_id: crate::any::TypeId) -> usize; + /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index cc35096c145c6..b398cf6dafc37 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -618,4 +618,26 @@ impl FieldId { pub fn name(self) -> &'static str { intrinsics::field_representing_type_name(self.frt_type_id) } + /// Returns the offset of the field wrt to its containing type. + /// + /// ``` + /// #![feature(type_info)] + /// use std::any::TypeId; + /// + /// #[repr(C)] + /// struct Point { + /// x: u32, + /// y: u32, + /// } + /// assert_eq!( + /// const { TypeId::of::().field(0, 1).offset() }, + /// 4, + /// ); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn offset(self) -> usize { + intrinsics::field_representing_type_offset(self.frt_type_id) + } } From 538f556305b45ab2efd8ca0d70fe518cd5b41924 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 21 Jul 2026 17:43:45 +0200 Subject: [PATCH 87/94] Add reflection method for fetching non-exhaustiveness --- .../rustc_const_eval/src/const_eval/machine.rs | 17 +++++++++++++++++ .../rustc_hir_analysis/src/check/intrinsic.rs | 2 ++ library/core/src/intrinsics/mod.rs | 6 ++++++ library/core/src/mem/type_info.rs | 13 +++++++++++++ library/coretests/tests/mem/type_info.rs | 1 + 5 files changed, 39 insertions(+) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index cb2016e515bc4..c80821f094cb4 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -767,6 +767,23 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_type_id(field_ty, dest)?; } + sym::non_exhaustive => { + let ty = ecx.read_type_id(&args[0])?; + + // FIXME(reflection): need a way to obtain non-exhaustiveness of a variant's fields. + let non_exhaustive = if let ty::Adt(def, _) = ty.kind() { + if def.is_enum() { + def.is_variant_list_non_exhaustive() + } else { + def.non_enum_variant().is_field_list_non_exhaustive() + } + } else { + false + }; + + ecx.write_scalar(Scalar::from_bool(non_exhaustive), dest)?; + } + _ => { // We haven't handled the intrinsic, let's see if we can use a fallback body. if ecx.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden { diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index a21114974f2c0..7c3a7b01f252b 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -165,6 +165,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::minimumf128 | sym::mul_with_overflow | sym::needs_drop + | sym::non_exhaustive | sym::offload | sym::offset_of | sym::overflow_checks @@ -353,6 +354,7 @@ pub(crate) fn check_intrinsic_type( sym::field_representing_type_actual_type_id => (0, 0, vec![type_id_ty()], type_id_ty()), sym::field_representing_type_name => (0, 0, vec![type_id_ty()], Ty::new_static_str(tcx)), sym::field_representing_type_offset => (0, 0, vec![type_id_ty()], tcx.types.usize), + sym::non_exhaustive => (0, 0, vec![type_id_ty()], tcx.types.bool), sym::offload => ( 3, 0, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 2f6677349ae2a..28804ee53d901 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3198,6 +3198,12 @@ pub fn field_representing_type_name(_frt_type_id: crate::any::TypeId) -> &'stati #[rustc_comptime] pub fn field_representing_type_offset(_frt_type_id: crate::any::TypeId) -> usize; +/// Checks whether this type is non-exhaustive. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn non_exhaustive(_id: crate::any::TypeId) -> bool; + /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index b398cf6dafc37..71a322abd7417 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -481,6 +481,10 @@ impl TypeId { #[unstable(feature = "type_info", issue = "146922")] #[rustc_const_unstable(feature = "type_info", issue = "146922")] #[rustc_comptime] + // FIXME(type_info): Add enum variant pattern types and use them to represent individual variants + // Then add a `variant` method to get a wrapper around such a pattern type (similar to the FRT + // type we have) and add methods on that. It's the only way to really sensibly represent + // things like `non_exhaustive` which can be applied to variants as well. pub fn fields(self, variant_index: usize) -> usize { intrinsics::type_id_fields(self, variant_index) } @@ -557,6 +561,15 @@ impl TypeId { ), } } + + /// Returns whether a type is marked with `#[non_exhaustive]`. + /// Returns `false` for everything but adts. + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn non_exhaustive(self) -> bool { + intrinsics::non_exhaustive(self) + } } /// Field representing type ID. Representing a field of a struct, tuple or enum variant. diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index 98d15c379b833..b2756babe7864 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -247,6 +247,7 @@ fn test_enums() { assert!(ty.variants[2].fields.len() == 2); let ty_id = TypeId::of::(); + assert!(!ty_id.non_exhaustive()); assert!(ty_id.size() == Some(size_of::())); assert!(ty_id.variants() == 3); assert!(ty_id.fields(0) == 1); From 9381472c711af721961b2ac9eddf0902ad8a3e0c Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 22 Jul 2026 12:30:54 +0200 Subject: [PATCH 88/94] Generalize some arguments to allow more kinds of input --- .../rustc_const_eval/src/const_eval/type_info.rs | 8 ++++---- .../src/const_eval/type_info/adt.rs | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index 8f58e39d419ce..8fbb1ba4c9193 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -20,7 +20,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { // A general method to write an array to a static slice place. fn allocate_fill_and_write_slice_ptr( &mut self, - slice_place: impl Writeable<'tcx, CtfeProvenance>, + slice_place: &impl Writeable<'tcx, CtfeProvenance>, len: u64, writer: impl Fn(&mut Self, /* index */ u64, MPlaceTy<'tcx>) -> InterpResult<'tcx>, ) -> InterpResult<'tcx> { @@ -45,7 +45,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { // Write the slice pointing to the array let array_place = array_place.map_provenance(CtfeProvenance::as_immutable); let ptr = Immediate::new_slice(array_place.ptr(), len, self); - self.write_immediate(ptr, &slice_place) + self.write_immediate(ptr, slice_place) } /// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place. @@ -262,7 +262,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { let tuple_layout = self.layout_of(tuple_ty)?; let fields_slice_place = self.project_field(&tuple_place, FieldIdx::ZERO)?; self.allocate_fill_and_write_slice_ptr( - fields_slice_place, + &fields_slice_place, fields.len() as u64, |this, i, place| { let field_ty = fields[i as usize]; @@ -424,7 +424,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { sym::inputs => { let inputs = sig.inputs(); self.allocate_fill_and_write_slice_ptr( - field_place, + &field_place, inputs.len() as _, |this, i, place| this.write_type_id(inputs[i as usize], &place), )?; diff --git a/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs b/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs index 298752324e382..09662d2bc0a98 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info/adt.rs @@ -66,7 +66,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { let field_place = self.project_field(&place, field_idx)?; match field.name { - sym::generics => self.write_generics(field_place, generics)?, + sym::generics => self.write_generics(&field_place, generics)?, sym::fields => { self.write_variant_fields(field_place, struct_def, struct_layout, generics)? } @@ -96,7 +96,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { let field_place = self.project_field(&place, field_idx)?; match field.name { - sym::generics => self.write_generics(field_place, generics)?, + sym::generics => self.write_generics(&field_place, generics)?, sym::fields => { self.write_variant_fields(field_place, union_def, union_layout, generics)? } @@ -126,10 +126,10 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { let field_place = self.project_field(&place, field_idx)?; match field.name { - sym::generics => self.write_generics(field_place, generics)?, + sym::generics => self.write_generics(&field_place, generics)?, sym::variants => { self.allocate_fill_and_write_slice_ptr( - field_place, + &field_place, enum_def.variants().len() as u64, |this, i, place| { let variant_idx = VariantIdx::from_usize(i as usize); @@ -190,7 +190,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { generics: &'tcx GenericArgs<'tcx>, ) -> InterpResult<'tcx> { self.allocate_fill_and_write_slice_ptr( - place, + &place, variant_def.fields.len() as u64, |this, i, place| { let field_def = &variant_def.fields[FieldIdx::from_usize(i as usize)]; @@ -200,9 +200,9 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { ) } - fn write_generics( + pub(super) fn write_generics( &mut self, - place: impl Writeable<'tcx, CtfeProvenance>, + place: &impl Writeable<'tcx, CtfeProvenance>, generics: &'tcx GenericArgs<'tcx>, ) -> InterpResult<'tcx> { self.allocate_fill_and_write_slice_ptr(place, generics.len() as u64, |this, i, place| { From cecd4a4fcb1fc28b330826f28ea20582051e353e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 22 Jul 2026 13:08:07 +0200 Subject: [PATCH 89/94] Add reflection method for getting a type's generic args --- .../src/const_eval/machine.rs | 5 +++ .../src/const_eval/type_info.rs | 43 +++++++++++++++++++ compiler/rustc_hir/src/lang_items.rs | 1 + .../rustc_hir_analysis/src/check/intrinsic.rs | 14 ++++++ compiler/rustc_span/src/symbol.rs | 2 + library/core/src/intrinsics/mod.rs | 7 +++ library/core/src/mem/type_info.rs | 10 +++++ library/coretests/tests/mem/type_info.rs | 1 + 8 files changed, 83 insertions(+) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index c80821f094cb4..3f52fbecb0950 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -767,6 +767,11 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_type_id(field_ty, dest)?; } + sym::type_id_generics => { + let ty = ecx.read_type_id(&args[0])?; + ecx.write_type_id_generics(dest, ty)?; + } + sym::non_exhaustive => { let ty = ecx.read_type_id(&args[0])?; diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index 8fbb1ba4c9193..7c0fef3734975 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -383,6 +383,49 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { interp_ok(()) } + pub(crate) fn write_type_id_generics( + &mut self, + place: &impl Writeable<'tcx, CtfeProvenance>, + ty: Ty<'tcx>, + ) -> InterpResult<'tcx> { + let generics: ty::Binder<'_, ty::GenericArgsRef<'_>> = match *ty.kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Foreign(..) + | ty::Str + | ty::Array(..) + | ty::Pat(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnPtr(..) + | ty::Dynamic(..) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(..) + | ty::Alias(..) + | ty::Param(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(..) + | ty::Error(..) + | ty::Slice(..) => ty::Binder::dummy(ty::GenericArgsRef::default()), + ty::Adt(_, args) => ty::Binder::dummy(args), + ty::FnDef(_, binder) => binder, + ty::UnsafeBinder(binder) => binder.rebind(ty::GenericArgsRef::default()), + ty::Closure(_, args) => ty::Binder::dummy(args), + ty::CoroutineClosure(_, args) => ty::Binder::dummy(args), + ty::Coroutine(_, args) => ty::Binder::dummy(args), + }; + + // FIXME(type_info): also provide the late bound vars to reflection + let generics = generics.skip_binder(); + + self.write_generics(place, generics) + } + pub(crate) fn write_fn_ptr_type_info( &mut self, place: impl Writeable<'tcx, CtfeProvenance>, diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index e6e0b3726552f..00a2fc299efd8 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -276,6 +276,7 @@ language_item_table! { CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None; Type, sym::type_info, type_struct, Target::Struct, GenericRequirement::None; + TypeGeneric, sym::type_info_generic, type_generic, Target::Enum, GenericRequirement::None; TypeId, sym::type_id, type_id, Target::Struct, GenericRequirement::None; // A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 7c3a7b01f252b..67fddd87fbb1d 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -219,6 +219,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::type_id_eq | sym::type_id_field_representing_type | sym::type_id_fields + | sym::type_id_generics | sym::type_id_variants | sym::type_id_vtable | sym::type_name @@ -355,6 +356,19 @@ pub(crate) fn check_intrinsic_type( sym::field_representing_type_name => (0, 0, vec![type_id_ty()], Ty::new_static_str(tcx)), sym::field_representing_type_offset => (0, 0, vec![type_id_ty()], tcx.types.usize), sym::non_exhaustive => (0, 0, vec![type_id_ty()], tcx.types.bool), + sym::type_id_generics => ( + 0, + 0, + vec![type_id_ty()], + Ty::new_imm_ref( + tcx, + tcx.lifetimes.re_static, + Ty::new_slice( + tcx, + tcx.type_of(tcx.lang_items().type_generic().unwrap()).no_bound_vars().unwrap(), + ), + ), + ), sym::offload => ( 3, 0, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 41b505a1df9be..a453cc6b555db 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2173,9 +2173,11 @@ symbols! { type_id_eq, type_id_field_representing_type, type_id_fields, + type_id_generics, type_id_variants, type_id_vtable, type_info, + type_info_generic, type_ir, type_ir_infer_ctxt_like, type_ir_inherent, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 28804ee53d901..fad2cb08a8a64 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3204,6 +3204,13 @@ pub fn field_representing_type_offset(_frt_type_id: crate::any::TypeId) -> usize #[rustc_comptime] pub fn non_exhaustive(_id: crate::any::TypeId) -> bool; +/// Returns the list of generic args on this type. +/// Only meaningful for Adts, closures, ... Everything else returns an empty slice. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_generics(_id: crate::any::TypeId) -> &'static [crate::mem::type_info::Generic]; + /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 71a322abd7417..66f85dab7b6c9 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -224,6 +224,7 @@ pub struct Variant { #[derive(Debug)] #[non_exhaustive] #[unstable(feature = "type_info", issue = "146922")] +#[lang = "type_info_generic"] pub enum Generic { /// Lifetimes. Lifetime(Lifetime), @@ -570,6 +571,15 @@ impl TypeId { pub fn non_exhaustive(self) -> bool { intrinsics::non_exhaustive(self) } + + /// Returns a list of generic parameters of the type. + /// Returns an empty slice for everything that doesn't have generics. + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn generics(self) -> &'static [Generic] { + intrinsics::type_id_generics(self) + } } /// Field representing type ID. Representing a field of a struct, tuple or enum variant. diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index b2756babe7864..095419b8792b1 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -257,6 +257,7 @@ fn test_enums() { assert!(ty_id.field(2, 0).type_id() == TypeId::of::<()>()); assert!(ty_id.field(2, 1).type_id() == TypeId::of::<&str>()); assert!(ty_id.field(2, 1).name() == "b"); + assert!(ty_id.generics().is_empty()); } const { From 691f04505e1380b82ae72756debf1c863b95494a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 3 Aug 2026 22:28:49 +0200 Subject: [PATCH 90/94] Add missing `cfg_attr` on doc tests in stdlib `os` module --- library/std/src/os/aix/fs.rs | 48 ++++++--- library/std/src/os/fortanix_sgx/ffi.rs | 12 ++- library/std/src/os/freebsd/net.rs | 3 +- library/std/src/os/hermit/ffi.rs | 6 +- library/std/src/os/hurd/fs.rs | 48 ++++++--- library/std/src/os/l4re/fs.rs | 51 ++++++--- library/std/src/os/linux/fs.rs | 51 ++++++--- library/std/src/os/linux/process.rs | 6 +- library/std/src/os/net/linux_ext/addr.rs | 18 +++- library/std/src/os/net/linux_ext/socket.rs | 9 +- library/std/src/os/net/linux_ext/tcp.rs | 27 ++++- library/std/src/os/netbsd/net.rs | 3 +- library/std/src/os/redox/fs.rs | 51 ++++++--- library/std/src/os/rtems/fs.rs | 48 ++++++--- library/std/src/os/solid/ffi.rs | 6 +- library/std/src/os/solid/io.rs | 3 +- library/std/src/os/unix/ffi/mod.rs | 6 +- library/std/src/os/unix/fs.rs | 120 ++++++++++++++------- library/std/src/os/unix/io/mod.rs | 3 +- library/std/src/os/unix/mod.rs | 3 +- library/std/src/os/unix/net/addr.rs | 21 ++-- library/std/src/os/unix/net/ancillary.rs | 16 ++- library/std/src/os/unix/net/datagram.rs | 78 +++++++++----- library/std/src/os/unix/net/listener.rs | 30 ++++-- library/std/src/os/unix/net/stream.rs | 56 ++++++---- library/std/src/os/unix/process.rs | 21 ++-- 26 files changed, 510 insertions(+), 234 deletions(-) diff --git a/library/std/src/os/aix/fs.rs b/library/std/src/os/aix/fs.rs index 36e56f23cc555..1a56736a38d54 100644 --- a/library/std/src/os/aix/fs.rs +++ b/library/std/src/os/aix/fs.rs @@ -16,7 +16,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -33,7 +34,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -50,7 +52,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -67,7 +70,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -84,7 +88,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -101,7 +106,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -118,7 +124,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -138,7 +145,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -155,7 +163,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -174,7 +183,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -191,7 +201,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -210,7 +221,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -227,7 +239,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -246,7 +259,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -263,7 +277,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; @@ -280,7 +295,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "aix", doc = "```no_run")] + #[cfg_attr(not(target_os = "aix"), doc = "```ignore (needs aix)")] /// use std::fs; /// use std::io; /// use std::os::aix::fs::MetadataExt; diff --git a/library/std/src/os/fortanix_sgx/ffi.rs b/library/std/src/os/fortanix_sgx/ffi.rs index ac1db0e5e39cc..c2d71eada095f 100644 --- a/library/std/src/os/fortanix_sgx/ffi.rs +++ b/library/std/src/os/fortanix_sgx/ffi.rs @@ -2,7 +2,11 @@ //! //! # Examples //! -//! ``` +#![cfg_attr(all(target_vendor = "fortanix", target_env = "sgx"), doc = "```")] +#![cfg_attr( + not(all(target_vendor = "fortanix", target_env = "sgx")), + doc = "```ignore (needs aix)" +)] //! use std::ffi::OsString; //! use std::os::fortanix_sgx::ffi::OsStringExt; //! @@ -17,7 +21,11 @@ //! assert_eq!(bytes, b"foo"); //! ``` //! -//! ``` +#![cfg_attr(all(target_vendor = "fortanix", target_env = "sgx"), doc = "```")] +#![cfg_attr( + not(all(target_vendor = "fortanix", target_env = "sgx")), + doc = "```ignore (needs aix)" +)] //! use std::ffi::OsStr; //! use std::os::fortanix_sgx::ffi::OsStrExt; //! diff --git a/library/std/src/os/freebsd/net.rs b/library/std/src/os/freebsd/net.rs index 68f39ab349a72..550696e9536dd 100644 --- a/library/std/src/os/freebsd/net.rs +++ b/library/std/src/os/freebsd/net.rs @@ -27,7 +27,8 @@ pub impl(self) trait UnixSocketExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "freebsd", doc = "```no_run")] + #[cfg_attr(not(target_os = "freebsd"), doc = "```ignore (needs freebsd)")] /// #![feature(unix_socket_ancillary_data)] /// use std::os::freebsd::net::UnixSocketExt; /// use std::os::unix::net::UnixDatagram; diff --git a/library/std/src/os/hermit/ffi.rs b/library/std/src/os/hermit/ffi.rs index 01a54e1ac8df8..d6be3c4615816 100644 --- a/library/std/src/os/hermit/ffi.rs +++ b/library/std/src/os/hermit/ffi.rs @@ -2,7 +2,8 @@ //! //! # Examples //! -//! ``` +#![cfg_attr(target_os = "hermit", doc = "```")] +#![cfg_attr(not(target_os = "hermit"), doc = "```ignore (needs hermit)")] //! use std::ffi::OsString; //! use std::os::hermit::ffi::OsStringExt; //! @@ -17,7 +18,8 @@ //! assert_eq!(bytes, b"foo"); //! ``` //! -//! ``` +#![cfg_attr(target_os = "hermit", doc = "```")] +#![cfg_attr(not(target_os = "hermit"), doc = "```ignore (needs hermit)")] //! use std::ffi::OsStr; //! use std::os::hermit::ffi::OsStrExt; //! diff --git a/library/std/src/os/hurd/fs.rs b/library/std/src/os/hurd/fs.rs index e0fc544fed1db..dc6f61180cb1b 100644 --- a/library/std/src/os/hurd/fs.rs +++ b/library/std/src/os/hurd/fs.rs @@ -16,7 +16,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -33,7 +34,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -50,7 +52,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -67,7 +70,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -84,7 +88,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -101,7 +106,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -118,7 +124,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -138,7 +145,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -155,7 +163,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -174,7 +183,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -191,7 +201,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -210,7 +221,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -227,7 +239,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -246,7 +259,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -263,7 +277,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; @@ -280,7 +295,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "hurd", doc = "```no_run")] + #[cfg_attr(not(target_os = "hurd"), doc = "```ignore (needs hurd)")] /// use std::fs; /// use std::io; /// use std::os::hurd::fs::MetadataExt; diff --git a/library/std/src/os/l4re/fs.rs b/library/std/src/os/l4re/fs.rs index 1f0bacae1ca68..491e04a4d25cf 100644 --- a/library/std/src/os/l4re/fs.rs +++ b/library/std/src/os/l4re/fs.rs @@ -25,7 +25,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -45,7 +46,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -62,7 +64,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -79,7 +82,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -96,7 +100,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -113,7 +118,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -130,7 +136,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -147,7 +154,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -167,7 +175,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -184,7 +193,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -203,7 +213,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -220,7 +231,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -239,7 +251,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -256,7 +269,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -275,7 +289,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -292,7 +307,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -309,7 +325,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "l4re", doc = "```no_run")] + #[cfg_attr(not(target_os = "l4re"), doc = "```ignore (needs l4re)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; diff --git a/library/std/src/os/linux/fs.rs b/library/std/src/os/linux/fs.rs index e52a63bc798ea..0e8fa0a15da09 100644 --- a/library/std/src/os/linux/fs.rs +++ b/library/std/src/os/linux/fs.rs @@ -25,7 +25,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -45,7 +46,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -62,7 +64,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -79,7 +82,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -96,7 +100,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -113,7 +118,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -130,7 +136,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -147,7 +154,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -167,7 +175,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -184,7 +193,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -203,7 +213,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -220,7 +231,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -239,7 +251,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -256,7 +269,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -275,7 +289,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -292,7 +307,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; @@ -309,7 +325,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "linux", doc = "```no_run")] + #[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// use std::fs; /// use std::io; /// use std::os::linux::fs::MetadataExt; diff --git a/library/std/src/os/linux/process.rs b/library/std/src/os/linux/process.rs index e4ab7622cfbd9..e0ddaa85cb82e 100644 --- a/library/std/src/os/linux/process.rs +++ b/library/std/src/os/linux/process.rs @@ -20,8 +20,10 @@ struct InnerPidFd; /// with [`create_pidfd`]. Subsequently, the created pidfd can be retrieved /// from the [`Child`] by calling [`pidfd`] or [`into_pidfd`]. /// -/// Example: -/// ```no_run +/// # Examples +/// +#[cfg_attr(target_os = "linux", doc = "```no_run")] +#[cfg_attr(not(target_os = "linux"), doc = "```ignore (needs linux)")] /// #![feature(linux_pidfd)] /// use std::os::linux::process::{CommandExt, ChildExt}; /// use std::process::Command; diff --git a/library/std/src/os/net/linux_ext/addr.rs b/library/std/src/os/net/linux_ext/addr.rs index ea7e436d11129..07adee90b0bc0 100644 --- a/library/std/src/os/net/linux_ext/addr.rs +++ b/library/std/src/os/net/linux_ext/addr.rs @@ -20,7 +20,14 @@ pub impl(in crate::os) trait SocketAddrExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr( + any(target_os = "linux", target_os = "android", target_os = "cygwin"), + doc = "```no_run" + )] + #[cfg_attr( + not(any(target_os = "linux", target_os = "android", target_os = "cygwin")), + doc = "```ignore (needs linux)" + )] /// use std::os::unix::net::{UnixListener, SocketAddr}; /// #[cfg(target_os = "linux")] /// use std::os::linux::net::SocketAddrExt; @@ -48,7 +55,14 @@ pub impl(in crate::os) trait SocketAddrExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr( + any(target_os = "linux", target_os = "android", target_os = "cygwin"), + doc = "```no_run" + )] + #[cfg_attr( + not(any(target_os = "linux", target_os = "android", target_os = "cygwin")), + doc = "```ignore (needs linux)" + )] /// use std::os::unix::net::{UnixListener, SocketAddr}; /// #[cfg(target_os = "linux")] /// use std::os::linux::net::SocketAddrExt; diff --git a/library/std/src/os/net/linux_ext/socket.rs b/library/std/src/os/net/linux_ext/socket.rs index b7bee94128534..777587fe4ce7c 100644 --- a/library/std/src/os/net/linux_ext/socket.rs +++ b/library/std/src/os/net/linux_ext/socket.rs @@ -24,7 +24,14 @@ pub impl(self) trait UnixSocketExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr( + any(target_os = "linux", target_os = "android", target_os = "cygwin"), + doc = "```no_run" + )] + #[cfg_attr( + not(any(target_os = "linux", target_os = "android", target_os = "cygwin")), + doc = "```ignore (needs linux)" + )] /// #![feature(unix_socket_ancillary_data)] /// #[cfg(target_os = "linux")] /// use std::os::linux::net::UnixSocketExt; diff --git a/library/std/src/os/net/linux_ext/tcp.rs b/library/std/src/os/net/linux_ext/tcp.rs index dd3a5ad7342b7..2fc6b2769568e 100644 --- a/library/std/src/os/net/linux_ext/tcp.rs +++ b/library/std/src/os/net/linux_ext/tcp.rs @@ -23,7 +23,14 @@ pub impl(self) trait TcpStreamExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr( + any(target_os = "linux", target_os = "android", target_os = "cygwin"), + doc = "```no_run" + )] + #[cfg_attr( + not(any(target_os = "linux", target_os = "android", target_os = "cygwin")), + doc = "```ignore (needs linux)" + )] /// use std::net::TcpStream; /// #[cfg(target_os = "linux")] /// use std::os::linux::net::TcpStreamExt; @@ -43,7 +50,14 @@ pub impl(self) trait TcpStreamExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr( + any(target_os = "linux", target_os = "android", target_os = "cygwin"), + doc = "```no_run" + )] + #[cfg_attr( + not(any(target_os = "linux", target_os = "android", target_os = "cygwin")), + doc = "```ignore (needs linux)" + )] /// use std::net::TcpStream; /// #[cfg(target_os = "linux")] /// use std::os::linux::net::TcpStreamExt; @@ -92,7 +106,14 @@ pub impl(self) trait TcpStreamExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr( + any(target_os = "linux", target_os = "android", target_os = "cygwin"), + doc = "```no_run" + )] + #[cfg_attr( + not(any(target_os = "linux", target_os = "android", target_os = "cygwin")), + doc = "```ignore (needs linux)" + )] /// #![feature(tcp_deferaccept)] /// use std::net::TcpStream; /// use std::os::linux::net::TcpStreamExt; diff --git a/library/std/src/os/netbsd/net.rs b/library/std/src/os/netbsd/net.rs index a77302ddbc675..a0e9ba3f7cf7c 100644 --- a/library/std/src/os/netbsd/net.rs +++ b/library/std/src/os/netbsd/net.rs @@ -27,7 +27,8 @@ pub impl(self) trait UnixSocketExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "netbsd", doc = "```no_run")] + #[cfg_attr(not(target_os = "netbsd"), doc = "```ignore (needs netbsd)")] /// #![feature(unix_socket_ancillary_data)] /// use std::os::netbsd::net::UnixSocketExt; /// use std::os::unix::net::UnixDatagram; diff --git a/library/std/src/os/redox/fs.rs b/library/std/src/os/redox/fs.rs index 0451e91071bcf..ed6c8cb08677d 100644 --- a/library/std/src/os/redox/fs.rs +++ b/library/std/src/os/redox/fs.rs @@ -21,7 +21,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -45,7 +46,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -62,7 +64,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -79,7 +82,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -96,7 +100,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -113,7 +118,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -130,7 +136,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -147,7 +154,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -167,7 +175,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -184,7 +193,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -203,7 +213,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -220,7 +231,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -239,7 +251,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -256,7 +269,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -275,7 +289,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -292,7 +307,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; @@ -309,7 +325,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "redox", doc = "```no_run")] + #[cfg_attr(not(target_os = "redox"), doc = "```ignore (needs redox)")] /// use std::fs; /// use std::io; /// use std::os::redox::fs::MetadataExt; diff --git a/library/std/src/os/rtems/fs.rs b/library/std/src/os/rtems/fs.rs index 97a0c9004c00a..ab662e5654071 100644 --- a/library/std/src/os/rtems/fs.rs +++ b/library/std/src/os/rtems/fs.rs @@ -12,7 +12,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -30,7 +31,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -48,7 +50,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -66,7 +69,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -84,7 +88,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -102,7 +107,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -120,7 +126,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -141,7 +148,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -159,7 +167,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -179,7 +188,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -197,7 +207,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -217,7 +228,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -235,7 +247,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -255,7 +268,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -273,7 +287,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; @@ -291,7 +306,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_os = "rtems", doc = "```no_run")] + #[cfg_attr(not(target_os = "rtems"), doc = "```ignore (needs rtems)")] /// use std::fs; /// use std::io; /// use std::os::rtems::fs::MetadataExt; diff --git a/library/std/src/os/solid/ffi.rs b/library/std/src/os/solid/ffi.rs index aaa2070a6abe9..d4c287ca70407 100644 --- a/library/std/src/os/solid/ffi.rs +++ b/library/std/src/os/solid/ffi.rs @@ -2,7 +2,8 @@ //! //! # Examples //! -//! ``` +#![cfg_attr(target_os = "solid", doc = "```")] +#![cfg_attr(not(target_os = "solid"), doc = "```ignore (needs solid)")] //! use std::ffi::OsString; //! use std::os::solid::ffi::OsStringExt; //! @@ -17,7 +18,8 @@ //! assert_eq!(bytes, b"foo"); //! ``` //! -//! ``` +#![cfg_attr(target_os = "solid", doc = "```")] +#![cfg_attr(not(target_os = "solid"), doc = "```ignore (needs solid)")] //! use std::ffi::OsStr; //! use std::os::solid::ffi::OsStrExt; //! diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index 808e0b874ebbf..d4defb5f47fb0 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -257,7 +257,8 @@ macro_rules! impl_owned_fd_traits { impl_owned_fd_traits! { TcpStream TcpListener UdpSocket } /// This impl allows implementing traits that require `AsFd` on Arc. -/// ``` +#[cfg_attr(target_os = "solid", doc = "```")] +#[cfg_attr(not(target_os = "solid"), doc = "```ignore (needs solid)")] /// # #[cfg(target_os = "solid_asp3")] mod group_cfg { /// # use std::os::solid::io::AsFd; /// use std::net::UdpSocket; diff --git a/library/std/src/os/unix/ffi/mod.rs b/library/std/src/os/unix/ffi/mod.rs index 5b49f50763d74..7736c8598ec45 100644 --- a/library/std/src/os/unix/ffi/mod.rs +++ b/library/std/src/os/unix/ffi/mod.rs @@ -2,7 +2,8 @@ //! //! # Examples //! -//! ``` +#![cfg_attr(target_family = "unix", doc = "```")] +#![cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] //! use std::ffi::OsString; //! use std::os::unix::ffi::OsStringExt; //! @@ -17,7 +18,8 @@ //! assert_eq!(bytes, b"foo"); //! ``` //! -//! ``` +#![cfg_attr(target_family = "unix", doc = "```")] +#![cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] //! use std::ffi::OsStr; //! use std::os::unix::ffi::OsStrExt; //! diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index c119912c3b022..ca16d62276f49 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -40,7 +40,8 @@ pub trait FileExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::io; /// use std::fs::File; /// use std::os::unix::prelude::FileExt; @@ -98,7 +99,8 @@ pub trait FileExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::io; /// use std::fs::File; /// use std::os::unix::prelude::FileExt; @@ -138,7 +140,8 @@ pub trait FileExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(core_io_borrowed_buf)] /// #![feature(read_buf_at)] /// @@ -174,7 +177,8 @@ pub trait FileExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(core_io_borrowed_buf)] /// #![feature(read_buf_at)] /// @@ -244,7 +248,8 @@ pub trait FileExt { /// Therefore, it is important to be vigilant while changing options to mitigate /// unexpected behavior. /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs::File; /// use std::io; /// use std::os::unix::prelude::FileExt; @@ -267,7 +272,8 @@ pub trait FileExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs::File; /// use std::io; /// use std::os::unix::prelude::FileExt; @@ -316,7 +322,8 @@ pub trait FileExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs::File; /// use std::io; /// use std::os::unix::prelude::FileExt; @@ -371,7 +378,8 @@ impl FileExt for fs::File { /// /// # Examples /// -/// ```no_run +#[cfg_attr(target_family = "unix", doc = "```no_run")] +#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs::{File, Permissions}; /// use std::io::{ErrorKind, Result as IoResult}; /// use std::os::unix::fs::PermissionsExt; @@ -426,7 +434,8 @@ impl FileExt for fs::File { /// } /// ``` /// -/// ```no_run +#[cfg_attr(target_family = "unix", doc = "```no_run")] +#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs::Permissions; /// use std::os::unix::fs::PermissionsExt; /// @@ -484,7 +493,8 @@ pub trait OpenOptionsExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs::OpenOptions; /// use std::os::unix::fs::OpenOptionsExt; /// @@ -507,7 +517,8 @@ pub trait OpenOptionsExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// # mod libc { pub const O_NOFOLLOW: i32 = 0; } /// use std::fs::OpenOptions; /// use std::os::unix::fs::OpenOptionsExt; @@ -543,7 +554,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::io; /// use std::fs; /// use std::os::unix::fs::MetadataExt; @@ -560,7 +572,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -577,7 +590,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -598,7 +612,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -615,7 +630,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -632,7 +648,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -649,7 +666,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -666,7 +684,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -683,7 +702,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -702,7 +722,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -719,7 +740,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -738,7 +760,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -755,7 +778,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -774,7 +798,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -791,7 +816,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -810,7 +836,8 @@ pub trait MetadataExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; @@ -894,7 +921,8 @@ pub trait FileTypeExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::FileTypeExt; /// use std::io; @@ -912,7 +940,8 @@ pub trait FileTypeExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::FileTypeExt; /// use std::io; @@ -930,7 +959,8 @@ pub trait FileTypeExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::FileTypeExt; /// use std::io; @@ -948,7 +978,8 @@ pub trait FileTypeExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::FileTypeExt; /// use std::io; @@ -988,7 +1019,8 @@ pub trait DirEntryExt { /// /// # Examples /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs; /// use std::os::unix::fs::DirEntryExt; /// @@ -1019,7 +1051,8 @@ pub impl(self) trait DirEntryExt2 { /// /// # Examples /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(dir_entry_ext2)] /// use std::os::unix::fs::DirEntryExt2; /// use std::{fs, io}; @@ -1051,7 +1084,8 @@ impl DirEntryExt2 for fs::DirEntry { /// /// # Examples /// -/// ```no_run +#[cfg_attr(target_family = "unix", doc = "```no_run")] +#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { @@ -1072,7 +1106,8 @@ pub trait DirBuilderExt { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::fs::DirBuilder; /// use std::os::unix::fs::DirBuilderExt; /// @@ -1109,7 +1144,8 @@ impl DirBuilderExt for fs::DirBuilder { /// /// # Examples /// -/// ```no_run +#[cfg_attr(target_family = "unix", doc = "```no_run")] +#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { @@ -1128,7 +1164,8 @@ pub fn chown>(dir: P, uid: Option, gid: Option) -> io:: /// /// # Examples /// -/// ```no_run +#[cfg_attr(target_family = "unix", doc = "```no_run")] +#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { @@ -1149,7 +1186,8 @@ pub fn fchown(fd: F, uid: Option, gid: Option) -> io::Result< /// /// # Examples /// -/// ```no_run +#[cfg_attr(target_family = "unix", doc = "```no_run")] +#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { @@ -1171,7 +1209,8 @@ pub fn lchown>(dir: P, uid: Option, gid: Option) -> io: /// /// # Examples /// -/// ```no_run +#[cfg_attr(target_family = "unix", doc = "```no_run")] +#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { @@ -1191,7 +1230,8 @@ pub fn chroot>(dir: P) -> io::Result<()> { /// /// # Examples /// -/// ```no_run +#[cfg_attr(target_family = "unix", doc = "```no_run")] +#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// # #![feature(unix_mkfifo)] /// # #[cfg(not(unix))] /// # fn main() {} diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs index 19fdf8ba2fb03..618e0dca7659b 100644 --- a/library/std/src/os/unix/io/mod.rs +++ b/library/std/src/os/unix/io/mod.rs @@ -119,7 +119,8 @@ pub impl(self) trait StdioExt { /// /// [currently]: crate::io#platform-specific-behavior /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(stdio_swap)] /// use std::io::{self, Read, Write}; /// use std::os::unix::io::StdioExt; diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index 25aa3bf7893f4..c994174b744dd 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -11,7 +11,8 @@ //! //! # Examples //! -//! ```no_run +#![cfg_attr(target_family = "unix", doc = "```no_run")] +#![cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] //! use std::fs::File; //! use std::os::unix::prelude::*; //! diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index e13f44d6fc9bd..08cd6138591e4 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -76,7 +76,8 @@ enum AddressKind<'a> { /// /// # Examples /// -/// ``` +#[cfg_attr(target_family = "unix", doc = "```")] +#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixListener; /// /// let socket = match UnixListener::bind("/tmp/sock") { @@ -145,7 +146,8 @@ impl SocketAddr { /// /// # Examples /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::SocketAddr; /// use std::path::Path; /// @@ -158,7 +160,8 @@ impl SocketAddr { /// /// Creating a `SocketAddr` with a NULL byte results in an error. /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::SocketAddr; /// /// assert!(SocketAddr::from_pathname("/path/with/\0/bytes").is_err()); @@ -177,7 +180,8 @@ impl SocketAddr { /// /// A named address: /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixListener; /// /// fn main() -> std::io::Result<()> { @@ -190,7 +194,8 @@ impl SocketAddr { /// /// An unnamed address: /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { @@ -212,7 +217,8 @@ impl SocketAddr { /// /// With a pathname: /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixListener; /// use std::path::Path; /// @@ -226,7 +232,8 @@ impl SocketAddr { /// /// Without a pathname: /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs index d0984bdfb99d1..d0e613cac519a 100644 --- a/library/std/src/os/unix/net/ancillary.rs +++ b/library/std/src/os/unix/net/ancillary.rs @@ -576,7 +576,9 @@ impl<'a> Iterator for Messages<'a> { /// A Unix socket Ancillary data struct. /// /// # Example -/// ```no_run +/// +#[cfg_attr(target_family = "unix", doc = "```no_run")] +#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::{UnixStream, SocketAncillary, AncillaryData}; /// use std::io::IoSliceMut; @@ -615,7 +617,8 @@ impl<'a> SocketAncillary<'a> { /// /// # Example /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// # #![allow(unused_mut)] /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::SocketAncillary; @@ -658,7 +661,8 @@ impl<'a> SocketAncillary<'a> { /// /// # Example /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::{UnixStream, SocketAncillary}; /// use std::io::IoSliceMut; @@ -692,7 +696,8 @@ impl<'a> SocketAncillary<'a> { /// /// # Example /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::{UnixStream, SocketAncillary}; /// use std::os::unix::io::AsRawFd; @@ -759,7 +764,8 @@ impl<'a> SocketAncillary<'a> { /// /// # Example /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(unix_socket_ancillary_data)] /// use std::os::unix::net::{UnixStream, SocketAncillary, AncillaryData}; /// use std::io::IoSliceMut; diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index e7bcd70140d67..e03ecd7eed9ea 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -46,7 +46,8 @@ const MSG_NOSIGNAL: core::ffi::c_int = 0x0; /// /// # Examples /// -/// ```no_run +#[cfg_attr(target_family = "unix", doc = "```no_run")] +#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { @@ -81,7 +82,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// let sock = match UnixDatagram::bind("/path/to/the/socket") { @@ -108,7 +110,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::{UnixDatagram}; /// /// fn main() -> std::io::Result<()> { @@ -142,7 +145,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// let sock = match UnixDatagram::unbound() { @@ -165,7 +169,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// let (sock1, sock2) = match UnixDatagram::pair() { @@ -193,7 +198,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { @@ -222,7 +228,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::{UnixDatagram}; /// /// fn main() -> std::io::Result<()> { @@ -260,7 +267,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { @@ -278,7 +286,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { @@ -300,7 +309,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { @@ -350,7 +360,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { @@ -372,7 +383,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { @@ -505,7 +517,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { @@ -539,7 +552,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::{UnixDatagram}; /// /// fn main() -> std::io::Result<()> { @@ -575,7 +589,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { @@ -696,7 +711,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// use std::time::Duration; /// @@ -711,7 +727,8 @@ impl UnixDatagram { /// An [`Err`] is returned if the zero [`Duration`] is passed to this /// method: /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::io; /// use std::os::unix::net::UnixDatagram; /// use std::time::Duration; @@ -740,7 +757,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// use std::time::Duration; /// @@ -755,7 +773,8 @@ impl UnixDatagram { /// An [`Err`] is returned if the zero [`Duration`] is passed to this /// method: /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::io; /// use std::os::unix::net::UnixDatagram; /// use std::time::Duration; @@ -777,7 +796,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// use std::time::Duration; /// @@ -798,7 +818,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// use std::time::Duration; /// @@ -819,7 +840,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { @@ -862,7 +884,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// /// fn main() -> std::io::Result<()> { @@ -884,7 +907,8 @@ impl UnixDatagram { /// specified portions to immediately return with an appropriate value /// (see the documentation of [`Shutdown`]). /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixDatagram; /// use std::net::Shutdown; /// @@ -908,7 +932,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(unix_socket_peek)] /// /// use std::os::unix::net::UnixDatagram; @@ -940,7 +965,8 @@ impl UnixDatagram { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(unix_socket_peek)] /// /// use std::os::unix::net::UnixDatagram; diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index 99eef7f4013d6..b7f8d25a85ae5 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -9,7 +9,8 @@ use crate::{fmt, io, mem}; /// /// # Examples /// -/// ```no_run +#[cfg_attr(target_family = "unix", doc = "```no_run")] +#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::thread; /// use std::os::unix::net::{UnixStream, UnixListener}; /// @@ -56,7 +57,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixListener; /// /// let listener = match UnixListener::bind("/path/to/the/socket") { @@ -115,7 +117,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::{UnixListener}; /// /// fn main() -> std::io::Result<()> { @@ -160,7 +163,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixListener; /// /// fn main() -> std::io::Result<()> { @@ -190,7 +194,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixListener; /// /// fn main() -> std::io::Result<()> { @@ -208,7 +213,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixListener; /// /// fn main() -> std::io::Result<()> { @@ -232,7 +238,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixListener; /// /// fn main() -> std::io::Result<()> { @@ -250,7 +257,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixListener; /// /// fn main() -> std::io::Result<()> { @@ -277,7 +285,8 @@ impl UnixListener { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::thread; /// use std::os::unix::net::{UnixStream, UnixListener}; /// @@ -372,7 +381,8 @@ impl<'a> IntoIterator for &'a UnixListener { /// /// # Examples /// -/// ```no_run +#[cfg_attr(target_family = "unix", doc = "```no_run")] +#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::thread; /// use std::os::unix::net::{UnixStream, UnixListener}; /// diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index a50b10539ebf5..8567e2fbb783d 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -44,7 +44,8 @@ use crate::time::Duration; /// /// # Examples /// -/// ```no_run +#[cfg_attr(target_family = "unix", doc = "```no_run")] +#[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixStream; /// use std::io::prelude::*; /// @@ -93,7 +94,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixStream; /// /// let socket = match UnixStream::connect("/tmp/sock") { @@ -121,7 +123,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::{UnixListener, UnixStream}; /// /// fn main() -> std::io::Result<()> { @@ -137,7 +140,7 @@ impl UnixStream { /// }; /// Ok(()) /// } - /// ```` + /// ``` #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result { unsafe { @@ -157,7 +160,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixStream; /// /// let (sock1, sock2) = match UnixStream::pair() { @@ -183,7 +187,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixStream; /// /// fn main() -> std::io::Result<()> { @@ -201,7 +206,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixStream; /// /// fn main() -> std::io::Result<()> { @@ -219,7 +225,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixStream; /// /// fn main() -> std::io::Result<()> { @@ -237,7 +244,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(peer_credentials_unix_socket)] /// use std::os::unix::net::UnixStream; /// @@ -274,7 +282,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixStream; /// use std::time::Duration; /// @@ -288,7 +297,8 @@ impl UnixStream { /// An [`Err`] is returned if the zero [`Duration`] is passed to this /// method: /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::io; /// use std::os::unix::net::UnixStream; /// use std::time::Duration; @@ -316,7 +326,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixStream; /// use std::time::Duration; /// @@ -331,7 +342,8 @@ impl UnixStream { /// An [`Err`] is returned if the zero [`Duration`] is passed to this /// method: /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::io; /// use std::os::unix::net::UnixStream; /// use std::time::Duration; @@ -353,7 +365,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixStream; /// use std::time::Duration; /// @@ -373,7 +386,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixStream; /// use std::time::Duration; /// @@ -394,7 +408,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixStream; /// /// fn main() -> std::io::Result<()> { @@ -437,7 +452,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixStream; /// /// fn main() -> std::io::Result<()> { @@ -464,7 +480,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::os::unix::net::UnixStream; /// use std::net::Shutdown; /// @@ -488,7 +505,8 @@ impl UnixStream { /// /// # Examples /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(unix_socket_peek)] /// /// use std::os::unix::net::UnixStream; diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index f55c821dfb7bc..9fa731de82691 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -188,7 +188,8 @@ pub impl(self) trait CommandExt { /// /// A process group ID of 0 will use the process ID as the PGID. /// - /// ```no_run + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// use std::process::Command; /// use std::os::unix::process::CommandExt; /// @@ -303,7 +304,8 @@ pub impl(self) trait ExitStatusExt { /// status. The following example relies on that convention and is therefore not guaranteed to /// hold on every target: /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// # if cfg!(target_os = "fuchsia") { return; } /// use std::os::unix::process::ExitStatusExt; /// use std::process::ExitStatus; @@ -324,7 +326,8 @@ pub impl(self) trait ExitStatusExt { /// 8-bit exit code in bits 8..16, so a status built with `(code & 0xff) << 8` will usually /// round-trip back to the original exit code: /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// # if cfg!(target_os = "fuchsia") { return; } /// use std::os::unix::process::ExitStatusExt; /// use std::process::ExitStatus; @@ -350,7 +353,8 @@ pub impl(self) trait ExitStatusExt { /// In other words, if [`WIFSIGNALED`][`wait`], this returns [`WTERMSIG`][`wait`]. For such a status, /// [`ExitStatus::code`] returns `None`: /// - /// ``` + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// # if cfg!(target_os = "fuchsia") { return; } /// use std::os::unix::process::ExitStatusExt; /// use std::process::ExitStatus; @@ -475,7 +479,8 @@ pub impl(self) trait ChildExt { /// /// # Examples /// - /// ```rust + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(unix_send_signal)] /// /// use std::{io, os::unix::process::ChildExt, process::{Command, Stdio}}; @@ -503,7 +508,8 @@ pub impl(self) trait ChildExt { /// /// # Examples /// - /// ```rust + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(unix_send_signal)] /// /// use std::{io, os::unix::process::{ChildExt, CommandExt}, process::{Command, Stdio}}; @@ -535,7 +541,8 @@ pub impl(self) trait ChildExt { /// /// # Examples /// - /// ```rust + #[cfg_attr(target_family = "unix", doc = "```no_run")] + #[cfg_attr(not(target_family = "unix"), doc = "```ignore (needs unix)")] /// #![feature(unix_kill_process_group)] /// /// use std::{os::unix::process::{ChildExt, CommandExt}, process::{Command, Stdio}}; From 08a1b4258cabe679f734e663becf4e8f0cff76c3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Aug 2026 19:55:14 +0200 Subject: [PATCH 91/94] implement -Zllvm-target-feature --- compiler/rustc_codegen_llvm/src/back/write.rs | 7 ++-- .../rustc_codegen_llvm/src/diagnostics.rs | 7 ++++ compiler/rustc_codegen_llvm/src/llvm_util.rs | 29 +++++++++++++++-- compiler/rustc_session/src/options.rs | 4 +++ .../ui/target-feature/llvm-target-feature.rs | 32 +++++++++++++++++++ 5 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 tests/ui/target-feature/llvm-target-feature.rs diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 94883a94f089a..edf52e67b434b 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -100,14 +100,17 @@ fn write_output_file<'ll>( result.into_result().unwrap_or_else(|()| llvm_err(dcx, LlvmError::WriteOutput { path: output })) } +/// If `for_cfg` is `true` then we are creating this machine for the purpose of populating +/// [`rustc_codegen_ssa::TargetConfig`] based on what LLVM actually enables in this configuration. +/// `-Ctarget-feature` should be ignored in that case since it is already processed separately. pub(crate) fn create_informational_target_machine( sess: &Session, - only_base_features: bool, + for_cfg: bool, ) -> OwnedTargetMachine { let config = TargetMachineFactoryConfig { split_dwarf_file: None, output_obj_file: None }; // Can't use query system here quite yet because this function is invoked before the query // system/tcx is set up. - let features = llvm_util::global_llvm_features(sess, only_base_features); + let features = llvm_util::global_llvm_features(sess, for_cfg); target_machine_factory(sess, config::OptLevel::No, &features)(sess.dcx(), config) } diff --git a/compiler/rustc_codegen_llvm/src/diagnostics.rs b/compiler/rustc_codegen_llvm/src/diagnostics.rs index bcbafab585b40..50a11417513a5 100644 --- a/compiler/rustc_codegen_llvm/src/diagnostics.rs +++ b/compiler/rustc_codegen_llvm/src/diagnostics.rs @@ -245,3 +245,10 @@ pub(crate) struct IntrinsicWrongArch<'a> { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag("unknown feature specified for `-Zllvm-target-feature`: `{$feature}`")] +#[note("features must begin with a `+` to enable or `-` to disable it")] +pub(crate) struct UnknownLlvmTargetFeaturePrefix<'a> { + pub feature: &'a str, +} diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 9ad14925afb14..6892e616e1f11 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -646,7 +646,11 @@ fn llvm_features_by_flags(sess: &Session, features: &mut Vec) { /// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, /// `--target` and similar). -pub(crate) fn global_llvm_features(sess: &Session, only_base_features: bool) -> Vec { +/// +/// If `for_cfg` is `true` then we are assembling the feature list for the purpose of populating +/// [`rustc_codegen_ssa::TargetConfig`] based on what LLVM actually enables in this configuration. +/// `-Ctarget-feature` should be ignored in that case since it is already processed separately. +pub(crate) fn global_llvm_features(sess: &Session, for_cfg: bool) -> Vec { // Features that come earlier are overridden by conflicting features later in the string. // Typically we'll want more explicit settings to override the implicit ones, so: // @@ -725,14 +729,33 @@ pub(crate) fn global_llvm_features(sess: &Session, only_base_features: bool) -> // Features implied by an implicit or explicit `--target`. target_features::target_spec_to_backend_features(sess, &mut extend_backend_features); - // -Ctarget-features - if !only_base_features { + // -Ctarget-features. Skipped for `cfg` as there we parse -Ctarget-features directly instead of + // going via an LLVM target machine (which avoids accidentally picking up LLVM-level target + // feature implications that we do not want). + if !for_cfg { target_features::flag_to_backend_features(sess, extend_backend_features); } // We add this in the "base target" so that these show up in `sess.unstable_target_features`. llvm_features_by_flags(sess, &mut features); + // `-Zllvm-target-features`, all the way at the end to overwrite everything. + // Should be picked up by `cfg` (e.g. if someone enables AVX this way). + for feature in sess.opts.unstable_opts.llvm_target_feature.split(',') { + if feature.is_empty() { + continue; + } + if feature.starts_with('+') || feature.starts_with('-') { + features.push(feature.to_owned()); + } else { + // LLVM seems to silently ignore entries without leading `+`/`-`. Let's emit a warning + // to avoid confusion. But only emit this warning once, under `for_cfg`. + if for_cfg { + sess.dcx().emit_warn(diagnostics::UnknownLlvmTargetFeaturePrefix { feature }); + } + } + } + features } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index c46b6418754fa..51d48b39fb35e 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2582,6 +2582,10 @@ options! { "a list of module flags to pass to LLVM (space separated)"), llvm_plugins: Vec = (Vec::new(), parse_list, [TRACKED], "a list LLVM plugins to enable (space separated)"), + llvm_target_feature: String = (String::new(), parse_target_feature, [TRACKED] { TARGET_MODIFIER: LlvmTargetFeature }, + "enable/disable LLVM-level target features. \ + This feature is unsafe and can cause ABI issues and compiler crashes, \ + because LLVM does not support all target feature combinations."), llvm_time_trace: bool = (false, parse_bool, [UNTRACKED], "generate JSON tracing data file from LLVM data (default: no)"), llvm_writable: bool = (false, parse_bool, [TRACKED], diff --git a/tests/ui/target-feature/llvm-target-feature.rs b/tests/ui/target-feature/llvm-target-feature.rs new file mode 100644 index 0000000000000..6b94574d3a8e4 --- /dev/null +++ b/tests/ui/target-feature/llvm-target-feature.rs @@ -0,0 +1,32 @@ +//! Sometimes `-Ctarget-cpu` can *disable* target features that would by default be enabled on the +//! current target. Ensure that we catch the case where those target features are important for the +//! ABI. + +//@ compile-flags: --crate-type=lib +//@ compile-flags: --target=x86_64-unknown-linux-gnu +//@ compile-flags: -Zllvm-target-feature=+avx2 +//@ needs-llvm-components: x86 + +//@ build-pass +//@ ignore-backends: gcc +//@ add-minicore + +#![feature(no_core, intrinsics, rustc_attrs)] +#![no_core] +#![allow(improper_ctypes_definitions)] + +extern crate minicore; +use minicore::*; + +// Also test the ABI checks by using `extern "C"` +#[no_mangle] // force codegen +pub extern "C" fn do_thing(x: simd::f32x8, y: simd::f32x8) -> simd::f32x8 { + #[rustc_intrinsic] + #[rustc_nounwind] + pub const unsafe fn simd_add(x: T, y: T) -> T; + + unsafe { simd_add(x, y) } +} + +#[cfg(not(target_feature = "avx2"))] +compile_error!("the avx2 cfg did not get set"); From c82207c46e5cefe4ddb000232aec281c28f0f6a8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Aug 2026 19:59:25 +0200 Subject: [PATCH 92/94] improve error message on missing target feature prefix --- compiler/rustc_codegen_llvm/src/diagnostics.rs | 2 +- compiler/rustc_codegen_ssa/src/diagnostics.rs | 2 +- tests/ui/target-feature/missing-plusminus-2.rs | 2 +- tests/ui/target-feature/missing-plusminus-2.stderr | 2 +- tests/ui/target-feature/missing-plusminus-llvm.rs | 13 +++++++++++++ .../ui/target-feature/missing-plusminus-llvm.stderr | 6 ++++++ tests/ui/target-feature/missing-plusminus.rs | 2 +- tests/ui/target-feature/missing-plusminus.stderr | 2 +- 8 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 tests/ui/target-feature/missing-plusminus-llvm.rs create mode 100644 tests/ui/target-feature/missing-plusminus-llvm.stderr diff --git a/compiler/rustc_codegen_llvm/src/diagnostics.rs b/compiler/rustc_codegen_llvm/src/diagnostics.rs index 50a11417513a5..ea29683b9d289 100644 --- a/compiler/rustc_codegen_llvm/src/diagnostics.rs +++ b/compiler/rustc_codegen_llvm/src/diagnostics.rs @@ -247,7 +247,7 @@ pub(crate) struct IntrinsicWrongArch<'a> { } #[derive(Diagnostic)] -#[diag("unknown feature specified for `-Zllvm-target-feature`: `{$feature}`")] +#[diag("ignoring feature with missing prefix in `-Zllvm-target-feature`: `{$feature}`")] #[note("features must begin with a `+` to enable or `-` to disable it")] pub(crate) struct UnknownLlvmTargetFeaturePrefix<'a> { pub feature: &'a str, diff --git a/compiler/rustc_codegen_ssa/src/diagnostics.rs b/compiler/rustc_codegen_ssa/src/diagnostics.rs index 6b182d795a9ec..e6aab553072f2 100644 --- a/compiler/rustc_codegen_ssa/src/diagnostics.rs +++ b/compiler/rustc_codegen_ssa/src/diagnostics.rs @@ -1198,7 +1198,7 @@ pub(crate) struct XcrunSdkPathWarning { pub(crate) struct Aarch64SoftfloatNeon; #[derive(Diagnostic)] -#[diag("unknown feature specified for `-Ctarget-feature`: `{$feature}`")] +#[diag("ignoring feature with missing prefix in `-Ctarget-feature`: `{$feature}`")] #[note("features must begin with a `+` to enable or `-` to disable it")] pub(crate) struct UnknownCTargetFeaturePrefix<'a> { pub feature: &'a str, diff --git a/tests/ui/target-feature/missing-plusminus-2.rs b/tests/ui/target-feature/missing-plusminus-2.rs index 06291ab23ad5e..f8b1ff58d535c 100644 --- a/tests/ui/target-feature/missing-plusminus-2.rs +++ b/tests/ui/target-feature/missing-plusminus-2.rs @@ -5,4 +5,4 @@ #![feature(no_core)] #![no_core] -//~? WARN unknown feature specified for `-Ctarget-feature`: `rdrand` +//~? WARN ignoring feature with missing prefix in `-Ctarget-feature`: `rdrand` diff --git a/tests/ui/target-feature/missing-plusminus-2.stderr b/tests/ui/target-feature/missing-plusminus-2.stderr index 5ed2652a06df2..d2bc227ccff7c 100644 --- a/tests/ui/target-feature/missing-plusminus-2.stderr +++ b/tests/ui/target-feature/missing-plusminus-2.stderr @@ -1,4 +1,4 @@ -warning: unknown feature specified for `-Ctarget-feature`: `rdrand` +warning: ignoring feature with missing prefix in `-Ctarget-feature`: `rdrand` | = note: features must begin with a `+` to enable or `-` to disable it diff --git a/tests/ui/target-feature/missing-plusminus-llvm.rs b/tests/ui/target-feature/missing-plusminus-llvm.rs new file mode 100644 index 0000000000000..fe3c2cfcf2bd3 --- /dev/null +++ b/tests/ui/target-feature/missing-plusminus-llvm.rs @@ -0,0 +1,13 @@ +//@ compile-flags: -Zllvm-target-feature=banana --crate-type=rlib +//@ build-pass + +//@ ignore-backends: gcc +//@ add-minicore + +#![feature(no_core, intrinsics, rustc_attrs)] +#![no_core] + +extern crate minicore; +use minicore::*; + +//~? WARN ignoring feature with missing prefix in `-Zllvm-target-feature`: `banana` diff --git a/tests/ui/target-feature/missing-plusminus-llvm.stderr b/tests/ui/target-feature/missing-plusminus-llvm.stderr new file mode 100644 index 0000000000000..90ae882577e32 --- /dev/null +++ b/tests/ui/target-feature/missing-plusminus-llvm.stderr @@ -0,0 +1,6 @@ +warning: ignoring feature with missing prefix in `-Zllvm-target-feature`: `banana` + | + = note: features must begin with a `+` to enable or `-` to disable it + +warning: 1 warning emitted + diff --git a/tests/ui/target-feature/missing-plusminus.rs b/tests/ui/target-feature/missing-plusminus.rs index e8356e0fa3552..4613862c803d6 100644 --- a/tests/ui/target-feature/missing-plusminus.rs +++ b/tests/ui/target-feature/missing-plusminus.rs @@ -1,4 +1,4 @@ //@ compile-flags: -Ctarget-feature=banana --crate-type=rlib //@ build-pass -//~? WARN unknown feature specified for `-Ctarget-feature`: `banana` +//~? WARN ignoring feature with missing prefix in `-Ctarget-feature`: `banana` diff --git a/tests/ui/target-feature/missing-plusminus.stderr b/tests/ui/target-feature/missing-plusminus.stderr index 93abf35080579..c0f49516dd820 100644 --- a/tests/ui/target-feature/missing-plusminus.stderr +++ b/tests/ui/target-feature/missing-plusminus.stderr @@ -1,4 +1,4 @@ -warning: unknown feature specified for `-Ctarget-feature`: `banana` +warning: ignoring feature with missing prefix in `-Ctarget-feature`: `banana` | = note: features must begin with a `+` to enable or `-` to disable it From 66175a7031a60a3cb63d64752c7ee85e09e992c3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 4 Aug 2026 11:29:43 +0200 Subject: [PATCH 93/94] clarify non-determinism docs for algebraic operations --- library/core/src/primitive_docs.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 78222a638c60c..d80c55538055b 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1333,10 +1333,12 @@ mod prim_f16 {} /// such as NaN, +/-Inf, or -0.0 may behave in unexpected ways, but these operations /// will never cause undefined behavior. /// -/// Because of the unpredictable nature of compiler optimizations, the same inputs may produce -/// different results even within a single program run. **Unsafe code must not rely on any property -/// of the return value for soundness.** However, implementations will generally do their best to -/// pick a reasonable tradeoff between performance and accuracy of the result. +/// Algebraic operations are non-deterministic. This means that two invocations of such an operation +/// with the same inputs may produce different results even within a single program run. No +/// guarantees are made about the results of individual operations, except that they produce *some* +/// valid floating-point value. **Unsafe code must not rely on any property of the return value for +/// soundness.** However, implementations will generally do their best to pick a reasonable tradeoff +/// between performance and accuracy of the result. /// /// For example: /// @@ -1362,6 +1364,21 @@ mod prim_f16 {} /// x = ((a + b) + c) + d; // As written /// x = (a + c) + (b + d); // Reordered to shorten critical path and enable vectorization /// ``` +/// +/// The following example demonstrates the non-determinism: +/// +/// ``` +/// # #![allow(unused_assignments)] +/// # let a: f32 = 1.0; +/// # let b: f32 = 2.0; +/// let x1 = a.algebraic_add(b); +/// let x2 = a.algebraic_add(b); +/// assert_eq!(x1.to_bits(), x1.to_bits()); // this is guaranteed +/// # if false { +/// assert_eq!(x1.to_bits(), x2.to_bits()); // but this may fail +/// assert!(!x2.is_nan()); // this may also fail, even if there was no NaN input +/// # } +/// ``` #[stable(feature = "rust1", since = "1.0.0")] mod prim_f32 {} From fec6eeb362e77d2fad471303b9b0dc7b67919db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 4 Aug 2026 13:05:22 +0200 Subject: [PATCH 94/94] Reduce number of miri tests executed on PR CI --- .../host-x86_64/x86_64-gnu-miri/check-miri.sh | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh b/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh index 3bbcc32c7f11b..8d7206d7391e2 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh @@ -11,9 +11,9 @@ X_PY="$1" # that bugs which only surface when the GC runs at a specific time are more likely to cause CI to fail. # This significantly increases the runtime of our test suite, or we'd do this in PR CI too. if [ -z "${PR_CI_JOB:-}" ]; then - MIRIFLAGS=-Zmiri-provenance-gc=1 python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri + MIRIFLAGS=-Zmiri-provenance-gc=1 python3 "$X_PY" test --stage 2 miri cargo-miri else - python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri + python3 "$X_PY" test --stage 2 miri cargo-miri fi # We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc. # Also cover some other targets via cross-testing, in particular all tier 1 targets. @@ -21,13 +21,15 @@ case $HOST_TARGET in x86_64-unknown-linux-gnu) # Only this branch runs in PR CI. # Fully test all main OSes, and all main architectures. - python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri --target aarch64-apple-darwin - python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri --target i686-pc-windows-msvc + python3 "$X_PY" test --stage 2 miri cargo-miri --target aarch64-apple-darwin + python3 "$X_PY" test --stage 2 miri cargo-miri --target i686-pc-windows-msvc # Only run "pass" tests for the remaining targets, which is quite a bit faster. - python3 "$X_PY" test --stage 2 src/tools/miri --target x86_64-pc-windows-gnu --test-args pass - python3 "$X_PY" test --stage 2 src/tools/miri --target i686-unknown-linux-gnu --test-args pass - python3 "$X_PY" test --stage 2 src/tools/miri --target aarch64-unknown-linux-gnu --test-args pass - python3 "$X_PY" test --stage 2 src/tools/miri --target s390x-unknown-linux-gnu --test-args pass + # We have to use `miri` instead of `src/tools/miri` here to avoid also running the cargo-miri + # tests. + python3 "$X_PY" test --stage 2 miri --target x86_64-pc-windows-gnu --test-args pass + python3 "$X_PY" test --stage 2 miri --target i686-unknown-linux-gnu --test-args pass + python3 "$X_PY" test --stage 2 miri --target aarch64-unknown-linux-gnu --test-args pass + python3 "$X_PY" test --stage 2 miri --target s390x-unknown-linux-gnu --test-args pass ;; x86_64-pc-windows-msvc) # Strangely, Linux targets do not work here. cargo always says @@ -36,7 +38,7 @@ case $HOST_TARGET in #FIXME: Re-enable this once CI issues are fixed # See # For now, these tests are moved to `x86_64-msvc-ext2` in `src/ci/github-actions/jobs.yml`. - #python3 "$X_PY" test --stage 2 src/tools/miri --target x86_64-apple-darwin --test-args pass + #python3 "$X_PY" test --stage 2 miri --target x86_64-apple-darwin --test-args pass ;; *) echo "FATAL: unexpected host $HOST_TARGET"