| author | bors <bors@rust-lang.org> 2026-02-17 02:03:25 UTC |
| committer | bors <bors@rust-lang.org> 2026-02-17 02:03:25 UTC |
| log | d1a11b670b617f1370f7b1cdf86e225a4e070f15 |
| tree | 2d9a0550ec3dd99619266c5d77b1939a1efcbf8a |
| parent | 3c9faa0d037b9eecda4a440cc482ff7f960fb8a5 |
| parent | f7699054f750cf3815147e5a3b8a82bee18c537b |
Rollup of 11 pull requests
Successful merges:
- rust-lang/rust#152700 (miri subtree update)
- rust-lang/rust#152715 (`rust-analyzer` subtree update)
- rust-lang/rust#151783 (Implement RFC 3678: Final trait methods)
- rust-lang/rust#152512 (core: Implement feature `float_exact_integer_constants`)
- rust-lang/rust#152661 (Avoid ICE in From/TryFrom diagnostic under -Znext-solver)
- rust-lang/rust#152703 (Remove `rustc_query_system`)
- rust-lang/rust#152206 (misc doc improvements)
- rust-lang/rust#152664 (Fix mis-constructed `file_span` when generating scraped examples)
- rust-lang/rust#152698 (Suppress unstable-trait notes under `-Zforce-unstable-if-unmarked`)
- rust-lang/rust#152727 (`probe_op` silence ambiguity errors if tainted)
- rust-lang/rust#152728 (Port #![default_lib_allocator] to the new attribute parser)268 files changed, 3922 insertions(+), 1934 deletions(-)
Cargo.lock-19| ... | ... | @@ -3640,7 +3640,6 @@ dependencies = [ |
| 3640 | 3640 | "rustc_macros", |
| 3641 | 3641 | "rustc_metadata", |
| 3642 | 3642 | "rustc_middle", |
| 3643 | "rustc_query_system", | |
| 3644 | 3643 | "rustc_sanitizers", |
| 3645 | 3644 | "rustc_session", |
| 3646 | 3645 | "rustc_span", |
| ... | ... | @@ -4243,7 +4242,6 @@ dependencies = [ |
| 4243 | 4242 | "rustc_index", |
| 4244 | 4243 | "rustc_lint_defs", |
| 4245 | 4244 | "rustc_macros", |
| 4246 | "rustc_query_system", | |
| 4247 | 4245 | "rustc_serialize", |
| 4248 | 4246 | "rustc_session", |
| 4249 | 4247 | "rustc_span", |
| ... | ... | @@ -4507,23 +4505,6 @@ dependencies = [ |
| 4507 | 4505 | "tracing", |
| 4508 | 4506 | ] |
| 4509 | 4507 | |
| 4510 | [[package]] | |
| 4511 | name = "rustc_query_system" | |
| 4512 | version = "0.0.0" | |
| 4513 | dependencies = [ | |
| 4514 | "rustc_abi", | |
| 4515 | "rustc_ast", | |
| 4516 | "rustc_data_structures", | |
| 4517 | "rustc_errors", | |
| 4518 | "rustc_feature", | |
| 4519 | "rustc_hir", | |
| 4520 | "rustc_macros", | |
| 4521 | "rustc_serialize", | |
| 4522 | "rustc_session", | |
| 4523 | "rustc_span", | |
| 4524 | "smallvec", | |
| 4525 | ] | |
| 4526 | ||
| 4527 | 4508 | [[package]] |
| 4528 | 4509 | name = "rustc_resolve" |
| 4529 | 4510 | version = "0.0.0" |
compiler/rustc_ast/src/ast.rs+10-2| ... | ... | @@ -3131,8 +3131,16 @@ pub enum Const { |
| 3131 | 3131 | /// For details see the [RFC #2532](https://github.com/rust-lang/rfcs/pull/2532). |
| 3132 | 3132 | #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Walkable)] |
| 3133 | 3133 | pub enum Defaultness { |
| 3134 | /// Item is unmarked. Implicitly determined based off of position. | |
| 3135 | /// For impls, this is `final`; for traits, this is `default`. | |
| 3136 | /// | |
| 3137 | /// If you're expanding an item in a built-in macro or parsing an item | |
| 3138 | /// by hand, you probably want to use this. | |
| 3139 | Implicit, | |
| 3140 | /// `default` | |
| 3134 | 3141 | Default(Span), |
| 3135 | Final, | |
| 3142 | /// `final`; per RFC 3678, only trait items may be *explicitly* marked final. | |
| 3143 | Final(Span), | |
| 3136 | 3144 | } |
| 3137 | 3145 | |
| 3138 | 3146 | #[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)] |
| ... | ... | @@ -4140,7 +4148,7 @@ impl AssocItemKind { |
| 4140 | 4148 | | Self::Fn(box Fn { defaultness, .. }) |
| 4141 | 4149 | | Self::Type(box TyAlias { defaultness, .. }) => defaultness, |
| 4142 | 4150 | Self::MacCall(..) | Self::Delegation(..) | Self::DelegationMac(..) => { |
| 4143 | Defaultness::Final | |
| 4151 | Defaultness::Implicit | |
| 4144 | 4152 | } |
| 4145 | 4153 | } |
| 4146 | 4154 | } |
compiler/rustc_ast_lowering/src/item.rs+13-8| ... | ... | @@ -939,7 +939,7 @@ impl<'hir> LoweringContext<'_, 'hir> { |
| 939 | 939 | ); |
| 940 | 940 | let trait_item_def_id = hir_id.expect_owner(); |
| 941 | 941 | |
| 942 | let (ident, generics, kind, has_default) = match &i.kind { | |
| 942 | let (ident, generics, kind, has_value) = match &i.kind { | |
| 943 | 943 | AssocItemKind::Const(box ConstItem { |
| 944 | 944 | ident, |
| 945 | 945 | generics, |
| ... | ... | @@ -1088,13 +1088,17 @@ impl<'hir> LoweringContext<'_, 'hir> { |
| 1088 | 1088 | } |
| 1089 | 1089 | }; |
| 1090 | 1090 | |
| 1091 | let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value, || { | |
| 1092 | hir::Defaultness::Default { has_value } | |
| 1093 | }); | |
| 1094 | ||
| 1091 | 1095 | let item = hir::TraitItem { |
| 1092 | 1096 | owner_id: trait_item_def_id, |
| 1093 | 1097 | ident: self.lower_ident(ident), |
| 1094 | 1098 | generics, |
| 1095 | 1099 | kind, |
| 1096 | 1100 | span: self.lower_span(i.span), |
| 1097 | defaultness: hir::Defaultness::Default { has_value: has_default }, | |
| 1101 | defaultness, | |
| 1098 | 1102 | has_delayed_lints: !self.delayed_lints.is_empty(), |
| 1099 | 1103 | }; |
| 1100 | 1104 | self.arena.alloc(item) |
| ... | ... | @@ -1122,7 +1126,8 @@ impl<'hir> LoweringContext<'_, 'hir> { |
| 1122 | 1126 | // `defaultness.has_value()` is never called for an `impl`, always `true` in order |
| 1123 | 1127 | // to not cause an assertion failure inside the `lower_defaultness` function. |
| 1124 | 1128 | let has_val = true; |
| 1125 | let (defaultness, defaultness_span) = self.lower_defaultness(defaultness, has_val); | |
| 1129 | let (defaultness, defaultness_span) = | |
| 1130 | self.lower_defaultness(defaultness, has_val, || hir::Defaultness::Final); | |
| 1126 | 1131 | let modifiers = TraitBoundModifiers { |
| 1127 | 1132 | constness: BoundConstness::Never, |
| 1128 | 1133 | asyncness: BoundAsyncness::Normal, |
| ... | ... | @@ -1151,7 +1156,8 @@ impl<'hir> LoweringContext<'_, 'hir> { |
| 1151 | 1156 | ) -> &'hir hir::ImplItem<'hir> { |
| 1152 | 1157 | // Since `default impl` is not yet implemented, this is always true in impls. |
| 1153 | 1158 | let has_value = true; |
| 1154 | let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); | |
| 1159 | let (defaultness, _) = | |
| 1160 | self.lower_defaultness(i.kind.defaultness(), has_value, || hir::Defaultness::Final); | |
| 1155 | 1161 | let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); |
| 1156 | 1162 | let attrs = self.lower_attrs( |
| 1157 | 1163 | hir_id, |
| ... | ... | @@ -1304,15 +1310,14 @@ impl<'hir> LoweringContext<'_, 'hir> { |
| 1304 | 1310 | &self, |
| 1305 | 1311 | d: Defaultness, |
| 1306 | 1312 | has_value: bool, |
| 1313 | implicit: impl FnOnce() -> hir::Defaultness, | |
| 1307 | 1314 | ) -> (hir::Defaultness, Option<Span>) { |
| 1308 | 1315 | match d { |
| 1316 | Defaultness::Implicit => (implicit(), None), | |
| 1309 | 1317 | Defaultness::Default(sp) => { |
| 1310 | 1318 | (hir::Defaultness::Default { has_value }, Some(self.lower_span(sp))) |
| 1311 | 1319 | } |
| 1312 | Defaultness::Final => { | |
| 1313 | assert!(has_value); | |
| 1314 | (hir::Defaultness::Final, None) | |
| 1315 | } | |
| 1320 | Defaultness::Final(sp) => (hir::Defaultness::Final, Some(self.lower_span(sp))), | |
| 1316 | 1321 | } |
| 1317 | 1322 | } |
| 1318 | 1323 |
compiler/rustc_ast_passes/src/ast_validation.rs+66-12| ... | ... | @@ -65,6 +65,28 @@ impl TraitOrImpl { |
| 65 | 65 | } |
| 66 | 66 | } |
| 67 | 67 | |
| 68 | enum AllowDefault { | |
| 69 | Yes, | |
| 70 | No, | |
| 71 | } | |
| 72 | ||
| 73 | impl AllowDefault { | |
| 74 | fn when(b: bool) -> Self { | |
| 75 | if b { Self::Yes } else { Self::No } | |
| 76 | } | |
| 77 | } | |
| 78 | ||
| 79 | enum AllowFinal { | |
| 80 | Yes, | |
| 81 | No, | |
| 82 | } | |
| 83 | ||
| 84 | impl AllowFinal { | |
| 85 | fn when(b: bool) -> Self { | |
| 86 | if b { Self::Yes } else { Self::No } | |
| 87 | } | |
| 88 | } | |
| 89 | ||
| 68 | 90 | struct AstValidator<'a> { |
| 69 | 91 | sess: &'a Session, |
| 70 | 92 | features: &'a Features, |
| ... | ... | @@ -563,10 +585,32 @@ impl<'a> AstValidator<'a> { |
| 563 | 585 | } |
| 564 | 586 | } |
| 565 | 587 | |
| 566 | fn check_defaultness(&self, span: Span, defaultness: Defaultness) { | |
| 567 | if let Defaultness::Default(def_span) = defaultness { | |
| 568 | let span = self.sess.source_map().guess_head_span(span); | |
| 569 | self.dcx().emit_err(errors::ForbiddenDefault { span, def_span }); | |
| 588 | fn check_defaultness( | |
| 589 | &self, | |
| 590 | span: Span, | |
| 591 | defaultness: Defaultness, | |
| 592 | allow_default: AllowDefault, | |
| 593 | allow_final: AllowFinal, | |
| 594 | ) { | |
| 595 | match defaultness { | |
| 596 | Defaultness::Default(def_span) if matches!(allow_default, AllowDefault::No) => { | |
| 597 | let span = self.sess.source_map().guess_head_span(span); | |
| 598 | self.dcx().emit_err(errors::ForbiddenDefault { span, def_span }); | |
| 599 | } | |
| 600 | Defaultness::Final(def_span) if matches!(allow_final, AllowFinal::No) => { | |
| 601 | let span = self.sess.source_map().guess_head_span(span); | |
| 602 | self.dcx().emit_err(errors::ForbiddenFinal { span, def_span }); | |
| 603 | } | |
| 604 | _ => (), | |
| 605 | } | |
| 606 | } | |
| 607 | ||
| 608 | fn check_final_has_body(&self, item: &Item<AssocItemKind>, defaultness: Defaultness) { | |
| 609 | if let AssocItemKind::Fn(box Fn { body: None, .. }) = &item.kind | |
| 610 | && let Defaultness::Final(def_span) = defaultness | |
| 611 | { | |
| 612 | let span = self.sess.source_map().guess_head_span(item.span); | |
| 613 | self.dcx().emit_err(errors::ForbiddenFinalWithoutBody { span, def_span }); | |
| 570 | 614 | } |
| 571 | 615 | } |
| 572 | 616 | |
| ... | ... | @@ -1190,7 +1234,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { |
| 1190 | 1234 | }, |
| 1191 | 1235 | ) => { |
| 1192 | 1236 | self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); |
| 1193 | self.check_defaultness(item.span, *defaultness); | |
| 1237 | self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); | |
| 1194 | 1238 | |
| 1195 | 1239 | for EiiImpl { eii_macro_path, .. } in eii_impls { |
| 1196 | 1240 | self.visit_path(eii_macro_path); |
| ... | ... | @@ -1360,7 +1404,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { |
| 1360 | 1404 | }); |
| 1361 | 1405 | } |
| 1362 | 1406 | ItemKind::Const(box ConstItem { defaultness, ident, rhs_kind, .. }) => { |
| 1363 | self.check_defaultness(item.span, *defaultness); | |
| 1407 | self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); | |
| 1364 | 1408 | if !rhs_kind.has_expr() { |
| 1365 | 1409 | self.dcx().emit_err(errors::ConstWithoutBody { |
| 1366 | 1410 | span: item.span, |
| ... | ... | @@ -1398,7 +1442,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { |
| 1398 | 1442 | ItemKind::TyAlias( |
| 1399 | 1443 | ty_alias @ box TyAlias { defaultness, bounds, after_where_clause, ty, .. }, |
| 1400 | 1444 | ) => { |
| 1401 | self.check_defaultness(item.span, *defaultness); | |
| 1445 | self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); | |
| 1402 | 1446 | if ty.is_none() { |
| 1403 | 1447 | self.dcx().emit_err(errors::TyAliasWithoutBody { |
| 1404 | 1448 | span: item.span, |
| ... | ... | @@ -1428,7 +1472,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { |
| 1428 | 1472 | fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { |
| 1429 | 1473 | match &fi.kind { |
| 1430 | 1474 | ForeignItemKind::Fn(box Fn { defaultness, ident, sig, body, .. }) => { |
| 1431 | self.check_defaultness(fi.span, *defaultness); | |
| 1475 | self.check_defaultness(fi.span, *defaultness, AllowDefault::No, AllowFinal::No); | |
| 1432 | 1476 | self.check_foreign_fn_bodyless(*ident, body.as_deref()); |
| 1433 | 1477 | self.check_foreign_fn_headerless(sig.header); |
| 1434 | 1478 | self.check_foreign_item_ascii_only(*ident); |
| ... | ... | @@ -1448,7 +1492,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { |
| 1448 | 1492 | ty, |
| 1449 | 1493 | .. |
| 1450 | 1494 | }) => { |
| 1451 | self.check_defaultness(fi.span, *defaultness); | |
| 1495 | self.check_defaultness(fi.span, *defaultness, AllowDefault::No, AllowFinal::No); | |
| 1452 | 1496 | self.check_foreign_kind_bodyless(*ident, "type", ty.as_ref().map(|b| b.span)); |
| 1453 | 1497 | self.check_type_no_bounds(bounds, "`extern` blocks"); |
| 1454 | 1498 | self.check_foreign_ty_genericless(generics, after_where_clause); |
| ... | ... | @@ -1707,9 +1751,19 @@ impl<'a> Visitor<'a> for AstValidator<'a> { |
| 1707 | 1751 | self.check_nomangle_item_asciionly(ident, item.span); |
| 1708 | 1752 | } |
| 1709 | 1753 | |
| 1710 | if ctxt == AssocCtxt::Trait || self.outer_trait_or_trait_impl.is_none() { | |
| 1711 | self.check_defaultness(item.span, item.kind.defaultness()); | |
| 1712 | } | |
| 1754 | let defaultness = item.kind.defaultness(); | |
| 1755 | self.check_defaultness( | |
| 1756 | item.span, | |
| 1757 | defaultness, | |
| 1758 | // `default` is allowed on all associated items in impls. | |
| 1759 | AllowDefault::when(matches!(ctxt, AssocCtxt::Impl { .. })), | |
| 1760 | // `final` is allowed on all associated *functions* in traits. | |
| 1761 | AllowFinal::when( | |
| 1762 | ctxt == AssocCtxt::Trait && matches!(item.kind, AssocItemKind::Fn(..)), | |
| 1763 | ), | |
| 1764 | ); | |
| 1765 | ||
| 1766 | self.check_final_has_body(item, defaultness); | |
| 1713 | 1767 | |
| 1714 | 1768 | if let AssocCtxt::Impl { .. } = ctxt { |
| 1715 | 1769 | match &item.kind { |
compiler/rustc_ast_passes/src/errors.rs+18| ... | ... | @@ -159,6 +159,24 @@ pub(crate) struct ForbiddenDefault { |
| 159 | 159 | pub def_span: Span, |
| 160 | 160 | } |
| 161 | 161 | |
| 162 | #[derive(Diagnostic)] | |
| 163 | #[diag("`final` is only allowed on associated functions in traits")] | |
| 164 | pub(crate) struct ForbiddenFinal { | |
| 165 | #[primary_span] | |
| 166 | pub span: Span, | |
| 167 | #[label("`final` because of this")] | |
| 168 | pub def_span: Span, | |
| 169 | } | |
| 170 | ||
| 171 | #[derive(Diagnostic)] | |
| 172 | #[diag("`final` is only allowed on associated functions if they have a body")] | |
| 173 | pub(crate) struct ForbiddenFinalWithoutBody { | |
| 174 | #[primary_span] | |
| 175 | pub span: Span, | |
| 176 | #[label("`final` because of this")] | |
| 177 | pub def_span: Span, | |
| 178 | } | |
| 179 | ||
| 162 | 180 | #[derive(Diagnostic)] |
| 163 | 181 | #[diag("associated constant in `impl` without body")] |
| 164 | 182 | pub(crate) struct AssocConstWithoutBody { |
compiler/rustc_ast_passes/src/feature_gate.rs+1| ... | ... | @@ -580,6 +580,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { |
| 580 | 580 | gate_all!(frontmatter, "frontmatters are experimental"); |
| 581 | 581 | gate_all!(coroutines, "coroutine syntax is experimental"); |
| 582 | 582 | gate_all!(const_block_items, "const block items are experimental"); |
| 583 | gate_all!(final_associated_functions, "`final` on trait functions is experimental"); | |
| 583 | 584 | |
| 584 | 585 | if !visitor.features.never_patterns() { |
| 585 | 586 | if let Some(spans) = spans.get(&sym::never_patterns) { |
compiler/rustc_ast_pretty/src/pprust/state/item.rs+2-2| ... | ... | @@ -51,7 +51,7 @@ impl<'a> State<'a> { |
| 51 | 51 | expr.as_deref(), |
| 52 | 52 | vis, |
| 53 | 53 | *safety, |
| 54 | ast::Defaultness::Final, | |
| 54 | ast::Defaultness::Implicit, | |
| 55 | 55 | define_opaque.as_deref(), |
| 56 | 56 | ), |
| 57 | 57 | ast::ForeignItemKind::TyAlias(box ast::TyAlias { |
| ... | ... | @@ -201,7 +201,7 @@ impl<'a> State<'a> { |
| 201 | 201 | body.as_deref(), |
| 202 | 202 | &item.vis, |
| 203 | 203 | ast::Safety::Default, |
| 204 | ast::Defaultness::Final, | |
| 204 | ast::Defaultness::Implicit, | |
| 205 | 205 | define_opaque.as_deref(), |
| 206 | 206 | ); |
| 207 | 207 | } |
compiler/rustc_attr_parsing/src/attributes/crate_level.rs+9| ... | ... | @@ -292,3 +292,12 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcNoImplicitBoundsParser { |
| 292 | 292 | const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); |
| 293 | 293 | const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitBounds; |
| 294 | 294 | } |
| 295 | ||
| 296 | pub(crate) struct DefaultLibAllocatorParser; | |
| 297 | ||
| 298 | impl<S: Stage> NoArgsAttributeParser<S> for DefaultLibAllocatorParser { | |
| 299 | const PATH: &[Symbol] = &[sym::default_lib_allocator]; | |
| 300 | const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; | |
| 301 | const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); | |
| 302 | const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::DefaultLibAllocator; | |
| 303 | } |
compiler/rustc_attr_parsing/src/context.rs+1| ... | ... | @@ -235,6 +235,7 @@ attribute_parsers!( |
| 235 | 235 | Single<WithoutArgs<ConstContinueParser>>, |
| 236 | 236 | Single<WithoutArgs<ConstStabilityIndirectParser>>, |
| 237 | 237 | Single<WithoutArgs<CoroutineParser>>, |
| 238 | Single<WithoutArgs<DefaultLibAllocatorParser>>, | |
| 238 | 239 | Single<WithoutArgs<DenyExplicitImplParser>>, |
| 239 | 240 | Single<WithoutArgs<DynIncompatibleTraitParser>>, |
| 240 | 241 | Single<WithoutArgs<EiiForeignItemParser>>, |
compiler/rustc_builtin_macros/src/alloc_error_handler.rs+1-1| ... | ... | @@ -83,7 +83,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span |
| 83 | 83 | |
| 84 | 84 | let body = Some(cx.block_expr(call)); |
| 85 | 85 | let kind = ItemKind::Fn(Box::new(Fn { |
| 86 | defaultness: ast::Defaultness::Final, | |
| 86 | defaultness: ast::Defaultness::Implicit, | |
| 87 | 87 | sig, |
| 88 | 88 | ident: Ident::from_str_and_span(&global_fn_name(ALLOC_ERROR_HANDLER), span), |
| 89 | 89 | generics: Generics::default(), |
compiler/rustc_builtin_macros/src/autodiff.rs+1-1| ... | ... | @@ -334,7 +334,7 @@ mod llvm_enzyme { |
| 334 | 334 | |
| 335 | 335 | // The first element of it is the name of the function to be generated |
| 336 | 336 | let d_fn = Box::new(ast::Fn { |
| 337 | defaultness: ast::Defaultness::Final, | |
| 337 | defaultness: ast::Defaultness::Implicit, | |
| 338 | 338 | sig: d_sig, |
| 339 | 339 | ident: first_ident(&meta_item_vec[0]), |
| 340 | 340 | generics, |
compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs+2-2| ... | ... | @@ -136,7 +136,7 @@ pub(crate) fn expand_deriving_coerce_pointee( |
| 136 | 136 | of_trait: Some(Box::new(ast::TraitImplHeader { |
| 137 | 137 | safety: ast::Safety::Default, |
| 138 | 138 | polarity: ast::ImplPolarity::Positive, |
| 139 | defaultness: ast::Defaultness::Final, | |
| 139 | defaultness: ast::Defaultness::Implicit, | |
| 140 | 140 | trait_ref, |
| 141 | 141 | })), |
| 142 | 142 | constness: ast::Const::No, |
| ... | ... | @@ -159,7 +159,7 @@ pub(crate) fn expand_deriving_coerce_pointee( |
| 159 | 159 | of_trait: Some(Box::new(ast::TraitImplHeader { |
| 160 | 160 | safety: ast::Safety::Default, |
| 161 | 161 | polarity: ast::ImplPolarity::Positive, |
| 162 | defaultness: ast::Defaultness::Final, | |
| 162 | defaultness: ast::Defaultness::Implicit, | |
| 163 | 163 | trait_ref, |
| 164 | 164 | })), |
| 165 | 165 | constness: ast::Const::No, |
compiler/rustc_builtin_macros/src/deriving/generic/mod.rs+3-3| ... | ... | @@ -614,7 +614,7 @@ impl<'a> TraitDef<'a> { |
| 614 | 614 | }, |
| 615 | 615 | attrs: ast::AttrVec::new(), |
| 616 | 616 | kind: ast::AssocItemKind::Type(Box::new(ast::TyAlias { |
| 617 | defaultness: ast::Defaultness::Final, | |
| 617 | defaultness: ast::Defaultness::Implicit, | |
| 618 | 618 | ident, |
| 619 | 619 | generics: Generics::default(), |
| 620 | 620 | after_where_clause: ast::WhereClause::default(), |
| ... | ... | @@ -851,7 +851,7 @@ impl<'a> TraitDef<'a> { |
| 851 | 851 | of_trait: Some(Box::new(ast::TraitImplHeader { |
| 852 | 852 | safety: self.safety, |
| 853 | 853 | polarity: ast::ImplPolarity::Positive, |
| 854 | defaultness: ast::Defaultness::Final, | |
| 854 | defaultness: ast::Defaultness::Implicit, | |
| 855 | 855 | trait_ref, |
| 856 | 856 | })), |
| 857 | 857 | constness: if self.is_const { ast::Const::Yes(DUMMY_SP) } else { ast::Const::No }, |
| ... | ... | @@ -1073,7 +1073,7 @@ impl<'a> MethodDef<'a> { |
| 1073 | 1073 | let trait_lo_sp = span.shrink_to_lo(); |
| 1074 | 1074 | |
| 1075 | 1075 | let sig = ast::FnSig { header: ast::FnHeader::default(), decl: fn_decl, span }; |
| 1076 | let defaultness = ast::Defaultness::Final; | |
| 1076 | let defaultness = ast::Defaultness::Implicit; | |
| 1077 | 1077 | |
| 1078 | 1078 | // Create the method. |
| 1079 | 1079 | Box::new(ast::AssocItem { |
compiler/rustc_builtin_macros/src/global_allocator.rs+1-1| ... | ... | @@ -77,7 +77,7 @@ impl AllocFnFactory<'_, '_> { |
| 77 | 77 | let sig = FnSig { decl, header, span: self.span }; |
| 78 | 78 | let body = Some(self.cx.block_expr(result)); |
| 79 | 79 | let kind = ItemKind::Fn(Box::new(Fn { |
| 80 | defaultness: ast::Defaultness::Final, | |
| 80 | defaultness: ast::Defaultness::Implicit, | |
| 81 | 81 | sig, |
| 82 | 82 | ident: Ident::from_str_and_span(&global_fn_name(method.name), self.span), |
| 83 | 83 | generics: Generics::default(), |
compiler/rustc_builtin_macros/src/test.rs+1-1| ... | ... | @@ -283,7 +283,7 @@ pub(crate) fn expand_test_or_bench( |
| 283 | 283 | // const $ident: test::TestDescAndFn = |
| 284 | 284 | ast::ItemKind::Const( |
| 285 | 285 | ast::ConstItem { |
| 286 | defaultness: ast::Defaultness::Final, | |
| 286 | defaultness: ast::Defaultness::Implicit, | |
| 287 | 287 | ident: Ident::new(fn_.ident.name, sp), |
| 288 | 288 | generics: ast::Generics::default(), |
| 289 | 289 | ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))), |
compiler/rustc_builtin_macros/src/test_harness.rs+1-1| ... | ... | @@ -330,7 +330,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box<ast::Item> { |
| 330 | 330 | |
| 331 | 331 | let decl = ecx.fn_decl(ThinVec::new(), ast::FnRetTy::Ty(main_ret_ty)); |
| 332 | 332 | let sig = ast::FnSig { decl, header: ast::FnHeader::default(), span: sp }; |
| 333 | let defaultness = ast::Defaultness::Final; | |
| 333 | let defaultness = ast::Defaultness::Implicit; | |
| 334 | 334 | |
| 335 | 335 | // Honor the reexport_test_harness_main attribute |
| 336 | 336 | let main_ident = match cx.reexport_test_harness_main { |
compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs+1-1| ... | ... | @@ -208,7 +208,7 @@ pub(crate) fn codegen_cast( |
| 208 | 208 | let ret_ty = if to_ty.bits() < 32 { types::I32 } else { to_ty }; |
| 209 | 209 | let name = format!( |
| 210 | 210 | "__fix{sign}tf{size}i", |
| 211 | sign = if from_signed { "" } else { "un" }, | |
| 211 | sign = if to_signed { "" } else { "uns" }, | |
| 212 | 212 | size = match ret_ty { |
| 213 | 213 | types::I32 => 's', |
| 214 | 214 | types::I64 => 'd', |
compiler/rustc_codegen_llvm/Cargo.toml-1| ... | ... | @@ -31,7 +31,6 @@ rustc_llvm = { path = "../rustc_llvm" } |
| 31 | 31 | rustc_macros = { path = "../rustc_macros" } |
| 32 | 32 | rustc_metadata = { path = "../rustc_metadata" } |
| 33 | 33 | rustc_middle = { path = "../rustc_middle" } |
| 34 | rustc_query_system = { path = "../rustc_query_system" } | |
| 35 | 34 | rustc_sanitizers = { path = "../rustc_sanitizers" } |
| 36 | 35 | rustc_session = { path = "../rustc_session" } |
| 37 | 36 | rustc_span = { path = "../rustc_span" } |
compiler/rustc_error_codes/src/error_codes/E0570.md+2-2| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | The requested ABI is unsupported by the current target. |
| 2 | 2 | |
| 3 | The rust compiler maintains for each target a list of unsupported ABIs on | |
| 4 | that target. If an ABI is present in such a list this usually means that the | |
| 3 | The Rust compiler maintains a list of unsupported ABIs for each target. | |
| 4 | If an ABI is present in such a list, this usually means that the | |
| 5 | 5 | target / ABI combination is currently unsupported by llvm. |
| 6 | 6 | |
| 7 | 7 | If necessary, you can circumvent this check using custom target specifications. |
compiler/rustc_expand/src/build.rs+1-1| ... | ... | @@ -729,7 +729,7 @@ impl<'a> ExtCtxt<'a> { |
| 729 | 729 | ty: Box<ast::Ty>, |
| 730 | 730 | rhs_kind: ast::ConstItemRhsKind, |
| 731 | 731 | ) -> Box<ast::Item> { |
| 732 | let defaultness = ast::Defaultness::Final; | |
| 732 | let defaultness = ast::Defaultness::Implicit; | |
| 733 | 733 | self.item( |
| 734 | 734 | span, |
| 735 | 735 | AttrVec::new(), |
compiler/rustc_expand/src/config.rs+2-3| ... | ... | @@ -86,8 +86,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - |
| 86 | 86 | if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| name == f.feature.name) { |
| 87 | 87 | let pull_note = if let Some(pull) = f.pull { |
| 88 | 88 | format!( |
| 89 | "; see <https://github.com/rust-lang/rust/pull/{}> for more information", | |
| 90 | pull | |
| 89 | "; see <https://github.com/rust-lang/rust/pull/{pull}> for more information", | |
| 91 | 90 | ) |
| 92 | 91 | } else { |
| 93 | 92 | "".to_owned() |
| ... | ... | @@ -123,7 +122,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - |
| 123 | 122 | |
| 124 | 123 | // If the enabled feature is unstable, record it. |
| 125 | 124 | if UNSTABLE_LANG_FEATURES.iter().find(|f| name == f.name).is_some() { |
| 126 | // When the ICE comes a standard library crate, there's a chance that the person | |
| 125 | // When the ICE comes from a standard library crate, there's a chance that the person | |
| 127 | 126 | // hitting the ICE may be using -Zbuild-std or similar with an untested target. |
| 128 | 127 | // The bug is probably in the standard library and not the compiler in that case, |
| 129 | 128 | // but that doesn't really matter - we want a bug report. |
compiler/rustc_feature/src/unstable.rs+5-4| ... | ... | @@ -64,8 +64,6 @@ pub struct EnabledLibFeature { |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | 66 | impl Features { |
| 67 | /// `since` should be set for stable features that are nevertheless enabled with a `#[feature]` | |
| 68 | /// attribute, indicating since when they are stable. | |
| 69 | 67 | pub fn set_enabled_lang_feature(&mut self, lang_feat: EnabledLangFeature) { |
| 70 | 68 | self.enabled_lang_features.push(lang_feat); |
| 71 | 69 | self.enabled_features.insert(lang_feat.gate_name); |
| ... | ... | @@ -494,6 +492,8 @@ declare_features! ( |
| 494 | 492 | (unstable, ffi_const, "1.45.0", Some(58328)), |
| 495 | 493 | /// Allows the use of `#[ffi_pure]` on foreign functions. |
| 496 | 494 | (unstable, ffi_pure, "1.45.0", Some(58329)), |
| 495 | /// Allows marking trait functions as `final` to prevent overriding impls | |
| 496 | (unstable, final_associated_functions, "CURRENT_RUSTC_VERSION", Some(1)), | |
| 497 | 497 | /// Controlling the behavior of fmt::Debug |
| 498 | 498 | (unstable, fmt_debug, "1.82.0", Some(129709)), |
| 499 | 499 | /// Allows using `#[align(...)]` on function items |
| ... | ... | @@ -779,8 +779,9 @@ impl Features { |
| 779 | 779 | } |
| 780 | 780 | } |
| 781 | 781 | |
| 782 | /// Some features are not allowed to be used together at the same time, if | |
| 783 | /// the two are present, produce an error. | |
| 782 | /// Some features are not allowed to be used together at the same time. | |
| 783 | /// | |
| 784 | /// If the two are present, produce an error. | |
| 784 | 785 | pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[ |
| 785 | 786 | // Experimental match ergonomics rulesets are incompatible with each other, to simplify the |
| 786 | 787 | // boolean logic required to tell which typing rules to use. |
compiler/rustc_hir/src/attrs/data_structures.rs+3| ... | ... | @@ -897,6 +897,9 @@ pub enum AttributeKind { |
| 897 | 897 | /// Represents `#[debugger_visualizer]`. |
| 898 | 898 | DebuggerVisualizer(ThinVec<DebugVisualizer>), |
| 899 | 899 | |
| 900 | /// Represents `#![default_lib_allocator]` | |
| 901 | DefaultLibAllocator, | |
| 902 | ||
| 900 | 903 | /// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute). |
| 901 | 904 | Deprecation { deprecation: Deprecation, span: Span }, |
| 902 | 905 |
compiler/rustc_hir/src/attrs/encode_cross_crate.rs+1| ... | ... | @@ -35,6 +35,7 @@ impl AttributeKind { |
| 35 | 35 | CrateType(_) => No, |
| 36 | 36 | CustomMir(_, _, _) => Yes, |
| 37 | 37 | DebuggerVisualizer(..) => No, |
| 38 | DefaultLibAllocator => No, | |
| 38 | 39 | Deprecation { .. } => Yes, |
| 39 | 40 | DoNotRecommend { .. } => Yes, |
| 40 | 41 | Doc(_) => Yes, |
compiler/rustc_hir_analysis/src/check/check.rs+25-1| ... | ... | @@ -1175,13 +1175,35 @@ pub(super) fn check_specialization_validity<'tcx>( |
| 1175 | 1175 | |
| 1176 | 1176 | if let Err(parent_impl) = result { |
| 1177 | 1177 | if !tcx.is_impl_trait_in_trait(impl_item) { |
| 1178 | report_forbidden_specialization(tcx, impl_item, parent_impl); | |
| 1178 | let span = tcx.def_span(impl_item); | |
| 1179 | let ident = tcx.item_ident(impl_item); | |
| 1180 | ||
| 1181 | let err = match tcx.span_of_impl(parent_impl) { | |
| 1182 | Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp }, | |
| 1183 | Err(cname) => errors::ImplNotMarkedDefault::Err { span, ident, cname }, | |
| 1184 | }; | |
| 1185 | ||
| 1186 | tcx.dcx().emit_err(err); | |
| 1179 | 1187 | } else { |
| 1180 | 1188 | tcx.dcx().delayed_bug(format!("parent item: {parent_impl:?} not marked as default")); |
| 1181 | 1189 | } |
| 1182 | 1190 | } |
| 1183 | 1191 | } |
| 1184 | 1192 | |
| 1193 | fn check_overriding_final_trait_item<'tcx>( | |
| 1194 | tcx: TyCtxt<'tcx>, | |
| 1195 | trait_item: ty::AssocItem, | |
| 1196 | impl_item: ty::AssocItem, | |
| 1197 | ) { | |
| 1198 | if trait_item.defaultness(tcx).is_final() { | |
| 1199 | tcx.dcx().emit_err(errors::OverridingFinalTraitFunction { | |
| 1200 | impl_span: tcx.def_span(impl_item.def_id), | |
| 1201 | trait_span: tcx.def_span(trait_item.def_id), | |
| 1202 | ident: tcx.item_ident(impl_item.def_id), | |
| 1203 | }); | |
| 1204 | } | |
| 1205 | } | |
| 1206 | ||
| 1185 | 1207 | fn check_impl_items_against_trait<'tcx>( |
| 1186 | 1208 | tcx: TyCtxt<'tcx>, |
| 1187 | 1209 | impl_id: LocalDefId, |
| ... | ... | @@ -1259,6 +1281,8 @@ fn check_impl_items_against_trait<'tcx>( |
| 1259 | 1281 | impl_id.to_def_id(), |
| 1260 | 1282 | impl_item, |
| 1261 | 1283 | ); |
| 1284 | ||
| 1285 | check_overriding_final_trait_item(tcx, ty_trait_item, ty_impl_item); | |
| 1262 | 1286 | } |
| 1263 | 1287 | |
| 1264 | 1288 | if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { |
compiler/rustc_hir_analysis/src/check/mod.rs-12| ... | ... | @@ -197,18 +197,6 @@ pub(super) fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDef |
| 197 | 197 | } |
| 198 | 198 | } |
| 199 | 199 | |
| 200 | fn report_forbidden_specialization(tcx: TyCtxt<'_>, impl_item: DefId, parent_impl: DefId) { | |
| 201 | let span = tcx.def_span(impl_item); | |
| 202 | let ident = tcx.item_ident(impl_item); | |
| 203 | ||
| 204 | let err = match tcx.span_of_impl(parent_impl) { | |
| 205 | Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp }, | |
| 206 | Err(cname) => errors::ImplNotMarkedDefault::Err { span, ident, cname }, | |
| 207 | }; | |
| 208 | ||
| 209 | tcx.dcx().emit_err(err); | |
| 210 | } | |
| 211 | ||
| 212 | 200 | fn missing_items_err( |
| 213 | 201 | tcx: TyCtxt<'_>, |
| 214 | 202 | impl_def_id: LocalDefId, |
compiler/rustc_hir_analysis/src/errors.rs+10| ... | ... | @@ -892,6 +892,16 @@ pub(crate) enum ImplNotMarkedDefault { |
| 892 | 892 | #[diag("this item cannot be used as its where bounds are not satisfied for the `Self` type")] |
| 893 | 893 | pub(crate) struct UselessImplItem; |
| 894 | 894 | |
| 895 | #[derive(Diagnostic)] | |
| 896 | #[diag("cannot override `{$ident}` because it already has a `final` definition in the trait")] | |
| 897 | pub(crate) struct OverridingFinalTraitFunction { | |
| 898 | #[primary_span] | |
| 899 | pub impl_span: Span, | |
| 900 | #[note("`{$ident}` is marked final here")] | |
| 901 | pub trait_span: Span, | |
| 902 | pub ident: Ident, | |
| 903 | } | |
| 904 | ||
| 895 | 905 | #[derive(Diagnostic)] |
| 896 | 906 | #[diag("not all trait items implemented, missing: `{$missing_items_msg}`", code = E0046)] |
| 897 | 907 | pub(crate) struct MissingTraitItem { |
compiler/rustc_hir_typeck/src/method/probe.rs+1| ... | ... | @@ -490,6 +490,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
| 490 | 490 | .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); |
| 491 | 491 | let ty = self.resolve_vars_if_possible(ty.value); |
| 492 | 492 | let guar = match *ty.kind() { |
| 493 | _ if let Some(guar) = self.tainted_by_errors() => guar, | |
| 493 | 494 | ty::Infer(ty::TyVar(_)) => { |
| 494 | 495 | // We want to get the variable name that the method |
| 495 | 496 | // is being called on. If it is a method call. |
compiler/rustc_interface/src/callbacks.rs+3-43| ... | ... | @@ -11,9 +11,8 @@ |
| 11 | 11 | |
| 12 | 12 | use std::fmt; |
| 13 | 13 | |
| 14 | use rustc_errors::{DiagInner, TRACK_DIAGNOSTIC}; | |
| 15 | use rustc_middle::dep_graph::dep_node::default_dep_kind_debug; | |
| 16 | use rustc_middle::dep_graph::{DepKind, DepNode, TaskDepsRef}; | |
| 14 | use rustc_errors::DiagInner; | |
| 15 | use rustc_middle::dep_graph::TaskDepsRef; | |
| 17 | 16 | use rustc_middle::ty::tls; |
| 18 | 17 | |
| 19 | 18 | fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) { |
| ... | ... | @@ -65,49 +64,10 @@ fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> |
| 65 | 64 | write!(f, ")") |
| 66 | 65 | } |
| 67 | 66 | |
| 68 | /// This is a callback from `rustc_query_system` as it cannot access the implicit state | |
| 69 | /// in `rustc_middle` otherwise. | |
| 70 | pub fn dep_kind_debug(kind: DepKind, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 71 | tls::with_opt(|opt_tcx| { | |
| 72 | if let Some(tcx) = opt_tcx { | |
| 73 | write!(f, "{}", tcx.dep_kind_vtable(kind).name) | |
| 74 | } else { | |
| 75 | default_dep_kind_debug(kind, f) | |
| 76 | } | |
| 77 | }) | |
| 78 | } | |
| 79 | ||
| 80 | /// This is a callback from `rustc_query_system` as it cannot access the implicit state | |
| 81 | /// in `rustc_middle` otherwise. | |
| 82 | pub fn dep_node_debug(node: DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 83 | write!(f, "{:?}(", node.kind)?; | |
| 84 | ||
| 85 | tls::with_opt(|opt_tcx| { | |
| 86 | if let Some(tcx) = opt_tcx { | |
| 87 | if let Some(def_id) = node.extract_def_id(tcx) { | |
| 88 | write!(f, "{}", tcx.def_path_debug_str(def_id))?; | |
| 89 | } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(node) { | |
| 90 | write!(f, "{s}")?; | |
| 91 | } else { | |
| 92 | write!(f, "{}", node.hash)?; | |
| 93 | } | |
| 94 | } else { | |
| 95 | write!(f, "{}", node.hash)?; | |
| 96 | } | |
| 97 | Ok(()) | |
| 98 | })?; | |
| 99 | ||
| 100 | write!(f, ")") | |
| 101 | } | |
| 102 | ||
| 103 | 67 | /// Sets up the callbacks in prior crates which we want to refer to the |
| 104 | 68 | /// TyCtxt in. |
| 105 | 69 | pub fn setup_callbacks() { |
| 106 | 70 | rustc_span::SPAN_TRACK.swap(&(track_span_parent as fn(_))); |
| 107 | 71 | rustc_hir::def_id::DEF_ID_DEBUG.swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); |
| 108 | rustc_middle::dep_graph::dep_node::DEP_KIND_DEBUG | |
| 109 | .swap(&(dep_kind_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); | |
| 110 | rustc_middle::dep_graph::dep_node::DEP_NODE_DEBUG | |
| 111 | .swap(&(dep_node_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); | |
| 112 | TRACK_DIAGNOSTIC.swap(&(track_diagnostic as _)); | |
| 72 | rustc_errors::TRACK_DIAGNOSTIC.swap(&(track_diagnostic as _)); | |
| 113 | 73 | } |
compiler/rustc_lint_defs/src/builtin.rs+3-3| ... | ... | @@ -3659,10 +3659,10 @@ declare_lint! { |
| 3659 | 3659 | /// `stdcall`, `fastcall`, and `cdecl` calling conventions (or their unwind |
| 3660 | 3660 | /// variants) on targets that cannot meaningfully be supported for the requested target. |
| 3661 | 3661 | /// |
| 3662 | /// For example `stdcall` does not make much sense for a x86_64 or, more apparently, powerpc | |
| 3662 | /// For example, `stdcall` does not make much sense for a x86_64 or, more apparently, powerpc | |
| 3663 | 3663 | /// code, because this calling convention was never specified for those targets. |
| 3664 | 3664 | /// |
| 3665 | /// Historically MSVC toolchains have fallen back to the regular C calling convention for | |
| 3665 | /// Historically, MSVC toolchains have fallen back to the regular C calling convention for | |
| 3666 | 3666 | /// targets other than x86, but Rust doesn't really see a similar need to introduce a similar |
| 3667 | 3667 | /// hack across many more targets. |
| 3668 | 3668 | /// |
| ... | ... | @@ -3689,7 +3689,7 @@ declare_lint! { |
| 3689 | 3689 | /// |
| 3690 | 3690 | /// ### Explanation |
| 3691 | 3691 | /// |
| 3692 | /// On most of the targets the behaviour of `stdcall` and similar calling conventions is not | |
| 3692 | /// On most of the targets, the behaviour of `stdcall` and similar calling conventions is not | |
| 3693 | 3693 | /// defined at all, but was previously accepted due to a bug in the implementation of the |
| 3694 | 3694 | /// compiler. |
| 3695 | 3695 | pub UNSUPPORTED_CALLING_CONVENTIONS, |
compiler/rustc_macros/src/hash_stable.rs+1-1| ... | ... | @@ -96,7 +96,7 @@ fn hash_stable_derive_with_mode( |
| 96 | 96 | |
| 97 | 97 | let context: syn::Type = match mode { |
| 98 | 98 | HashStableMode::Normal => { |
| 99 | parse_quote!(::rustc_query_system::ich::StableHashingContext<'__ctx>) | |
| 99 | parse_quote!(::rustc_middle::ich::StableHashingContext<'__ctx>) | |
| 100 | 100 | } |
| 101 | 101 | HashStableMode::Generic | HashStableMode::NoContext => parse_quote!(__CTX), |
| 102 | 102 | }; |
compiler/rustc_metadata/src/rmeta/encoder.rs+1-4| ... | ... | @@ -734,10 +734,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { |
| 734 | 734 | has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE), |
| 735 | 735 | has_alloc_error_handler: tcx.has_alloc_error_handler(LOCAL_CRATE), |
| 736 | 736 | has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE), |
| 737 | has_default_lib_allocator: ast::attr::contains_name( | |
| 738 | attrs, | |
| 739 | sym::default_lib_allocator, | |
| 740 | ), | |
| 737 | has_default_lib_allocator: find_attr!(attrs, AttributeKind::DefaultLibAllocator), | |
| 741 | 738 | externally_implementable_items, |
| 742 | 739 | proc_macro_data, |
| 743 | 740 | debugger_visualizers, |
compiler/rustc_middle/Cargo.toml-1| ... | ... | @@ -26,7 +26,6 @@ rustc_hir_pretty = { path = "../rustc_hir_pretty" } |
| 26 | 26 | rustc_index = { path = "../rustc_index" } |
| 27 | 27 | rustc_lint_defs = { path = "../rustc_lint_defs" } |
| 28 | 28 | rustc_macros = { path = "../rustc_macros" } |
| 29 | rustc_query_system = { path = "../rustc_query_system" } | |
| 30 | 29 | rustc_serialize = { path = "../rustc_serialize" } |
| 31 | 30 | rustc_session = { path = "../rustc_session" } |
| 32 | 31 | rustc_span = { path = "../rustc_span" } |
compiler/rustc_middle/src/dep_graph/dep_node.rs+27-19| ... | ... | @@ -58,18 +58,17 @@ |
| 58 | 58 | use std::fmt; |
| 59 | 59 | use std::hash::Hash; |
| 60 | 60 | |
| 61 | use rustc_data_structures::AtomicRef; | |
| 62 | 61 | use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; |
| 63 | 62 | use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey}; |
| 64 | 63 | use rustc_hir::def_id::DefId; |
| 65 | 64 | use rustc_hir::definitions::DefPathHash; |
| 66 | 65 | use rustc_macros::{Decodable, Encodable}; |
| 67 | use rustc_query_system::ich::StableHashingContext; | |
| 68 | 66 | use rustc_span::Symbol; |
| 69 | 67 | |
| 70 | 68 | use super::{FingerprintStyle, SerializedDepNodeIndex}; |
| 69 | use crate::ich::StableHashingContext; | |
| 71 | 70 | use crate::mir::mono::MonoItem; |
| 72 | use crate::ty::TyCtxt; | |
| 71 | use crate::ty::{TyCtxt, tls}; | |
| 73 | 72 | |
| 74 | 73 | /// This serves as an index into arrays built by `make_dep_kind_array`. |
| 75 | 74 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] |
| ... | ... | @@ -114,16 +113,15 @@ impl DepKind { |
| 114 | 113 | pub(crate) const MAX: u16 = DEP_KIND_VARIANTS - 1; |
| 115 | 114 | } |
| 116 | 115 | |
| 117 | pub fn default_dep_kind_debug(kind: DepKind, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| 118 | f.debug_struct("DepKind").field("variant", &kind.variant).finish() | |
| 119 | } | |
| 120 | ||
| 121 | pub static DEP_KIND_DEBUG: AtomicRef<fn(DepKind, &mut fmt::Formatter<'_>) -> fmt::Result> = | |
| 122 | AtomicRef::new(&(default_dep_kind_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); | |
| 123 | ||
| 124 | 116 | impl fmt::Debug for DepKind { |
| 125 | 117 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 126 | (*DEP_KIND_DEBUG)(*self, f) | |
| 118 | tls::with_opt(|opt_tcx| { | |
| 119 | if let Some(tcx) = opt_tcx { | |
| 120 | write!(f, "{}", tcx.dep_kind_vtable(*self).name) | |
| 121 | } else { | |
| 122 | f.debug_struct("DepKind").field("variant", &self.variant).finish() | |
| 123 | } | |
| 124 | }) | |
| 127 | 125 | } |
| 128 | 126 | } |
| 129 | 127 | |
| ... | ... | @@ -175,16 +173,26 @@ impl DepNode { |
| 175 | 173 | } |
| 176 | 174 | } |
| 177 | 175 | |
| 178 | pub fn default_dep_node_debug(node: DepNode, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| 179 | f.debug_struct("DepNode").field("kind", &node.kind).field("hash", &node.hash).finish() | |
| 180 | } | |
| 181 | ||
| 182 | pub static DEP_NODE_DEBUG: AtomicRef<fn(DepNode, &mut fmt::Formatter<'_>) -> fmt::Result> = | |
| 183 | AtomicRef::new(&(default_dep_node_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); | |
| 184 | ||
| 185 | 176 | impl fmt::Debug for DepNode { |
| 186 | 177 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 187 | (*DEP_NODE_DEBUG)(*self, f) | |
| 178 | write!(f, "{:?}(", self.kind)?; | |
| 179 | ||
| 180 | tls::with_opt(|opt_tcx| { | |
| 181 | if let Some(tcx) = opt_tcx { | |
| 182 | if let Some(def_id) = self.extract_def_id(tcx) { | |
| 183 | write!(f, "{}", tcx.def_path_debug_str(def_id))?; | |
| 184 | } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(*self) { | |
| 185 | write!(f, "{s}")?; | |
| 186 | } else { | |
| 187 | write!(f, "{}", self.hash)?; | |
| 188 | } | |
| 189 | } else { | |
| 190 | write!(f, "{}", self.hash)?; | |
| 191 | } | |
| 192 | Ok(()) | |
| 193 | })?; | |
| 194 | ||
| 195 | write!(f, ")") | |
| 188 | 196 | } |
| 189 | 197 | } |
| 190 | 198 |
compiler/rustc_middle/src/dep_graph/graph.rs+18-2| ... | ... | @@ -15,8 +15,6 @@ use rustc_data_structures::{assert_matches, outline}; |
| 15 | 15 | use rustc_errors::DiagInner; |
| 16 | 16 | use rustc_index::IndexVec; |
| 17 | 17 | use rustc_macros::{Decodable, Encodable}; |
| 18 | use rustc_query_system::ich::StableHashingContext; | |
| 19 | use rustc_query_system::query::QuerySideEffect; | |
| 20 | 18 | use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; |
| 21 | 19 | use rustc_session::Session; |
| 22 | 20 | use tracing::{debug, instrument}; |
| ... | ... | @@ -27,9 +25,27 @@ use super::query::DepGraphQuery; |
| 27 | 25 | use super::serialized::{GraphEncoder, SerializedDepGraph, SerializedDepNodeIndex}; |
| 28 | 26 | use super::{DepKind, DepNode, HasDepContext, WorkProductId, read_deps, with_deps}; |
| 29 | 27 | use crate::dep_graph::edges::EdgesVec; |
| 28 | use crate::ich::StableHashingContext; | |
| 30 | 29 | use crate::ty::TyCtxt; |
| 31 | 30 | use crate::verify_ich::incremental_verify_ich; |
| 32 | 31 | |
| 32 | /// Tracks 'side effects' for a particular query. | |
| 33 | /// This struct is saved to disk along with the query result, | |
| 34 | /// and loaded from disk if we mark the query as green. | |
| 35 | /// This allows us to 'replay' changes to global state | |
| 36 | /// that would otherwise only occur if we actually | |
| 37 | /// executed the query method. | |
| 38 | /// | |
| 39 | /// Each side effect gets an unique dep node index which is added | |
| 40 | /// as a dependency of the query which had the effect. | |
| 41 | #[derive(Debug, Encodable, Decodable)] | |
| 42 | pub enum QuerySideEffect { | |
| 43 | /// Stores a diagnostic emitted during query execution. | |
| 44 | /// This diagnostic will be re-emitted if we mark | |
| 45 | /// the query as green, as that query will have the side | |
| 46 | /// effect dep node as a dependency. | |
| 47 | Diagnostic(DiagInner), | |
| 48 | } | |
| 33 | 49 | #[derive(Clone)] |
| 34 | 50 | pub struct DepGraph { |
| 35 | 51 | data: Option<Arc<DepGraphData>>, |
compiler/rustc_middle/src/dep_graph/mod.rs+2-1| ... | ... | @@ -7,7 +7,8 @@ pub use self::dep_node::{ |
| 7 | 7 | label_strs, |
| 8 | 8 | }; |
| 9 | 9 | pub use self::graph::{ |
| 10 | DepGraph, DepGraphData, DepNodeIndex, TaskDepsRef, WorkProduct, WorkProductMap, hash_result, | |
| 10 | DepGraph, DepGraphData, DepNodeIndex, QuerySideEffect, TaskDepsRef, WorkProduct, | |
| 11 | WorkProductMap, hash_result, | |
| 11 | 12 | }; |
| 12 | 13 | use self::graph::{MarkFrame, print_markframe_trace}; |
| 13 | 14 | pub use self::query::DepGraphQuery; |
compiler/rustc_middle/src/ich/hcx.rs created+196| ... | ... | @@ -0,0 +1,196 @@ |
| 1 | use std::hash::Hash; | |
| 2 | ||
| 3 | use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher}; | |
| 4 | use rustc_hir::def_id::{DefId, LocalDefId}; | |
| 5 | use rustc_hir::definitions::DefPathHash; | |
| 6 | use rustc_session::Session; | |
| 7 | use rustc_session::cstore::Untracked; | |
| 8 | use rustc_span::source_map::SourceMap; | |
| 9 | use rustc_span::{CachingSourceMapView, DUMMY_SP, Pos, Span}; | |
| 10 | ||
| 11 | // Very often, we are hashing something that does not need the `CachingSourceMapView`, so we | |
| 12 | // initialize it lazily. | |
| 13 | #[derive(Clone)] | |
| 14 | enum CachingSourceMap<'a> { | |
| 15 | Unused(&'a SourceMap), | |
| 16 | InUse(CachingSourceMapView<'a>), | |
| 17 | } | |
| 18 | ||
| 19 | /// This is the context state available during incr. comp. hashing. It contains | |
| 20 | /// enough information to transform `DefId`s and `HirId`s into stable `DefPath`s (i.e., | |
| 21 | /// a reference to the `TyCtxt`) and it holds a few caches for speeding up various | |
| 22 | /// things (e.g., each `DefId`/`DefPath` is only hashed once). | |
| 23 | #[derive(Clone)] | |
| 24 | pub struct StableHashingContext<'a> { | |
| 25 | untracked: &'a Untracked, | |
| 26 | // The value of `-Z incremental-ignore-spans`. | |
| 27 | // This field should only be used by `unstable_opts_incremental_ignore_span` | |
| 28 | incremental_ignore_spans: bool, | |
| 29 | caching_source_map: CachingSourceMap<'a>, | |
| 30 | hashing_controls: HashingControls, | |
| 31 | } | |
| 32 | ||
| 33 | impl<'a> StableHashingContext<'a> { | |
| 34 | #[inline] | |
| 35 | pub fn new(sess: &'a Session, untracked: &'a Untracked) -> Self { | |
| 36 | let hash_spans_initial = !sess.opts.unstable_opts.incremental_ignore_spans; | |
| 37 | ||
| 38 | StableHashingContext { | |
| 39 | untracked, | |
| 40 | incremental_ignore_spans: sess.opts.unstable_opts.incremental_ignore_spans, | |
| 41 | caching_source_map: CachingSourceMap::Unused(sess.source_map()), | |
| 42 | hashing_controls: HashingControls { hash_spans: hash_spans_initial }, | |
| 43 | } | |
| 44 | } | |
| 45 | ||
| 46 | #[inline] | |
| 47 | pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self, hash_spans: bool, f: F) { | |
| 48 | let prev_hash_spans = self.hashing_controls.hash_spans; | |
| 49 | self.hashing_controls.hash_spans = hash_spans; | |
| 50 | f(self); | |
| 51 | self.hashing_controls.hash_spans = prev_hash_spans; | |
| 52 | } | |
| 53 | ||
| 54 | #[inline] | |
| 55 | fn source_map(&mut self) -> &mut CachingSourceMapView<'a> { | |
| 56 | match self.caching_source_map { | |
| 57 | CachingSourceMap::InUse(ref mut sm) => sm, | |
| 58 | CachingSourceMap::Unused(sm) => { | |
| 59 | self.caching_source_map = CachingSourceMap::InUse(CachingSourceMapView::new(sm)); | |
| 60 | self.source_map() // this recursive call will hit the `InUse` case | |
| 61 | } | |
| 62 | } | |
| 63 | } | |
| 64 | ||
| 65 | #[inline] | |
| 66 | fn def_span(&self, def_id: LocalDefId) -> Span { | |
| 67 | self.untracked.source_span.get(def_id).unwrap_or(DUMMY_SP) | |
| 68 | } | |
| 69 | ||
| 70 | #[inline] | |
| 71 | pub fn hashing_controls(&self) -> HashingControls { | |
| 72 | self.hashing_controls | |
| 73 | } | |
| 74 | } | |
| 75 | ||
| 76 | impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { | |
| 77 | /// Hashes a span in a stable way. We can't directly hash the span's `BytePos` fields (that | |
| 78 | /// would be similar to hashing pointers, since those are just offsets into the `SourceMap`). | |
| 79 | /// Instead, we hash the (file name, line, column) triple, which stays the same even if the | |
| 80 | /// containing `SourceFile` has moved within the `SourceMap`. | |
| 81 | /// | |
| 82 | /// Also note that we are hashing byte offsets for the column, not unicode codepoint offsets. | |
| 83 | /// For the purpose of the hash that's sufficient. Also, hashing filenames is expensive so we | |
| 84 | /// avoid doing it twice when the span starts and ends in the same file, which is almost always | |
| 85 | /// the case. | |
| 86 | /// | |
| 87 | /// IMPORTANT: changes to this method should be reflected in implementations of `SpanEncoder`. | |
| 88 | #[inline] | |
| 89 | fn span_hash_stable(&mut self, span: Span, hasher: &mut StableHasher) { | |
| 90 | const TAG_VALID_SPAN: u8 = 0; | |
| 91 | const TAG_INVALID_SPAN: u8 = 1; | |
| 92 | const TAG_RELATIVE_SPAN: u8 = 2; | |
| 93 | ||
| 94 | if !self.hashing_controls().hash_spans { | |
| 95 | return; | |
| 96 | } | |
| 97 | ||
| 98 | let span = span.data_untracked(); | |
| 99 | span.ctxt.hash_stable(self, hasher); | |
| 100 | span.parent.hash_stable(self, hasher); | |
| 101 | ||
| 102 | if span.is_dummy() { | |
| 103 | Hash::hash(&TAG_INVALID_SPAN, hasher); | |
| 104 | return; | |
| 105 | } | |
| 106 | ||
| 107 | let parent = span.parent.map(|parent| self.def_span(parent).data_untracked()); | |
| 108 | if let Some(parent) = parent | |
| 109 | && parent.contains(span) | |
| 110 | { | |
| 111 | // This span is enclosed in a definition: only hash the relative position. This catches | |
| 112 | // a subset of the cases from the `file.contains(parent.lo)`. But we can do this check | |
| 113 | // cheaply without the expensive `span_data_to_lines_and_cols` query. | |
| 114 | Hash::hash(&TAG_RELATIVE_SPAN, hasher); | |
| 115 | (span.lo - parent.lo).to_u32().hash_stable(self, hasher); | |
| 116 | (span.hi - parent.lo).to_u32().hash_stable(self, hasher); | |
| 117 | return; | |
| 118 | } | |
| 119 | ||
| 120 | // If this is not an empty or invalid span, we want to hash the last position that belongs | |
| 121 | // to it, as opposed to hashing the first position past it. | |
| 122 | let Some((file, line_lo, col_lo, line_hi, col_hi)) = | |
| 123 | self.source_map().span_data_to_lines_and_cols(&span) | |
| 124 | else { | |
| 125 | Hash::hash(&TAG_INVALID_SPAN, hasher); | |
| 126 | return; | |
| 127 | }; | |
| 128 | ||
| 129 | if let Some(parent) = parent | |
| 130 | && file.contains(parent.lo) | |
| 131 | { | |
| 132 | // This span is relative to another span in the same file, | |
| 133 | // only hash the relative position. | |
| 134 | Hash::hash(&TAG_RELATIVE_SPAN, hasher); | |
| 135 | Hash::hash(&(span.lo.0.wrapping_sub(parent.lo.0)), hasher); | |
| 136 | Hash::hash(&(span.hi.0.wrapping_sub(parent.lo.0)), hasher); | |
| 137 | return; | |
| 138 | } | |
| 139 | ||
| 140 | Hash::hash(&TAG_VALID_SPAN, hasher); | |
| 141 | Hash::hash(&file.stable_id, hasher); | |
| 142 | ||
| 143 | // Hash both the length and the end location (line/column) of a span. If we hash only the | |
| 144 | // length, for example, then two otherwise equal spans with different end locations will | |
| 145 | // have the same hash. This can cause a problem during incremental compilation wherein a | |
| 146 | // previous result for a query that depends on the end location of a span will be | |
| 147 | // incorrectly reused when the end location of the span it depends on has changed (see | |
| 148 | // issue #74890). A similar analysis applies if some query depends specifically on the | |
| 149 | // length of the span, but we only hash the end location. So hash both. | |
| 150 | ||
| 151 | let col_lo_trunc = (col_lo.0 as u64) & 0xFF; | |
| 152 | let line_lo_trunc = ((line_lo as u64) & 0xFF_FF_FF) << 8; | |
| 153 | let col_hi_trunc = (col_hi.0 as u64) & 0xFF << 32; | |
| 154 | let line_hi_trunc = ((line_hi as u64) & 0xFF_FF_FF) << 40; | |
| 155 | let col_line = col_lo_trunc | line_lo_trunc | col_hi_trunc | line_hi_trunc; | |
| 156 | let len = (span.hi - span.lo).0; | |
| 157 | Hash::hash(&col_line, hasher); | |
| 158 | Hash::hash(&len, hasher); | |
| 159 | } | |
| 160 | ||
| 161 | #[inline] | |
| 162 | fn def_path_hash(&self, def_id: DefId) -> DefPathHash { | |
| 163 | if let Some(def_id) = def_id.as_local() { | |
| 164 | self.untracked.definitions.read().def_path_hash(def_id) | |
| 165 | } else { | |
| 166 | self.untracked.cstore.read().def_path_hash(def_id) | |
| 167 | } | |
| 168 | } | |
| 169 | ||
| 170 | /// Assert that the provided `HashStableContext` is configured with the default | |
| 171 | /// `HashingControls`. We should always have bailed out before getting to here with a | |
| 172 | /// non-default mode. With this check in place, we can avoid the need to maintain separate | |
| 173 | /// versions of `ExpnData` hashes for each permutation of `HashingControls` settings. | |
| 174 | #[inline] | |
| 175 | fn assert_default_hashing_controls(&self, msg: &str) { | |
| 176 | let hashing_controls = self.hashing_controls; | |
| 177 | let HashingControls { hash_spans } = hashing_controls; | |
| 178 | ||
| 179 | // Note that we require that `hash_spans` be the inverse of the global `-Z | |
| 180 | // incremental-ignore-spans` option. Normally, this option is disabled, in which case | |
| 181 | // `hash_spans` must be true. | |
| 182 | // | |
| 183 | // Span hashing can also be disabled without `-Z incremental-ignore-spans`. This is the | |
| 184 | // case for instance when building a hash for name mangling. Such configuration must not be | |
| 185 | // used for metadata. | |
| 186 | assert_eq!( | |
| 187 | hash_spans, !self.incremental_ignore_spans, | |
| 188 | "Attempted hashing of {msg} with non-default HashingControls: {hashing_controls:?}" | |
| 189 | ); | |
| 190 | } | |
| 191 | } | |
| 192 | ||
| 193 | impl<'a> rustc_abi::HashStableContext for StableHashingContext<'a> {} | |
| 194 | impl<'a> rustc_ast::HashStableContext for StableHashingContext<'a> {} | |
| 195 | impl<'a> rustc_hir::HashStableContext for StableHashingContext<'a> {} | |
| 196 | impl<'a> rustc_session::HashStableContext for StableHashingContext<'a> {} |
compiler/rustc_middle/src/ich/impls_syntax.rs created+130| ... | ... | @@ -0,0 +1,130 @@ |
| 1 | //! This module contains `HashStable` implementations for various data types | |
| 2 | //! from various crates in no particular order. | |
| 3 | ||
| 4 | use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; | |
| 5 | use rustc_span::{SourceFile, Symbol, sym}; | |
| 6 | use smallvec::SmallVec; | |
| 7 | use {rustc_ast as ast, rustc_hir as hir}; | |
| 8 | ||
| 9 | use super::StableHashingContext; | |
| 10 | ||
| 11 | impl<'a> HashStable<StableHashingContext<'a>> for ast::NodeId { | |
| 12 | #[inline] | |
| 13 | fn hash_stable(&self, _: &mut StableHashingContext<'a>, _: &mut StableHasher) { | |
| 14 | panic!("Node IDs should not appear in incremental state"); | |
| 15 | } | |
| 16 | } | |
| 17 | ||
| 18 | impl<'a> HashStable<StableHashingContext<'a>> for [hir::Attribute] { | |
| 19 | fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { | |
| 20 | if self.is_empty() { | |
| 21 | self.len().hash_stable(hcx, hasher); | |
| 22 | return; | |
| 23 | } | |
| 24 | ||
| 25 | // Some attributes are always ignored during hashing. | |
| 26 | let filtered: SmallVec<[&hir::Attribute; 8]> = self | |
| 27 | .iter() | |
| 28 | .filter(|attr| { | |
| 29 | attr.is_doc_comment().is_none() | |
| 30 | // FIXME(jdonszelmann) have a better way to handle ignored attrs | |
| 31 | && !attr.name().is_some_and(|ident| is_ignored_attr(ident)) | |
| 32 | }) | |
| 33 | .collect(); | |
| 34 | ||
| 35 | filtered.len().hash_stable(hcx, hasher); | |
| 36 | for attr in filtered { | |
| 37 | attr.hash_stable(hcx, hasher); | |
| 38 | } | |
| 39 | } | |
| 40 | } | |
| 41 | ||
| 42 | #[inline] | |
| 43 | fn is_ignored_attr(name: Symbol) -> bool { | |
| 44 | const IGNORED_ATTRIBUTES: &[Symbol] = &[ | |
| 45 | sym::cfg_trace, // FIXME(#138844) should this really be ignored? | |
| 46 | sym::rustc_if_this_changed, | |
| 47 | sym::rustc_then_this_would_need, | |
| 48 | sym::rustc_clean, | |
| 49 | sym::rustc_partition_reused, | |
| 50 | sym::rustc_partition_codegened, | |
| 51 | sym::rustc_expected_cgu_reuse, | |
| 52 | ]; | |
| 53 | IGNORED_ATTRIBUTES.contains(&name) | |
| 54 | } | |
| 55 | ||
| 56 | impl<'a> HashStable<StableHashingContext<'a>> for SourceFile { | |
| 57 | fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { | |
| 58 | let SourceFile { | |
| 59 | name: _, // We hash the smaller stable_id instead of this | |
| 60 | stable_id, | |
| 61 | cnum, | |
| 62 | // Do not hash the source as it is not encoded | |
| 63 | src: _, | |
| 64 | ref src_hash, | |
| 65 | // Already includes src_hash, this is redundant | |
| 66 | checksum_hash: _, | |
| 67 | external_src: _, | |
| 68 | start_pos: _, | |
| 69 | normalized_source_len: _, | |
| 70 | unnormalized_source_len: _, | |
| 71 | lines: _, | |
| 72 | ref multibyte_chars, | |
| 73 | ref normalized_pos, | |
| 74 | } = *self; | |
| 75 | ||
| 76 | stable_id.hash_stable(hcx, hasher); | |
| 77 | ||
| 78 | src_hash.hash_stable(hcx, hasher); | |
| 79 | ||
| 80 | { | |
| 81 | // We are always in `Lines` form by the time we reach here. | |
| 82 | assert!(self.lines.read().is_lines()); | |
| 83 | let lines = self.lines(); | |
| 84 | // We only hash the relative position within this source_file | |
| 85 | lines.len().hash_stable(hcx, hasher); | |
| 86 | for &line in lines.iter() { | |
| 87 | line.hash_stable(hcx, hasher); | |
| 88 | } | |
| 89 | } | |
| 90 | ||
| 91 | // We only hash the relative position within this source_file | |
| 92 | multibyte_chars.len().hash_stable(hcx, hasher); | |
| 93 | for &char_pos in multibyte_chars.iter() { | |
| 94 | char_pos.hash_stable(hcx, hasher); | |
| 95 | } | |
| 96 | ||
| 97 | normalized_pos.len().hash_stable(hcx, hasher); | |
| 98 | for &char_pos in normalized_pos.iter() { | |
| 99 | char_pos.hash_stable(hcx, hasher); | |
| 100 | } | |
| 101 | ||
| 102 | cnum.hash_stable(hcx, hasher); | |
| 103 | } | |
| 104 | } | |
| 105 | ||
| 106 | impl<'tcx> HashStable<StableHashingContext<'tcx>> for rustc_feature::Features { | |
| 107 | fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { | |
| 108 | // Unfortunately we cannot exhaustively list fields here, since the | |
| 109 | // struct has private fields (to ensure its invariant is maintained) | |
| 110 | self.enabled_lang_features().hash_stable(hcx, hasher); | |
| 111 | self.enabled_lib_features().hash_stable(hcx, hasher); | |
| 112 | } | |
| 113 | } | |
| 114 | ||
| 115 | impl<'tcx> HashStable<StableHashingContext<'tcx>> for rustc_feature::EnabledLangFeature { | |
| 116 | fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { | |
| 117 | let rustc_feature::EnabledLangFeature { gate_name, attr_sp, stable_since } = self; | |
| 118 | gate_name.hash_stable(hcx, hasher); | |
| 119 | attr_sp.hash_stable(hcx, hasher); | |
| 120 | stable_since.hash_stable(hcx, hasher); | |
| 121 | } | |
| 122 | } | |
| 123 | ||
| 124 | impl<'tcx> HashStable<StableHashingContext<'tcx>> for rustc_feature::EnabledLibFeature { | |
| 125 | fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { | |
| 126 | let rustc_feature::EnabledLibFeature { gate_name, attr_sp } = self; | |
| 127 | gate_name.hash_stable(hcx, hasher); | |
| 128 | attr_sp.hash_stable(hcx, hasher); | |
| 129 | } | |
| 130 | } |
compiler/rustc_middle/src/ich/mod.rs created+6| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | //! ICH - Incremental Compilation Hash | |
| 2 | ||
| 3 | pub use self::hcx::StableHashingContext; | |
| 4 | ||
| 5 | mod hcx; | |
| 6 | mod impls_syntax; |
compiler/rustc_middle/src/lib.rs+1| ... | ... | @@ -72,6 +72,7 @@ pub mod arena; |
| 72 | 72 | pub mod error; |
| 73 | 73 | pub mod hir; |
| 74 | 74 | pub mod hooks; |
| 75 | pub mod ich; | |
| 75 | 76 | pub mod infer; |
| 76 | 77 | pub mod lint; |
| 77 | 78 | pub mod metadata; |
compiler/rustc_middle/src/middle/privacy.rs+1-1| ... | ... | @@ -8,9 +8,9 @@ use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; |
| 8 | 8 | use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; |
| 9 | 9 | use rustc_hir::def::DefKind; |
| 10 | 10 | use rustc_macros::HashStable; |
| 11 | use rustc_query_system::ich::StableHashingContext; | |
| 12 | 11 | use rustc_span::def_id::{CRATE_DEF_ID, LocalDefId}; |
| 13 | 12 | |
| 13 | use crate::ich::StableHashingContext; | |
| 14 | 14 | use crate::ty::{TyCtxt, Visibility}; |
| 15 | 15 | |
| 16 | 16 | /// Represents the levels of effective visibility an item can have. |
compiler/rustc_middle/src/mir/mod.rs+2-1| ... | ... | @@ -911,7 +911,8 @@ pub struct VarBindingIntroduction { |
| 911 | 911 | |
| 912 | 912 | mod binding_form_impl { |
| 913 | 913 | use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; |
| 914 | use rustc_query_system::ich::StableHashingContext; | |
| 914 | ||
| 915 | use crate::ich::StableHashingContext; | |
| 915 | 916 | |
| 916 | 917 | impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for super::BindingForm<'tcx> { |
| 917 | 918 | fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { |
compiler/rustc_middle/src/mir/mono.rs+1-1| ... | ... | @@ -12,7 +12,6 @@ use rustc_hir::ItemId; |
| 12 | 12 | use rustc_hir::attrs::{InlineAttr, Linkage}; |
| 13 | 13 | use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, LOCAL_CRATE}; |
| 14 | 14 | use rustc_macros::{HashStable, TyDecodable, TyEncodable}; |
| 15 | use rustc_query_system::ich::StableHashingContext; | |
| 16 | 15 | use rustc_session::config::OptLevel; |
| 17 | 16 | use rustc_span::{Span, Symbol}; |
| 18 | 17 | use rustc_target::spec::SymbolVisibility; |
| ... | ... | @@ -20,6 +19,7 @@ use tracing::debug; |
| 20 | 19 | |
| 21 | 20 | use crate::dep_graph::dep_node::{make_compile_codegen_unit, make_compile_mono_item}; |
| 22 | 21 | use crate::dep_graph::{DepNode, WorkProduct, WorkProductId}; |
| 22 | use crate::ich::StableHashingContext; | |
| 23 | 23 | use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; |
| 24 | 24 | use crate::ty::{self, GenericArgs, Instance, InstanceKind, SymbolName, Ty, TyCtxt}; |
| 25 | 25 |
compiler/rustc_middle/src/query/caches.rs+1-1| ... | ... | @@ -7,10 +7,10 @@ use rustc_data_structures::stable_hasher::HashStable; |
| 7 | 7 | pub use rustc_data_structures::vec_cache::VecCache; |
| 8 | 8 | use rustc_hir::def_id::LOCAL_CRATE; |
| 9 | 9 | use rustc_index::Idx; |
| 10 | use rustc_query_system::ich::StableHashingContext; | |
| 11 | 10 | use rustc_span::def_id::{DefId, DefIndex}; |
| 12 | 11 | |
| 13 | 12 | use crate::dep_graph::DepNodeIndex; |
| 13 | use crate::ich::StableHashingContext; | |
| 14 | 14 | |
| 15 | 15 | /// Traits that all query keys must satisfy. |
| 16 | 16 | pub trait QueryCacheKey = Hash + Eq + Copy + Debug + for<'a> HashStable<StableHashingContext<'a>>; |
compiler/rustc_middle/src/query/on_disk_cache.rs+1-2| ... | ... | @@ -11,7 +11,6 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE, LocalDefId, Stab |
| 11 | 11 | use rustc_hir::definitions::DefPathHash; |
| 12 | 12 | use rustc_index::{Idx, IndexVec}; |
| 13 | 13 | use rustc_macros::{Decodable, Encodable}; |
| 14 | use rustc_query_system::query::QuerySideEffect; | |
| 15 | 14 | use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder}; |
| 16 | 15 | use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; |
| 17 | 16 | use rustc_session::Session; |
| ... | ... | @@ -24,7 +23,7 @@ use rustc_span::{ |
| 24 | 23 | SourceFile, Span, SpanDecoder, SpanEncoder, StableSourceFileId, Symbol, |
| 25 | 24 | }; |
| 26 | 25 | |
| 27 | use crate::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; | |
| 26 | use crate::dep_graph::{DepNodeIndex, QuerySideEffect, SerializedDepNodeIndex}; | |
| 28 | 27 | use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState}; |
| 29 | 28 | use crate::mir::mono::MonoItem; |
| 30 | 29 | use crate::mir::{self, interpret}; |
compiler/rustc_middle/src/query/plumbing.rs+1-4| ... | ... | @@ -8,12 +8,12 @@ use rustc_data_structures::sync::{AtomicU64, WorkerLocal}; |
| 8 | 8 | use rustc_hir::def_id::{DefId, LocalDefId}; |
| 9 | 9 | use rustc_hir::hir_id::OwnerId; |
| 10 | 10 | use rustc_macros::HashStable; |
| 11 | use rustc_query_system::ich::StableHashingContext; | |
| 12 | 11 | use rustc_span::{ErrorGuaranteed, Span}; |
| 13 | 12 | pub use sealed::IntoQueryParam; |
| 14 | 13 | |
| 15 | 14 | use crate::dep_graph; |
| 16 | 15 | use crate::dep_graph::{DepKind, DepNodeIndex, SerializedDepNodeIndex}; |
| 16 | use crate::ich::StableHashingContext; | |
| 17 | 17 | use crate::queries::{ |
| 18 | 18 | ExternProviders, PerQueryVTables, Providers, QueryArenas, QueryCaches, QueryEngine, QueryStates, |
| 19 | 19 | }; |
| ... | ... | @@ -102,9 +102,6 @@ pub enum QueryMode { |
| 102 | 102 | } |
| 103 | 103 | |
| 104 | 104 | /// Stores function pointers and other metadata for a particular query. |
| 105 | /// | |
| 106 | /// Used indirectly by query plumbing in `rustc_query_system` via a trait, | |
| 107 | /// and also used directly by query plumbing in `rustc_query_impl`. | |
| 108 | 105 | pub struct QueryVTable<'tcx, C: QueryCache> { |
| 109 | 106 | pub name: &'static str, |
| 110 | 107 | pub eval_always: bool, |
compiler/rustc_middle/src/ty/adt.rs+1-1| ... | ... | @@ -15,7 +15,6 @@ use rustc_hir::def_id::DefId; |
| 15 | 15 | use rustc_hir::{self as hir, LangItem, find_attr}; |
| 16 | 16 | use rustc_index::{IndexSlice, IndexVec}; |
| 17 | 17 | use rustc_macros::{HashStable, TyDecodable, TyEncodable}; |
| 18 | use rustc_query_system::ich::StableHashingContext; | |
| 19 | 18 | use rustc_session::DataTypeKind; |
| 20 | 19 | use rustc_type_ir::solve::AdtDestructorKind; |
| 21 | 20 | use tracing::{debug, info, trace}; |
| ... | ... | @@ -23,6 +22,7 @@ use tracing::{debug, info, trace}; |
| 23 | 22 | use super::{ |
| 24 | 23 | AsyncDestructor, Destructor, FieldDef, GenericPredicates, Ty, TyCtxt, VariantDef, VariantDiscr, |
| 25 | 24 | }; |
| 25 | use crate::ich::StableHashingContext; | |
| 26 | 26 | use crate::mir::interpret::ErrorHandled; |
| 27 | 27 | use crate::ty; |
| 28 | 28 | use crate::ty::util::{Discr, IntTypeExt}; |
compiler/rustc_middle/src/ty/context.rs+2-2| ... | ... | @@ -39,7 +39,6 @@ use rustc_hir::lang_items::LangItem; |
| 39 | 39 | use rustc_hir::limit::Limit; |
| 40 | 40 | use rustc_hir::{self as hir, HirId, Node, TraitCandidate, find_attr}; |
| 41 | 41 | use rustc_index::IndexVec; |
| 42 | use rustc_query_system::ich::StableHashingContext; | |
| 43 | 42 | use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; |
| 44 | 43 | use rustc_session::Session; |
| 45 | 44 | use rustc_session::config::CrateType; |
| ... | ... | @@ -55,6 +54,7 @@ use tracing::{debug, instrument}; |
| 55 | 54 | use crate::arena::Arena; |
| 56 | 55 | use crate::dep_graph::dep_node::make_metadata; |
| 57 | 56 | use crate::dep_graph::{DepGraph, DepKindVTable, DepNodeIndex}; |
| 57 | use crate::ich::StableHashingContext; | |
| 58 | 58 | use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarKind}; |
| 59 | 59 | use crate::lint::lint_level; |
| 60 | 60 | use crate::metadata::ModChild; |
| ... | ... | @@ -1220,7 +1220,7 @@ impl<'tcx> TyCtxt<'tcx> { |
| 1220 | 1220 | pub fn needs_crate_hash(self) -> bool { |
| 1221 | 1221 | // Why is the crate hash needed for these configurations? |
| 1222 | 1222 | // - debug_assertions: for the "fingerprint the result" check in |
| 1223 | // `rustc_query_system::query::plumbing::execute_job`. | |
| 1223 | // `rustc_query_impl::execution::execute_job`. | |
| 1224 | 1224 | // - incremental: for query lookups. |
| 1225 | 1225 | // - needs_metadata: for putting into crate metadata. |
| 1226 | 1226 | // - instrument_coverage: for putting into coverage data (see |
compiler/rustc_middle/src/ty/impls_ty.rs+1-1| ... | ... | @@ -9,9 +9,9 @@ use rustc_data_structures::fx::FxHashMap; |
| 9 | 9 | use rustc_data_structures::stable_hasher::{ |
| 10 | 10 | HashStable, HashingControls, StableHasher, ToStableHashKey, |
| 11 | 11 | }; |
| 12 | use rustc_query_system::ich::StableHashingContext; | |
| 13 | 12 | use tracing::trace; |
| 14 | 13 | |
| 14 | use crate::ich::StableHashingContext; | |
| 15 | 15 | use crate::middle::region; |
| 16 | 16 | use crate::{mir, ty}; |
| 17 | 17 |
compiler/rustc_middle/src/ty/mod.rs+1-1| ... | ... | @@ -47,7 +47,6 @@ use rustc_macros::{ |
| 47 | 47 | BlobDecodable, Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, |
| 48 | 48 | TypeVisitable, extension, |
| 49 | 49 | }; |
| 50 | use rustc_query_system::ich::StableHashingContext; | |
| 51 | 50 | use rustc_serialize::{Decodable, Encodable}; |
| 52 | 51 | pub use rustc_session::lint::RegisteredTools; |
| 53 | 52 | use rustc_span::hygiene::MacroKind; |
| ... | ... | @@ -112,6 +111,7 @@ pub use self::typeck_results::{ |
| 112 | 111 | Rust2024IncompatiblePatInfo, TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind, |
| 113 | 112 | }; |
| 114 | 113 | use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; |
| 114 | use crate::ich::StableHashingContext; | |
| 115 | 115 | use crate::metadata::{AmbigModChild, ModChild}; |
| 116 | 116 | use crate::middle::privacy::EffectiveVisibilities; |
| 117 | 117 | use crate::mir::{Body, CoroutineLayout, CoroutineSavedLocal, SourceInfo}; |
compiler/rustc_middle/src/verify_ich.rs+1-1| ... | ... | @@ -1,10 +1,10 @@ |
| 1 | 1 | use std::cell::Cell; |
| 2 | 2 | |
| 3 | 3 | use rustc_data_structures::fingerprint::Fingerprint; |
| 4 | use rustc_query_system::ich::StableHashingContext; | |
| 5 | 4 | use tracing::instrument; |
| 6 | 5 | |
| 7 | 6 | use crate::dep_graph::{DepGraphData, SerializedDepNodeIndex}; |
| 7 | use crate::ich::StableHashingContext; | |
| 8 | 8 | use crate::ty::TyCtxt; |
| 9 | 9 | |
| 10 | 10 | #[inline] |
compiler/rustc_parse/src/errors.rs+11| ... | ... | @@ -3787,6 +3787,17 @@ pub(crate) struct RecoverImportAsUse { |
| 3787 | 3787 | pub token_name: String, |
| 3788 | 3788 | } |
| 3789 | 3789 | |
| 3790 | #[derive(Diagnostic)] | |
| 3791 | #[diag("{$article} {$descr} cannot be `final`")] | |
| 3792 | #[note("only associated functions in traits can be `final`")] | |
| 3793 | pub(crate) struct InappropriateFinal { | |
| 3794 | #[primary_span] | |
| 3795 | #[label("`final` because of this")] | |
| 3796 | pub span: Span, | |
| 3797 | pub article: &'static str, | |
| 3798 | pub descr: &'static str, | |
| 3799 | } | |
| 3800 | ||
| 3790 | 3801 | #[derive(Diagnostic)] |
| 3791 | 3802 | #[diag("expected `::`, found `:`")] |
| 3792 | 3803 | #[note("import paths are delimited using `::`")] |
compiler/rustc_parse/src/parser/item.rs+24-11| ... | ... | @@ -206,14 +206,24 @@ impl<'a> Parser<'a> { |
| 206 | 206 | }) |
| 207 | 207 | } |
| 208 | 208 | |
| 209 | /// Error in-case `default` was parsed in an in-appropriate context. | |
| 209 | /// Error in-case `default`/`final` was parsed in an in-appropriate context. | |
| 210 | 210 | fn error_on_unconsumed_default(&self, def: Defaultness, kind: &ItemKind) { |
| 211 | if let Defaultness::Default(span) = def { | |
| 212 | self.dcx().emit_err(errors::InappropriateDefault { | |
| 213 | span, | |
| 214 | article: kind.article(), | |
| 215 | descr: kind.descr(), | |
| 216 | }); | |
| 211 | match def { | |
| 212 | Defaultness::Default(span) => { | |
| 213 | self.dcx().emit_err(errors::InappropriateDefault { | |
| 214 | span, | |
| 215 | article: kind.article(), | |
| 216 | descr: kind.descr(), | |
| 217 | }); | |
| 218 | } | |
| 219 | Defaultness::Final(span) => { | |
| 220 | self.dcx().emit_err(errors::InappropriateFinal { | |
| 221 | span, | |
| 222 | article: kind.article(), | |
| 223 | descr: kind.descr(), | |
| 224 | }); | |
| 225 | } | |
| 226 | Defaultness::Implicit => (), | |
| 217 | 227 | } |
| 218 | 228 | } |
| 219 | 229 | |
| ... | ... | @@ -229,8 +239,8 @@ impl<'a> Parser<'a> { |
| 229 | 239 | fn_parse_mode: FnParseMode, |
| 230 | 240 | case: Case, |
| 231 | 241 | ) -> PResult<'a, Option<ItemKind>> { |
| 232 | let check_pub = def == &Defaultness::Final; | |
| 233 | let mut def_ = || mem::replace(def, Defaultness::Final); | |
| 242 | let check_pub = def == &Defaultness::Implicit; | |
| 243 | let mut def_ = || mem::replace(def, Defaultness::Implicit); | |
| 234 | 244 | |
| 235 | 245 | let info = if !self.is_use_closure() && self.eat_keyword_case(exp!(Use), case) { |
| 236 | 246 | self.parse_use_item()? |
| ... | ... | @@ -1005,8 +1015,11 @@ impl<'a> Parser<'a> { |
| 1005 | 1015 | { |
| 1006 | 1016 | self.bump(); // `default` |
| 1007 | 1017 | Defaultness::Default(self.prev_token_uninterpolated_span()) |
| 1018 | } else if self.eat_keyword(exp!(Final)) { | |
| 1019 | self.psess.gated_spans.gate(sym::final_associated_functions, self.prev_token.span); | |
| 1020 | Defaultness::Final(self.prev_token_uninterpolated_span()) | |
| 1008 | 1021 | } else { |
| 1009 | Defaultness::Final | |
| 1022 | Defaultness::Implicit | |
| 1010 | 1023 | } |
| 1011 | 1024 | } |
| 1012 | 1025 | |
| ... | ... | @@ -1130,7 +1143,7 @@ impl<'a> Parser<'a> { |
| 1130 | 1143 | }) => { |
| 1131 | 1144 | self.dcx().emit_err(errors::AssociatedStaticItemNotAllowed { span }); |
| 1132 | 1145 | AssocItemKind::Const(Box::new(ConstItem { |
| 1133 | defaultness: Defaultness::Final, | |
| 1146 | defaultness: Defaultness::Implicit, | |
| 1134 | 1147 | ident, |
| 1135 | 1148 | generics: Generics::default(), |
| 1136 | 1149 | ty, |
compiler/rustc_parse/src/parser/token_type.rs+4| ... | ... | @@ -91,6 +91,7 @@ pub enum TokenType { |
| 91 | 91 | KwElse, |
| 92 | 92 | KwEnum, |
| 93 | 93 | KwExtern, |
| 94 | KwFinal, | |
| 94 | 95 | KwFn, |
| 95 | 96 | KwFor, |
| 96 | 97 | KwGen, |
| ... | ... | @@ -233,6 +234,7 @@ impl TokenType { |
| 233 | 234 | KwExtern, |
| 234 | 235 | KwFn, |
| 235 | 236 | KwFor, |
| 237 | KwFinal, | |
| 236 | 238 | KwGen, |
| 237 | 239 | KwIf, |
| 238 | 240 | KwImpl, |
| ... | ... | @@ -309,6 +311,7 @@ impl TokenType { |
| 309 | 311 | TokenType::KwExtern => Some(kw::Extern), |
| 310 | 312 | TokenType::KwFn => Some(kw::Fn), |
| 311 | 313 | TokenType::KwFor => Some(kw::For), |
| 314 | TokenType::KwFinal => Some(kw::Final), | |
| 312 | 315 | TokenType::KwGen => Some(kw::Gen), |
| 313 | 316 | TokenType::KwIf => Some(kw::If), |
| 314 | 317 | TokenType::KwImpl => Some(kw::Impl), |
| ... | ... | @@ -524,6 +527,7 @@ macro_rules! exp { |
| 524 | 527 | (Extern) => { exp!(@kw, Extern, KwExtern) }; |
| 525 | 528 | (Fn) => { exp!(@kw, Fn, KwFn) }; |
| 526 | 529 | (For) => { exp!(@kw, For, KwFor) }; |
| 530 | (Final) => { exp!(@kw, Final, KwFinal) }; | |
| 527 | 531 | (Gen) => { exp!(@kw, Gen, KwGen) }; |
| 528 | 532 | (If) => { exp!(@kw, If, KwIf) }; |
| 529 | 533 | (Impl) => { exp!(@kw, Impl, KwImpl) }; |
compiler/rustc_passes/src/check_attr.rs+1-1| ... | ... | @@ -246,6 +246,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { |
| 246 | 246 | | AttributeKind::CrateName { .. } |
| 247 | 247 | | AttributeKind::CrateType(..) |
| 248 | 248 | | AttributeKind::DebuggerVisualizer(..) |
| 249 | | AttributeKind::DefaultLibAllocator | |
| 249 | 250 | // `#[doc]` is actually a lot more than just doc comments, so is checked below |
| 250 | 251 | | AttributeKind::DocComment {..} |
| 251 | 252 | | AttributeKind::EiiDeclaration { .. } |
| ... | ... | @@ -399,7 +400,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { |
| 399 | 400 | | sym::deny |
| 400 | 401 | | sym::forbid |
| 401 | 402 | // internal |
| 402 | | sym::default_lib_allocator | |
| 403 | 403 | | sym::rustc_inherit_overflow_checks |
| 404 | 404 | | sym::rustc_on_unimplemented |
| 405 | 405 | | sym::rustc_doc_primitive |
compiler/rustc_query_system/Cargo.toml deleted-19| ... | ... | @@ -1,19 +0,0 @@ |
| 1 | [package] | |
| 2 | name = "rustc_query_system" | |
| 3 | version = "0.0.0" | |
| 4 | edition = "2024" | |
| 5 | ||
| 6 | [dependencies] | |
| 7 | # tidy-alphabetical-start | |
| 8 | rustc_abi = { path = "../rustc_abi" } | |
| 9 | rustc_ast = { path = "../rustc_ast" } | |
| 10 | rustc_data_structures = { path = "../rustc_data_structures" } | |
| 11 | rustc_errors = { path = "../rustc_errors" } | |
| 12 | rustc_feature = { path = "../rustc_feature" } | |
| 13 | rustc_hir = { path = "../rustc_hir" } | |
| 14 | rustc_macros = { path = "../rustc_macros" } | |
| 15 | rustc_serialize = { path = "../rustc_serialize" } | |
| 16 | rustc_session = { path = "../rustc_session" } | |
| 17 | rustc_span = { path = "../rustc_span" } | |
| 18 | smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } | |
| 19 | # tidy-alphabetical-end |
compiler/rustc_query_system/src/ich/hcx.rs deleted-196| ... | ... | @@ -1,196 +0,0 @@ |
| 1 | use std::hash::Hash; | |
| 2 | ||
| 3 | use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher}; | |
| 4 | use rustc_hir::def_id::{DefId, LocalDefId}; | |
| 5 | use rustc_hir::definitions::DefPathHash; | |
| 6 | use rustc_session::Session; | |
| 7 | use rustc_session::cstore::Untracked; | |
| 8 | use rustc_span::source_map::SourceMap; | |
| 9 | use rustc_span::{CachingSourceMapView, DUMMY_SP, Pos, Span}; | |
| 10 | ||
| 11 | // Very often, we are hashing something that does not need the `CachingSourceMapView`, so we | |
| 12 | // initialize it lazily. | |
| 13 | #[derive(Clone)] | |
| 14 | enum CachingSourceMap<'a> { | |
| 15 | Unused(&'a SourceMap), | |
| 16 | InUse(CachingSourceMapView<'a>), | |
| 17 | } | |
| 18 | ||
| 19 | /// This is the context state available during incr. comp. hashing. It contains | |
| 20 | /// enough information to transform `DefId`s and `HirId`s into stable `DefPath`s (i.e., | |
| 21 | /// a reference to the `TyCtxt`) and it holds a few caches for speeding up various | |
| 22 | /// things (e.g., each `DefId`/`DefPath` is only hashed once). | |
| 23 | #[derive(Clone)] | |
| 24 | pub struct StableHashingContext<'a> { | |
| 25 | untracked: &'a Untracked, | |
| 26 | // The value of `-Z incremental-ignore-spans`. | |
| 27 | // This field should only be used by `unstable_opts_incremental_ignore_span` | |
| 28 | incremental_ignore_spans: bool, | |
| 29 | caching_source_map: CachingSourceMap<'a>, | |
| 30 | hashing_controls: HashingControls, | |
| 31 | } | |
| 32 | ||
| 33 | impl<'a> StableHashingContext<'a> { | |
| 34 | #[inline] | |
| 35 | pub fn new(sess: &'a Session, untracked: &'a Untracked) -> Self { | |
| 36 | let hash_spans_initial = !sess.opts.unstable_opts.incremental_ignore_spans; | |
| 37 | ||
| 38 | StableHashingContext { | |
| 39 | untracked, | |
| 40 | incremental_ignore_spans: sess.opts.unstable_opts.incremental_ignore_spans, | |
| 41 | caching_source_map: CachingSourceMap::Unused(sess.source_map()), | |
| 42 | hashing_controls: HashingControls { hash_spans: hash_spans_initial }, | |
| 43 | } | |
| 44 | } | |
| 45 | ||
| 46 | #[inline] | |
| 47 | pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self, hash_spans: bool, f: F) { | |
| 48 | let prev_hash_spans = self.hashing_controls.hash_spans; | |
| 49 | self.hashing_controls.hash_spans = hash_spans; | |
| 50 | f(self); | |
| 51 | self.hashing_controls.hash_spans = prev_hash_spans; | |
| 52 | } | |
| 53 | ||
| 54 | #[inline] | |
| 55 | fn source_map(&mut self) -> &mut CachingSourceMapView<'a> { | |
| 56 | match self.caching_source_map { | |
| 57 | CachingSourceMap::InUse(ref mut sm) => sm, | |
| 58 | CachingSourceMap::Unused(sm) => { | |
| 59 | self.caching_source_map = CachingSourceMap::InUse(CachingSourceMapView::new(sm)); | |
| 60 | self.source_map() // this recursive call will hit the `InUse` case | |
| 61 | } | |
| 62 | } | |
| 63 | } | |
| 64 | ||
| 65 | #[inline] | |
| 66 | fn def_span(&self, def_id: LocalDefId) -> Span { | |
| 67 | self.untracked.source_span.get(def_id).unwrap_or(DUMMY_SP) | |
| 68 | } | |
| 69 | ||
| 70 | #[inline] | |
| 71 | pub fn hashing_controls(&self) -> HashingControls { | |
| 72 | self.hashing_controls | |
| 73 | } | |
| 74 | } | |
| 75 | ||
| 76 | impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { | |
| 77 | /// Hashes a span in a stable way. We can't directly hash the span's `BytePos` fields (that | |
| 78 | /// would be similar to hashing pointers, since those are just offsets into the `SourceMap`). | |
| 79 | /// Instead, we hash the (file name, line, column) triple, which stays the same even if the | |
| 80 | /// containing `SourceFile` has moved within the `SourceMap`. | |
| 81 | /// | |
| 82 | /// Also note that we are hashing byte offsets for the column, not unicode codepoint offsets. | |
| 83 | /// For the purpose of the hash that's sufficient. Also, hashing filenames is expensive so we | |
| 84 | /// avoid doing it twice when the span starts and ends in the same file, which is almost always | |
| 85 | /// the case. | |
| 86 | /// | |
| 87 | /// IMPORTANT: changes to this method should be reflected in implementations of `SpanEncoder`. | |
| 88 | #[inline] | |
| 89 | fn span_hash_stable(&mut self, span: Span, hasher: &mut StableHasher) { | |
| 90 | const TAG_VALID_SPAN: u8 = 0; | |
| 91 | const TAG_INVALID_SPAN: u8 = 1; | |
| 92 | const TAG_RELATIVE_SPAN: u8 = 2; | |
| 93 | ||
| 94 | if !self.hashing_controls().hash_spans { | |
| 95 | return; | |
| 96 | } | |
| 97 | ||
| 98 | let span = span.data_untracked(); | |
| 99 | span.ctxt.hash_stable(self, hasher); | |
| 100 | span.parent.hash_stable(self, hasher); | |
| 101 | ||
| 102 | if span.is_dummy() { | |
| 103 | Hash::hash(&TAG_INVALID_SPAN, hasher); | |
| 104 | return; | |
| 105 | } | |
| 106 | ||
| 107 | let parent = span.parent.map(|parent| self.def_span(parent).data_untracked()); | |
| 108 | if let Some(parent) = parent | |
| 109 | && parent.contains(span) | |
| 110 | { | |
| 111 | // This span is enclosed in a definition: only hash the relative position. This catches | |
| 112 | // a subset of the cases from the `file.contains(parent.lo)`. But we can do this check | |
| 113 | // cheaply without the expensive `span_data_to_lines_and_cols` query. | |
| 114 | Hash::hash(&TAG_RELATIVE_SPAN, hasher); | |
| 115 | (span.lo - parent.lo).to_u32().hash_stable(self, hasher); | |
| 116 | (span.hi - parent.lo).to_u32().hash_stable(self, hasher); | |
| 117 | return; | |
| 118 | } | |
| 119 | ||
| 120 | // If this is not an empty or invalid span, we want to hash the last position that belongs | |
| 121 | // to it, as opposed to hashing the first position past it. | |
| 122 | let Some((file, line_lo, col_lo, line_hi, col_hi)) = | |
| 123 | self.source_map().span_data_to_lines_and_cols(&span) | |
| 124 | else { | |
| 125 | Hash::hash(&TAG_INVALID_SPAN, hasher); | |
| 126 | return; | |
| 127 | }; | |
| 128 | ||
| 129 | if let Some(parent) = parent | |
| 130 | && file.contains(parent.lo) | |
| 131 | { | |
| 132 | // This span is relative to another span in the same file, | |
| 133 | // only hash the relative position. | |
| 134 | Hash::hash(&TAG_RELATIVE_SPAN, hasher); | |
| 135 | Hash::hash(&(span.lo.0.wrapping_sub(parent.lo.0)), hasher); | |
| 136 | Hash::hash(&(span.hi.0.wrapping_sub(parent.lo.0)), hasher); | |
| 137 | return; | |
| 138 | } | |
| 139 | ||
| 140 | Hash::hash(&TAG_VALID_SPAN, hasher); | |
| 141 | Hash::hash(&file.stable_id, hasher); | |
| 142 | ||
| 143 | // Hash both the length and the end location (line/column) of a span. If we hash only the | |
| 144 | // length, for example, then two otherwise equal spans with different end locations will | |
| 145 | // have the same hash. This can cause a problem during incremental compilation wherein a | |
| 146 | // previous result for a query that depends on the end location of a span will be | |
| 147 | // incorrectly reused when the end location of the span it depends on has changed (see | |
| 148 | // issue #74890). A similar analysis applies if some query depends specifically on the | |
| 149 | // length of the span, but we only hash the end location. So hash both. | |
| 150 | ||
| 151 | let col_lo_trunc = (col_lo.0 as u64) & 0xFF; | |
| 152 | let line_lo_trunc = ((line_lo as u64) & 0xFF_FF_FF) << 8; | |
| 153 | let col_hi_trunc = (col_hi.0 as u64) & 0xFF << 32; | |
| 154 | let line_hi_trunc = ((line_hi as u64) & 0xFF_FF_FF) << 40; | |
| 155 | let col_line = col_lo_trunc | line_lo_trunc | col_hi_trunc | line_hi_trunc; | |
| 156 | let len = (span.hi - span.lo).0; | |
| 157 | Hash::hash(&col_line, hasher); | |
| 158 | Hash::hash(&len, hasher); | |
| 159 | } | |
| 160 | ||
| 161 | #[inline] | |
| 162 | fn def_path_hash(&self, def_id: DefId) -> DefPathHash { | |
| 163 | if let Some(def_id) = def_id.as_local() { | |
| 164 | self.untracked.definitions.read().def_path_hash(def_id) | |
| 165 | } else { | |
| 166 | self.untracked.cstore.read().def_path_hash(def_id) | |
| 167 | } | |
| 168 | } | |
| 169 | ||
| 170 | /// Assert that the provided `HashStableContext` is configured with the default | |
| 171 | /// `HashingControls`. We should always have bailed out before getting to here with a | |
| 172 | /// non-default mode. With this check in place, we can avoid the need to maintain separate | |
| 173 | /// versions of `ExpnData` hashes for each permutation of `HashingControls` settings. | |
| 174 | #[inline] | |
| 175 | fn assert_default_hashing_controls(&self, msg: &str) { | |
| 176 | let hashing_controls = self.hashing_controls; | |
| 177 | let HashingControls { hash_spans } = hashing_controls; | |
| 178 | ||
| 179 | // Note that we require that `hash_spans` be the inverse of the global `-Z | |
| 180 | // incremental-ignore-spans` option. Normally, this option is disabled, in which case | |
| 181 | // `hash_spans` must be true. | |
| 182 | // | |
| 183 | // Span hashing can also be disabled without `-Z incremental-ignore-spans`. This is the | |
| 184 | // case for instance when building a hash for name mangling. Such configuration must not be | |
| 185 | // used for metadata. | |
| 186 | assert_eq!( | |
| 187 | hash_spans, !self.incremental_ignore_spans, | |
| 188 | "Attempted hashing of {msg} with non-default HashingControls: {hashing_controls:?}" | |
| 189 | ); | |
| 190 | } | |
| 191 | } | |
| 192 | ||
| 193 | impl<'a> rustc_abi::HashStableContext for StableHashingContext<'a> {} | |
| 194 | impl<'a> rustc_ast::HashStableContext for StableHashingContext<'a> {} | |
| 195 | impl<'a> rustc_hir::HashStableContext for StableHashingContext<'a> {} | |
| 196 | impl<'a> rustc_session::HashStableContext for StableHashingContext<'a> {} |
compiler/rustc_query_system/src/ich/impls_syntax.rs deleted-130| ... | ... | @@ -1,130 +0,0 @@ |
| 1 | //! This module contains `HashStable` implementations for various data types | |
| 2 | //! from various crates in no particular order. | |
| 3 | ||
| 4 | use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; | |
| 5 | use rustc_span::{SourceFile, Symbol, sym}; | |
| 6 | use smallvec::SmallVec; | |
| 7 | use {rustc_ast as ast, rustc_hir as hir}; | |
| 8 | ||
| 9 | use crate::ich::StableHashingContext; | |
| 10 | ||
| 11 | impl<'a> HashStable<StableHashingContext<'a>> for ast::NodeId { | |
| 12 | #[inline] | |
| 13 | fn hash_stable(&self, _: &mut StableHashingContext<'a>, _: &mut StableHasher) { | |
| 14 | panic!("Node IDs should not appear in incremental state"); | |
| 15 | } | |
| 16 | } | |
| 17 | ||
| 18 | impl<'a> HashStable<StableHashingContext<'a>> for [hir::Attribute] { | |
| 19 | fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { | |
| 20 | if self.is_empty() { | |
| 21 | self.len().hash_stable(hcx, hasher); | |
| 22 | return; | |
| 23 | } | |
| 24 | ||
| 25 | // Some attributes are always ignored during hashing. | |
| 26 | let filtered: SmallVec<[&hir::Attribute; 8]> = self | |
| 27 | .iter() | |
| 28 | .filter(|attr| { | |
| 29 | attr.is_doc_comment().is_none() | |
| 30 | // FIXME(jdonszelmann) have a better way to handle ignored attrs | |
| 31 | && !attr.name().is_some_and(|ident| is_ignored_attr(ident)) | |
| 32 | }) | |
| 33 | .collect(); | |
| 34 | ||
| 35 | filtered.len().hash_stable(hcx, hasher); | |
| 36 | for attr in filtered { | |
| 37 | attr.hash_stable(hcx, hasher); | |
| 38 | } | |
| 39 | } | |
| 40 | } | |
| 41 | ||
| 42 | #[inline] | |
| 43 | fn is_ignored_attr(name: Symbol) -> bool { | |
| 44 | const IGNORED_ATTRIBUTES: &[Symbol] = &[ | |
| 45 | sym::cfg_trace, // FIXME(#138844) should this really be ignored? | |
| 46 | sym::rustc_if_this_changed, | |
| 47 | sym::rustc_then_this_would_need, | |
| 48 | sym::rustc_clean, | |
| 49 | sym::rustc_partition_reused, | |
| 50 | sym::rustc_partition_codegened, | |
| 51 | sym::rustc_expected_cgu_reuse, | |
| 52 | ]; | |
| 53 | IGNORED_ATTRIBUTES.contains(&name) | |
| 54 | } | |
| 55 | ||
| 56 | impl<'a> HashStable<StableHashingContext<'a>> for SourceFile { | |
| 57 | fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { | |
| 58 | let SourceFile { | |
| 59 | name: _, // We hash the smaller stable_id instead of this | |
| 60 | stable_id, | |
| 61 | cnum, | |
| 62 | // Do not hash the source as it is not encoded | |
| 63 | src: _, | |
| 64 | ref src_hash, | |
| 65 | // Already includes src_hash, this is redundant | |
| 66 | checksum_hash: _, | |
| 67 | external_src: _, | |
| 68 | start_pos: _, | |
| 69 | normalized_source_len: _, | |
| 70 | unnormalized_source_len: _, | |
| 71 | lines: _, | |
| 72 | ref multibyte_chars, | |
| 73 | ref normalized_pos, | |
| 74 | } = *self; | |
| 75 | ||
| 76 | stable_id.hash_stable(hcx, hasher); | |
| 77 | ||
| 78 | src_hash.hash_stable(hcx, hasher); | |
| 79 | ||
| 80 | { | |
| 81 | // We are always in `Lines` form by the time we reach here. | |
| 82 | assert!(self.lines.read().is_lines()); | |
| 83 | let lines = self.lines(); | |
| 84 | // We only hash the relative position within this source_file | |
| 85 | lines.len().hash_stable(hcx, hasher); | |
| 86 | for &line in lines.iter() { | |
| 87 | line.hash_stable(hcx, hasher); | |
| 88 | } | |
| 89 | } | |
| 90 | ||
| 91 | // We only hash the relative position within this source_file | |
| 92 | multibyte_chars.len().hash_stable(hcx, hasher); | |
| 93 | for &char_pos in multibyte_chars.iter() { | |
| 94 | char_pos.hash_stable(hcx, hasher); | |
| 95 | } | |
| 96 | ||
| 97 | normalized_pos.len().hash_stable(hcx, hasher); | |
| 98 | for &char_pos in normalized_pos.iter() { | |
| 99 | char_pos.hash_stable(hcx, hasher); | |
| 100 | } | |
| 101 | ||
| 102 | cnum.hash_stable(hcx, hasher); | |
| 103 | } | |
| 104 | } | |
| 105 | ||
| 106 | impl<'tcx> HashStable<StableHashingContext<'tcx>> for rustc_feature::Features { | |
| 107 | fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { | |
| 108 | // Unfortunately we cannot exhaustively list fields here, since the | |
| 109 | // struct has private fields (to ensure its invariant is maintained) | |
| 110 | self.enabled_lang_features().hash_stable(hcx, hasher); | |
| 111 | self.enabled_lib_features().hash_stable(hcx, hasher); | |
| 112 | } | |
| 113 | } | |
| 114 | ||
| 115 | impl<'tcx> HashStable<StableHashingContext<'tcx>> for rustc_feature::EnabledLangFeature { | |
| 116 | fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { | |
| 117 | let rustc_feature::EnabledLangFeature { gate_name, attr_sp, stable_since } = self; | |
| 118 | gate_name.hash_stable(hcx, hasher); | |
| 119 | attr_sp.hash_stable(hcx, hasher); | |
| 120 | stable_since.hash_stable(hcx, hasher); | |
| 121 | } | |
| 122 | } | |
| 123 | ||
| 124 | impl<'tcx> HashStable<StableHashingContext<'tcx>> for rustc_feature::EnabledLibFeature { | |
| 125 | fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { | |
| 126 | let rustc_feature::EnabledLibFeature { gate_name, attr_sp } = self; | |
| 127 | gate_name.hash_stable(hcx, hasher); | |
| 128 | attr_sp.hash_stable(hcx, hasher); | |
| 129 | } | |
| 130 | } |
compiler/rustc_query_system/src/ich/mod.rs deleted-6| ... | ... | @@ -1,6 +0,0 @@ |
| 1 | //! ICH - Incremental Compilation Hash | |
| 2 | ||
| 3 | pub use self::hcx::StableHashingContext; | |
| 4 | ||
| 5 | mod hcx; | |
| 6 | mod impls_syntax; |
compiler/rustc_query_system/src/lib.rs deleted-8| ... | ... | @@ -1,8 +0,0 @@ |
| 1 | // tidy-alphabetical-start | |
| 2 | #![allow(internal_features)] | |
| 3 | #![cfg_attr(bootstrap, feature(assert_matches))] | |
| 4 | #![feature(min_specialization)] | |
| 5 | // tidy-alphabetical-end | |
| 6 | ||
| 7 | pub mod ich; | |
| 8 | pub mod query; |
compiler/rustc_query_system/src/query/README.md deleted-3| ... | ... | @@ -1,3 +0,0 @@ |
| 1 | For more information about how the query system works, see the [rustc dev guide]. | |
| 2 | ||
| 3 | [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/query.html |
compiler/rustc_query_system/src/query/mod.rs deleted-22| ... | ... | @@ -1,22 +0,0 @@ |
| 1 | use std::fmt::Debug; | |
| 2 | ||
| 3 | use rustc_errors::DiagInner; | |
| 4 | use rustc_macros::{Decodable, Encodable}; | |
| 5 | ||
| 6 | /// Tracks 'side effects' for a particular query. | |
| 7 | /// This struct is saved to disk along with the query result, | |
| 8 | /// and loaded from disk if we mark the query as green. | |
| 9 | /// This allows us to 'replay' changes to global state | |
| 10 | /// that would otherwise only occur if we actually | |
| 11 | /// executed the query method. | |
| 12 | /// | |
| 13 | /// Each side effect gets an unique dep node index which is added | |
| 14 | /// as a dependency of the query which had the effect. | |
| 15 | #[derive(Debug, Encodable, Decodable)] | |
| 16 | pub enum QuerySideEffect { | |
| 17 | /// Stores a diagnostic emitted during query execution. | |
| 18 | /// This diagnostic will be re-emitted if we mark | |
| 19 | /// the query as green, as that query will have the side | |
| 20 | /// effect dep node as a dependency. | |
| 21 | Diagnostic(DiagInner), | |
| 22 | } |
compiler/rustc_span/src/symbol.rs+1| ... | ... | @@ -1096,6 +1096,7 @@ symbols! { |
| 1096 | 1096 | fields, |
| 1097 | 1097 | file, |
| 1098 | 1098 | file_options, |
| 1099 | final_associated_functions, | |
| 1099 | 1100 | flags, |
| 1100 | 1101 | float, |
| 1101 | 1102 | float_to_int_unchecked, |
compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs+21-13| ... | ... | @@ -282,23 +282,31 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { |
| 282 | 282 | if self.tcx.is_diagnostic_item(sym::From, trait_def_id) |
| 283 | 283 | || self.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id) |
| 284 | 284 | { |
| 285 | let found_ty = leaf_trait_predicate.skip_binder().trait_ref.args.type_at(1); | |
| 286 | let ty = main_trait_predicate.skip_binder().self_ty(); | |
| 287 | if let Some(cast_ty) = self.find_explicit_cast_type( | |
| 288 | obligation.param_env, | |
| 289 | found_ty, | |
| 290 | ty, | |
| 291 | ) { | |
| 292 | let found_ty_str = self.tcx.short_string(found_ty, &mut long_ty_file); | |
| 293 | let cast_ty_str = self.tcx.short_string(cast_ty, &mut long_ty_file); | |
| 294 | err.help( | |
| 295 | format!( | |
| 285 | let trait_ref = leaf_trait_predicate.skip_binder().trait_ref; | |
| 286 | ||
| 287 | // Defensive: next-solver may produce fewer args than expected. | |
| 288 | if trait_ref.args.len() > 1 { | |
| 289 | let found_ty = trait_ref.args.type_at(1); | |
| 290 | let ty = main_trait_predicate.skip_binder().self_ty(); | |
| 291 | ||
| 292 | if let Some(cast_ty) = self.find_explicit_cast_type( | |
| 293 | obligation.param_env, | |
| 294 | found_ty, | |
| 295 | ty, | |
| 296 | ) { | |
| 297 | let found_ty_str = | |
| 298 | self.tcx.short_string(found_ty, &mut long_ty_file); | |
| 299 | let cast_ty_str = | |
| 300 | self.tcx.short_string(cast_ty, &mut long_ty_file); | |
| 301 | ||
| 302 | err.help(format!( | |
| 296 | 303 | "consider casting the `{found_ty_str}` value to `{cast_ty_str}`", |
| 297 | ), | |
| 298 | ); | |
| 304 | )); | |
| 305 | } | |
| 299 | 306 | } |
| 300 | 307 | } |
| 301 | 308 | |
| 309 | ||
| 302 | 310 | *err.long_ty_path() = long_ty_file; |
| 303 | 311 | |
| 304 | 312 | let mut suggested = false; |
compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs+10-8| ... | ... | @@ -5608,15 +5608,17 @@ pub(super) fn get_explanation_based_on_obligation<'tcx>( |
| 5608 | 5608 | None => String::new(), |
| 5609 | 5609 | }; |
| 5610 | 5610 | if let ty::PredicatePolarity::Positive = trait_predicate.polarity() { |
| 5611 | // If the trait in question is unstable, mention that fact in the diagnostic. | |
| 5612 | // But if we're building with `-Zforce-unstable-if-unmarked` then _any_ trait | |
| 5613 | // not explicitly marked stable is considered unstable, so the extra text is | |
| 5614 | // unhelpful noise. See <https://github.com/rust-lang/rust/issues/152692>. | |
| 5615 | let mention_unstable = !tcx.sess.opts.unstable_opts.force_unstable_if_unmarked | |
| 5616 | && try { tcx.lookup_stability(trait_predicate.def_id())?.level.is_stable() } | |
| 5617 | == Some(false); | |
| 5618 | let unstable = if mention_unstable { "nightly-only, unstable " } else { "" }; | |
| 5619 | ||
| 5611 | 5620 | format!( |
| 5612 | "{pre_message}the {}trait `{}` is not implemented for{desc} `{}`", | |
| 5613 | if tcx.lookup_stability(trait_predicate.def_id()).map(|s| s.level.is_stable()) | |
| 5614 | == Some(false) | |
| 5615 | { | |
| 5616 | "nightly-only, unstable " | |
| 5617 | } else { | |
| 5618 | "" | |
| 5619 | }, | |
| 5621 | "{pre_message}the {unstable}trait `{}` is not implemented for{desc} `{}`", | |
| 5620 | 5622 | trait_predicate.print_modifiers_and_trait_path(), |
| 5621 | 5623 | tcx.short_string(trait_predicate.self_ty().skip_binder(), long_ty_path), |
| 5622 | 5624 | ) |
compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs+5| ... | ... | @@ -314,6 +314,11 @@ pub fn dyn_compatibility_violations_for_assoc_item( |
| 314 | 314 | trait_def_id: DefId, |
| 315 | 315 | item: ty::AssocItem, |
| 316 | 316 | ) -> Vec<DynCompatibilityViolation> { |
| 317 | // `final` assoc functions don't prevent a trait from being dyn-compatible | |
| 318 | if tcx.defaultness(item.def_id).is_final() { | |
| 319 | return Vec::new(); | |
| 320 | } | |
| 321 | ||
| 317 | 322 | // Any item that has a `Self: Sized` requisite is otherwise exempt from the regulations. |
| 318 | 323 | if tcx.generics_require_sized_self(item.def_id) { |
| 319 | 324 | return Vec::new(); |
compiler/rustc_trait_selection/src/traits/vtable.rs+5| ... | ... | @@ -210,6 +210,11 @@ fn own_existential_vtable_entries_iter( |
| 210 | 210 | debug!("own_existential_vtable_entry: trait_method={:?}", trait_method); |
| 211 | 211 | let def_id = trait_method.def_id; |
| 212 | 212 | |
| 213 | // Final methods should not be included in the vtable. | |
| 214 | if trait_method.defaultness(tcx).is_final() { | |
| 215 | return None; | |
| 216 | } | |
| 217 | ||
| 213 | 218 | // Some methods cannot be called on an object; skip those. |
| 214 | 219 | if !is_vtable_safe_method(tcx, trait_def_id, trait_method) { |
| 215 | 220 | debug!("own_existential_vtable_entry: not vtable safe"); |
compiler/rustc_ty_utils/src/instance.rs+6| ... | ... | @@ -237,6 +237,12 @@ fn resolve_associated_item<'tcx>( |
| 237 | 237 | } |
| 238 | 238 | traits::ImplSource::Builtin(BuiltinImplSource::Object(_), _) => { |
| 239 | 239 | let trait_ref = ty::TraitRef::from_assoc(tcx, trait_id, rcvr_args); |
| 240 | ||
| 241 | // `final` methods should be called directly. | |
| 242 | if tcx.defaultness(trait_item_id).is_final() { | |
| 243 | return Ok(Some(ty::Instance::new_raw(trait_item_id, rcvr_args))); | |
| 244 | } | |
| 245 | ||
| 240 | 246 | if trait_ref.has_non_region_infer() || trait_ref.has_non_region_param() { |
| 241 | 247 | // We only resolve totally substituted vtable entries. |
| 242 | 248 | None |
compiler/rustc_type_ir/src/predicate.rs+11-2| ... | ... | @@ -42,7 +42,9 @@ where |
| 42 | 42 | } |
| 43 | 43 | } |
| 44 | 44 | |
| 45 | /// A complete reference to a trait. These take numerous guises in syntax, | |
| 45 | /// A complete reference to a trait. | |
| 46 | /// | |
| 47 | /// These take numerous guises in syntax, | |
| 46 | 48 | /// but perhaps the most recognizable form is in a where-clause: |
| 47 | 49 | /// ```ignore (illustrative) |
| 48 | 50 | /// T: Foo<U> |
| ... | ... | @@ -241,7 +243,9 @@ impl ImplPolarity { |
| 241 | 243 | } |
| 242 | 244 | } |
| 243 | 245 | |
| 244 | /// Polarity for a trait predicate. May either be negative or positive. | |
| 246 | /// Polarity for a trait predicate. | |
| 247 | /// | |
| 248 | /// May either be negative or positive. | |
| 245 | 249 | /// Distinguished from [`ImplPolarity`] since we never compute goals with |
| 246 | 250 | /// "reservation" level. |
| 247 | 251 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] |
| ... | ... | @@ -327,6 +331,7 @@ impl<I: Interner> ty::Binder<I, ExistentialPredicate<I>> { |
| 327 | 331 | } |
| 328 | 332 | |
| 329 | 333 | /// An existential reference to a trait, where `Self` is erased. |
| 334 | /// | |
| 330 | 335 | /// For example, the trait object `Trait<'a, 'b, X, Y>` is: |
| 331 | 336 | /// ```ignore (illustrative) |
| 332 | 337 | /// exists T. T: Trait<'a, 'b, X, Y> |
| ... | ... | @@ -442,6 +447,7 @@ impl<I: Interner> ExistentialProjection<I> { |
| 442 | 447 | } |
| 443 | 448 | |
| 444 | 449 | /// Extracts the underlying existential trait reference from this projection. |
| 450 | /// | |
| 445 | 451 | /// For example, if this is a projection of `exists T. <T as Iterator>::Item == X`, |
| 446 | 452 | /// then this function would return an `exists T. T: Iterator` existential trait |
| 447 | 453 | /// reference. |
| ... | ... | @@ -493,14 +499,17 @@ impl<I: Interner> ty::Binder<I, ExistentialProjection<I>> { |
| 493 | 499 | #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] |
| 494 | 500 | pub enum AliasTermKind { |
| 495 | 501 | /// A projection `<Type as Trait>::AssocType`. |
| 502 | /// | |
| 496 | 503 | /// Can get normalized away if monomorphic enough. |
| 497 | 504 | ProjectionTy, |
| 498 | 505 | /// An associated type in an inherent `impl` |
| 499 | 506 | InherentTy, |
| 500 | 507 | /// An opaque type (usually from `impl Trait` in type aliases or function return types) |
| 508 | /// | |
| 501 | 509 | /// Can only be normalized away in PostAnalysis mode or its defining scope. |
| 502 | 510 | OpaqueTy, |
| 503 | 511 | /// A free type alias that actually checks its trait bounds. |
| 512 | /// | |
| 504 | 513 | /// Currently only used if the type alias references opaque types. |
| 505 | 514 | /// Can always be normalized away. |
| 506 | 515 | FreeTy, |
compiler/rustc_type_ir/src/ty_kind.rs+25-9| ... | ... | @@ -29,14 +29,17 @@ mod closure; |
| 29 | 29 | )] |
| 30 | 30 | pub enum AliasTyKind { |
| 31 | 31 | /// A projection `<Type as Trait>::AssocType`. |
| 32 | /// | |
| 32 | 33 | /// Can get normalized away if monomorphic enough. |
| 33 | 34 | Projection, |
| 34 | 35 | /// An associated type in an inherent `impl` |
| 35 | 36 | Inherent, |
| 36 | 37 | /// An opaque type (usually from `impl Trait` in type aliases or function return types) |
| 38 | /// | |
| 37 | 39 | /// Can only be normalized away in PostAnalysis mode or its defining scope. |
| 38 | 40 | Opaque, |
| 39 | 41 | /// A type alias that actually checks its trait bounds. |
| 42 | /// | |
| 40 | 43 | /// Currently only used if the type alias references opaque types. |
| 41 | 44 | /// Can always be normalized away. |
| 42 | 45 | Free, |
| ... | ... | @@ -99,7 +102,9 @@ pub enum TyKind<I: Interner> { |
| 99 | 102 | /// An array with the given length. Written as `[T; N]`. |
| 100 | 103 | Array(I::Ty, I::Const), |
| 101 | 104 | |
| 102 | /// A pattern newtype. Takes any type and restricts its valid values to its pattern. | |
| 105 | /// A pattern newtype. | |
| 106 | /// | |
| 107 | /// Takes any type and restricts its valid values to its pattern. | |
| 103 | 108 | /// This will also change the layout to take advantage of this restriction. |
| 104 | 109 | /// Only `Copy` and `Clone` will automatically get implemented for pattern types. |
| 105 | 110 | /// Auto-traits treat this as if it were an aggregate with a single nested type. |
| ... | ... | @@ -116,8 +121,9 @@ pub enum TyKind<I: Interner> { |
| 116 | 121 | /// `&'a mut T` or `&'a T`. |
| 117 | 122 | Ref(I::Region, I::Ty, Mutability), |
| 118 | 123 | |
| 119 | /// The anonymous type of a function declaration/definition. Each | |
| 120 | /// function has a unique type. | |
| 124 | /// The anonymous type of a function declaration/definition. | |
| 125 | /// | |
| 126 | /// Each function has a unique type. | |
| 121 | 127 | /// |
| 122 | 128 | /// For the function `fn foo() -> i32 { 3 }` this type would be |
| 123 | 129 | /// shown to the user as `fn() -> i32 {foo}`. |
| ... | ... | @@ -129,7 +135,9 @@ pub enum TyKind<I: Interner> { |
| 129 | 135 | /// ``` |
| 130 | 136 | FnDef(I::FunctionId, I::GenericArgs), |
| 131 | 137 | |
| 132 | /// A pointer to a function. Written as `fn() -> i32`. | |
| 138 | /// A pointer to a function. | |
| 139 | /// | |
| 140 | /// Written as `fn() -> i32`. | |
| 133 | 141 | /// |
| 134 | 142 | /// Note that both functions and closures start out as either |
| 135 | 143 | /// [FnDef] or [Closure] which can be then be coerced to this variant. |
| ... | ... | @@ -179,6 +187,7 @@ pub enum TyKind<I: Interner> { |
| 179 | 187 | Coroutine(I::CoroutineId, I::GenericArgs), |
| 180 | 188 | |
| 181 | 189 | /// A type representing the types stored inside a coroutine. |
| 190 | /// | |
| 182 | 191 | /// This should only appear as part of the `CoroutineArgs`. |
| 183 | 192 | /// |
| 184 | 193 | /// Unlike upvars, the witness can reference lifetimes from |
| ... | ... | @@ -210,6 +219,7 @@ pub enum TyKind<I: Interner> { |
| 210 | 219 | Tuple(I::Tys), |
| 211 | 220 | |
| 212 | 221 | /// A projection, opaque type, free type alias, or inherent associated type. |
| 222 | /// | |
| 213 | 223 | /// All of these types are represented as pairs of def-id and args, and can |
| 214 | 224 | /// be normalized, so they are grouped conceptually. |
| 215 | 225 | Alias(AliasTyKind, AliasTy<I>), |
| ... | ... | @@ -253,8 +263,9 @@ pub enum TyKind<I: Interner> { |
| 253 | 263 | /// inside of the type. |
| 254 | 264 | Infer(InferTy), |
| 255 | 265 | |
| 256 | /// A placeholder for a type which could not be computed; this is | |
| 257 | /// propagated to avoid useless error messages. | |
| 266 | /// A placeholder for a type which could not be computed. | |
| 267 | /// | |
| 268 | /// This is propagated to avoid useless error messages. | |
| 258 | 269 | Error(I::ErrorGuaranteed), |
| 259 | 270 | } |
| 260 | 271 | |
| ... | ... | @@ -282,7 +293,9 @@ impl<I: Interner> TyKind<I> { |
| 282 | 293 | } |
| 283 | 294 | |
| 284 | 295 | /// Returns `true` when the outermost type cannot be further normalized, |
| 285 | /// resolved, or instantiated. This includes all primitive types, but also | |
| 296 | /// resolved, or instantiated. | |
| 297 | /// | |
| 298 | /// This includes all primitive types, but also | |
| 286 | 299 | /// things like ADTs and trait objects, since even if their arguments or |
| 287 | 300 | /// nested types may be further simplified, the outermost [`ty::TyKind`] or |
| 288 | 301 | /// type constructor remains the same. |
| ... | ... | @@ -481,6 +494,7 @@ impl<I: Interner> AliasTy<I> { |
| 481 | 494 | } |
| 482 | 495 | |
| 483 | 496 | /// Extracts the underlying trait reference and own args from this projection. |
| 497 | /// | |
| 484 | 498 | /// For example, if this is a projection of `<T as StreamingIterator>::Item<'a>`, |
| 485 | 499 | /// then this function would return a `T: StreamingIterator` trait reference and |
| 486 | 500 | /// `['a]` as the own args. |
| ... | ... | @@ -490,6 +504,7 @@ impl<I: Interner> AliasTy<I> { |
| 490 | 504 | } |
| 491 | 505 | |
| 492 | 506 | /// Extracts the underlying trait reference from this projection. |
| 507 | /// | |
| 493 | 508 | /// For example, if this is a projection of `<T as Iterator>::Item`, |
| 494 | 509 | /// then this function would return a `T: Iterator` trait reference. |
| 495 | 510 | /// |
| ... | ... | @@ -593,8 +608,9 @@ pub enum InferTy { |
| 593 | 608 | FloatVar(FloatVid), |
| 594 | 609 | |
| 595 | 610 | /// A [`FreshTy`][Self::FreshTy] is one that is generated as a replacement |
| 596 | /// for an unbound type variable. This is convenient for caching etc. See | |
| 597 | /// `TypeFreshener` for more details. | |
| 611 | /// for an unbound type variable. | |
| 612 | /// | |
| 613 | /// This is convenient for caching etc. See `TypeFreshener` for more details. | |
| 598 | 614 | /// |
| 599 | 615 | /// Compare with [`TyVar`][Self::TyVar]. |
| 600 | 616 | FreshTy(u32), |
library/core/src/num/f128.rs+64| ... | ... | @@ -275,6 +275,70 @@ impl f128 { |
| 275 | 275 | #[unstable(feature = "f128", issue = "116909")] |
| 276 | 276 | pub const NEG_INFINITY: f128 = -1.0_f128 / 0.0_f128; |
| 277 | 277 | |
| 278 | /// Maximum integer that can be represented exactly in an [`f128`] value, | |
| 279 | /// with no other integer converting to the same floating point value. | |
| 280 | /// | |
| 281 | /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, | |
| 282 | /// there is a "one-to-one" mapping between [`i128`] and [`f128`] values. | |
| 283 | /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f128`] and back to | |
| 284 | /// [`i128`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f128`] value | |
| 285 | /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a | |
| 286 | /// "one-to-one" mapping. | |
| 287 | /// | |
| 288 | /// [`MAX_EXACT_INTEGER`]: f128::MAX_EXACT_INTEGER | |
| 289 | /// [`MIN_EXACT_INTEGER`]: f128::MIN_EXACT_INTEGER | |
| 290 | /// ``` | |
| 291 | /// #![feature(f128)] | |
| 292 | /// #![feature(float_exact_integer_constants)] | |
| 293 | /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 | |
| 294 | /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { | |
| 295 | /// # #[cfg(target_has_reliable_f128)] { | |
| 296 | /// let max_exact_int = f128::MAX_EXACT_INTEGER; | |
| 297 | /// assert_eq!(max_exact_int, max_exact_int as f128 as i128); | |
| 298 | /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f128 as i128); | |
| 299 | /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f128 as i128); | |
| 300 | /// | |
| 301 | /// // Beyond `f128::MAX_EXACT_INTEGER`, multiple integers can map to one float value | |
| 302 | /// assert_eq!((max_exact_int + 1) as f128, (max_exact_int + 2) as f128); | |
| 303 | /// # }} | |
| 304 | /// ``` | |
| 305 | // #[unstable(feature = "f128", issue = "116909")] | |
| 306 | #[unstable(feature = "float_exact_integer_constants", issue = "152466")] | |
| 307 | pub const MAX_EXACT_INTEGER: i128 = (1 << Self::MANTISSA_DIGITS) - 1; | |
| 308 | ||
| 309 | /// Minimum integer that can be represented exactly in an [`f128`] value, | |
| 310 | /// with no other integer converting to the same floating point value. | |
| 311 | /// | |
| 312 | /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, | |
| 313 | /// there is a "one-to-one" mapping between [`i128`] and [`f128`] values. | |
| 314 | /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f128`] and back to | |
| 315 | /// [`i128`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f128`] value | |
| 316 | /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a | |
| 317 | /// "one-to-one" mapping. | |
| 318 | /// | |
| 319 | /// This constant is equivalent to `-MAX_EXACT_INTEGER`. | |
| 320 | /// | |
| 321 | /// [`MAX_EXACT_INTEGER`]: f128::MAX_EXACT_INTEGER | |
| 322 | /// [`MIN_EXACT_INTEGER`]: f128::MIN_EXACT_INTEGER | |
| 323 | /// ``` | |
| 324 | /// #![feature(f128)] | |
| 325 | /// #![feature(float_exact_integer_constants)] | |
| 326 | /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 | |
| 327 | /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { | |
| 328 | /// # #[cfg(target_has_reliable_f128)] { | |
| 329 | /// let min_exact_int = f128::MIN_EXACT_INTEGER; | |
| 330 | /// assert_eq!(min_exact_int, min_exact_int as f128 as i128); | |
| 331 | /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f128 as i128); | |
| 332 | /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f128 as i128); | |
| 333 | /// | |
| 334 | /// // Below `f128::MIN_EXACT_INTEGER`, multiple integers can map to one float value | |
| 335 | /// assert_eq!((min_exact_int - 1) as f128, (min_exact_int - 2) as f128); | |
| 336 | /// # }} | |
| 337 | /// ``` | |
| 338 | // #[unstable(feature = "f128", issue = "116909")] | |
| 339 | #[unstable(feature = "float_exact_integer_constants", issue = "152466")] | |
| 340 | pub const MIN_EXACT_INTEGER: i128 = -Self::MAX_EXACT_INTEGER; | |
| 341 | ||
| 278 | 342 | /// Sign bit |
| 279 | 343 | pub(crate) const SIGN_MASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; |
| 280 | 344 |
library/core/src/num/f16.rs+64| ... | ... | @@ -269,6 +269,70 @@ impl f16 { |
| 269 | 269 | #[unstable(feature = "f16", issue = "116909")] |
| 270 | 270 | pub const NEG_INFINITY: f16 = -1.0_f16 / 0.0_f16; |
| 271 | 271 | |
| 272 | /// Maximum integer that can be represented exactly in an [`f16`] value, | |
| 273 | /// with no other integer converting to the same floating point value. | |
| 274 | /// | |
| 275 | /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, | |
| 276 | /// there is a "one-to-one" mapping between [`i16`] and [`f16`] values. | |
| 277 | /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f16`] and back to | |
| 278 | /// [`i16`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f16`] value | |
| 279 | /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a | |
| 280 | /// "one-to-one" mapping. | |
| 281 | /// | |
| 282 | /// [`MAX_EXACT_INTEGER`]: f16::MAX_EXACT_INTEGER | |
| 283 | /// [`MIN_EXACT_INTEGER`]: f16::MIN_EXACT_INTEGER | |
| 284 | /// ``` | |
| 285 | /// #![feature(f16)] | |
| 286 | /// #![feature(float_exact_integer_constants)] | |
| 287 | /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 | |
| 288 | /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { | |
| 289 | /// # #[cfg(target_has_reliable_f16)] { | |
| 290 | /// let max_exact_int = f16::MAX_EXACT_INTEGER; | |
| 291 | /// assert_eq!(max_exact_int, max_exact_int as f16 as i16); | |
| 292 | /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f16 as i16); | |
| 293 | /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f16 as i16); | |
| 294 | /// | |
| 295 | /// // Beyond `f16::MAX_EXACT_INTEGER`, multiple integers can map to one float value | |
| 296 | /// assert_eq!((max_exact_int + 1) as f16, (max_exact_int + 2) as f16); | |
| 297 | /// # }} | |
| 298 | /// ``` | |
| 299 | // #[unstable(feature = "f16", issue = "116909")] | |
| 300 | #[unstable(feature = "float_exact_integer_constants", issue = "152466")] | |
| 301 | pub const MAX_EXACT_INTEGER: i16 = (1 << Self::MANTISSA_DIGITS) - 1; | |
| 302 | ||
| 303 | /// Minimum integer that can be represented exactly in an [`f16`] value, | |
| 304 | /// with no other integer converting to the same floating point value. | |
| 305 | /// | |
| 306 | /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, | |
| 307 | /// there is a "one-to-one" mapping between [`i16`] and [`f16`] values. | |
| 308 | /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f16`] and back to | |
| 309 | /// [`i16`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f16`] value | |
| 310 | /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a | |
| 311 | /// "one-to-one" mapping. | |
| 312 | /// | |
| 313 | /// This constant is equivalent to `-MAX_EXACT_INTEGER`. | |
| 314 | /// | |
| 315 | /// [`MAX_EXACT_INTEGER`]: f16::MAX_EXACT_INTEGER | |
| 316 | /// [`MIN_EXACT_INTEGER`]: f16::MIN_EXACT_INTEGER | |
| 317 | /// ``` | |
| 318 | /// #![feature(f16)] | |
| 319 | /// #![feature(float_exact_integer_constants)] | |
| 320 | /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 | |
| 321 | /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { | |
| 322 | /// # #[cfg(target_has_reliable_f16)] { | |
| 323 | /// let min_exact_int = f16::MIN_EXACT_INTEGER; | |
| 324 | /// assert_eq!(min_exact_int, min_exact_int as f16 as i16); | |
| 325 | /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f16 as i16); | |
| 326 | /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f16 as i16); | |
| 327 | /// | |
| 328 | /// // Below `f16::MIN_EXACT_INTEGER`, multiple integers can map to one float value | |
| 329 | /// assert_eq!((min_exact_int - 1) as f16, (min_exact_int - 2) as f16); | |
| 330 | /// # }} | |
| 331 | /// ``` | |
| 332 | // #[unstable(feature = "f16", issue = "116909")] | |
| 333 | #[unstable(feature = "float_exact_integer_constants", issue = "152466")] | |
| 334 | pub const MIN_EXACT_INTEGER: i16 = -Self::MAX_EXACT_INTEGER; | |
| 335 | ||
| 272 | 336 | /// Sign bit |
| 273 | 337 | pub(crate) const SIGN_MASK: u16 = 0x8000; |
| 274 | 338 |
library/core/src/num/f32.rs+58| ... | ... | @@ -513,6 +513,64 @@ impl f32 { |
| 513 | 513 | #[stable(feature = "assoc_int_consts", since = "1.43.0")] |
| 514 | 514 | pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32; |
| 515 | 515 | |
| 516 | /// Maximum integer that can be represented exactly in an [`f32`] value, | |
| 517 | /// with no other integer converting to the same floating point value. | |
| 518 | /// | |
| 519 | /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, | |
| 520 | /// there is a "one-to-one" mapping between [`i32`] and [`f32`] values. | |
| 521 | /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f32`] and back to | |
| 522 | /// [`i32`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f32`] value | |
| 523 | /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a | |
| 524 | /// "one-to-one" mapping. | |
| 525 | /// | |
| 526 | /// [`MAX_EXACT_INTEGER`]: f32::MAX_EXACT_INTEGER | |
| 527 | /// [`MIN_EXACT_INTEGER`]: f32::MIN_EXACT_INTEGER | |
| 528 | /// ``` | |
| 529 | /// #![feature(float_exact_integer_constants)] | |
| 530 | /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 | |
| 531 | /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { | |
| 532 | /// let max_exact_int = f32::MAX_EXACT_INTEGER; | |
| 533 | /// assert_eq!(max_exact_int, max_exact_int as f32 as i32); | |
| 534 | /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f32 as i32); | |
| 535 | /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f32 as i32); | |
| 536 | /// | |
| 537 | /// // Beyond `f32::MAX_EXACT_INTEGER`, multiple integers can map to one float value | |
| 538 | /// assert_eq!((max_exact_int + 1) as f32, (max_exact_int + 2) as f32); | |
| 539 | /// # } | |
| 540 | /// ``` | |
| 541 | #[unstable(feature = "float_exact_integer_constants", issue = "152466")] | |
| 542 | pub const MAX_EXACT_INTEGER: i32 = (1 << Self::MANTISSA_DIGITS) - 1; | |
| 543 | ||
| 544 | /// Minimum integer that can be represented exactly in an [`f32`] value, | |
| 545 | /// with no other integer converting to the same floating point value. | |
| 546 | /// | |
| 547 | /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, | |
| 548 | /// there is a "one-to-one" mapping between [`i32`] and [`f32`] values. | |
| 549 | /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f32`] and back to | |
| 550 | /// [`i32`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f32`] value | |
| 551 | /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a | |
| 552 | /// "one-to-one" mapping. | |
| 553 | /// | |
| 554 | /// This constant is equivalent to `-MAX_EXACT_INTEGER`. | |
| 555 | /// | |
| 556 | /// [`MAX_EXACT_INTEGER`]: f32::MAX_EXACT_INTEGER | |
| 557 | /// [`MIN_EXACT_INTEGER`]: f32::MIN_EXACT_INTEGER | |
| 558 | /// ``` | |
| 559 | /// #![feature(float_exact_integer_constants)] | |
| 560 | /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 | |
| 561 | /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { | |
| 562 | /// let min_exact_int = f32::MIN_EXACT_INTEGER; | |
| 563 | /// assert_eq!(min_exact_int, min_exact_int as f32 as i32); | |
| 564 | /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f32 as i32); | |
| 565 | /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f32 as i32); | |
| 566 | /// | |
| 567 | /// // Below `f32::MIN_EXACT_INTEGER`, multiple integers can map to one float value | |
| 568 | /// assert_eq!((min_exact_int - 1) as f32, (min_exact_int - 2) as f32); | |
| 569 | /// # } | |
| 570 | /// ``` | |
| 571 | #[unstable(feature = "float_exact_integer_constants", issue = "152466")] | |
| 572 | pub const MIN_EXACT_INTEGER: i32 = -Self::MAX_EXACT_INTEGER; | |
| 573 | ||
| 516 | 574 | /// Sign bit |
| 517 | 575 | pub(crate) const SIGN_MASK: u32 = 0x8000_0000; |
| 518 | 576 |
library/core/src/num/f64.rs+58| ... | ... | @@ -512,6 +512,64 @@ impl f64 { |
| 512 | 512 | #[stable(feature = "assoc_int_consts", since = "1.43.0")] |
| 513 | 513 | pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64; |
| 514 | 514 | |
| 515 | /// Maximum integer that can be represented exactly in an [`f64`] value, | |
| 516 | /// with no other integer converting to the same floating point value. | |
| 517 | /// | |
| 518 | /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, | |
| 519 | /// there is a "one-to-one" mapping between [`i64`] and [`f64`] values. | |
| 520 | /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f64`] and back to | |
| 521 | /// [`i64`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f64`] value | |
| 522 | /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a | |
| 523 | /// "one-to-one" mapping. | |
| 524 | /// | |
| 525 | /// [`MAX_EXACT_INTEGER`]: f64::MAX_EXACT_INTEGER | |
| 526 | /// [`MIN_EXACT_INTEGER`]: f64::MIN_EXACT_INTEGER | |
| 527 | /// ``` | |
| 528 | /// #![feature(float_exact_integer_constants)] | |
| 529 | /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 | |
| 530 | /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { | |
| 531 | /// let max_exact_int = f64::MAX_EXACT_INTEGER; | |
| 532 | /// assert_eq!(max_exact_int, max_exact_int as f64 as i64); | |
| 533 | /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f64 as i64); | |
| 534 | /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f64 as i64); | |
| 535 | /// | |
| 536 | /// // Beyond `f64::MAX_EXACT_INTEGER`, multiple integers can map to one float value | |
| 537 | /// assert_eq!((max_exact_int + 1) as f64, (max_exact_int + 2) as f64); | |
| 538 | /// # } | |
| 539 | /// ``` | |
| 540 | #[unstable(feature = "float_exact_integer_constants", issue = "152466")] | |
| 541 | pub const MAX_EXACT_INTEGER: i64 = (1 << Self::MANTISSA_DIGITS) - 1; | |
| 542 | ||
| 543 | /// Minimum integer that can be represented exactly in an [`f64`] value, | |
| 544 | /// with no other integer converting to the same floating point value. | |
| 545 | /// | |
| 546 | /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, | |
| 547 | /// there is a "one-to-one" mapping between [`i64`] and [`f64`] values. | |
| 548 | /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f64`] and back to | |
| 549 | /// [`i64`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f64`] value | |
| 550 | /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a | |
| 551 | /// "one-to-one" mapping. | |
| 552 | /// | |
| 553 | /// This constant is equivalent to `-MAX_EXACT_INTEGER`. | |
| 554 | /// | |
| 555 | /// [`MAX_EXACT_INTEGER`]: f64::MAX_EXACT_INTEGER | |
| 556 | /// [`MIN_EXACT_INTEGER`]: f64::MIN_EXACT_INTEGER | |
| 557 | /// ``` | |
| 558 | /// #![feature(float_exact_integer_constants)] | |
| 559 | /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 | |
| 560 | /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { | |
| 561 | /// let min_exact_int = f64::MIN_EXACT_INTEGER; | |
| 562 | /// assert_eq!(min_exact_int, min_exact_int as f64 as i64); | |
| 563 | /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f64 as i64); | |
| 564 | /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f64 as i64); | |
| 565 | /// | |
| 566 | /// // Below `f64::MIN_EXACT_INTEGER`, multiple integers can map to one float value | |
| 567 | /// assert_eq!((min_exact_int - 1) as f64, (min_exact_int - 2) as f64); | |
| 568 | /// # } | |
| 569 | /// ``` | |
| 570 | #[unstable(feature = "float_exact_integer_constants", issue = "152466")] | |
| 571 | pub const MIN_EXACT_INTEGER: i64 = -Self::MAX_EXACT_INTEGER; | |
| 572 | ||
| 515 | 573 | /// Sign bit |
| 516 | 574 | pub(crate) const SIGN_MASK: u64 = 0x8000_0000_0000_0000; |
| 517 | 575 |
library/coretests/tests/floats/mod.rs+93| ... | ... | @@ -5,6 +5,8 @@ trait TestableFloat: Sized { |
| 5 | 5 | const BITS: u32; |
| 6 | 6 | /// Unsigned int with the same size, for converting to/from bits. |
| 7 | 7 | type Int; |
| 8 | /// Signed int with the same size. | |
| 9 | type SInt; | |
| 8 | 10 | /// Set the default tolerance for float comparison based on the type. |
| 9 | 11 | const APPROX: Self; |
| 10 | 12 | /// Allow looser tolerance for f32 on miri |
| ... | ... | @@ -61,6 +63,7 @@ trait TestableFloat: Sized { |
| 61 | 63 | impl TestableFloat for f16 { |
| 62 | 64 | const BITS: u32 = 16; |
| 63 | 65 | type Int = u16; |
| 66 | type SInt = i16; | |
| 64 | 67 | const APPROX: Self = 1e-3; |
| 65 | 68 | const POWF_APPROX: Self = 5e-1; |
| 66 | 69 | const _180_TO_RADIANS_APPROX: Self = 1e-2; |
| ... | ... | @@ -101,6 +104,7 @@ impl TestableFloat for f16 { |
| 101 | 104 | impl TestableFloat for f32 { |
| 102 | 105 | const BITS: u32 = 32; |
| 103 | 106 | type Int = u32; |
| 107 | type SInt = i32; | |
| 104 | 108 | const APPROX: Self = 1e-6; |
| 105 | 109 | /// Miri adds some extra errors to float functions; make sure the tests still pass. |
| 106 | 110 | /// These values are purely used as a canary to test against and are thus not a stable guarantee Rust provides. |
| ... | ... | @@ -143,6 +147,7 @@ impl TestableFloat for f32 { |
| 143 | 147 | impl TestableFloat for f64 { |
| 144 | 148 | const BITS: u32 = 64; |
| 145 | 149 | type Int = u64; |
| 150 | type SInt = i64; | |
| 146 | 151 | const APPROX: Self = 1e-6; |
| 147 | 152 | const GAMMA_APPROX_LOOSE: Self = 1e-4; |
| 148 | 153 | const LNGAMMA_APPROX_LOOSE: Self = 1e-4; |
| ... | ... | @@ -170,6 +175,7 @@ impl TestableFloat for f64 { |
| 170 | 175 | impl TestableFloat for f128 { |
| 171 | 176 | const BITS: u32 = 128; |
| 172 | 177 | type Int = u128; |
| 178 | type SInt = i128; | |
| 173 | 179 | const APPROX: Self = 1e-9; |
| 174 | 180 | const EXP_APPROX: Self = 1e-12; |
| 175 | 181 | const LN_APPROX: Self = 1e-12; |
| ... | ... | @@ -2003,6 +2009,93 @@ float_test! { |
| 2003 | 2009 | } |
| 2004 | 2010 | } |
| 2005 | 2011 | |
| 2012 | // Test the `float_exact_integer_constants` feature | |
| 2013 | float_test! { | |
| 2014 | name: max_exact_integer_constant, | |
| 2015 | attrs: { | |
| 2016 | f16: #[cfg(any(miri, target_has_reliable_f16))], | |
| 2017 | f128: #[cfg(any(miri, target_has_reliable_f128))], | |
| 2018 | }, | |
| 2019 | test<Float> { | |
| 2020 | // The maximum integer that converts to a unique floating point | |
| 2021 | // value. | |
| 2022 | const MAX_EXACT_INTEGER: <Float as TestableFloat>::SInt = Float::MAX_EXACT_INTEGER; | |
| 2023 | ||
| 2024 | let max_minus_one = (MAX_EXACT_INTEGER - 1) as Float as <Float as TestableFloat>::SInt; | |
| 2025 | let max_plus_one = (MAX_EXACT_INTEGER + 1) as Float as <Float as TestableFloat>::SInt; | |
| 2026 | let max_plus_two = (MAX_EXACT_INTEGER + 2) as Float as <Float as TestableFloat>::SInt; | |
| 2027 | ||
| 2028 | // This does an extra round trip back to float for the second operand in | |
| 2029 | // order to print the results if there is a mismatch | |
| 2030 | assert_biteq!((MAX_EXACT_INTEGER - 1) as Float, max_minus_one as Float); | |
| 2031 | assert_biteq!(MAX_EXACT_INTEGER as Float, MAX_EXACT_INTEGER as Float as <Float as TestableFloat>::SInt as Float); | |
| 2032 | assert_biteq!((MAX_EXACT_INTEGER + 1) as Float, max_plus_one as Float); | |
| 2033 | // The first non-unique conversion, where `max_plus_two` roundtrips to | |
| 2034 | // `max_plus_one` | |
| 2035 | assert_biteq!((MAX_EXACT_INTEGER + 1) as Float, (MAX_EXACT_INTEGER + 2) as Float); | |
| 2036 | assert_biteq!((MAX_EXACT_INTEGER + 2) as Float, max_plus_one as Float); | |
| 2037 | assert_biteq!((MAX_EXACT_INTEGER + 2) as Float, max_plus_two as Float); | |
| 2038 | ||
| 2039 | // Lossless roundtrips, for integers | |
| 2040 | assert!(MAX_EXACT_INTEGER - 1 == max_minus_one); | |
| 2041 | assert!(MAX_EXACT_INTEGER == MAX_EXACT_INTEGER as Float as <Float as TestableFloat>::SInt); | |
| 2042 | assert!(MAX_EXACT_INTEGER + 1 == max_plus_one); | |
| 2043 | // The first non-unique conversion, where `max_plus_two` roundtrips to | |
| 2044 | // one less than the starting value | |
| 2045 | assert!(MAX_EXACT_INTEGER + 2 != max_plus_two); | |
| 2046 | ||
| 2047 | // max-1 | max+0 | max+1 | max+2 | |
| 2048 | // After roundtripping, +1 and +2 will equal each other | |
| 2049 | assert!(max_minus_one != MAX_EXACT_INTEGER); | |
| 2050 | assert!(MAX_EXACT_INTEGER != max_plus_one); | |
| 2051 | assert!(max_plus_one == max_plus_two); | |
| 2052 | } | |
| 2053 | } | |
| 2054 | ||
| 2055 | float_test! { | |
| 2056 | name: min_exact_integer_constant, | |
| 2057 | attrs: { | |
| 2058 | f16: #[cfg(any(miri, target_has_reliable_f16))], | |
| 2059 | f128: #[cfg(any(miri, target_has_reliable_f128))], | |
| 2060 | }, | |
| 2061 | test<Float> { | |
| 2062 | // The minimum integer that converts to a unique floating point | |
| 2063 | // value. | |
| 2064 | const MIN_EXACT_INTEGER: <Float as TestableFloat>::SInt = Float::MIN_EXACT_INTEGER; | |
| 2065 | ||
| 2066 | // Same logic as the `max` test, but we work our way leftward | |
| 2067 | // across the number line from (min_exact + 1) to (min_exact - 2). | |
| 2068 | let min_plus_one = (MIN_EXACT_INTEGER + 1) as Float as <Float as TestableFloat>::SInt; | |
| 2069 | let min_minus_one = (MIN_EXACT_INTEGER - 1) as Float as <Float as TestableFloat>::SInt; | |
| 2070 | let min_minus_two = (MIN_EXACT_INTEGER - 2) as Float as <Float as TestableFloat>::SInt; | |
| 2071 | ||
| 2072 | // This does an extra round trip back to float for the second operand in | |
| 2073 | // order to print the results if there is a mismatch | |
| 2074 | assert_biteq!((MIN_EXACT_INTEGER + 1) as Float, min_plus_one as Float); | |
| 2075 | assert_biteq!(MIN_EXACT_INTEGER as Float, MIN_EXACT_INTEGER as Float as <Float as TestableFloat>::SInt as Float); | |
| 2076 | assert_biteq!((MIN_EXACT_INTEGER - 1) as Float, min_minus_one as Float); | |
| 2077 | // The first non-unique conversion, which roundtrips to one | |
| 2078 | // greater than the starting value. | |
| 2079 | assert_biteq!((MIN_EXACT_INTEGER - 1) as Float, (MIN_EXACT_INTEGER - 2) as Float); | |
| 2080 | assert_biteq!((MIN_EXACT_INTEGER - 2) as Float, min_minus_one as Float); | |
| 2081 | assert_biteq!((MIN_EXACT_INTEGER - 2) as Float, min_minus_two as Float); | |
| 2082 | ||
| 2083 | // Lossless roundtrips, for integers | |
| 2084 | assert!(MIN_EXACT_INTEGER + 1 == min_plus_one); | |
| 2085 | assert!(MIN_EXACT_INTEGER == MIN_EXACT_INTEGER as Float as <Float as TestableFloat>::SInt); | |
| 2086 | assert!(MIN_EXACT_INTEGER - 1 == min_minus_one); | |
| 2087 | // The first non-unique conversion, which roundtrips to one | |
| 2088 | // greater than the starting value. | |
| 2089 | assert!(MIN_EXACT_INTEGER - 2 != min_minus_two); | |
| 2090 | ||
| 2091 | // min-2 | min-1 | min | min+1 | |
| 2092 | // After roundtripping, -2 and -1 will equal each other. | |
| 2093 | assert!(min_plus_one != MIN_EXACT_INTEGER); | |
| 2094 | assert!(MIN_EXACT_INTEGER != min_minus_one); | |
| 2095 | assert!(min_minus_one == min_minus_two); | |
| 2096 | } | |
| 2097 | } | |
| 2098 | ||
| 2006 | 2099 | // FIXME(f128): Uncomment and adapt these tests once the From<{u64,i64}> impls are added. |
| 2007 | 2100 | // float_test! { |
| 2008 | 2101 | // name: from_u64_i64, |
library/coretests/tests/lib.rs+1| ... | ... | @@ -53,6 +53,7 @@ |
| 53 | 53 | #![feature(f128)] |
| 54 | 54 | #![feature(float_algebraic)] |
| 55 | 55 | #![feature(float_bits_const)] |
| 56 | #![feature(float_exact_integer_constants)] | |
| 56 | 57 | #![feature(float_gamma)] |
| 57 | 58 | #![feature(float_minimum_maximum)] |
| 58 | 59 | #![feature(flt2dec)] |
rust-bors.toml+6-6| ... | ... | @@ -25,7 +25,7 @@ labels_blocking_approval = [ |
| 25 | 25 | "S-waiting-on-t-rustdoc-frontend", |
| 26 | 26 | "S-waiting-on-t-clippy", |
| 27 | 27 | # PR manually set to blocked |
| 28 | "S-blocked" | |
| 28 | "S-blocked", | |
| 29 | 29 | ] |
| 30 | 30 | |
| 31 | 31 | # If CI runs quicker than this duration, consider it to be a failure |
| ... | ... | @@ -41,7 +41,7 @@ approved = [ |
| 41 | 41 | "-S-waiting-on-author", |
| 42 | 42 | "-S-waiting-on-crater", |
| 43 | 43 | "-S-waiting-on-review", |
| 44 | "-S-waiting-on-team" | |
| 44 | "-S-waiting-on-team", | |
| 45 | 45 | ] |
| 46 | 46 | unapproved = [ |
| 47 | 47 | "+S-waiting-on-author", |
| ... | ... | @@ -49,16 +49,16 @@ unapproved = [ |
| 49 | 49 | "-S-waiting-on-bors", |
| 50 | 50 | "-S-waiting-on-crater", |
| 51 | 51 | "-S-waiting-on-review", |
| 52 | "-S-waiting-on-team" | |
| 52 | "-S-waiting-on-team", | |
| 53 | 53 | ] |
| 54 | 54 | try_failed = [ |
| 55 | 55 | "+S-waiting-on-author", |
| 56 | 56 | "-S-waiting-on-review", |
| 57 | "-S-waiting-on-crater" | |
| 57 | "-S-waiting-on-crater", | |
| 58 | 58 | ] |
| 59 | 59 | auto_build_succeeded = [ |
| 60 | 60 | "+merged-by-bors", |
| 61 | "-S-waiting-on-bors" | |
| 61 | "-S-waiting-on-bors", | |
| 62 | 62 | ] |
| 63 | 63 | auto_build_failed = [ |
| 64 | 64 | "+S-waiting-on-review", |
| ... | ... | @@ -66,5 +66,5 @@ auto_build_failed = [ |
| 66 | 66 | "-S-waiting-on-bors", |
| 67 | 67 | "-S-waiting-on-author", |
| 68 | 68 | "-S-waiting-on-crater", |
| 69 | "-S-waiting-on-team" | |
| 69 | "-S-waiting-on-team", | |
| 70 | 70 | ] |
src/bootstrap/src/core/builder/cli_paths/snapshots/x_bench.snap-1| ... | ... | @@ -79,7 +79,6 @@ expression: bench |
| 79 | 79 | - Set({bench::compiler/rustc_public}) |
| 80 | 80 | - Set({bench::compiler/rustc_public_bridge}) |
| 81 | 81 | - Set({bench::compiler/rustc_query_impl}) |
| 82 | - Set({bench::compiler/rustc_query_system}) | |
| 83 | 82 | - Set({bench::compiler/rustc_resolve}) |
| 84 | 83 | - Set({bench::compiler/rustc_sanitizers}) |
| 85 | 84 | - Set({bench::compiler/rustc_serialize}) |
src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_compiler.snap-1| ... | ... | @@ -61,7 +61,6 @@ expression: build compiler |
| 61 | 61 | - Set({build::compiler/rustc_public}) |
| 62 | 62 | - Set({build::compiler/rustc_public_bridge}) |
| 63 | 63 | - Set({build::compiler/rustc_query_impl}) |
| 64 | - Set({build::compiler/rustc_query_system}) | |
| 65 | 64 | - Set({build::compiler/rustc_resolve}) |
| 66 | 65 | - Set({build::compiler/rustc_sanitizers}) |
| 67 | 66 | - Set({build::compiler/rustc_serialize}) |
src/bootstrap/src/core/builder/cli_paths/snapshots/x_check.snap-1| ... | ... | @@ -63,7 +63,6 @@ expression: check |
| 63 | 63 | - Set({check::compiler/rustc_public}) |
| 64 | 64 | - Set({check::compiler/rustc_public_bridge}) |
| 65 | 65 | - Set({check::compiler/rustc_query_impl}) |
| 66 | - Set({check::compiler/rustc_query_system}) | |
| 67 | 66 | - Set({check::compiler/rustc_resolve}) |
| 68 | 67 | - Set({check::compiler/rustc_sanitizers}) |
| 69 | 68 | - Set({check::compiler/rustc_serialize}) |
src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiler.snap-1| ... | ... | @@ -63,7 +63,6 @@ expression: check compiler |
| 63 | 63 | - Set({check::compiler/rustc_public}) |
| 64 | 64 | - Set({check::compiler/rustc_public_bridge}) |
| 65 | 65 | - Set({check::compiler/rustc_query_impl}) |
| 66 | - Set({check::compiler/rustc_query_system}) | |
| 67 | 66 | - Set({check::compiler/rustc_resolve}) |
| 68 | 67 | - Set({check::compiler/rustc_sanitizers}) |
| 69 | 68 | - Set({check::compiler/rustc_serialize}) |
src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiletest_include_default_paths.snap-1| ... | ... | @@ -63,7 +63,6 @@ expression: check compiletest --include-default-paths |
| 63 | 63 | - Set({check::compiler/rustc_public}) |
| 64 | 64 | - Set({check::compiler/rustc_public_bridge}) |
| 65 | 65 | - Set({check::compiler/rustc_query_impl}) |
| 66 | - Set({check::compiler/rustc_query_system}) | |
| 67 | 66 | - Set({check::compiler/rustc_resolve}) |
| 68 | 67 | - Set({check::compiler/rustc_sanitizers}) |
| 69 | 68 | - Set({check::compiler/rustc_serialize}) |
src/bootstrap/src/core/builder/cli_paths/snapshots/x_clippy.snap-1| ... | ... | @@ -78,7 +78,6 @@ expression: clippy |
| 78 | 78 | - Set({clippy::compiler/rustc_public}) |
| 79 | 79 | - Set({clippy::compiler/rustc_public_bridge}) |
| 80 | 80 | - Set({clippy::compiler/rustc_query_impl}) |
| 81 | - Set({clippy::compiler/rustc_query_system}) | |
| 82 | 81 | - Set({clippy::compiler/rustc_resolve}) |
| 83 | 82 | - Set({clippy::compiler/rustc_sanitizers}) |
| 84 | 83 | - Set({clippy::compiler/rustc_serialize}) |
src/bootstrap/src/core/builder/cli_paths/snapshots/x_fix.snap-1| ... | ... | @@ -63,7 +63,6 @@ expression: fix |
| 63 | 63 | - Set({fix::compiler/rustc_public}) |
| 64 | 64 | - Set({fix::compiler/rustc_public_bridge}) |
| 65 | 65 | - Set({fix::compiler/rustc_query_impl}) |
| 66 | - Set({fix::compiler/rustc_query_system}) | |
| 67 | 66 | - Set({fix::compiler/rustc_resolve}) |
| 68 | 67 | - Set({fix::compiler/rustc_sanitizers}) |
| 69 | 68 | - Set({fix::compiler/rustc_serialize}) |
src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap-1| ... | ... | @@ -129,7 +129,6 @@ expression: test |
| 129 | 129 | - Set({test::compiler/rustc_public}) |
| 130 | 130 | - Set({test::compiler/rustc_public_bridge}) |
| 131 | 131 | - Set({test::compiler/rustc_query_impl}) |
| 132 | - Set({test::compiler/rustc_query_system}) | |
| 133 | 132 | - Set({test::compiler/rustc_resolve}) |
| 134 | 133 | - Set({test::compiler/rustc_sanitizers}) |
| 135 | 134 | - Set({test::compiler/rustc_serialize}) |
src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap-1| ... | ... | @@ -128,7 +128,6 @@ expression: test --skip=coverage |
| 128 | 128 | - Set({test::compiler/rustc_public}) |
| 129 | 129 | - Set({test::compiler/rustc_public_bridge}) |
| 130 | 130 | - Set({test::compiler/rustc_query_impl}) |
| 131 | - Set({test::compiler/rustc_query_system}) | |
| 132 | 131 | - Set({test::compiler/rustc_resolve}) |
| 133 | 132 | - Set({test::compiler/rustc_sanitizers}) |
| 134 | 133 | - Set({test::compiler/rustc_serialize}) |
src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap-1| ... | ... | @@ -92,7 +92,6 @@ expression: test --skip=tests |
| 92 | 92 | - Set({test::compiler/rustc_public}) |
| 93 | 93 | - Set({test::compiler/rustc_public_bridge}) |
| 94 | 94 | - Set({test::compiler/rustc_query_impl}) |
| 95 | - Set({test::compiler/rustc_query_system}) | |
| 96 | 95 | - Set({test::compiler/rustc_resolve}) |
| 97 | 96 | - Set({test::compiler/rustc_sanitizers}) |
| 98 | 97 | - Set({test::compiler/rustc_serialize}) |
src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_etc.snap-1| ... | ... | @@ -72,7 +72,6 @@ expression: test --skip=tests --skip=coverage-map --skip=coverage-run --skip=lib |
| 72 | 72 | - Set({test::compiler/rustc_public}) |
| 73 | 73 | - Set({test::compiler/rustc_public_bridge}) |
| 74 | 74 | - Set({test::compiler/rustc_query_impl}) |
| 75 | - Set({test::compiler/rustc_query_system}) | |
| 76 | 75 | - Set({test::compiler/rustc_resolve}) |
| 77 | 76 | - Set({test::compiler/rustc_sanitizers}) |
| 78 | 77 | - Set({test::compiler/rustc_serialize}) |
src/bootstrap/src/core/builder/tests.rs+7-7| ... | ... | @@ -1815,7 +1815,7 @@ mod snapshot { |
| 1815 | 1815 | insta::assert_snapshot!( |
| 1816 | 1816 | ctx.config("check") |
| 1817 | 1817 | .path("compiler") |
| 1818 | .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (74 crates)"); | |
| 1818 | .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (73 crates)"); | |
| 1819 | 1819 | } |
| 1820 | 1820 | |
| 1821 | 1821 | #[test] |
| ... | ... | @@ -1841,7 +1841,7 @@ mod snapshot { |
| 1841 | 1841 | ctx.config("check") |
| 1842 | 1842 | .path("compiler") |
| 1843 | 1843 | .stage(1) |
| 1844 | .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (74 crates)"); | |
| 1844 | .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (73 crates)"); | |
| 1845 | 1845 | } |
| 1846 | 1846 | |
| 1847 | 1847 | #[test] |
| ... | ... | @@ -1851,11 +1851,11 @@ mod snapshot { |
| 1851 | 1851 | ctx.config("check") |
| 1852 | 1852 | .path("compiler") |
| 1853 | 1853 | .stage(2) |
| 1854 | .render_steps(), @r" | |
| 1854 | .render_steps(), @" | |
| 1855 | 1855 | [build] llvm <host> |
| 1856 | 1856 | [build] rustc 0 <host> -> rustc 1 <host> |
| 1857 | 1857 | [build] rustc 1 <host> -> std 1 <host> |
| 1858 | [check] rustc 1 <host> -> rustc 2 <host> (74 crates) | |
| 1858 | [check] rustc 1 <host> -> rustc 2 <host> (73 crates) | |
| 1859 | 1859 | "); |
| 1860 | 1860 | } |
| 1861 | 1861 | |
| ... | ... | @@ -1866,12 +1866,12 @@ mod snapshot { |
| 1866 | 1866 | ctx.config("check") |
| 1867 | 1867 | .targets(&[TEST_TRIPLE_1]) |
| 1868 | 1868 | .hosts(&[TEST_TRIPLE_1]) |
| 1869 | .render_steps(), @r" | |
| 1869 | .render_steps(), @" | |
| 1870 | 1870 | [build] llvm <host> |
| 1871 | 1871 | [build] rustc 0 <host> -> rustc 1 <host> |
| 1872 | 1872 | [build] rustc 1 <host> -> std 1 <host> |
| 1873 | 1873 | [check] rustc 1 <host> -> std 1 <target1> |
| 1874 | [check] rustc 1 <host> -> rustc 2 <target1> (74 crates) | |
| 1874 | [check] rustc 1 <host> -> rustc 2 <target1> (73 crates) | |
| 1875 | 1875 | [check] rustc 1 <host> -> rustc 2 <target1> |
| 1876 | 1876 | [check] rustc 1 <host> -> Rustdoc 2 <target1> |
| 1877 | 1877 | [check] rustc 1 <host> -> rustc_codegen_cranelift 2 <target1> |
| ... | ... | @@ -1967,7 +1967,7 @@ mod snapshot { |
| 1967 | 1967 | ctx.config("check") |
| 1968 | 1968 | .paths(&["library", "compiler"]) |
| 1969 | 1969 | .args(&args) |
| 1970 | .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (74 crates)"); | |
| 1970 | .render_steps(), @"[check] rustc 0 <host> -> rustc 1 <host> (73 crates)"); | |
| 1971 | 1971 | } |
| 1972 | 1972 | |
| 1973 | 1973 | #[test] |
src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md+2-2| ... | ... | @@ -178,7 +178,7 @@ fn try_mark_green(tcx, current_node) -> bool { |
| 178 | 178 | |
| 179 | 179 | > NOTE: |
| 180 | 180 | > The actual implementation can be found in |
| 181 | > [`compiler/rustc_query_system/src/dep_graph/graph.rs`][try_mark_green] | |
| 181 | > [`compiler/rustc_middle/src/dep_graph/graph.rs`][try_mark_green] | |
| 182 | 182 | |
| 183 | 183 | By using red-green marking we can avoid the devastating cumulative effect of |
| 184 | 184 | having false positives during change detection. Whenever a query is executed |
| ... | ... | @@ -534,4 +534,4 @@ information. |
| 534 | 534 | |
| 535 | 535 | |
| 536 | 536 | [query-model]: ./query-evaluation-model-in-detail.html |
| 537 | [try_mark_green]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_query_system/dep_graph/graph.rs.html | |
| 537 | [try_mark_green]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_middle/dep_graph/graph.rs.html |
src/librustdoc/clean/mod.rs+13-11| ... | ... | @@ -1222,11 +1222,11 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext |
| 1222 | 1222 | } |
| 1223 | 1223 | hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { |
| 1224 | 1224 | let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Body(body)); |
| 1225 | MethodItem(m, None) | |
| 1225 | MethodItem(m, Defaultness::from_trait_item(trait_item.defaultness)) | |
| 1226 | 1226 | } |
| 1227 | 1227 | hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(idents)) => { |
| 1228 | 1228 | let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Idents(idents)); |
| 1229 | RequiredMethodItem(m) | |
| 1229 | RequiredMethodItem(m, Defaultness::from_trait_item(trait_item.defaultness)) | |
| 1230 | 1230 | } |
| 1231 | 1231 | hir::TraitItemKind::Type(bounds, Some(default)) => { |
| 1232 | 1232 | let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); |
| ... | ... | @@ -1271,7 +1271,7 @@ pub(crate) fn clean_impl_item<'tcx>( |
| 1271 | 1271 | hir::ImplItemImplKind::Inherent { .. } => hir::Defaultness::Final, |
| 1272 | 1272 | hir::ImplItemImplKind::Trait { defaultness, .. } => defaultness, |
| 1273 | 1273 | }; |
| 1274 | MethodItem(m, Some(defaultness)) | |
| 1274 | MethodItem(m, Defaultness::from_impl_item(defaultness)) | |
| 1275 | 1275 | } |
| 1276 | 1276 | hir::ImplItemKind::Type(hir_ty) => { |
| 1277 | 1277 | let type_ = clean_ty(hir_ty, cx); |
| ... | ... | @@ -1353,18 +1353,20 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo |
| 1353 | 1353 | } |
| 1354 | 1354 | } |
| 1355 | 1355 | |
| 1356 | let provided = match assoc_item.container { | |
| 1357 | ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => true, | |
| 1358 | ty::AssocContainer::Trait => assoc_item.defaultness(tcx).has_value(), | |
| 1356 | let defaultness = assoc_item.defaultness(tcx); | |
| 1357 | let (provided, defaultness) = match assoc_item.container { | |
| 1358 | ty::AssocContainer::Trait => { | |
| 1359 | (defaultness.has_value(), Defaultness::from_trait_item(defaultness)) | |
| 1360 | } | |
| 1361 | ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => { | |
| 1362 | (true, Defaultness::from_impl_item(defaultness)) | |
| 1363 | } | |
| 1359 | 1364 | }; |
| 1365 | ||
| 1360 | 1366 | if provided { |
| 1361 | let defaultness = match assoc_item.container { | |
| 1362 | ty::AssocContainer::TraitImpl(_) => Some(assoc_item.defaultness(tcx)), | |
| 1363 | ty::AssocContainer::InherentImpl | ty::AssocContainer::Trait => None, | |
| 1364 | }; | |
| 1365 | 1367 | MethodItem(item, defaultness) |
| 1366 | 1368 | } else { |
| 1367 | RequiredMethodItem(item) | |
| 1369 | RequiredMethodItem(item, defaultness) | |
| 1368 | 1370 | } |
| 1369 | 1371 | } |
| 1370 | 1372 | ty::AssocKind::Type { .. } => { |
src/librustdoc/clean/types.rs+33-10| ... | ... | @@ -61,6 +61,29 @@ pub(crate) enum ItemId { |
| 61 | 61 | Blanket { impl_id: DefId, for_: DefId }, |
| 62 | 62 | } |
| 63 | 63 | |
| 64 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | |
| 65 | pub(crate) enum Defaultness { | |
| 66 | Implicit, | |
| 67 | Default, | |
| 68 | Final, | |
| 69 | } | |
| 70 | ||
| 71 | impl Defaultness { | |
| 72 | pub(crate) fn from_trait_item(defaultness: hir::Defaultness) -> Self { | |
| 73 | match defaultness { | |
| 74 | hir::Defaultness::Default { .. } => Self::Implicit, | |
| 75 | hir::Defaultness::Final => Self::Final, | |
| 76 | } | |
| 77 | } | |
| 78 | ||
| 79 | pub(crate) fn from_impl_item(defaultness: hir::Defaultness) -> Self { | |
| 80 | match defaultness { | |
| 81 | hir::Defaultness::Default { .. } => Self::Default, | |
| 82 | hir::Defaultness::Final => Self::Implicit, | |
| 83 | } | |
| 84 | } | |
| 85 | } | |
| 86 | ||
| 64 | 87 | impl ItemId { |
| 65 | 88 | #[inline] |
| 66 | 89 | pub(crate) fn is_local(self) -> bool { |
| ... | ... | @@ -707,12 +730,12 @@ impl Item { |
| 707 | 730 | ItemType::from(self) |
| 708 | 731 | } |
| 709 | 732 | |
| 710 | pub(crate) fn is_default(&self) -> bool { | |
| 733 | pub(crate) fn defaultness(&self) -> Option<Defaultness> { | |
| 711 | 734 | match self.kind { |
| 712 | ItemKind::MethodItem(_, Some(defaultness)) => { | |
| 713 | defaultness.has_value() && !defaultness.is_final() | |
| 735 | ItemKind::MethodItem(_, defaultness) | ItemKind::RequiredMethodItem(_, defaultness) => { | |
| 736 | Some(defaultness) | |
| 714 | 737 | } |
| 715 | _ => false, | |
| 738 | _ => None, | |
| 716 | 739 | } |
| 717 | 740 | } |
| 718 | 741 | |
| ... | ... | @@ -774,8 +797,8 @@ impl Item { |
| 774 | 797 | } |
| 775 | 798 | } |
| 776 | 799 | ItemKind::FunctionItem(_) |
| 777 | | ItemKind::MethodItem(_, _) | |
| 778 | | ItemKind::RequiredMethodItem(_) => { | |
| 800 | | ItemKind::MethodItem(..) | |
| 801 | | ItemKind::RequiredMethodItem(..) => { | |
| 779 | 802 | let def_id = self.def_id().unwrap(); |
| 780 | 803 | build_fn_header(def_id, tcx, tcx.asyncness(def_id)) |
| 781 | 804 | } |
| ... | ... | @@ -859,11 +882,11 @@ pub(crate) enum ItemKind { |
| 859 | 882 | TraitAliasItem(TraitAlias), |
| 860 | 883 | ImplItem(Box<Impl>), |
| 861 | 884 | /// A required method in a trait declaration meaning it's only a function signature. |
| 862 | RequiredMethodItem(Box<Function>), | |
| 885 | RequiredMethodItem(Box<Function>, Defaultness), | |
| 863 | 886 | /// A method in a trait impl or a provided method in a trait declaration. |
| 864 | 887 | /// |
| 865 | 888 | /// Compared to [RequiredMethodItem], it also contains a method body. |
| 866 | MethodItem(Box<Function>, Option<hir::Defaultness>), | |
| 889 | MethodItem(Box<Function>, Defaultness), | |
| 867 | 890 | StructFieldItem(Type), |
| 868 | 891 | VariantItem(Variant), |
| 869 | 892 | /// `fn`s from an extern block |
| ... | ... | @@ -921,8 +944,8 @@ impl ItemKind { |
| 921 | 944 | | StaticItem(_) |
| 922 | 945 | | ConstantItem(_) |
| 923 | 946 | | TraitAliasItem(_) |
| 924 | | RequiredMethodItem(_) | |
| 925 | | MethodItem(_, _) | |
| 947 | | RequiredMethodItem(..) | |
| 948 | | MethodItem(..) | |
| 926 | 949 | | StructFieldItem(_) |
| 927 | 950 | | ForeignFunctionItem(_, _) |
| 928 | 951 | | ForeignStaticItem(_, _) |
src/librustdoc/fold.rs+2-2| ... | ... | @@ -82,8 +82,8 @@ pub(crate) trait DocFolder: Sized { |
| 82 | 82 | | StaticItem(_) |
| 83 | 83 | | ConstantItem(..) |
| 84 | 84 | | TraitAliasItem(_) |
| 85 | | RequiredMethodItem(_) | |
| 86 | | MethodItem(_, _) | |
| 85 | | RequiredMethodItem(..) | |
| 86 | | MethodItem(..) | |
| 87 | 87 | | StructFieldItem(_) |
| 88 | 88 | | ForeignFunctionItem(..) |
| 89 | 89 | | ForeignStaticItem(..) |
src/librustdoc/html/format.rs-4| ... | ... | @@ -1566,10 +1566,6 @@ pub(crate) fn print_abi_with_space(abi: ExternAbi) -> impl Display { |
| 1566 | 1566 | }) |
| 1567 | 1567 | } |
| 1568 | 1568 | |
| 1569 | pub(crate) fn print_default_space(v: bool) -> &'static str { | |
| 1570 | if v { "default " } else { "" } | |
| 1571 | } | |
| 1572 | ||
| 1573 | 1569 | fn print_generic_arg(generic_arg: &clean::GenericArg, cx: &Context<'_>) -> impl Display { |
| 1574 | 1570 | fmt::from_fn(move |f| match generic_arg { |
| 1575 | 1571 | clean::GenericArg::Lifetime(lt) => f.write_str(print_lifetime(lt)), |
src/librustdoc/html/render/mod.rs+21-49| ... | ... | @@ -59,14 +59,14 @@ use rustc_hir::def_id::{DefId, DefIdSet}; |
| 59 | 59 | use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, StableSince}; |
| 60 | 60 | use rustc_middle::ty::print::PrintTraitRefExt; |
| 61 | 61 | use rustc_middle::ty::{self, TyCtxt}; |
| 62 | use rustc_span::DUMMY_SP; | |
| 62 | 63 | use rustc_span::symbol::{Symbol, sym}; |
| 63 | use rustc_span::{BytePos, DUMMY_SP, FileName}; | |
| 64 | 64 | use tracing::{debug, info}; |
| 65 | 65 | |
| 66 | 66 | pub(crate) use self::context::*; |
| 67 | 67 | pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources}; |
| 68 | 68 | pub(crate) use self::write_shared::*; |
| 69 | use crate::clean::{self, ItemId, RenderedLink}; | |
| 69 | use crate::clean::{self, Defaultness, ItemId, RenderedLink}; | |
| 70 | 70 | use crate::display::{Joined as _, MaybeDisplay as _}; |
| 71 | 71 | use crate::error::Error; |
| 72 | 72 | use crate::formats::Impl; |
| ... | ... | @@ -75,8 +75,8 @@ use crate::formats::item_type::ItemType; |
| 75 | 75 | use crate::html::escape::Escape; |
| 76 | 76 | use crate::html::format::{ |
| 77 | 77 | Ending, HrefError, HrefInfo, PrintWithSpace, full_print_fn_decl, href, print_abi_with_space, |
| 78 | print_constness_with_space, print_default_space, print_generic_bounds, print_generics, | |
| 79 | print_impl, print_path, print_type, print_where_clause, visibility_print_with_space, | |
| 78 | print_constness_with_space, print_generic_bounds, print_generics, print_impl, print_path, | |
| 79 | print_type, print_where_clause, visibility_print_with_space, | |
| 80 | 80 | }; |
| 81 | 81 | use crate::html::markdown::{ |
| 82 | 82 | HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine, |
| ... | ... | @@ -1110,7 +1110,11 @@ fn assoc_method( |
| 1110 | 1110 | let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item"); |
| 1111 | 1111 | let name = meth.name.as_ref().unwrap(); |
| 1112 | 1112 | let vis = visibility_print_with_space(meth, cx).to_string(); |
| 1113 | let defaultness = print_default_space(meth.is_default()); | |
| 1113 | let defaultness = match meth.defaultness().expect("Expected assoc method to have defaultness") { | |
| 1114 | Defaultness::Implicit => "", | |
| 1115 | Defaultness::Final => "final ", | |
| 1116 | Defaultness::Default => "default ", | |
| 1117 | }; | |
| 1114 | 1118 | // FIXME: Once https://github.com/rust-lang/rust/issues/143874 is implemented, we can remove |
| 1115 | 1119 | // this condition. |
| 1116 | 1120 | let constness = match render_mode { |
| ... | ... | @@ -1261,7 +1265,7 @@ fn render_assoc_item( |
| 1261 | 1265 | ) -> impl fmt::Display { |
| 1262 | 1266 | fmt::from_fn(move |f| match &item.kind { |
| 1263 | 1267 | clean::StrippedItem(..) => Ok(()), |
| 1264 | clean::RequiredMethodItem(m) | clean::MethodItem(m, _) => { | |
| 1268 | clean::RequiredMethodItem(m, _) | clean::MethodItem(m, _) => { | |
| 1265 | 1269 | assoc_method(item, &m.generics, &m.decl, link, parent, cx, render_mode).fmt(f) |
| 1266 | 1270 | } |
| 1267 | 1271 | clean::RequiredAssocConstItem(generics, ty) => assoc_const( |
| ... | ... | @@ -1586,7 +1590,7 @@ fn render_deref_methods( |
| 1586 | 1590 | fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool { |
| 1587 | 1591 | let self_type_opt = match item.kind { |
| 1588 | 1592 | clean::MethodItem(ref method, _) => method.decl.receiver_type(), |
| 1589 | clean::RequiredMethodItem(ref method) => method.decl.receiver_type(), | |
| 1593 | clean::RequiredMethodItem(ref method, _) => method.decl.receiver_type(), | |
| 1590 | 1594 | _ => None, |
| 1591 | 1595 | }; |
| 1592 | 1596 | |
| ... | ... | @@ -1856,7 +1860,7 @@ fn render_impl( |
| 1856 | 1860 | deprecation_class = ""; |
| 1857 | 1861 | } |
| 1858 | 1862 | match &item.kind { |
| 1859 | clean::MethodItem(..) | clean::RequiredMethodItem(_) => { | |
| 1863 | clean::MethodItem(..) | clean::RequiredMethodItem(..) => { | |
| 1860 | 1864 | // Only render when the method is not static or we allow static methods |
| 1861 | 1865 | if render_method_item { |
| 1862 | 1866 | let id = cx.derive_id(format!("{item_type}.{name}")); |
| ... | ... | @@ -2034,7 +2038,9 @@ fn render_impl( |
| 2034 | 2038 | if !impl_.is_negative_trait_impl() { |
| 2035 | 2039 | for impl_item in &impl_.items { |
| 2036 | 2040 | match impl_item.kind { |
| 2037 | clean::MethodItem(..) | clean::RequiredMethodItem(_) => methods.push(impl_item), | |
| 2041 | clean::MethodItem(..) | clean::RequiredMethodItem(..) => { | |
| 2042 | methods.push(impl_item) | |
| 2043 | } | |
| 2038 | 2044 | clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => { |
| 2039 | 2045 | assoc_types.push(impl_item) |
| 2040 | 2046 | } |
| ... | ... | @@ -2779,46 +2785,12 @@ fn render_call_locations<W: fmt::Write>( |
| 2779 | 2785 | let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES; |
| 2780 | 2786 | let locations_encoded = serde_json::to_string(&line_ranges).unwrap(); |
| 2781 | 2787 | |
| 2782 | let source_map = tcx.sess.source_map(); | |
| 2783 | let files = source_map.files(); | |
| 2784 | let local = tcx.sess.local_crate_source_file().unwrap(); | |
| 2785 | ||
| 2786 | let get_file_start_pos = || { | |
| 2787 | let crate_src = local.clone().into_local_path()?; | |
| 2788 | let abs_crate_src = crate_src.canonicalize().ok()?; | |
| 2789 | let crate_root = abs_crate_src.parent()?.parent()?; | |
| 2790 | let rel_path = path.strip_prefix(crate_root).ok()?; | |
| 2791 | files | |
| 2792 | .iter() | |
| 2793 | .find(|file| match &file.name { | |
| 2794 | FileName::Real(real) => real.local_path().map_or(false, |p| p == rel_path), | |
| 2795 | _ => false, | |
| 2796 | }) | |
| 2797 | .map(|file| file.start_pos) | |
| 2798 | }; | |
| 2799 | ||
| 2800 | // Look for the example file in the source map if it exists, otherwise | |
| 2801 | // return a span to the local crate's source file | |
| 2802 | let Some(file_span) = get_file_start_pos() | |
| 2803 | .or_else(|| { | |
| 2804 | files | |
| 2805 | .iter() | |
| 2806 | .find(|file| match &file.name { | |
| 2807 | FileName::Real(file_name) => file_name == &local, | |
| 2808 | _ => false, | |
| 2809 | }) | |
| 2810 | .map(|file| file.start_pos) | |
| 2811 | }) | |
| 2812 | .map(|start_pos| { | |
| 2813 | rustc_span::Span::with_root_ctxt( | |
| 2814 | start_pos + BytePos(byte_min), | |
| 2815 | start_pos + BytePos(byte_max), | |
| 2816 | ) | |
| 2817 | }) | |
| 2818 | else { | |
| 2819 | // if the fallback span can't be built, don't render the code for this example | |
| 2820 | return false; | |
| 2821 | }; | |
| 2788 | // For scraped examples, we don't need a real span from the SourceMap. | |
| 2789 | // The URL is already provided in ScrapedInfo, and sources::print_src | |
| 2790 | // will use that directly. We use DUMMY_SP as a placeholder. | |
| 2791 | // Note: DUMMY_SP is safe here because href_from_span won't be called | |
| 2792 | // for scraped examples. | |
| 2793 | let file_span = rustc_span::DUMMY_SP; | |
| 2822 | 2794 | |
| 2823 | 2795 | let mut decoration_info = FxIndexMap::default(); |
| 2824 | 2796 | decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]); |
src/librustdoc/html/render/search_index.rs+1-1| ... | ... | @@ -1977,7 +1977,7 @@ pub(crate) fn get_function_type_for_search( |
| 1977 | 1977 | clean::ForeignFunctionItem(ref f, _) |
| 1978 | 1978 | | clean::FunctionItem(ref f) |
| 1979 | 1979 | | clean::MethodItem(ref f, _) |
| 1980 | | clean::RequiredMethodItem(ref f) => { | |
| 1980 | | clean::RequiredMethodItem(ref f, _) => { | |
| 1981 | 1981 | get_fn_inputs_and_outputs(f, tcx, impl_or_trait_generics, cache) |
| 1982 | 1982 | } |
| 1983 | 1983 | clean::ConstantItem(ref c) => make_nullary_fn(&c.type_), |
src/librustdoc/html/sources.rs+9-3| ... | ... | @@ -344,9 +344,15 @@ pub(crate) fn print_src( |
| 344 | 344 | lines += line_info.start_line as usize; |
| 345 | 345 | } |
| 346 | 346 | let code = fmt::from_fn(move |fmt| { |
| 347 | let current_href = context | |
| 348 | .href_from_span(clean::Span::new(file_span), false) | |
| 349 | .expect("only local crates should have sources emitted"); | |
| 347 | // For scraped examples, use the URL from ScrapedInfo directly. | |
| 348 | // For regular sources, derive it from the span. | |
| 349 | let current_href = if let SourceContext::Embedded(info) = source_context { | |
| 350 | info.url.to_string() | |
| 351 | } else { | |
| 352 | context | |
| 353 | .href_from_span(clean::Span::new(file_span), false) | |
| 354 | .expect("only local crates should have sources emitted") | |
| 355 | }; | |
| 350 | 356 | highlight::write_code( |
| 351 | 357 | fmt, |
| 352 | 358 | s, |
src/librustdoc/json/conversions.rs+1-1| ... | ... | @@ -296,7 +296,7 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum |
| 296 | 296 | MethodItem(m, _) => { |
| 297 | 297 | ItemEnum::Function(from_clean_function(m, true, header.unwrap(), renderer)) |
| 298 | 298 | } |
| 299 | RequiredMethodItem(m) => { | |
| 299 | RequiredMethodItem(m, _) => { | |
| 300 | 300 | ItemEnum::Function(from_clean_function(m, false, header.unwrap(), renderer)) |
| 301 | 301 | } |
| 302 | 302 | ImplItem(i) => ItemEnum::Impl(i.into_json(renderer)), |
src/librustdoc/visit.rs+2-2| ... | ... | @@ -35,8 +35,8 @@ pub(crate) trait DocVisitor<'a>: Sized { |
| 35 | 35 | | StaticItem(_) |
| 36 | 36 | | ConstantItem(..) |
| 37 | 37 | | TraitAliasItem(_) |
| 38 | | RequiredMethodItem(_) | |
| 39 | | MethodItem(_, _) | |
| 38 | | RequiredMethodItem(..) | |
| 39 | | MethodItem(..) | |
| 40 | 40 | | StructFieldItem(_) |
| 41 | 41 | | ForeignFunctionItem(..) |
| 42 | 42 | | ForeignStaticItem(..) |
src/tools/clippy/clippy_utils/src/ast_utils/mod.rs+3-1| ... | ... | @@ -819,7 +819,9 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { |
| 819 | 819 | pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { |
| 820 | 820 | matches!( |
| 821 | 821 | (l, r), |
| 822 | (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) | |
| 822 | (Defaultness::Implicit, Defaultness::Implicit) | |
| 823 | | (Defaultness::Default(_), Defaultness::Default(_)) | |
| 824 | | (Defaultness::Final(_), Defaultness::Final(_)) | |
| 823 | 825 | ) |
| 824 | 826 | } |
| 825 | 827 |
src/tools/miri/CONTRIBUTING.md+17| ... | ... | @@ -66,6 +66,23 @@ process for such contributions: |
| 66 | 66 | This process is largely informal, and its primary goal is to more clearly communicate expectations. |
| 67 | 67 | Please get in touch with us if you have any questions! |
| 68 | 68 | |
| 69 | ## Scope of Miri shims | |
| 70 | ||
| 71 | Miri has "shims" to implement functionality that is usually implemented in C libraries which are | |
| 72 | invoked from Rust code, such as opening files or spawning threads, as well as for | |
| 73 | CPU-vendor-provided SIMD intrinsics. However, the set of C functions that Rust code invokes this way | |
| 74 | is enormous, and for obvious reasons we have no intention of implementing every C API ever written | |
| 75 | in Miri. | |
| 76 | ||
| 77 | At the moment, the general guideline for "could this function have a shim in Miri" is: we will | |
| 78 | generally only add shims for functions that can be implemented in a portable way using just what is | |
| 79 | provided by the Rust standard library. The function should also be reasonably widely-used in Rust | |
| 80 | code to justify the review and maintenance effort (i.e. the easier the function is to implement, the | |
| 81 | lower the barrier). Other than that, we might make exceptions for certain cases if (a) there is a | |
| 82 | good case for why Miri should support those APIs, and (b) robust and widely-used portable libraries | |
| 83 | exist in the Rust ecosystem. We will generally not add shims to Miri that would require Miri to | |
| 84 | directly interact with platform-specific APIs (such as `libc` or `windows-sys`). | |
| 85 | ||
| 69 | 86 | ## Preparing the build environment |
| 70 | 87 | |
| 71 | 88 | Miri heavily relies on internal and unstable rustc interfaces to execute MIR, |
src/tools/miri/Cargo.lock+4-4| ... | ... | @@ -562,9 +562,9 @@ checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" |
| 562 | 562 | |
| 563 | 563 | [[package]] |
| 564 | 564 | name = "git2" |
| 565 | version = "0.20.2" | |
| 565 | version = "0.20.4" | |
| 566 | 566 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 567 | checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110" | |
| 567 | checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" | |
| 568 | 568 | dependencies = [ |
| 569 | 569 | "bitflags", |
| 570 | 570 | "libc", |
| ... | ... | @@ -804,9 +804,9 @@ dependencies = [ |
| 804 | 804 | |
| 805 | 805 | [[package]] |
| 806 | 806 | name = "libgit2-sys" |
| 807 | version = "0.18.2+1.9.1" | |
| 807 | version = "0.18.3+1.9.2" | |
| 808 | 808 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 809 | checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222" | |
| 809 | checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487" | |
| 810 | 810 | dependencies = [ |
| 811 | 811 | "cc", |
| 812 | 812 | "libc", |
src/tools/miri/Cargo.toml+1-1| ... | ... | @@ -39,7 +39,7 @@ serde = { version = "1.0.219", features = ["derive"], optional = true } |
| 39 | 39 | [target.'cfg(target_os = "linux")'.dependencies] |
| 40 | 40 | nix = { version = "0.30.1", features = ["mman", "ptrace", "signal"], optional = true } |
| 41 | 41 | ipc-channel = { version = "0.20.0", optional = true } |
| 42 | capstone = { version = "0.14", optional = true } | |
| 42 | capstone = { version = "0.14", features = ["arch_x86", "full"], default-features = false, optional = true} | |
| 43 | 43 | |
| 44 | 44 | [target.'cfg(all(target_os = "linux", target_pointer_width = "64", target_endian = "little"))'.dependencies] |
| 45 | 45 | genmc-sys = { path = "./genmc-sys/", version = "0.1.0", optional = true } |
src/tools/miri/README.md+2| ... | ... | @@ -626,6 +626,8 @@ Definite bugs found: |
| 626 | 626 | * [`ReentrantLock` not correctly dealing with reuse of addresses for TLS storage of different threads](https://github.com/rust-lang/rust/pull/141248) |
| 627 | 627 | * [Rare Deadlock in the thread (un)parking example code](https://github.com/rust-lang/rust/issues/145816) |
| 628 | 628 | * [`winit` registering a global constructor with the wrong ABI on Windows](https://github.com/rust-windowing/winit/issues/4435) |
| 629 | * [`VecDeque::splice` confusing physical and logical indices](https://github.com/rust-lang/rust/issues/151758) | |
| 630 | * [Data race in `oneshot` channel](https://github.com/faern/oneshot/issues/69) | |
| 629 | 631 | |
| 630 | 632 | Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment): |
| 631 | 633 |
src/tools/miri/cargo-miri/src/phases.rs+4| ... | ... | @@ -63,6 +63,9 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) { |
| 63 | 63 | "setup" => MiriCommand::Setup, |
| 64 | 64 | "test" | "t" | "run" | "r" | "nextest" => MiriCommand::Forward(subcommand), |
| 65 | 65 | "clean" => MiriCommand::Clean, |
| 66 | // For use by the `./miri test` dependency builder. | |
| 67 | "build" if env::var_os("MIRI_BUILD_TEST_DEPS").is_some() => | |
| 68 | MiriCommand::Forward("build".into()), | |
| 66 | 69 | _ => { |
| 67 | 70 | // Check for version and help flags. |
| 68 | 71 | if has_arg_flag("--help") || has_arg_flag("-h") { |
| ... | ... | @@ -309,6 +312,7 @@ pub fn phase_rustc(args: impl Iterator<Item = String>, phase: RustcPhase) { |
| 309 | 312 | // Ask rustc for the filename (since that is target-dependent). |
| 310 | 313 | let mut rustc = miri_for_host(); // sysroot doesn't matter for this so we just use the host |
| 311 | 314 | rustc.arg("--print").arg("file-names"); |
| 315 | rustc.arg("-Zunstable-options"); // needed for JSON targets | |
| 312 | 316 | for flag in ["--crate-name", "--crate-type", "--target"] { |
| 313 | 317 | for val in get_arg_flag_values(flag) { |
| 314 | 318 | rustc.arg(flag).arg(val); |
src/tools/miri/cargo-miri/src/setup.rs+5| ... | ... | @@ -88,6 +88,11 @@ pub fn setup( |
| 88 | 88 | }; |
| 89 | 89 | let cargo_cmd = { |
| 90 | 90 | let mut command = cargo(); |
| 91 | // Allow JSON targets since users do not have a good way to set this flag otherwise. | |
| 92 | if env::var("RUSTC_STAGE").is_err() { | |
| 93 | // ^ is a HACK for bootstrap cargo. FIXME(cfg(bootstrap)) remove the hack. | |
| 94 | command.arg("-Zjson-target-spec"); | |
| 95 | } | |
| 91 | 96 | // Use Miri as rustc to build a libstd compatible with us (and use the right flags). |
| 92 | 97 | // We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags |
| 93 | 98 | // for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves). |
src/tools/miri/ci/ci.sh+4-3| ... | ... | @@ -129,7 +129,7 @@ function run_tests_minimal { |
| 129 | 129 | time ./miri test $TARGET_FLAG "$@" |
| 130 | 130 | |
| 131 | 131 | # Ensure that a small smoke test of cargo-miri works. |
| 132 | time cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml $TARGET_FLAG | |
| 132 | time cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml -Zjson-target-spec $TARGET_FLAG | |
| 133 | 133 | |
| 134 | 134 | endgroup |
| 135 | 135 | } |
| ... | ... | @@ -173,7 +173,9 @@ case $HOST_TARGET in |
| 173 | 173 | # Host |
| 174 | 174 | MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests |
| 175 | 175 | # Custom target JSON file |
| 176 | TEST_TARGET=tests/x86_64-unknown-kernel.json MIRI_NO_STD=1 run_tests_minimal no_std | |
| 176 | TEST_TARGET=tests/x86_64-unknown-kernel.json MIRI_NO_STD=1 MIRIFLAGS="-Zunstable-options" run_tests_minimal no_std | |
| 177 | # Not officially supported tier 2 | |
| 178 | MANY_SEEDS=16 TEST_TARGET=x86_64-pc-solaris run_tests | |
| 177 | 179 | ;; |
| 178 | 180 | aarch64-apple-darwin) |
| 179 | 181 | # Host |
| ... | ... | @@ -184,7 +186,6 @@ case $HOST_TARGET in |
| 184 | 186 | # Not officially supported tier 2 |
| 185 | 187 | MANY_SEEDS=16 TEST_TARGET=mips-unknown-linux-gnu run_tests # a 32bit big-endian target, and also a target without 64bit atomics |
| 186 | 188 | MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-illumos run_tests |
| 187 | MANY_SEEDS=16 TEST_TARGET=x86_64-pc-solaris run_tests | |
| 188 | 189 | ;; |
| 189 | 190 | i686-pc-windows-msvc) |
| 190 | 191 | # Host |
src/tools/miri/rust-version+1-1| ... | ... | @@ -1 +1 @@ |
| 1 | d10ac47c20152feb5e99b1c35a2e6830f77c66dc | |
| 1 | 7bee525095c0872e87c038c412c781b9bbb3f5dc |
src/tools/miri/src/alloc/isolated_alloc.rs+2-2| ... | ... | @@ -66,10 +66,10 @@ impl IsolatedAlloc { |
| 66 | 66 | // And make sure the align is at least one page |
| 67 | 67 | let align = std::cmp::max(layout.align(), self.page_size); |
| 68 | 68 | // pg_count gives us the # of pages needed to satisfy the size. For |
| 69 | // align > page_size where align = n * page_size, a sufficently-aligned | |
| 69 | // align > page_size where align = n * page_size, a sufficiently-aligned | |
| 70 | 70 | // address must exist somewhere in the range of |
| 71 | 71 | // some_page_aligned_address..some_page_aligned_address + (n-1) * page_size |
| 72 | // (since if some_page_aligned_address + n * page_size is sufficently aligned, | |
| 72 | // (since if some_page_aligned_address + n * page_size is sufficiently aligned, | |
| 73 | 73 | // then so is some_page_aligned_address itself per the definition of n, so we |
| 74 | 74 | // can avoid using that 1 extra page). |
| 75 | 75 | // Thus we allocate n-1 extra pages |
src/tools/miri/src/bin/log/tracing_chrome_instant.rs+2-2| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | //! <https://github.com/tikv/minstant/blob/27c9ec5ec90b5b67113a748a4defee0d2519518c/src/tsc_now.rs>. |
| 3 | 3 | //! A useful resource is also |
| 4 | 4 | //! <https://www.pingcap.com/blog/how-we-trace-a-kv-database-with-less-than-5-percent-performance-impact/>, |
| 5 | //! although this file does not implement TSC synchronization but insteads pins threads to CPUs, | |
| 5 | //! although this file does not implement TSC synchronization but instead pins threads to CPUs, | |
| 6 | 6 | //! since the former is not reliable (i.e. it might lead to non-monotonic time measurements). |
| 7 | 7 | //! Another useful resource for future improvements might be measureme's time measurement utils: |
| 8 | 8 | //! <https://github.com/rust-lang/measureme/blob/master/measureme/src/counters.rs>. |
| ... | ... | @@ -11,7 +11,7 @@ |
| 11 | 11 | #![cfg(feature = "tracing")] |
| 12 | 12 | |
| 13 | 13 | /// This alternative `TracingChromeInstant` implementation was made entirely to suit the needs of |
| 14 | /// [crate::log::tracing_chrome], and shouldn't be used for anything else. It featues two functions: | |
| 14 | /// [crate::log::tracing_chrome], and shouldn't be used for anything else. It features two functions: | |
| 15 | 15 | /// - [TracingChromeInstant::setup_for_thread_and_start], which sets up the current thread to do |
| 16 | 16 | /// proper time tracking and returns a point in time to use as "t=0", and |
| 17 | 17 | /// - [TracingChromeInstant::with_elapsed_micros_subtracting_tracing], which allows |
src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs+1-1| ... | ... | @@ -578,7 +578,7 @@ pub mod diagnostics { |
| 578 | 578 | // - created as Reserved { conflicted: false }, |
| 579 | 579 | // then Unique -> Disabled is forbidden |
| 580 | 580 | // A potential `Reserved { conflicted: false } |
| 581 | // -> Reserved { conflicted: true }` is inexistant or irrelevant, | |
| 581 | // -> Reserved { conflicted: true }` is inexistent or irrelevant, | |
| 582 | 582 | // and so is the `Reserved { conflicted: false } -> Unique` |
| 583 | 583 | (Unique, Frozen) => false, |
| 584 | 584 | (ReservedFrz { conflicted: true }, _) => false, |
src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs+32-49| ... | ... | @@ -26,7 +26,7 @@ use super::foreign_access_skipping::IdempotentForeignAccess; |
| 26 | 26 | use super::perms::{PermTransition, Permission}; |
| 27 | 27 | use super::tree_visitor::{ChildrenVisitMode, ContinueTraversal, NodeAppArgs, TreeVisitor}; |
| 28 | 28 | use super::unimap::{UniIndex, UniKeyMap, UniValMap}; |
| 29 | use super::wildcard::WildcardState; | |
| 29 | use super::wildcard::ExposedCache; | |
| 30 | 30 | use crate::borrow_tracker::{AccessKind, GlobalState, ProtectorKind}; |
| 31 | 31 | use crate::*; |
| 32 | 32 | |
| ... | ... | @@ -89,7 +89,7 @@ impl LocationState { |
| 89 | 89 | &mut self, |
| 90 | 90 | idx: UniIndex, |
| 91 | 91 | nodes: &mut UniValMap<Node>, |
| 92 | wildcard_accesses: &mut UniValMap<WildcardState>, | |
| 92 | exposed_cache: &mut ExposedCache, | |
| 93 | 93 | access_kind: AccessKind, |
| 94 | 94 | relatedness: AccessRelatedness, |
| 95 | 95 | protected: bool, |
| ... | ... | @@ -99,7 +99,7 @@ impl LocationState { |
| 99 | 99 | // ensures it is only called when `skip_if_known_noop` returns |
| 100 | 100 | // `Recurse`, due to the contract of `traverse_this_parents_children_other`. |
| 101 | 101 | self.record_new_access(access_kind, relatedness); |
| 102 | ||
| 102 | let old_access_level = self.permission.strongest_allowed_local_access(protected); | |
| 103 | 103 | let transition = self.perform_access(access_kind, relatedness, protected)?; |
| 104 | 104 | if !transition.is_noop() { |
| 105 | 105 | let node = nodes.get_mut(idx).unwrap(); |
| ... | ... | @@ -111,8 +111,8 @@ impl LocationState { |
| 111 | 111 | // We need to update the wildcard state, if the permission |
| 112 | 112 | // of an exposed pointer changes. |
| 113 | 113 | if node.is_exposed { |
| 114 | let access_type = self.permission.strongest_allowed_local_access(protected); | |
| 115 | WildcardState::update_exposure(idx, access_type, nodes, wildcard_accesses); | |
| 114 | let access_level = self.permission.strongest_allowed_local_access(protected); | |
| 115 | exposed_cache.update_exposure(nodes, idx, old_access_level, access_level); | |
| 116 | 116 | } |
| 117 | 117 | } |
| 118 | 118 | Ok(()) |
| ... | ... | @@ -226,7 +226,7 @@ impl LocationState { |
| 226 | 226 | |
| 227 | 227 | /// Records a new access, so that future access can potentially be skipped |
| 228 | 228 | /// by `skip_if_known_noop`. This must be called on child accesses, and otherwise |
| 229 | /// shoud be called on foreign accesses for increased performance. It should not be called | |
| 229 | /// should be called on foreign accesses for increased performance. It should not be called | |
| 230 | 230 | /// when `skip_if_known_noop` indicated skipping, since it then is a no-op. |
| 231 | 231 | /// See `foreign_access_skipping.rs` |
| 232 | 232 | fn record_new_access(&mut self, access_kind: AccessKind, rel_pos: AccessRelatedness) { |
| ... | ... | @@ -261,14 +261,8 @@ pub struct LocationTree { |
| 261 | 261 | /// |
| 262 | 262 | /// We do uphold the fact that `keys(perms)` is a subset of `keys(nodes)` |
| 263 | 263 | pub perms: UniValMap<LocationState>, |
| 264 | /// Maps a tag and a location to its wildcard access tracking information, | |
| 265 | /// with possible lazy initialization. | |
| 266 | /// | |
| 267 | /// If this allocation doesn't have any exposed nodes, then this map doesn't get | |
| 268 | /// initialized. This way we only need to allocate the map if we need it. | |
| 269 | /// | |
| 270 | /// NOTE: same guarantees on entry initialization as for `perms`. | |
| 271 | pub wildcard_accesses: UniValMap<WildcardState>, | |
| 264 | /// Caches information about the relatedness of nodes for a wildcard access. | |
| 265 | pub exposed_cache: ExposedCache, | |
| 272 | 266 | } |
| 273 | 267 | /// Tree structure with both parents and children since we want to be |
| 274 | 268 | /// able to traverse the tree efficiently in both directions. |
| ... | ... | @@ -276,7 +270,7 @@ pub struct LocationTree { |
| 276 | 270 | pub struct Tree { |
| 277 | 271 | /// Mapping from tags to keys. The key obtained can then be used in |
| 278 | 272 | /// any of the `UniValMap` relative to this allocation, i.e. |
| 279 | /// `nodes`, `LocationTree::perms` and `LocationTree::wildcard_accesses` | |
| 273 | /// `nodes`, `LocationTree::perms` and `LocationTree::exposed_cache` | |
| 280 | 274 | /// of the same `Tree`. |
| 281 | 275 | /// The parent-child relationship in `Node` is encoded in terms of these same |
| 282 | 276 | /// keys, so traversing the entire tree needs exactly one access to |
| ... | ... | @@ -372,8 +366,8 @@ impl Tree { |
| 372 | 366 | IdempotentForeignAccess::None, |
| 373 | 367 | ), |
| 374 | 368 | ); |
| 375 | let wildcard_accesses = UniValMap::default(); | |
| 376 | DedupRangeMap::new(size, LocationTree { perms, wildcard_accesses }) | |
| 369 | let exposed_cache = ExposedCache::default(); | |
| 370 | DedupRangeMap::new(size, LocationTree { perms, exposed_cache }) | |
| 377 | 371 | }; |
| 378 | 372 | Self { roots: SmallVec::from_slice(&[root_idx]), nodes, locations, tag_mapping } |
| 379 | 373 | } |
| ... | ... | @@ -451,19 +445,9 @@ impl<'tcx> Tree { |
| 451 | 445 | } |
| 452 | 446 | } |
| 453 | 447 | |
| 454 | // We need to ensure the consistency of the wildcard access tracking data structure. | |
| 455 | // For this, we insert the correct entry for this tag based on its parent, if it exists. | |
| 456 | // If we are inserting a new wildcard root (with Wildcard as parent_prov) then we insert | |
| 457 | // the special wildcard root initial state instead. | |
| 458 | for (_range, loc) in self.locations.iter_mut_all() { | |
| 459 | if let Some(parent_idx) = parent_idx { | |
| 460 | if let Some(parent_access) = loc.wildcard_accesses.get(parent_idx) { | |
| 461 | loc.wildcard_accesses.insert(idx, parent_access.for_new_child()); | |
| 462 | } | |
| 463 | } else { | |
| 464 | loc.wildcard_accesses.insert(idx, WildcardState::for_wildcard_root()); | |
| 465 | } | |
| 466 | } | |
| 448 | // We don't have to update `exposed_cache` as the new node is not exposed and | |
| 449 | // has no children so the default counts of 0 are correct. | |
| 450 | ||
| 467 | 451 | // If the parent is a wildcard pointer, then it doesn't track SIFA and doesn't need to be updated. |
| 468 | 452 | if let Some(parent_idx) = parent_idx { |
| 469 | 453 | // Inserting the new perms might have broken the SIFA invariant (see |
| ... | ... | @@ -807,7 +791,7 @@ impl Tree { |
| 807 | 791 | let node = self.nodes.remove(this).unwrap(); |
| 808 | 792 | for (_range, loc) in self.locations.iter_mut_all() { |
| 809 | 793 | loc.perms.remove(this); |
| 810 | loc.wildcard_accesses.remove(this); | |
| 794 | loc.exposed_cache.remove(this); | |
| 811 | 795 | } |
| 812 | 796 | self.tag_mapping.remove(&node.tag); |
| 813 | 797 | } |
| ... | ... | @@ -943,7 +927,7 @@ impl<'tcx> LocationTree { |
| 943 | 927 | }; |
| 944 | 928 | |
| 945 | 929 | let accessed_root_tag = accessed_root.map(|idx| nodes.get(idx).unwrap().tag); |
| 946 | for root in roots { | |
| 930 | for (i, root) in roots.enumerate() { | |
| 947 | 931 | let tag = nodes.get(root).unwrap().tag; |
| 948 | 932 | // On a protector release access we have to skip the children of the accessed tag. |
| 949 | 933 | // However, if the tag has exposed children then some of the wildcard subtrees could |
| ... | ... | @@ -981,6 +965,7 @@ impl<'tcx> LocationTree { |
| 981 | 965 | access_kind, |
| 982 | 966 | global, |
| 983 | 967 | diagnostics, |
| 968 | /*is_wildcard_tree*/ i != 0, | |
| 984 | 969 | )?; |
| 985 | 970 | } |
| 986 | 971 | interp_ok(()) |
| ... | ... | @@ -1029,7 +1014,7 @@ impl<'tcx> LocationTree { |
| 1029 | 1014 | .perform_transition( |
| 1030 | 1015 | args.idx, |
| 1031 | 1016 | args.nodes, |
| 1032 | &mut args.data.wildcard_accesses, | |
| 1017 | &mut args.data.exposed_cache, | |
| 1033 | 1018 | access_kind, |
| 1034 | 1019 | args.rel_pos, |
| 1035 | 1020 | protected, |
| ... | ... | @@ -1074,12 +1059,18 @@ impl<'tcx> LocationTree { |
| 1074 | 1059 | access_kind: AccessKind, |
| 1075 | 1060 | global: &GlobalState, |
| 1076 | 1061 | diagnostics: &DiagnosticInfo, |
| 1062 | is_wildcard_tree: bool, | |
| 1077 | 1063 | ) -> InterpResult<'tcx> { |
| 1078 | 1064 | let get_relatedness = |idx: UniIndex, node: &Node, loc: &LocationTree| { |
| 1079 | let wildcard_state = loc.wildcard_accesses.get(idx).cloned().unwrap_or_default(); | |
| 1080 | 1065 | // If the tag is larger than `max_local_tag` then the access can only be foreign. |
| 1081 | 1066 | let only_foreign = max_local_tag.is_some_and(|max_local_tag| max_local_tag < node.tag); |
| 1082 | wildcard_state.access_relatedness(access_kind, only_foreign) | |
| 1067 | loc.exposed_cache.access_relatedness( | |
| 1068 | root, | |
| 1069 | idx, | |
| 1070 | access_kind, | |
| 1071 | is_wildcard_tree, | |
| 1072 | only_foreign, | |
| 1073 | ) | |
| 1083 | 1074 | }; |
| 1084 | 1075 | |
| 1085 | 1076 | // Whether there is an exposed node in this tree that allows this access. |
| ... | ... | @@ -1156,7 +1147,7 @@ impl<'tcx> LocationTree { |
| 1156 | 1147 | perm.perform_transition( |
| 1157 | 1148 | args.idx, |
| 1158 | 1149 | args.nodes, |
| 1159 | &mut args.data.wildcard_accesses, | |
| 1150 | &mut args.data.exposed_cache, | |
| 1160 | 1151 | access_kind, |
| 1161 | 1152 | relatedness, |
| 1162 | 1153 | protected, |
| ... | ... | @@ -1175,19 +1166,11 @@ impl<'tcx> LocationTree { |
| 1175 | 1166 | }) |
| 1176 | 1167 | }, |
| 1177 | 1168 | )?; |
| 1178 | // If there is no exposed node in this tree that allows this access, then the | |
| 1179 | // access *must* be foreign. So we check if the root of this tree would allow this | |
| 1180 | // as a foreign access, and if not, then we can error. | |
| 1181 | // In practice, all wildcard trees accept foreign accesses, but the main tree does | |
| 1182 | // not, so this catches UB when none of the nodes in the main tree allows this access. | |
| 1183 | if !has_valid_exposed | |
| 1184 | && self | |
| 1185 | .wildcard_accesses | |
| 1186 | .get(root) | |
| 1187 | .unwrap() | |
| 1188 | .access_relatedness(access_kind, /* only_foreign */ true) | |
| 1189 | .is_none() | |
| 1190 | { | |
| 1169 | // If there is no exposed node in this tree that allows this access, then the access *must* | |
| 1170 | // be foreign to the entire subtree. Foreign accesses are only possible on wildcard subtrees | |
| 1171 | // as there are no ancestors to the main root. So if we do not find a valid exposed node in | |
| 1172 | // the main tree then this access is UB. | |
| 1173 | if !has_valid_exposed && !is_wildcard_tree { | |
| 1191 | 1174 | return Err(no_valid_exposed_references_error(diagnostics)).into(); |
| 1192 | 1175 | } |
| 1193 | 1176 | interp_ok(()) |
src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs+1-1| ... | ... | @@ -741,7 +741,7 @@ mod spurious_read { |
| 741 | 741 | ); |
| 742 | 742 | eprintln!(" (arbitrary code instanciated with '{opaque}')"); |
| 743 | 743 | err += 1; |
| 744 | // We found an instanciation of the opaque code that makes this Pattern | |
| 744 | // We found an instantiation of the opaque code that makes this Pattern | |
| 745 | 745 | // fail, we don't really need to check the rest. |
| 746 | 746 | break; |
| 747 | 747 | } |
src/tools/miri/src/borrow_tracker/tree_borrows/tree_visitor.rs+1-1| ... | ... | @@ -99,7 +99,7 @@ where |
| 99 | 99 | assert!(self.stack.is_empty()); |
| 100 | 100 | // First, handle accessed node. A bunch of things need to |
| 101 | 101 | // be handled differently here compared to the further parents |
| 102 | // of `accesssed_node`. | |
| 102 | // of `accessesed_node`. | |
| 103 | 103 | { |
| 104 | 104 | self.propagate_at(this, accessed_node, AccessRelatedness::LocalAccess)?; |
| 105 | 105 | if matches!(visit_children, ChildrenVisitMode::VisitChildrenOfAccessed) { |
src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs+159-432| ... | ... | @@ -1,4 +1,3 @@ |
| 1 | use std::cmp::max; | |
| 2 | 1 | use std::fmt::Debug; |
| 3 | 2 | |
| 4 | 3 | use super::Tree; |
| ... | ... | @@ -51,374 +50,142 @@ impl WildcardAccessRelatedness { |
| 51 | 50 | } |
| 52 | 51 | } |
| 53 | 52 | |
| 53 | /// Caches information about where in the tree exposed nodes with permission to do reads/ rites are | |
| 54 | /// located. [`ExposedCache`] stores this information a single location (or rather, a range of | |
| 55 | /// homogeneous locations) for all nodes in an allocation. | |
| 56 | /// | |
| 57 | /// Nodes not in this map have a default [`ExposedCacheNode`], i.e. they have no exposed children. | |
| 58 | /// In particular, this map remains empty (and thus consumes no memory) until the first | |
| 59 | /// node in the tree gets exposed. | |
| 60 | #[derive(Clone, Debug, Default, PartialEq, Eq)] | |
| 61 | pub struct ExposedCache(UniValMap<ExposedCacheNode>); | |
| 62 | ||
| 54 | 63 | /// State per location per node keeping track of where relative to this |
| 55 | 64 | /// node exposed nodes are and what access permissions they have. |
| 56 | /// | |
| 57 | /// Designed to be completely determined by its parent, siblings and | |
| 58 | /// direct children's max_local_access/max_foreign_access. | |
| 59 | #[derive(Clone, Default, PartialEq, Eq)] | |
| 60 | pub struct WildcardState { | |
| 61 | /// How many of this node's direct children have `max_local_access()==Write`. | |
| 62 | child_writes: u16, | |
| 63 | /// How many of this node's direct children have `max_local_access()>=Read`. | |
| 64 | child_reads: u16, | |
| 65 | /// The maximum access level that could happen from an exposed node | |
| 66 | /// that is foreign to this node. | |
| 67 | /// | |
| 68 | /// This is calculated as the `max()` of the parent's `max_foreign_access`, | |
| 69 | /// `exposed_as` and the siblings' `max_local_access()`. | |
| 70 | max_foreign_access: WildcardAccessLevel, | |
| 71 | /// At what access level this node itself is exposed. | |
| 72 | exposed_as: WildcardAccessLevel, | |
| 73 | } | |
| 74 | impl Debug for WildcardState { | |
| 75 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| 76 | f.debug_struct("WildcardState") | |
| 77 | .field("child_r/w", &(self.child_reads, self.child_writes)) | |
| 78 | .field("foreign", &self.max_foreign_access) | |
| 79 | .field("exposed_as", &self.exposed_as) | |
| 80 | .finish() | |
| 81 | } | |
| 65 | #[derive(Clone, Default, Debug, PartialEq, Eq)] | |
| 66 | struct ExposedCacheNode { | |
| 67 | /// How many local nodes (in this subtree) are exposed with write permissions. | |
| 68 | local_writes: u16, | |
| 69 | /// How many local nodes (in this subtree) are exposed with read permissions. | |
| 70 | local_reads: u16, | |
| 82 | 71 | } |
| 83 | impl WildcardState { | |
| 84 | /// The maximum access level that could happen from an exposed | |
| 85 | /// node that is local to this node. | |
| 86 | fn max_local_access(&self) -> WildcardAccessLevel { | |
| 87 | use WildcardAccessLevel::*; | |
| 88 | max( | |
| 89 | self.exposed_as, | |
| 90 | if self.child_writes > 0 { | |
| 91 | Write | |
| 92 | } else if self.child_reads > 0 { | |
| 93 | Read | |
| 94 | } else { | |
| 95 | None | |
| 96 | }, | |
| 97 | ) | |
| 98 | } | |
| 99 | 72 | |
| 100 | /// From where relative to the node with this wildcard info a read or write access could happen. | |
| 101 | /// If `only_foreign` is true then we treat `LocalAccess` as impossible. This means we return | |
| 102 | /// `None` if only a `LocalAccess` is possible, and we treat `EitherAccess` as a | |
| 103 | /// `ForeignAccess`. | |
| 73 | impl ExposedCache { | |
| 74 | /// Returns the relatedness of a wildcard access to a node. | |
| 75 | /// | |
| 76 | /// This function only considers a single subtree. If the current subtree does not contain | |
| 77 | /// any valid exposed nodes then the function return `None`. | |
| 78 | /// | |
| 79 | /// * `root`: The root of the subtree the node belongs to. | |
| 80 | /// * `id`: The id of the node. | |
| 81 | /// * `kind`: The kind of the wildcard access. | |
| 82 | /// * `is_wildcard_tree`: This nodes belongs to a wildcard subtree. | |
| 83 | /// This means we always treat foreign accesses as possible. | |
| 84 | /// * `only_foreign`: Assume the access cannot come from a local node. | |
| 104 | 85 | pub fn access_relatedness( |
| 105 | 86 | &self, |
| 87 | root: UniIndex, | |
| 88 | id: UniIndex, | |
| 106 | 89 | kind: AccessKind, |
| 90 | is_wildcard_tree: bool, | |
| 107 | 91 | only_foreign: bool, |
| 108 | 92 | ) -> Option<WildcardAccessRelatedness> { |
| 109 | let rel = match kind { | |
| 110 | AccessKind::Read => self.read_access_relatedness(), | |
| 111 | AccessKind::Write => self.write_access_relatedness(), | |
| 93 | // All nodes in the tree are local to the root, so we can use the root to get the total | |
| 94 | // number of valid exposed nodes in the tree. | |
| 95 | let root = self.0.get(root).cloned().unwrap_or_default(); | |
| 96 | let node = self.0.get(id).cloned().unwrap_or_default(); | |
| 97 | ||
| 98 | let (total_num, local_num) = match kind { | |
| 99 | AccessKind::Read => (root.local_reads, node.local_reads), | |
| 100 | AccessKind::Write => (root.local_writes, node.local_writes), | |
| 112 | 101 | }; |
| 113 | if only_foreign { | |
| 114 | use WildcardAccessRelatedness as E; | |
| 115 | match rel { | |
| 116 | Some(E::EitherAccess | E::ForeignAccess) => Some(E::ForeignAccess), | |
| 117 | Some(E::LocalAccess) | None => None, | |
| 118 | } | |
| 119 | } else { | |
| 120 | rel | |
| 121 | } | |
| 122 | } | |
| 123 | 102 | |
| 124 | /// From where relative to the node with this wildcard info a read access could happen. | |
| 125 | fn read_access_relatedness(&self) -> Option<WildcardAccessRelatedness> { | |
| 126 | let has_foreign = self.max_foreign_access >= WildcardAccessLevel::Read; | |
| 127 | let has_local = self.max_local_access() >= WildcardAccessLevel::Read; | |
| 128 | use WildcardAccessRelatedness as E; | |
| 129 | match (has_foreign, has_local) { | |
| 130 | (true, true) => Some(E::EitherAccess), | |
| 131 | (true, false) => Some(E::ForeignAccess), | |
| 132 | (false, true) => Some(E::LocalAccess), | |
| 133 | (false, false) => None, | |
| 134 | } | |
| 135 | } | |
| 136 | ||
| 137 | /// From where relative to the node with this wildcard info a write access could happen. | |
| 138 | fn write_access_relatedness(&self) -> Option<WildcardAccessRelatedness> { | |
| 139 | let has_foreign = self.max_foreign_access == WildcardAccessLevel::Write; | |
| 140 | let has_local = self.max_local_access() == WildcardAccessLevel::Write; | |
| 141 | use WildcardAccessRelatedness as E; | |
| 142 | match (has_foreign, has_local) { | |
| 143 | (true, true) => Some(E::EitherAccess), | |
| 144 | (true, false) => Some(E::ForeignAccess), | |
| 145 | (false, true) => Some(E::LocalAccess), | |
| 146 | (false, false) => None, | |
| 147 | } | |
| 148 | } | |
| 149 | ||
| 150 | /// Gets the access tracking information for a new child node of a parent with this | |
| 151 | /// wildcard info. | |
| 152 | /// The new node doesn't have any child reads/writes, but calculates `max_foreign_access` | |
| 153 | /// from its parent. | |
| 154 | pub fn for_new_child(&self) -> Self { | |
| 155 | Self { | |
| 156 | max_foreign_access: max(self.max_foreign_access, self.max_local_access()), | |
| 157 | ..Default::default() | |
| 158 | } | |
| 159 | } | |
| 160 | /// Crates the initial `WildcardState` for a wildcard root. | |
| 161 | /// This has `max_foreign_access==Write` as it actually is the child of *some* exposed node | |
| 162 | /// through which we can receive foreign accesses. | |
| 163 | /// | |
| 164 | /// This is different from the main root which has `max_foreign_access==None`, since there | |
| 165 | /// cannot be a foreign access to the root of the allocation. | |
| 166 | pub fn for_wildcard_root() -> Self { | |
| 167 | Self { max_foreign_access: WildcardAccessLevel::Write, ..Default::default() } | |
| 168 | } | |
| 169 | ||
| 170 | /// Pushes the nodes of `children` onto the stack who's `max_foreign_access` | |
| 171 | /// needs to be updated. | |
| 172 | /// | |
| 173 | /// * `children`: A list of nodes with the same parent. `children` doesn't | |
| 174 | /// necessarily have to contain all children of parent, but can just be | |
| 175 | /// a subset. | |
| 176 | /// | |
| 177 | /// * `child_reads`, `child_writes`: How many of `children` have `max_local_access()` | |
| 178 | /// of at least `read`/`write` | |
| 179 | /// | |
| 180 | /// * `new_foreign_access`, `old_foreign_access`: | |
| 181 | /// The max possible access level that is foreign to all `children` | |
| 182 | /// (i.e., it is not local to *any* of them). | |
| 183 | /// This can be calculated as the max of the parent's `exposed_as()`, `max_foreign_access` | |
| 184 | /// and of all `max_local_access()` of any nodes with the same parent that are | |
| 185 | /// not listed in `children`. | |
| 186 | /// | |
| 187 | /// This access level changed from `old` to `new`, which is why we need to | |
| 188 | /// update `children`. | |
| 189 | fn push_relevant_children( | |
| 190 | stack: &mut Vec<(UniIndex, WildcardAccessLevel)>, | |
| 191 | new_foreign_access: WildcardAccessLevel, | |
| 192 | old_foreign_access: WildcardAccessLevel, | |
| 193 | child_reads: u16, | |
| 194 | child_writes: u16, | |
| 195 | children: impl Iterator<Item = UniIndex>, | |
| 196 | ||
| 197 | wildcard_accesses: &UniValMap<WildcardState>, | |
| 198 | ) { | |
| 199 | use WildcardAccessLevel::*; | |
| 200 | ||
| 201 | // Nothing changed so we don't need to update anything. | |
| 202 | if new_foreign_access == old_foreign_access { | |
| 203 | return; | |
| 204 | } | |
| 205 | ||
| 206 | // We need to consider that the children's `max_local_access()` affect each | |
| 207 | // other's `max_foreign_access`, but do not affect their own `max_foreign_access`. | |
| 208 | ||
| 209 | // The new `max_foreign_acces` for children with `max_local_access()==Write`. | |
| 210 | let write_foreign_access = max( | |
| 211 | new_foreign_access, | |
| 212 | if child_writes > 1 { | |
| 213 | // There exists at least one more child with exposed write access. | |
| 214 | // This means that a foreign write through that node is possible. | |
| 215 | Write | |
| 216 | } else if child_reads > 1 { | |
| 217 | // There exists at least one more child with exposed read access, | |
| 218 | // but no other with write access. | |
| 219 | // This means that a foreign read but no write through that node | |
| 220 | // is possible. | |
| 221 | Read | |
| 222 | } else { | |
| 223 | // There are no other nodes with read or write access. | |
| 224 | // This means no foreign writes through other children are possible. | |
| 225 | None | |
| 226 | }, | |
| 227 | ); | |
| 228 | ||
| 229 | // The new `max_foreign_acces` for children with `max_local_access()==Read`. | |
| 230 | let read_foreign_access = max( | |
| 231 | new_foreign_access, | |
| 232 | if child_writes > 0 { | |
| 233 | // There exists at least one child with write access (and it's not this one). | |
| 234 | Write | |
| 235 | } else if child_reads > 1 { | |
| 236 | // There exists at least one more child with exposed read access, | |
| 237 | // but no other with write access. | |
| 238 | Read | |
| 239 | } else { | |
| 240 | // There are no other nodes with read or write access, | |
| 241 | None | |
| 242 | }, | |
| 243 | ); | |
| 244 | ||
| 245 | // The new `max_foreign_acces` for children with `max_local_access()==None`. | |
| 246 | let none_foreign_access = max( | |
| 247 | new_foreign_access, | |
| 248 | if child_writes > 0 { | |
| 249 | // There exists at least one child with write access (and it's not this one). | |
| 250 | Write | |
| 251 | } else if child_reads > 0 { | |
| 252 | // There exists at least one child with read access (and it's not this one), | |
| 253 | // but none with write access. | |
| 254 | Read | |
| 103 | // If this is a wildcard tree then an access can always be foreign as | |
| 104 | // it could come from another tree. | |
| 105 | // We can represent this by adding 1 to the total which means there | |
| 106 | // always exists a foreign exposed node. | |
| 107 | // (We cannot bake this into the root's count as then if `node == root` it would | |
| 108 | // affect both `total` and `local`.) | |
| 109 | let total_num = total_num + u16::from(is_wildcard_tree); | |
| 110 | ||
| 111 | use WildcardAccessRelatedness::*; | |
| 112 | let relatedness = if total_num == 0 { | |
| 113 | // we return None if the tree does not contain any valid exposed nodes. | |
| 114 | None | |
| 115 | } else { | |
| 116 | Some(if total_num == local_num { | |
| 117 | // If all valid exposed nodes are local to this node then the access is local. | |
| 118 | LocalAccess | |
| 119 | } else if local_num == 0 { | |
| 120 | // If the node does not have any exposed nodes as children then the access is foreign. | |
| 121 | ForeignAccess | |
| 255 | 122 | } else { |
| 256 | // No children are exposed as read or write. | |
| 257 | None | |
| 258 | }, | |
| 259 | ); | |
| 260 | ||
| 261 | stack.extend(children.filter_map(|child| { | |
| 262 | let state = wildcard_accesses.get(child).cloned().unwrap_or_default(); | |
| 263 | ||
| 264 | let new_foreign_access = match state.max_local_access() { | |
| 265 | Write => write_foreign_access, | |
| 266 | Read => read_foreign_access, | |
| 267 | None => none_foreign_access, | |
| 268 | }; | |
| 123 | // If some but not all of the valid exposed nodes are local then we cannot determine the correct relatedness. | |
| 124 | EitherAccess | |
| 125 | }) | |
| 126 | }; | |
| 269 | 127 | |
| 270 | if new_foreign_access != state.max_foreign_access { | |
| 271 | Some((child, new_foreign_access)) | |
| 272 | } else { | |
| 273 | Option::None | |
| 128 | if only_foreign { | |
| 129 | // This is definitely not a local access; clamp the result accordingly. | |
| 130 | match relatedness { | |
| 131 | Some(LocalAccess) => None, | |
| 132 | Some(ForeignAccess) => Some(ForeignAccess), | |
| 133 | Some(EitherAccess) => Some(ForeignAccess), | |
| 134 | None => None, | |
| 274 | 135 | } |
| 275 | })); | |
| 136 | } else { | |
| 137 | relatedness | |
| 138 | } | |
| 276 | 139 | } |
| 277 | ||
| 278 | 140 | /// Update the tracking information of a tree, to reflect that the node specified by `id` is |
| 279 | /// now exposed with `new_exposed_as`. | |
| 141 | /// now exposed with `new_exposed_as` permission. | |
| 280 | 142 | /// |
| 281 | 143 | /// Propagates the Willard access information over the tree. This needs to be called every |
| 282 | 144 | /// time the access level of an exposed node changes, to keep the state in sync with |
| 283 | 145 | /// the rest of the tree. |
| 146 | /// | |
| 147 | /// * `from`: The previous access level of the exposed node. | |
| 148 | /// Set to `None` if the node was not exposed before. | |
| 149 | /// * `to`: The new access level. | |
| 284 | 150 | pub fn update_exposure( |
| 285 | id: UniIndex, | |
| 286 | new_exposed_as: WildcardAccessLevel, | |
| 151 | &mut self, | |
| 287 | 152 | nodes: &UniValMap<Node>, |
| 288 | wildcard_accesses: &mut UniValMap<WildcardState>, | |
| 153 | id: UniIndex, | |
| 154 | from: WildcardAccessLevel, | |
| 155 | to: WildcardAccessLevel, | |
| 289 | 156 | ) { |
| 290 | let mut entry = wildcard_accesses.entry(id); | |
| 291 | let src_state = entry.or_insert(Default::default()); | |
| 292 | let old_exposed_as = src_state.exposed_as; | |
| 293 | ||
| 294 | 157 | // If the exposure doesn't change, then we don't need to update anything. |
| 295 | if old_exposed_as == new_exposed_as { | |
| 158 | if from == to { | |
| 296 | 159 | return; |
| 297 | 160 | } |
| 298 | 161 | |
| 299 | let src_old_local_access = src_state.max_local_access(); | |
| 300 | ||
| 301 | src_state.exposed_as = new_exposed_as; | |
| 302 | ||
| 303 | let src_new_local_access = src_state.max_local_access(); | |
| 304 | ||
| 305 | // Stack of nodes for which the max_foreign_access field needs to be updated. | |
| 306 | // Will be filled with the children of this node and its parents children before | |
| 307 | // we begin downwards traversal. | |
| 308 | let mut stack: Vec<(UniIndex, WildcardAccessLevel)> = Vec::new(); | |
| 309 | ||
| 310 | // Add the direct children of this node to the stack. | |
| 311 | { | |
| 162 | // Update the counts of this node and all its ancestors. | |
| 163 | let mut next_id = Some(id); | |
| 164 | while let Some(id) = next_id { | |
| 312 | 165 | let node = nodes.get(id).unwrap(); |
| 313 | Self::push_relevant_children( | |
| 314 | &mut stack, | |
| 315 | // new_foreign_access | |
| 316 | max(src_state.max_foreign_access, new_exposed_as), | |
| 317 | // old_foreign_access | |
| 318 | max(src_state.max_foreign_access, old_exposed_as), | |
| 319 | // Consider all children. | |
| 320 | src_state.child_reads, | |
| 321 | src_state.child_writes, | |
| 322 | node.children.iter().copied(), | |
| 323 | wildcard_accesses, | |
| 324 | ); | |
| 325 | } | |
| 326 | // We need to propagate the tracking info up the tree, for this we traverse | |
| 327 | // up the parents. | |
| 328 | // We can skip propagating info to the parent and siblings of a node if its | |
| 329 | // access didn't change. | |
| 330 | { | |
| 331 | // The child from which we came. | |
| 332 | let mut child = id; | |
| 333 | // This is the `max_local_access()` of the child we came from, before | |
| 334 | // this update... | |
| 335 | let mut old_child_access = src_old_local_access; | |
| 336 | // and after this update. | |
| 337 | let mut new_child_access = src_new_local_access; | |
| 338 | while let Some(parent_id) = nodes.get(child).unwrap().parent { | |
| 339 | let parent_node = nodes.get(parent_id).unwrap(); | |
| 340 | let mut entry = wildcard_accesses.entry(parent_id); | |
| 341 | let parent_state = entry.or_insert(Default::default()); | |
| 342 | ||
| 343 | let old_parent_local_access = parent_state.max_local_access(); | |
| 344 | use WildcardAccessLevel::*; | |
| 345 | // Updating this node's tracking state for its children. | |
| 346 | match (old_child_access, new_child_access) { | |
| 347 | (None | Read, Write) => parent_state.child_writes += 1, | |
| 348 | (Write, None | Read) => parent_state.child_writes -= 1, | |
| 349 | _ => {} | |
| 350 | } | |
| 351 | match (old_child_access, new_child_access) { | |
| 352 | (None, Read | Write) => parent_state.child_reads += 1, | |
| 353 | (Read | Write, None) => parent_state.child_reads -= 1, | |
| 354 | _ => {} | |
| 355 | } | |
| 356 | ||
| 357 | let new_parent_local_access = parent_state.max_local_access(); | |
| 358 | ||
| 359 | { | |
| 360 | // We need to update the `max_foreign_access` of `child`'s | |
| 361 | // siblings. For this we can reuse the `push_relevant_children` | |
| 362 | // function. | |
| 363 | // | |
| 364 | // We pass it just the siblings without child itself. Since | |
| 365 | // `child`'s `max_local_access()` is foreign to all of its | |
| 366 | // siblings we can pass it as part of the foreign access. | |
| 367 | ||
| 368 | let parent_access = | |
| 369 | max(parent_state.exposed_as, parent_state.max_foreign_access); | |
| 370 | // This is how many of `child`'s siblings have read/write local access. | |
| 371 | // If `child` itself has access, then we need to subtract its access from the count. | |
| 372 | let sibling_reads = | |
| 373 | parent_state.child_reads - if new_child_access >= Read { 1 } else { 0 }; | |
| 374 | let sibling_writes = | |
| 375 | parent_state.child_writes - if new_child_access >= Write { 1 } else { 0 }; | |
| 376 | Self::push_relevant_children( | |
| 377 | &mut stack, | |
| 378 | // new_foreign_access | |
| 379 | max(parent_access, new_child_access), | |
| 380 | // old_foreign_access | |
| 381 | max(parent_access, old_child_access), | |
| 382 | // Consider only siblings of child. | |
| 383 | sibling_reads, | |
| 384 | sibling_writes, | |
| 385 | parent_node.children.iter().copied().filter(|id| child != *id), | |
| 386 | wildcard_accesses, | |
| 387 | ); | |
| 388 | } | |
| 389 | if old_parent_local_access == new_parent_local_access { | |
| 390 | // We didn't change `max_local_access()` for parent, so we don't need to propagate further upwards. | |
| 391 | break; | |
| 392 | } | |
| 393 | ||
| 394 | old_child_access = old_parent_local_access; | |
| 395 | new_child_access = new_parent_local_access; | |
| 396 | child = parent_id; | |
| 166 | let mut state = self.0.entry(id); | |
| 167 | let state = state.or_insert(Default::default()); | |
| 168 | ||
| 169 | use WildcardAccessLevel::*; | |
| 170 | match (from, to) { | |
| 171 | (None | Read, Write) => state.local_writes += 1, | |
| 172 | (Write, None | Read) => state.local_writes -= 1, | |
| 173 | _ => {} | |
| 397 | 174 | } |
| 398 | } | |
| 399 | // Traverses down the tree to update max_foreign_access fields of children and cousins who need to be updated. | |
| 400 | while let Some((id, new_access)) = stack.pop() { | |
| 401 | let node = nodes.get(id).unwrap(); | |
| 402 | let mut entry = wildcard_accesses.entry(id); | |
| 403 | let state = entry.or_insert(Default::default()); | |
| 404 | ||
| 405 | let old_access = state.max_foreign_access; | |
| 406 | state.max_foreign_access = new_access; | |
| 407 | ||
| 408 | Self::push_relevant_children( | |
| 409 | &mut stack, | |
| 410 | // new_foreign_access | |
| 411 | max(state.exposed_as, new_access), | |
| 412 | // old_foreign_access | |
| 413 | max(state.exposed_as, old_access), | |
| 414 | // Consider all children. | |
| 415 | state.child_reads, | |
| 416 | state.child_writes, | |
| 417 | node.children.iter().copied(), | |
| 418 | wildcard_accesses, | |
| 419 | ); | |
| 175 | match (from, to) { | |
| 176 | (None, Read | Write) => state.local_reads += 1, | |
| 177 | (Read | Write, None) => state.local_reads -= 1, | |
| 178 | _ => {} | |
| 179 | } | |
| 180 | next_id = node.parent; | |
| 420 | 181 | } |
| 421 | 182 | } |
| 183 | /// Removes a node from the datastructure. | |
| 184 | /// | |
| 185 | /// The caller needs to ensure that the node does not have any children. | |
| 186 | pub fn remove(&mut self, idx: UniIndex) { | |
| 187 | self.0.remove(idx); | |
| 188 | } | |
| 422 | 189 | } |
| 423 | 190 | |
| 424 | 191 | impl Tree { |
| ... | ... | @@ -428,25 +195,28 @@ impl Tree { |
| 428 | 195 | pub fn expose_tag(&mut self, tag: BorTag, protected: bool) { |
| 429 | 196 | let id = self.tag_mapping.get(&tag).unwrap(); |
| 430 | 197 | let node = self.nodes.get_mut(id).unwrap(); |
| 431 | node.is_exposed = true; | |
| 432 | let node = self.nodes.get(id).unwrap(); | |
| 433 | ||
| 434 | // When the first tag gets exposed then we initialize the | |
| 435 | // wildcard state for every node and location in the tree. | |
| 436 | for (_, loc) in self.locations.iter_mut_all() { | |
| 437 | let perm = loc | |
| 438 | .perms | |
| 439 | .get(id) | |
| 440 | .map(|p| p.permission()) | |
| 441 | .unwrap_or_else(|| node.default_location_state().permission()); | |
| 442 | ||
| 443 | let access_type = perm.strongest_allowed_local_access(protected); | |
| 444 | WildcardState::update_exposure( | |
| 445 | id, | |
| 446 | access_type, | |
| 447 | &self.nodes, | |
| 448 | &mut loc.wildcard_accesses, | |
| 449 | ); | |
| 198 | if !node.is_exposed { | |
| 199 | node.is_exposed = true; | |
| 200 | let node = self.nodes.get(id).unwrap(); | |
| 201 | ||
| 202 | for (_, loc) in self.locations.iter_mut_all() { | |
| 203 | let perm = loc | |
| 204 | .perms | |
| 205 | .get(id) | |
| 206 | .map(|p| p.permission()) | |
| 207 | .unwrap_or_else(|| node.default_location_state().permission()); | |
| 208 | ||
| 209 | let access_level = perm.strongest_allowed_local_access(protected); | |
| 210 | // An unexposed node gets treated as access level `None`. Therefore, | |
| 211 | // the initial exposure transitions from `None` to the node's actual | |
| 212 | // `access_level`. | |
| 213 | loc.exposed_cache.update_exposure( | |
| 214 | &self.nodes, | |
| 215 | id, | |
| 216 | WildcardAccessLevel::None, | |
| 217 | access_level, | |
| 218 | ); | |
| 219 | } | |
| 450 | 220 | } |
| 451 | 221 | } |
| 452 | 222 | |
| ... | ... | @@ -457,10 +227,19 @@ impl Tree { |
| 457 | 227 | |
| 458 | 228 | // We check if the node is already exposed, as we don't want to expose any |
| 459 | 229 | // nodes which aren't already exposed. |
| 460 | ||
| 461 | if self.nodes.get(idx).unwrap().is_exposed { | |
| 462 | // Updates the exposure to the new permission on every location. | |
| 463 | self.expose_tag(tag, /* protected */ false); | |
| 230 | let node = self.nodes.get(idx).unwrap(); | |
| 231 | if node.is_exposed { | |
| 232 | for (_, loc) in self.locations.iter_mut_all() { | |
| 233 | let perm = loc | |
| 234 | .perms | |
| 235 | .get(idx) | |
| 236 | .map(|p| p.permission()) | |
| 237 | .unwrap_or_else(|| node.default_location_state().permission()); | |
| 238 | // We are transitioning from protected to unprotected. | |
| 239 | let old_access_type = perm.strongest_allowed_local_access(/*protected*/ true); | |
| 240 | let access_type = perm.strongest_allowed_local_access(/*protected*/ false); | |
| 241 | loc.exposed_cache.update_exposure(&self.nodes, idx, old_access_type, access_type); | |
| 242 | } | |
| 464 | 243 | } |
| 465 | 244 | } |
| 466 | 245 | } |
| ... | ... | @@ -472,20 +251,15 @@ impl Tree { |
| 472 | 251 | pub fn verify_wildcard_consistency(&self, global: &GlobalState) { |
| 473 | 252 | // We rely on the fact that `roots` is ordered according to tag from low to high. |
| 474 | 253 | assert!(self.roots.is_sorted_by_key(|idx| self.nodes.get(*idx).unwrap().tag)); |
| 475 | let main_root_idx = self.roots[0]; | |
| 476 | 254 | |
| 477 | 255 | let protected_tags = &global.borrow().protected_tags; |
| 478 | 256 | for (_, loc) in self.locations.iter_all() { |
| 479 | let wildcard_accesses = &loc.wildcard_accesses; | |
| 257 | let exposed_cache = &loc.exposed_cache; | |
| 480 | 258 | let perms = &loc.perms; |
| 481 | // Checks if accesses is empty. | |
| 482 | if wildcard_accesses.is_empty() { | |
| 483 | return; | |
| 484 | } | |
| 485 | 259 | for (id, node) in self.nodes.iter() { |
| 486 | let state = wildcard_accesses.get(id).unwrap(); | |
| 260 | let state = exposed_cache.0.get(id).cloned().unwrap_or_default(); | |
| 487 | 261 | |
| 488 | let expected_exposed_as = if node.is_exposed { | |
| 262 | let exposed_as = if node.is_exposed { | |
| 489 | 263 | let perm = |
| 490 | 264 | perms.get(id).copied().unwrap_or_else(|| node.default_location_state()); |
| 491 | 265 | |
| ... | ... | @@ -495,72 +269,25 @@ impl Tree { |
| 495 | 269 | WildcardAccessLevel::None |
| 496 | 270 | }; |
| 497 | 271 | |
| 498 | // The foreign wildcard accesses possible at a node are determined by which | |
| 499 | // accesses can originate from their siblings, their parent, and from above | |
| 500 | // their parent. | |
| 501 | let expected_max_foreign_access = if let Some(parent) = node.parent { | |
| 502 | let parent_node = self.nodes.get(parent).unwrap(); | |
| 503 | let parent_state = wildcard_accesses.get(parent).unwrap(); | |
| 504 | ||
| 505 | let max_sibling_access = parent_node | |
| 506 | .children | |
| 507 | .iter() | |
| 508 | .copied() | |
| 509 | .filter(|child| *child != id) | |
| 510 | .map(|child| { | |
| 511 | let state = wildcard_accesses.get(child).unwrap(); | |
| 512 | state.max_local_access() | |
| 513 | }) | |
| 514 | .fold(WildcardAccessLevel::None, max); | |
| 515 | ||
| 516 | max_sibling_access | |
| 517 | .max(parent_state.max_foreign_access) | |
| 518 | .max(parent_state.exposed_as) | |
| 519 | } else { | |
| 520 | if main_root_idx == id { | |
| 521 | // There can never be a foreign access to the root of the allocation. | |
| 522 | // So its foreign access level is always `None`. | |
| 523 | WildcardAccessLevel::None | |
| 524 | } else { | |
| 525 | // For wildcard roots any access on a different subtree can be foreign | |
| 526 | // to it. So a wildcard root has the maximum possible foreign access | |
| 527 | // level. | |
| 528 | WildcardAccessLevel::Write | |
| 529 | } | |
| 530 | }; | |
| 531 | ||
| 532 | // Count how many children can be the source of wildcard reads or writes | |
| 533 | // (either directly, or via their children). | |
| 534 | let child_accesses = node.children.iter().copied().map(|child| { | |
| 535 | let state = wildcard_accesses.get(child).unwrap(); | |
| 536 | state.max_local_access() | |
| 537 | }); | |
| 538 | let expected_child_reads = | |
| 539 | child_accesses.clone().filter(|a| *a >= WildcardAccessLevel::Read).count(); | |
| 540 | let expected_child_writes = | |
| 541 | child_accesses.filter(|a| *a >= WildcardAccessLevel::Write).count(); | |
| 542 | ||
| 543 | assert_eq!( | |
| 544 | expected_exposed_as, state.exposed_as, | |
| 545 | "tag {:?} (id:{id:?}) should be exposed as {expected_exposed_as:?} but is exposed as {:?}", | |
| 546 | node.tag, state.exposed_as | |
| 547 | ); | |
| 548 | assert_eq!( | |
| 549 | expected_max_foreign_access, state.max_foreign_access, | |
| 550 | "expected {:?}'s (id:{id:?}) max_foreign_access to be {:?} instead of {:?}", | |
| 551 | node.tag, expected_max_foreign_access, state.max_foreign_access | |
| 552 | ); | |
| 553 | let child_reads: usize = state.child_reads.into(); | |
| 272 | let (child_reads, child_writes) = node | |
| 273 | .children | |
| 274 | .iter() | |
| 275 | .copied() | |
| 276 | .map(|id| exposed_cache.0.get(id).cloned().unwrap_or_default()) | |
| 277 | .fold((0, 0), |acc, wc| (acc.0 + wc.local_reads, acc.1 + wc.local_writes)); | |
| 278 | let expected_reads = | |
| 279 | child_reads + u16::from(exposed_as >= WildcardAccessLevel::Read); | |
| 280 | let expected_writes = | |
| 281 | child_writes + u16::from(exposed_as >= WildcardAccessLevel::Write); | |
| 554 | 282 | assert_eq!( |
| 555 | expected_child_reads, child_reads, | |
| 556 | "expected {:?}'s (id:{id:?}) child_reads to be {} instead of {}", | |
| 557 | node.tag, expected_child_reads, child_reads | |
| 283 | state.local_reads, expected_reads, | |
| 284 | "expected {:?}'s (id:{id:?}) local_reads to be {expected_reads:?} instead of {:?} (child_reads: {child_reads:?}, exposed_as: {exposed_as:?})", | |
| 285 | node.tag, state.local_reads | |
| 558 | 286 | ); |
| 559 | let child_writes: usize = state.child_writes.into(); | |
| 560 | 287 | assert_eq!( |
| 561 | expected_child_writes, child_writes, | |
| 562 | "expected {:?}'s (id:{id:?}) child_writes to be {} instead of {}", | |
| 563 | node.tag, expected_child_writes, child_writes | |
| 288 | state.local_writes, expected_writes, | |
| 289 | "expected {:?}'s (id:{id:?}) local_writes to be {expected_writes:?} instead of {:?} (child_writes: {child_writes:?}, exposed_as: {exposed_as:?})", | |
| 290 | node.tag, state.local_writes | |
| 564 | 291 | ); |
| 565 | 292 | } |
| 566 | 293 | } |
src/tools/miri/src/concurrency/data_race.rs+1-1| ... | ... | @@ -371,7 +371,7 @@ impl AccessType { |
| 371 | 371 | |
| 372 | 372 | if let Some(size) = size { |
| 373 | 373 | if size == Size::ZERO { |
| 374 | // In this case there were multiple read accesss with different sizes and then a write. | |
| 374 | // In this case there were multiple read accesses with different sizes and then a write. | |
| 375 | 375 | // We will be reporting *one* of the other reads, but we don't have enough information |
| 376 | 376 | // to determine which one had which size. |
| 377 | 377 | assert!(self == AccessType::AtomicLoad); |
src/tools/miri/src/concurrency/genmc/global_allocations.rs+1-1| ... | ... | @@ -62,7 +62,7 @@ impl GlobalStateInner { |
| 62 | 62 | let entry = match self.base_addr.entry(alloc_id) { |
| 63 | 63 | Entry::Occupied(occupied_entry) => { |
| 64 | 64 | // Looks like some other thread allocated this for us |
| 65 | // between when we released the read lock and aquired the write lock, | |
| 65 | // between when we released the read lock and acquired the write lock, | |
| 66 | 66 | // so we just return that value. |
| 67 | 67 | return interp_ok(*occupied_entry.get()); |
| 68 | 68 | } |
src/tools/miri/src/concurrency/genmc/mod.rs+5-5| ... | ... | @@ -252,7 +252,7 @@ impl GenmcCtx { |
| 252 | 252 | /// Inform GenMC about an atomic load. |
| 253 | 253 | /// Returns that value that the load should read. |
| 254 | 254 | /// |
| 255 | /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. | |
| 255 | /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitialized. | |
| 256 | 256 | pub(crate) fn atomic_load<'tcx>( |
| 257 | 257 | &self, |
| 258 | 258 | ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, |
| ... | ... | @@ -275,7 +275,7 @@ impl GenmcCtx { |
| 275 | 275 | /// Inform GenMC about an atomic store. |
| 276 | 276 | /// Returns `true` if the stored value should be reflected in Miri's memory. |
| 277 | 277 | /// |
| 278 | /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. | |
| 278 | /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitialized. | |
| 279 | 279 | pub(crate) fn atomic_store<'tcx>( |
| 280 | 280 | &self, |
| 281 | 281 | ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, |
| ... | ... | @@ -320,7 +320,7 @@ impl GenmcCtx { |
| 320 | 320 | /// |
| 321 | 321 | /// Returns `(old_val, Option<new_val>)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. |
| 322 | 322 | /// |
| 323 | /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. | |
| 323 | /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitialized. | |
| 324 | 324 | pub(crate) fn atomic_rmw_op<'tcx>( |
| 325 | 325 | &self, |
| 326 | 326 | ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, |
| ... | ... | @@ -345,7 +345,7 @@ impl GenmcCtx { |
| 345 | 345 | |
| 346 | 346 | /// Returns `(old_val, Option<new_val>)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. |
| 347 | 347 | /// |
| 348 | /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. | |
| 348 | /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitialized. | |
| 349 | 349 | pub(crate) fn atomic_exchange<'tcx>( |
| 350 | 350 | &self, |
| 351 | 351 | ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, |
| ... | ... | @@ -370,7 +370,7 @@ impl GenmcCtx { |
| 370 | 370 | /// |
| 371 | 371 | /// Returns the old value read by the compare exchange, optionally the value that Miri should write back to its memory, and whether the compare-exchange was a success or not. |
| 372 | 372 | /// |
| 373 | /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. | |
| 373 | /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitialized. | |
| 374 | 374 | pub(crate) fn atomic_compare_exchange<'tcx>( |
| 375 | 375 | &self, |
| 376 | 376 | ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, |
src/tools/miri/src/concurrency/genmc/run.rs+1-1| ... | ... | @@ -30,7 +30,7 @@ pub fn run_genmc_mode<'tcx>( |
| 30 | 30 | config: &MiriConfig, |
| 31 | 31 | eval_entry: impl Fn(Rc<GenmcCtx>) -> Result<(), NonZeroI32>, |
| 32 | 32 | ) -> Result<(), NonZeroI32> { |
| 33 | // Check for supported target: endianess and pointer size must match the host. | |
| 33 | // Check for supported target: endianness and pointer size must match the host. | |
| 34 | 34 | if tcx.data_layout.endian != Endian::Little || tcx.data_layout.pointer_size().bits() != 64 { |
| 35 | 35 | tcx.dcx().fatal("GenMC only supports 64bit little-endian targets"); |
| 36 | 36 | } |
src/tools/miri/src/concurrency/weak_memory.rs+1-1| ... | ... | @@ -389,7 +389,7 @@ impl<'tcx> StoreBuffer { |
| 389 | 389 | }) |
| 390 | 390 | .filter(|&store_elem| { |
| 391 | 391 | if is_seqcst && store_elem.is_seqcst { |
| 392 | // An SC load needs to ignore all but last store maked SC (stores not marked SC are not | |
| 392 | // An SC load needs to ignore all but last store marked SC (stores not marked SC are not | |
| 393 | 393 | // affected) |
| 394 | 394 | let include = !found_sc; |
| 395 | 395 | found_sc = true; |
src/tools/miri/src/eval.rs+1-1| ... | ... | @@ -113,7 +113,7 @@ pub struct MiriConfig { |
| 113 | 113 | pub float_nondet: bool, |
| 114 | 114 | /// Whether floating-point operations can have a non-deterministic rounding error. |
| 115 | 115 | pub float_rounding_error: FloatRoundingErrorMode, |
| 116 | /// Whether Miri artifically introduces short reads/writes on file descriptors. | |
| 116 | /// Whether Miri artificially introduces short reads/writes on file descriptors. | |
| 117 | 117 | pub short_fd_operations: bool, |
| 118 | 118 | /// A list of crates that are considered user-relevant. |
| 119 | 119 | pub user_relevant_crates: Vec<String>, |
src/tools/miri/src/helpers.rs+2-2| ... | ... | @@ -138,7 +138,7 @@ pub fn iter_exported_symbols<'tcx>( |
| 138 | 138 | } |
| 139 | 139 | |
| 140 | 140 | // Next, all our dependencies. |
| 141 | // `dependency_formats` includes all the transitive informations needed to link a crate, | |
| 141 | // `dependency_formats` includes all the transitive information needed to link a crate, | |
| 142 | 142 | // which is what we need here since we need to dig out `exported_symbols` from all transitive |
| 143 | 143 | // dependencies. |
| 144 | 144 | let dependency_formats = tcx.dependency_formats(()); |
| ... | ... | @@ -1148,7 +1148,7 @@ impl ToUsize for u32 { |
| 1148 | 1148 | } |
| 1149 | 1149 | |
| 1150 | 1150 | /// Similarly, a maximum address size of `u64` is assumed widely here, so let's have ergonomic |
| 1151 | /// converion from `usize` to `u64`. | |
| 1151 | /// conversion from `usize` to `u64`. | |
| 1152 | 1152 | pub trait ToU64 { |
| 1153 | 1153 | fn to_u64(self) -> u64; |
| 1154 | 1154 | } |
src/tools/miri/src/machine.rs+2-2| ... | ... | @@ -654,7 +654,7 @@ pub struct MiriMachine<'tcx> { |
| 654 | 654 | /// Whether floating-point operations can have a non-deterministic rounding error. |
| 655 | 655 | pub float_rounding_error: FloatRoundingErrorMode, |
| 656 | 656 | |
| 657 | /// Whether Miri artifically introduces short reads/writes on file descriptors. | |
| 657 | /// Whether Miri artificially introduces short reads/writes on file descriptors. | |
| 658 | 658 | pub short_fd_operations: bool, |
| 659 | 659 | } |
| 660 | 660 | |
| ... | ... | @@ -1802,7 +1802,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { |
| 1802 | 1802 | // We have to skip the frame that is just being popped. |
| 1803 | 1803 | ecx.active_thread_mut().recompute_top_user_relevant_frame(/* skip */ 1); |
| 1804 | 1804 | } |
| 1805 | // tracing-tree can autoamtically annotate scope changes, but it gets very confused by our | |
| 1805 | // tracing-tree can automatically annotate scope changes, but it gets very confused by our | |
| 1806 | 1806 | // concurrency and what it prints is just plain wrong. So we print our own information |
| 1807 | 1807 | // instead. (Cc https://github.com/rust-lang/miri/issues/2266) |
| 1808 | 1808 | info!("Leaving {}", ecx.frame().instance()); |
src/tools/miri/src/shims/aarch64.rs+26| ... | ... | @@ -58,7 +58,33 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 58 | 58 | this.write_immediate(*res_lane, &dest)?; |
| 59 | 59 | } |
| 60 | 60 | } |
| 61 | // Vector table lookup: each index selects a byte from the 16-byte table, out-of-range -> 0. | |
| 62 | // Used to implement vtbl1_u8 function. | |
| 63 | // LLVM does not have a portable shuffle that takes non-const indices | |
| 64 | // so we need to implement this ourselves. | |
| 65 | // https://developer.arm.com/architectures/instruction-sets/intrinsics/vtbl1_u8 | |
| 66 | "neon.tbl1.v16i8" => { | |
| 67 | let [table, indices] = | |
| 68 | this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; | |
| 69 | ||
| 70 | let (table, table_len) = this.project_to_simd(table)?; | |
| 71 | let (indices, idx_len) = this.project_to_simd(indices)?; | |
| 72 | let (dest, dest_len) = this.project_to_simd(dest)?; | |
| 73 | assert_eq!(table_len, 16); | |
| 74 | assert_eq!(idx_len, dest_len); | |
| 61 | 75 | |
| 76 | for i in 0..dest_len { | |
| 77 | let idx = this.read_immediate(&this.project_index(&indices, i)?)?; | |
| 78 | let idx_u = idx.to_scalar().to_u8()?; | |
| 79 | let val = if u64::from(idx_u) < table_len { | |
| 80 | let t = this.read_immediate(&this.project_index(&table, idx_u.into())?)?; | |
| 81 | t.to_scalar() | |
| 82 | } else { | |
| 83 | Scalar::from_u8(0) | |
| 84 | }; | |
| 85 | this.write_scalar(val, &this.project_index(&dest, i)?)?; | |
| 86 | } | |
| 87 | } | |
| 62 | 88 | _ => return interp_ok(EmulateItemResult::NotSupported), |
| 63 | 89 | } |
| 64 | 90 | interp_ok(EmulateItemResult::NeedsReturn) |
src/tools/miri/src/shims/native_lib/trace/child.rs+1-1| ... | ... | @@ -31,7 +31,7 @@ pub struct Supervisor { |
| 31 | 31 | /// Used for synchronisation, allowing us to receive confirmation that the |
| 32 | 32 | /// parent process has handled the request from `message_tx`. |
| 33 | 33 | confirm_rx: ipc::IpcReceiver<Confirmation>, |
| 34 | /// Receiver for memory acceses that ocurred during the FFI call. | |
| 34 | /// Receiver for memory accesses that occurred during the FFI call. | |
| 35 | 35 | event_rx: ipc::IpcReceiver<MemEvents>, |
| 36 | 36 | } |
| 37 | 37 |
src/tools/miri/src/shims/native_lib/trace/parent.rs-2| ... | ... | @@ -395,8 +395,6 @@ fn capstone_find_events( |
| 395 | 395 | _ => (), |
| 396 | 396 | } |
| 397 | 397 | } |
| 398 | // FIXME: arm64 | |
| 399 | _ => unimplemented!(), | |
| 400 | 398 | } |
| 401 | 399 | |
| 402 | 400 | false |
src/tools/miri/src/shims/os_str.rs+1-1| ... | ... | @@ -316,7 +316,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 316 | 316 | // The new path is still absolute on Windows. |
| 317 | 317 | path.remove(0); |
| 318 | 318 | } |
| 319 | // If this starts withs a `\` but not a `\\`, then this was absolute on Unix but is | |
| 319 | // If this starts with a `\` but not a `\\`, then this was absolute on Unix but is | |
| 320 | 320 | // relative on Windows (relative to "the root of the current directory", e.g. the |
| 321 | 321 | // drive letter). |
| 322 | 322 | else if path.first() == Some(&sep) && path.get(1) != Some(&sep) { |
src/tools/miri/src/shims/time.rs+2-2| ... | ... | @@ -401,11 +401,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 401 | 401 | }; |
| 402 | 402 | |
| 403 | 403 | let timeout_anchor = if flags == 0 { |
| 404 | // No flags set, the timespec should be interperted as a duration | |
| 404 | // No flags set, the timespec should be interpreted as a duration | |
| 405 | 405 | // to sleep for |
| 406 | 406 | TimeoutAnchor::Relative |
| 407 | 407 | } else if flags == this.eval_libc_i32("TIMER_ABSTIME") { |
| 408 | // Only flag TIMER_ABSTIME set, the timespec should be interperted as | |
| 408 | // Only flag TIMER_ABSTIME set, the timespec should be interpreted as | |
| 409 | 409 | // an absolute time. |
| 410 | 410 | TimeoutAnchor::Absolute |
| 411 | 411 | } else { |
src/tools/miri/src/shims/unix/foreign_items.rs+1-1| ... | ... | @@ -1045,7 +1045,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 1045 | 1045 | &[Os::Linux, Os::FreeBsd, Os::Illumos, Os::Solaris, Os::Android, Os::MacOs], |
| 1046 | 1046 | link_name, |
| 1047 | 1047 | )?; |
| 1048 | // This function looks and behaves excatly like miri_start_unwind. | |
| 1048 | // This function looks and behaves exactly like miri_start_unwind. | |
| 1049 | 1049 | let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; |
| 1050 | 1050 | this.handle_miri_start_unwind(payload)?; |
| 1051 | 1051 | return interp_ok(EmulateItemResult::NeedsUnwind); |
src/tools/miri/src/shims/unix/freebsd/sync.rs+1-1| ... | ... | @@ -169,7 +169,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 169 | 169 | let Some(futex_ref) = |
| 170 | 170 | this.get_sync_or_init(obj, |_| FreeBsdFutex { futex: Default::default() }) |
| 171 | 171 | else { |
| 172 | // From Linux implemenation: | |
| 172 | // From Linux implementation: | |
| 173 | 173 | // No AllocId, or no live allocation at that AllocId. |
| 174 | 174 | // Return an error code. (That seems nicer than silently doing something non-intuitive.) |
| 175 | 175 | // This means that if an address gets reused by a new allocation, |
src/tools/miri/src/shims/unix/linux/foreign_items.rs+2-2| ... | ... | @@ -165,7 +165,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 165 | 165 | )? { |
| 166 | 166 | ThreadNameResult::Ok => Scalar::from_u32(0), |
| 167 | 167 | ThreadNameResult::NameTooLong => this.eval_libc("ERANGE"), |
| 168 | // Act like we faild to open `/proc/self/task/$tid/comm`. | |
| 168 | // Act like we failed to open `/proc/self/task/$tid/comm`. | |
| 169 | 169 | ThreadNameResult::ThreadNotFound => this.eval_libc("ENOENT"), |
| 170 | 170 | }; |
| 171 | 171 | this.write_scalar(res, dest)?; |
| ... | ... | @@ -186,7 +186,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 186 | 186 | )? { |
| 187 | 187 | ThreadNameResult::Ok => Scalar::from_u32(0), |
| 188 | 188 | ThreadNameResult::NameTooLong => unreachable!(), |
| 189 | // Act like we faild to open `/proc/self/task/$tid/comm`. | |
| 189 | // Act like we failed to open `/proc/self/task/$tid/comm`. | |
| 190 | 190 | ThreadNameResult::ThreadNotFound => this.eval_libc("ENOENT"), |
| 191 | 191 | } |
| 192 | 192 | } else { |
src/tools/miri/src/shims/unix/linux_like/epoll.rs+1-1| ... | ... | @@ -176,7 +176,7 @@ impl EpollInterestTable { |
| 176 | 176 | if let Some(epolls) = self.0.remove(&id) { |
| 177 | 177 | for epoll in epolls.iter().filter_map(|(_id, epoll)| epoll.upgrade()) { |
| 178 | 178 | // This is a still-live epoll with interest in this FD. Remove all |
| 179 | // relevent interests (including from the ready set). | |
| 179 | // relevant interests (including from the ready set). | |
| 180 | 180 | epoll |
| 181 | 181 | .interest_list |
| 182 | 182 | .borrow_mut() |
src/tools/miri/src/shims/unix/macos/sync.rs+1-1| ... | ... | @@ -169,7 +169,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 169 | 169 | |
| 170 | 170 | let is_shared = flags == shared; |
| 171 | 171 | let timeout = clock_timeout.map(|(_, anchor, timeout)| { |
| 172 | // The only clock that is currenlty supported is the monotonic clock. | |
| 172 | // The only clock that is currently supported is the monotonic clock. | |
| 173 | 173 | // While the deadline argument of `os_sync_wait_on_address_with_deadline` |
| 174 | 174 | // is actually not in nanoseconds but in the units of `mach_current_time`, |
| 175 | 175 | // the two are equivalent in miri. |
src/tools/miri/src/shims/windows/foreign_items.rs+1-1| ... | ... | @@ -1199,7 +1199,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 1199 | 1199 | "`_Unwind_RaiseException` is not supported on non-MinGW Windows", |
| 1200 | 1200 | ); |
| 1201 | 1201 | } |
| 1202 | // This function looks and behaves excatly like miri_start_unwind. | |
| 1202 | // This function looks and behaves exactly like miri_start_unwind. | |
| 1203 | 1203 | let [payload] = this.check_shim_sig( |
| 1204 | 1204 | shim_sig!(extern "C" fn(*mut _) -> unwind::libunwind::_Unwind_Reason_Code), |
| 1205 | 1205 | link_name, |
src/tools/miri/src/shims/windows/sync.rs+1-1| ... | ... | @@ -67,7 +67,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 67 | 67 | ) |
| 68 | 68 | } |
| 69 | 69 | |
| 70 | /// Returns `true` if we were succssful, `false` if we would block. | |
| 70 | /// Returns `true` if we were successful, `false` if we would block. | |
| 71 | 71 | fn init_once_try_begin( |
| 72 | 72 | &mut self, |
| 73 | 73 | init_once_ref: &InitOnceRef, |
src/tools/miri/src/shims/x86/avx2.rs+1-1| ... | ... | @@ -194,7 +194,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 194 | 194 | // Used to implement the _mm256_sign_epi{8,16,32} functions. |
| 195 | 195 | // Negates elements from `left` when the corresponding element in |
| 196 | 196 | // `right` is negative. If an element from `right` is zero, zero |
| 197 | // is writen to the corresponding output element. | |
| 197 | // is written to the corresponding output element. | |
| 198 | 198 | // Basically, we multiply `left` with `right.signum()`. |
| 199 | 199 | "psign.b" | "psign.w" | "psign.d" => { |
| 200 | 200 | let [left, right] = |
src/tools/miri/src/shims/x86/bmi.rs+1-1| ... | ... | @@ -44,7 +44,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 44 | 44 | let right = if is_64_bit { right.to_u64()? } else { u64::from(right.to_u32()?) }; |
| 45 | 45 | |
| 46 | 46 | let result = match unprefixed_name { |
| 47 | // Extract a contigous range of bits from an unsigned integer. | |
| 47 | // Extract a contiguous range of bits from an unsigned integer. | |
| 48 | 48 | // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_bextr_u32 |
| 49 | 49 | "bextr" => { |
| 50 | 50 | let start = u32::try_from(right & 0xff).unwrap(); |
src/tools/miri/src/shims/x86/sse.rs+2-2| ... | ... | @@ -24,8 +24,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 24 | 24 | // Prefix should have already been checked. |
| 25 | 25 | let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse.").unwrap(); |
| 26 | 26 | // All these intrinsics operate on 128-bit (f32x4) SIMD vectors unless stated otherwise. |
| 27 | // Many intrinsic names are sufixed with "ps" (packed single) or "ss" (scalar single), | |
| 28 | // where single means single precision floating point (f32). "ps" means thet the operation | |
| 27 | // Many intrinsic names are suffixed with "ps" (packed single) or "ss" (scalar single), | |
| 28 | // where single means single precision floating point (f32). "ps" means that the operation | |
| 29 | 29 | // is performed on each element of the vector, while "ss" means that the operation is |
| 30 | 30 | // performed only on the first element, copying the remaining elements from the input |
| 31 | 31 | // vector (for binary operations, from the left-hand side). |
src/tools/miri/src/shims/x86/sse2.rs+3-3| ... | ... | @@ -26,14 +26,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 26 | 26 | |
| 27 | 27 | // These intrinsics operate on 128-bit (f32x4, f64x2, i8x16, i16x8, i32x4, i64x2) SIMD |
| 28 | 28 | // vectors unless stated otherwise. |
| 29 | // Many intrinsic names are sufixed with "ps" (packed single), "ss" (scalar signle), | |
| 29 | // Many intrinsic names are suffixed with "ps" (packed single), "ss" (scalar single), | |
| 30 | 30 | // "pd" (packed double) or "sd" (scalar double), where single means single precision |
| 31 | 31 | // floating point (f32) and double means double precision floating point (f64). "ps" |
| 32 | // and "pd" means thet the operation is performed on each element of the vector, while | |
| 32 | // and "pd" means that the operation is performed on each element of the vector, while | |
| 33 | 33 | // "ss" and "sd" means that the operation is performed only on the first element, copying |
| 34 | 34 | // the remaining elements from the input vector (for binary operations, from the left-hand |
| 35 | 35 | // side). |
| 36 | // Intrinsincs sufixed with "epiX" or "epuX" operate with X-bit signed or unsigned | |
| 36 | // Intrinsics suffixed with "epiX" or "epuX" operate with X-bit signed or unsigned | |
| 37 | 37 | // vectors. |
| 38 | 38 | match unprefixed_name { |
| 39 | 39 | // Used to implement the _mm_sad_epu8 function. |
src/tools/miri/src/shims/x86/sse41.rs+1-1| ... | ... | @@ -115,7 +115,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 115 | 115 | round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?; |
| 116 | 116 | } |
| 117 | 117 | // Used to implement the _mm_minpos_epu16 function. |
| 118 | // Find the minimum unsinged 16-bit integer in `op` and | |
| 118 | // Find the minimum unsigned 16-bit integer in `op` and | |
| 119 | 119 | // returns its value and position. |
| 120 | 120 | "phminposuw" => { |
| 121 | 121 | let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; |
src/tools/miri/src/shims/x86/sse42.rs+3-3| ... | ... | @@ -213,7 +213,7 @@ fn deconstruct_args<'tcx>( |
| 213 | 213 | }; |
| 214 | 214 | |
| 215 | 215 | // The fourth letter of each string comparison intrinsic is either 'e' for "explicit" or 'i' for "implicit". |
| 216 | // The distinction will correspond to the intrinsics type signature. In this constext, "explicit" and "implicit" | |
| 216 | // The distinction will correspond to the intrinsics type signature. In this context, "explicit" and "implicit" | |
| 217 | 217 | // refer to the way the string length is determined. The length is either passed explicitly in the "explicit" |
| 218 | 218 | // case or determined by a null terminator in the "implicit" case. |
| 219 | 219 | let is_explicit = match unprefixed_name.as_bytes().get(4) { |
| ... | ... | @@ -297,7 +297,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 297 | 297 | deconstruct_args(unprefixed_name, this, link_name, abi, args)?; |
| 298 | 298 | let mask = compare_strings(this, &str1, &str2, len, imm)?; |
| 299 | 299 | |
| 300 | // The sixth bit inside the immediate byte distiguishes | |
| 300 | // The sixth bit inside the immediate byte distinguishes | |
| 301 | 301 | // between a bit mask or a byte mask when generating a mask. |
| 302 | 302 | if imm & 0b100_0000 != 0 { |
| 303 | 303 | let (array_layout, size) = if imm & USE_WORDS != 0 { |
| ... | ... | @@ -347,7 +347,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 347 | 347 | let mask = compare_strings(this, &str1, &str2, len, imm)?; |
| 348 | 348 | |
| 349 | 349 | let len = default_len::<u32>(imm); |
| 350 | // The sixth bit inside the immediate byte distiguishes between the least | |
| 350 | // The sixth bit inside the immediate byte distinguishes between the least | |
| 351 | 351 | // significant bit and the most significant bit when generating an index. |
| 352 | 352 | let result = if imm & 0b100_0000 != 0 { |
| 353 | 353 | // most significant bit |
src/tools/miri/src/shims/x86/ssse3.rs+1-1| ... | ... | @@ -68,7 +68,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 68 | 68 | // Used to implement the _mm_sign_epi{8,16,32} functions. |
| 69 | 69 | // Negates elements from `left` when the corresponding element in |
| 70 | 70 | // `right` is negative. If an element from `right` is zero, zero |
| 71 | // is writen to the corresponding output element. | |
| 71 | // is written to the corresponding output element. | |
| 72 | 72 | // Basically, we multiply `left` with `right.signum()`. |
| 73 | 73 | "psign.b.128" | "psign.w.128" | "psign.d.128" => { |
| 74 | 74 | let [left, right] = |
src/tools/miri/tests/deps/Cargo.lock+2-2| ... | ... | @@ -46,9 +46,9 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" |
| 46 | 46 | |
| 47 | 47 | [[package]] |
| 48 | 48 | name = "bytes" |
| 49 | version = "1.10.1" | |
| 49 | version = "1.11.1" | |
| 50 | 50 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 51 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" | |
| 51 | checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" | |
| 52 | 52 | |
| 53 | 53 | [[package]] |
| 54 | 54 | name = "cfg-if" |
src/tools/miri/tests/deps/src/main.rs+3-1| ... | ... | @@ -1 +1,3 @@ |
| 1 | fn main() {} | |
| 1 | fn main() { | |
| 2 | unreachable!() | |
| 3 | } |
src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr-1| ... | ... | @@ -11,7 +11,6 @@ help: ALLOC was allocated here: |
| 11 | 11 | | |
| 12 | 12 | LL | let mut buf = vec![0u8; 15]; |
| 13 | 13 | | ^^^^^^^^^^^^^ |
| 14 | = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) | |
| 15 | 14 | |
| 16 | 15 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace |
| 17 | 16 |
src/tools/miri/tests/fail/match/all_variants_uninhabited.rs+1-1| ... | ... | @@ -5,7 +5,7 @@ enum Never {} |
| 5 | 5 | fn main() { |
| 6 | 6 | unsafe { |
| 7 | 7 | match *std::ptr::null::<Result<Never, Never>>() { |
| 8 | //~^ ERROR: read discriminant of an uninhabited enum variant | |
| 8 | //~^ ERROR: read discriminant of an uninhabited enum variant | |
| 9 | 9 | Ok(_) => { |
| 10 | 10 | lol(); |
| 11 | 11 | } |
src/tools/miri/tests/fail/match/closures/uninhabited-variant2.rs+2-1| ... | ... | @@ -22,7 +22,8 @@ fn main() { |
| 22 | 22 | // After rust-lang/rust#138961, constructing the closure performs a reborrow of r. |
| 23 | 23 | // Nevertheless, the discriminant is only actually inspected when the closure |
| 24 | 24 | // is called. |
| 25 | match r { //~ ERROR: read discriminant of an uninhabited enum variant | |
| 25 | match r { | |
| 26 | //~^ ERROR: read discriminant of an uninhabited enum variant | |
| 26 | 27 | E::V0 => {} |
| 27 | 28 | E::V1(_) => {} |
| 28 | 29 | } |
src/tools/miri/tests/fail/match/only_inhabited_variant.rs+4-3| ... | ... | @@ -4,8 +4,8 @@ |
| 4 | 4 | #[repr(C)] |
| 5 | 5 | #[allow(dead_code)] |
| 6 | 6 | enum E { |
| 7 | V0, // discriminant: 0 | |
| 8 | V1(!), // 1 | |
| 7 | V0, // discriminant: 0 | |
| 8 | V1(!), // 1 | |
| 9 | 9 | } |
| 10 | 10 | |
| 11 | 11 | fn main() { |
| ... | ... | @@ -14,7 +14,8 @@ fn main() { |
| 14 | 14 | let val = 1u32; |
| 15 | 15 | let ptr = (&raw const val).cast::<E>(); |
| 16 | 16 | let r = unsafe { &*ptr }; |
| 17 | match r { //~ ERROR: read discriminant of an uninhabited enum variant | |
| 17 | match r { | |
| 18 | //~^ ERROR: read discriminant of an uninhabited enum variant | |
| 18 | 19 | E::V0 => {} |
| 19 | 20 | E::V1(_) => {} |
| 20 | 21 | } |
src/tools/miri/tests/fail/match/single_variant.rs+4-3| ... | ... | @@ -20,12 +20,13 @@ fn main() { |
| 20 | 20 | let x: &[u8; 2] = &[21, 37]; |
| 21 | 21 | let y: &Exhaustive = std::mem::transmute(x); |
| 22 | 22 | match y { |
| 23 | Exhaustive::A(_) => {}, | |
| 23 | Exhaustive::A(_) => {} | |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | let y: &NonExhaustive = std::mem::transmute(x); |
| 27 | match y { //~ ERROR: enum value has invalid tag | |
| 28 | NonExhaustive::A(_) => {}, | |
| 27 | match y { | |
| 28 | //~^ ERROR: enum value has invalid tag | |
| 29 | NonExhaustive::A(_) => {} | |
| 29 | 30 | } |
| 30 | 31 | } |
| 31 | 32 | } |
src/tools/miri/tests/fail/match/single_variant_uninit.rs+2-1| ... | ... | @@ -28,7 +28,8 @@ fn main() { |
| 28 | 28 | _ => {} |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | match *nexh { //~ ERROR: memory is uninitialized | |
| 31 | match *nexh { | |
| 32 | //~^ ERROR: memory is uninitialized | |
| 32 | 33 | NonExhaustive::A(ref _val) => {} |
| 33 | 34 | _ => {} |
| 34 | 35 | } |
src/tools/miri/tests/fail/validity/uninhabited_variant.rs created+17| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | // NOTE: this is essentially a smoke-test, with more comprehensive tests living in the rustc | |
| 2 | // repository at tests/ui/consts/const-eval/ub-enum.rs | |
| 3 | #![feature(never_type)] | |
| 4 | ||
| 5 | #[repr(C)] | |
| 6 | #[allow(dead_code)] | |
| 7 | enum E { | |
| 8 | V1, // discriminant: 0 | |
| 9 | V2(!), // 1 | |
| 10 | } | |
| 11 | ||
| 12 | fn main() { | |
| 13 | unsafe { | |
| 14 | std::mem::transmute::<u32, E>(1); | |
| 15 | //~^ ERROR: encountered an uninhabited enum variant | |
| 16 | } | |
| 17 | } |
src/tools/miri/tests/fail/validity/uninhabited_variant.stderr created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | error: Undefined Behavior: constructing invalid value at .<enum-tag>: encountered an uninhabited enum variant | |
| 2 | --> tests/fail/validity/uninhabited_variant.rs:LL:CC | |
| 3 | | | |
| 4 | LL | std::mem::transmute::<u32, E>(1); | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here | |
| 6 | | | |
| 7 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior | |
| 8 | = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information | |
| 9 | ||
| 10 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | |
| 11 | ||
| 12 | error: aborting due to 1 previous error | |
| 13 |
src/tools/miri/tests/pass/align_strange_enum_discriminant_offset.rs+2-3| ... | ... | @@ -1,5 +1,4 @@ |
| 1 | #![allow(unused)] | |
| 2 | ||
| 1 | #[allow(unused)] | |
| 3 | 2 | #[repr(u16)] |
| 4 | 3 | enum DeviceKind { |
| 5 | 4 | Nil = 0, |
| ... | ... | @@ -19,5 +18,5 @@ fn main() { |
| 19 | 18 | let x = None::<(DeviceInfo, u8)>; |
| 20 | 19 | let y = None::<(DeviceInfo, u16)>; |
| 21 | 20 | let z = None::<(DeviceInfo, u64)>; |
| 22 | format!("{} {} {}", x.is_some(), y.is_some(), y.is_some()); | |
| 21 | let _out = format!("{} {} {}", x.is_some(), y.is_some(), z.is_some()); | |
| 23 | 22 | } |
src/tools/miri/tests/pass/async-closure.rs-1| ... | ... | @@ -1,5 +1,4 @@ |
| 1 | 1 | #![feature(async_fn_traits)] |
| 2 | #![allow(unused)] | |
| 3 | 2 | |
| 4 | 3 | use std::future::Future; |
| 5 | 4 | use std::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; |
src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs+29-2| ... | ... | @@ -4,17 +4,19 @@ |
| 4 | 4 | |
| 5 | 5 | use std::arch::aarch64::*; |
| 6 | 6 | use std::arch::is_aarch64_feature_detected; |
| 7 | use std::mem::transmute; | |
| 7 | 8 | |
| 8 | 9 | fn main() { |
| 9 | 10 | assert!(is_aarch64_feature_detected!("neon")); |
| 10 | 11 | |
| 11 | 12 | unsafe { |
| 12 | test_neon(); | |
| 13 | test_vpmaxq_u8(); | |
| 14 | test_tbl1_v16i8_basic(); | |
| 13 | 15 | } |
| 14 | 16 | } |
| 15 | 17 | |
| 16 | 18 | #[target_feature(enable = "neon")] |
| 17 | unsafe fn test_neon() { | |
| 19 | unsafe fn test_vpmaxq_u8() { | |
| 18 | 20 | // Adapted from library/stdarch/crates/core_arch/src/aarch64/neon/mod.rs |
| 19 | 21 | unsafe fn test_vpmaxq_u8() { |
| 20 | 22 | let a = vld1q_u8([1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8].as_ptr()); |
| ... | ... | @@ -38,3 +40,28 @@ unsafe fn test_neon() { |
| 38 | 40 | } |
| 39 | 41 | test_vpmaxq_u8_is_unsigned(); |
| 40 | 42 | } |
| 43 | ||
| 44 | #[target_feature(enable = "neon")] | |
| 45 | fn test_tbl1_v16i8_basic() { | |
| 46 | unsafe { | |
| 47 | // table = 0..15 | |
| 48 | let table: uint8x16_t = | |
| 49 | transmute::<[u8; 16], _>([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); | |
| 50 | // indices | |
| 51 | let idx: uint8x16_t = | |
| 52 | transmute::<[u8; 16], _>([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); | |
| 53 | let got = vqtbl1q_u8(table, idx); | |
| 54 | let got_arr: [u8; 16] = transmute(got); | |
| 55 | assert_eq!(got_arr, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); | |
| 56 | ||
| 57 | // Also try different order and out-of-range indices (16, 255). | |
| 58 | let idx2: uint8x16_t = | |
| 59 | transmute::<[u8; 16], _>([15, 16, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); | |
| 60 | let got2 = vqtbl1q_u8(table, idx2); | |
| 61 | let got2_arr: [u8; 16] = transmute(got2); | |
| 62 | assert_eq!(got2_arr[0], 15); | |
| 63 | assert_eq!(got2_arr[1], 0); // out-of-range | |
| 64 | assert_eq!(got2_arr[2], 0); // out-of-range | |
| 65 | assert_eq!(&got2_arr[3..16], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12][..]); | |
| 66 | } | |
| 67 | } |
src/tools/miri/tests/ui.rs+14-8| ... | ... | @@ -132,14 +132,19 @@ fn miri_config( |
| 132 | 132 | // (It's a separate crate, so we don't get an env var from cargo.) |
| 133 | 133 | program: miri_path() |
| 134 | 134 | .with_file_name(format!("cargo-miri{}", env::consts::EXE_SUFFIX)), |
| 135 | // There is no `cargo miri build` so we just use `cargo miri run`. | |
| 136 | 135 | // Add `-Zbinary-dep-depinfo` since it is needed for bootstrap builds (and doesn't harm otherwise). |
| 137 | args: ["miri", "run", "--quiet", "-Zbinary-dep-depinfo"] | |
| 136 | args: ["miri", "build", "-Zbinary-dep-depinfo"] | |
| 138 | 137 | .into_iter() |
| 139 | 138 | .map(Into::into) |
| 140 | 139 | .collect(), |
| 141 | // Reset `RUSTFLAGS` to work around <https://github.com/rust-lang/rust/pull/119574#issuecomment-1876878344>. | |
| 142 | envs: vec![("RUSTFLAGS".into(), None)], | |
| 140 | envs: vec![ | |
| 141 | // Reset `RUSTFLAGS` to work around <https://github.com/rust-lang/rust/pull/119574#issuecomment-1876878344>. | |
| 142 | ("RUSTFLAGS".into(), None), | |
| 143 | // Reset `MIRIFLAGS` because it caused trouble in the past and should not be needed. | |
| 144 | ("MIRIFLAGS".into(), None), | |
| 145 | // Allow `cargo miri build`. | |
| 146 | ("MIRI_BUILD_TEST_DEPS".into(), Some("1".into())), | |
| 147 | ], | |
| 143 | 148 | ..CommandBuilder::cargo() |
| 144 | 149 | }, |
| 145 | 150 | crate_manifest_path: Path::new("tests/deps").join("Cargo.toml"), |
| ... | ... | @@ -361,15 +366,16 @@ fn run_dep_mode(target: String, args: impl Iterator<Item = OsString>) -> Result< |
| 361 | 366 | miri_config(&target, "", Mode::RunDep, Some(WithDependencies { bless: false })); |
| 362 | 367 | config.comment_defaults.base().custom.remove("edition"); // `./miri` adds an `--edition` in `args`, so don't set it twice |
| 363 | 368 | config.fill_host_and_target()?; |
| 369 | let dep_builder = BuildManager::one_off(config.clone()); | |
| 370 | // Only set these for the actual run, not the dep builder, so invalid flags do not fail | |
| 371 | // the dependency build. | |
| 364 | 372 | config.program.args = args.collect(); |
| 373 | let test_config = TestConfig::one_off_runner(config, PathBuf::new()); | |
| 365 | 374 | |
| 366 | let test_config = TestConfig::one_off_runner(config.clone(), PathBuf::new()); | |
| 367 | ||
| 368 | let build_manager = BuildManager::one_off(config); | |
| 369 | 375 | let mut cmd = test_config.config.program.build(&test_config.config.out_dir); |
| 370 | 376 | cmd.arg("--target").arg(test_config.config.target.as_ref().unwrap()); |
| 371 | 377 | // Build dependencies |
| 372 | test_config.apply_custom(&mut cmd, &build_manager).unwrap(); | |
| 378 | test_config.apply_custom(&mut cmd, &dep_builder).expect("failed to build dependencies"); | |
| 373 | 379 | |
| 374 | 380 | if cmd.spawn()?.wait()?.success() { Ok(()) } else { std::process::exit(1) } |
| 375 | 381 | } |
src/tools/rust-analyzer/.github/workflows/ci.yaml+29-8| ... | ... | @@ -96,7 +96,7 @@ jobs: |
| 96 | 96 | run: | |
| 97 | 97 | rustup update --no-self-update stable |
| 98 | 98 | rustup default stable |
| 99 | rustup component add --toolchain stable rust-src clippy rustfmt | |
| 99 | rustup component add --toolchain stable rust-src rustfmt | |
| 100 | 100 | # We also install a nightly rustfmt, because we use `--file-lines` in |
| 101 | 101 | # a test. |
| 102 | 102 | rustup toolchain install nightly --profile minimal --component rustfmt |
| ... | ... | @@ -128,10 +128,6 @@ jobs: |
| 128 | 128 | - name: Run cargo-machete |
| 129 | 129 | run: cargo machete |
| 130 | 130 | |
| 131 | - name: Run Clippy | |
| 132 | if: matrix.os == 'macos-latest' | |
| 133 | run: cargo clippy --all-targets -- -D clippy::disallowed_macros -D clippy::dbg_macro -D clippy::todo -D clippy::print_stdout -D clippy::print_stderr | |
| 134 | ||
| 135 | 131 | analysis-stats: |
| 136 | 132 | if: github.repository == 'rust-lang/rust-analyzer' |
| 137 | 133 | runs-on: ubuntu-latest |
| ... | ... | @@ -178,6 +174,28 @@ jobs: |
| 178 | 174 | |
| 179 | 175 | - run: cargo fmt -- --check |
| 180 | 176 | |
| 177 | clippy: | |
| 178 | if: github.repository == 'rust-lang/rust-analyzer' | |
| 179 | runs-on: ubuntu-latest | |
| 180 | ||
| 181 | steps: | |
| 182 | - name: Checkout repository | |
| 183 | uses: actions/checkout@v4 | |
| 184 | ||
| 185 | # Note that clippy output is currently dependent on whether rust-src is installed, | |
| 186 | # https://github.com/rust-lang/rust-clippy/issues/14625 | |
| 187 | - name: Install Rust toolchain | |
| 188 | run: | | |
| 189 | rustup update --no-self-update stable | |
| 190 | rustup default stable | |
| 191 | rustup component add --toolchain stable rust-src clippy | |
| 192 | ||
| 193 | # https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json | |
| 194 | - name: Install Rust Problem Matcher | |
| 195 | run: echo "::add-matcher::.github/rust.json" | |
| 196 | ||
| 197 | - run: cargo clippy --all-targets -- -D clippy::disallowed_macros -D clippy::dbg_macro -D clippy::todo -D clippy::print_stdout -D clippy::print_stderr | |
| 198 | ||
| 181 | 199 | miri: |
| 182 | 200 | if: github.repository == 'rust-lang/rust-analyzer' |
| 183 | 201 | runs-on: ubuntu-latest |
| ... | ... | @@ -188,8 +206,11 @@ jobs: |
| 188 | 206 | |
| 189 | 207 | - name: Install Rust toolchain |
| 190 | 208 | run: | |
| 191 | rustup update --no-self-update nightly | |
| 192 | rustup default nightly | |
| 209 | # FIXME: Pin nightly due to a regression in miri on nightly-2026-02-12. | |
| 210 | # See https://github.com/rust-lang/miri/issues/4855. | |
| 211 | # Revert to plain `nightly` once this is fixed upstream. | |
| 212 | rustup toolchain install nightly-2026-02-10 | |
| 213 | rustup default nightly-2026-02-10 | |
| 193 | 214 | rustup component add miri |
| 194 | 215 | |
| 195 | 216 | # - name: Cache Dependencies |
| ... | ... | @@ -309,7 +330,7 @@ jobs: |
| 309 | 330 | run: typos |
| 310 | 331 | |
| 311 | 332 | conclusion: |
| 312 | needs: [rust, rust-cross, typescript, typo-check, proc-macro-srv, miri, rustfmt, analysis-stats] | |
| 333 | needs: [rust, rust-cross, typescript, typo-check, proc-macro-srv, miri, rustfmt, clippy, analysis-stats] | |
| 313 | 334 | # We need to ensure this job does *not* get skipped if its dependencies fail, |
| 314 | 335 | # because a skipped job is considered a success by GitHub. So we have to |
| 315 | 336 | # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run |
src/tools/rust-analyzer/Cargo.lock+7-6| ... | ... | @@ -1845,6 +1845,7 @@ dependencies = [ |
| 1845 | 1845 | "paths", |
| 1846 | 1846 | "postcard", |
| 1847 | 1847 | "proc-macro-srv", |
| 1848 | "rayon", | |
| 1848 | 1849 | "rustc-hash 2.1.1", |
| 1849 | 1850 | "semver", |
| 1850 | 1851 | "serde", |
| ... | ... | @@ -2453,9 +2454,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" |
| 2453 | 2454 | |
| 2454 | 2455 | [[package]] |
| 2455 | 2456 | name = "salsa" |
| 2456 | version = "0.26.0" | |
| 2457 | version = "0.25.2" | |
| 2457 | 2458 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 2458 | checksum = "f77debccd43ba198e9cee23efd7f10330ff445e46a98a2b107fed9094a1ee676" | |
| 2459 | checksum = "e2e2aa2fca57727371eeafc975acc8e6f4c52f8166a78035543f6ee1c74c2dcc" | |
| 2459 | 2460 | dependencies = [ |
| 2460 | 2461 | "boxcar", |
| 2461 | 2462 | "crossbeam-queue", |
| ... | ... | @@ -2478,15 +2479,15 @@ dependencies = [ |
| 2478 | 2479 | |
| 2479 | 2480 | [[package]] |
| 2480 | 2481 | name = "salsa-macro-rules" |
| 2481 | version = "0.26.0" | |
| 2482 | version = "0.25.2" | |
| 2482 | 2483 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 2483 | checksum = "ea07adbf42d91cc076b7daf3b38bc8168c19eb362c665964118a89bc55ef19a5" | |
| 2484 | checksum = "1bfc2a1e7bf06964105515451d728f2422dedc3a112383324a00b191a5c397a3" | |
| 2484 | 2485 | |
| 2485 | 2486 | [[package]] |
| 2486 | 2487 | name = "salsa-macros" |
| 2487 | version = "0.26.0" | |
| 2488 | version = "0.25.2" | |
| 2488 | 2489 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 2489 | checksum = "d16d4d8b66451b9c75ddf740b7fc8399bc7b8ba33e854a5d7526d18708f67b05" | |
| 2490 | checksum = "3d844c1aa34946da46af683b5c27ec1088a3d9d84a2b837a108223fd830220e1" | |
| 2490 | 2491 | dependencies = [ |
| 2491 | 2492 | "proc-macro2", |
| 2492 | 2493 | "quote", |
src/tools/rust-analyzer/Cargo.toml+2-2| ... | ... | @@ -135,13 +135,13 @@ rayon = "1.10.0" |
| 135 | 135 | rowan = "=0.15.17" |
| 136 | 136 | # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work |
| 137 | 137 | # on impls without it |
| 138 | salsa = { version = "0.26", default-features = false, features = [ | |
| 138 | salsa = { version = "0.25.2", default-features = false, features = [ | |
| 139 | 139 | "rayon", |
| 140 | 140 | "salsa_unstable", |
| 141 | 141 | "macros", |
| 142 | 142 | "inventory", |
| 143 | 143 | ] } |
| 144 | salsa-macros = "0.26" | |
| 144 | salsa-macros = "0.25.2" | |
| 145 | 145 | semver = "1.0.26" |
| 146 | 146 | serde = { version = "1.0.219" } |
| 147 | 147 | serde_derive = { version = "1.0.219" } |
src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs+1-1| ... | ... | @@ -60,7 +60,7 @@ const _: () = { |
| 60 | 60 | } |
| 61 | 61 | } |
| 62 | 62 | |
| 63 | impl zalsa_::HashEqLike<WithoutCrate> for EditionedFileIdData { | |
| 63 | impl zalsa_struct_::HashEqLike<WithoutCrate> for EditionedFileIdData { | |
| 64 | 64 | #[inline] |
| 65 | 65 | fn hash<H: Hasher>(&self, state: &mut H) { |
| 66 | 66 | Hash::hash(self, state); |
src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs+39-3| ... | ... | @@ -32,8 +32,8 @@ use triomphe::Arc; |
| 32 | 32 | use tt::TextRange; |
| 33 | 33 | |
| 34 | 34 | use crate::{ |
| 35 | AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId, | |
| 36 | ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, | |
| 35 | AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, ItemContainerId, | |
| 36 | MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, | |
| 37 | 37 | attrs::AttrFlags, |
| 38 | 38 | db::DefDatabase, |
| 39 | 39 | expr_store::{ |
| ... | ... | @@ -141,9 +141,19 @@ pub(super) fn lower_body( |
| 141 | 141 | source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn))); |
| 142 | 142 | } |
| 143 | 143 | |
| 144 | let is_extern = matches!( | |
| 145 | owner, | |
| 146 | DefWithBodyId::FunctionId(id) | |
| 147 | if matches!(id.loc(db).container, ItemContainerId::ExternBlockId(_)), | |
| 148 | ); | |
| 149 | ||
| 144 | 150 | for param in param_list.params() { |
| 145 | 151 | if collector.check_cfg(&param) { |
| 146 | let param_pat = collector.collect_pat_top(param.pat()); | |
| 152 | let param_pat = if is_extern { | |
| 153 | collector.collect_extern_fn_param(param.pat()) | |
| 154 | } else { | |
| 155 | collector.collect_pat_top(param.pat()) | |
| 156 | }; | |
| 147 | 157 | params.push(param_pat); |
| 148 | 158 | } |
| 149 | 159 | } |
| ... | ... | @@ -2248,6 +2258,32 @@ impl<'db> ExprCollector<'db> { |
| 2248 | 2258 | } |
| 2249 | 2259 | } |
| 2250 | 2260 | |
| 2261 | fn collect_extern_fn_param(&mut self, pat: Option<ast::Pat>) -> PatId { | |
| 2262 | // `extern` functions cannot have pattern-matched parameters, and furthermore, the identifiers | |
| 2263 | // in their parameters are always interpreted as bindings, even if in a normal function they | |
| 2264 | // won't be, because they would refer to a path pattern. | |
| 2265 | let Some(pat) = pat else { return self.missing_pat() }; | |
| 2266 | ||
| 2267 | match &pat { | |
| 2268 | ast::Pat::IdentPat(bp) => { | |
| 2269 | // FIXME: Emit an error if `!bp.is_simple_ident()`. | |
| 2270 | ||
| 2271 | let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); | |
| 2272 | let hygiene = bp | |
| 2273 | .name() | |
| 2274 | .map(|name| self.hygiene_id_for(name.syntax().text_range())) | |
| 2275 | .unwrap_or(HygieneId::ROOT); | |
| 2276 | let binding = self.alloc_binding(name, BindingAnnotation::Unannotated, hygiene); | |
| 2277 | let pat = | |
| 2278 | self.alloc_pat(Pat::Bind { id: binding, subpat: None }, AstPtr::new(&pat)); | |
| 2279 | self.add_definition_to_binding(binding, pat); | |
| 2280 | pat | |
| 2281 | } | |
| 2282 | // FIXME: Emit an error. | |
| 2283 | _ => self.missing_pat(), | |
| 2284 | } | |
| 2285 | } | |
| 2286 | ||
| 2251 | 2287 | // region: patterns |
| 2252 | 2288 | |
| 2253 | 2289 | fn collect_pat_top(&mut self, pat: Option<ast::Pat>) -> PatId { |
src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs+5| ... | ... | @@ -483,6 +483,11 @@ impl ItemScope { |
| 483 | 483 | self.declarations.push(def) |
| 484 | 484 | } |
| 485 | 485 | |
| 486 | pub(crate) fn remove_from_value_ns(&mut self, name: &Name, def: ModuleDefId) { | |
| 487 | let entry = self.values.shift_remove(name); | |
| 488 | assert!(entry.is_some_and(|entry| entry.def == def)) | |
| 489 | } | |
| 490 | ||
| 486 | 491 | pub(crate) fn get_legacy_macro(&self, name: &Name) -> Option<&[MacroId]> { |
| 487 | 492 | self.legacy_macros.get(name).map(|it| &**it) |
| 488 | 493 | } |
src/tools/rust-analyzer/crates/hir-def/src/nameres.rs+8| ... | ... | @@ -211,6 +211,7 @@ struct DefMapCrateData { |
| 211 | 211 | /// Side table for resolving derive helpers. |
| 212 | 212 | exported_derives: FxHashMap<MacroId, Box<[Name]>>, |
| 213 | 213 | fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>, |
| 214 | fn_proc_macro_mapping_back: FxHashMap<ProcMacroId, FunctionId>, | |
| 214 | 215 | |
| 215 | 216 | /// Custom tool modules registered with `#![register_tool]`. |
| 216 | 217 | registered_tools: Vec<Symbol>, |
| ... | ... | @@ -230,6 +231,7 @@ impl DefMapCrateData { |
| 230 | 231 | Self { |
| 231 | 232 | exported_derives: FxHashMap::default(), |
| 232 | 233 | fn_proc_macro_mapping: FxHashMap::default(), |
| 234 | fn_proc_macro_mapping_back: FxHashMap::default(), | |
| 233 | 235 | registered_tools: PREDEFINED_TOOLS.iter().map(|it| Symbol::intern(it)).collect(), |
| 234 | 236 | unstable_features: FxHashSet::default(), |
| 235 | 237 | rustc_coherence_is_core: false, |
| ... | ... | @@ -244,6 +246,7 @@ impl DefMapCrateData { |
| 244 | 246 | let Self { |
| 245 | 247 | exported_derives, |
| 246 | 248 | fn_proc_macro_mapping, |
| 249 | fn_proc_macro_mapping_back, | |
| 247 | 250 | registered_tools, |
| 248 | 251 | unstable_features, |
| 249 | 252 | rustc_coherence_is_core: _, |
| ... | ... | @@ -254,6 +257,7 @@ impl DefMapCrateData { |
| 254 | 257 | } = self; |
| 255 | 258 | exported_derives.shrink_to_fit(); |
| 256 | 259 | fn_proc_macro_mapping.shrink_to_fit(); |
| 260 | fn_proc_macro_mapping_back.shrink_to_fit(); | |
| 257 | 261 | registered_tools.shrink_to_fit(); |
| 258 | 262 | unstable_features.shrink_to_fit(); |
| 259 | 263 | } |
| ... | ... | @@ -570,6 +574,10 @@ impl DefMap { |
| 570 | 574 | self.data.fn_proc_macro_mapping.get(&id).copied() |
| 571 | 575 | } |
| 572 | 576 | |
| 577 | pub fn proc_macro_as_fn(&self, id: ProcMacroId) -> Option<FunctionId> { | |
| 578 | self.data.fn_proc_macro_mapping_back.get(&id).copied() | |
| 579 | } | |
| 580 | ||
| 573 | 581 | pub fn krate(&self) -> Crate { |
| 574 | 582 | self.krate |
| 575 | 583 | } |
src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs+10-2| ... | ... | @@ -634,6 +634,7 @@ impl<'db> DefCollector<'db> { |
| 634 | 634 | crate_data.exported_derives.insert(proc_macro_id.into(), helpers); |
| 635 | 635 | } |
| 636 | 636 | crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id); |
| 637 | crate_data.fn_proc_macro_mapping_back.insert(proc_macro_id, fn_id); | |
| 637 | 638 | } |
| 638 | 639 | |
| 639 | 640 | /// Define a macro with `macro_rules`. |
| ... | ... | @@ -2095,6 +2096,8 @@ impl ModCollector<'_, '_> { |
| 2095 | 2096 | |
| 2096 | 2097 | let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]); |
| 2097 | 2098 | |
| 2099 | update_def(self.def_collector, fn_id.into(), &it.name, vis, false); | |
| 2100 | ||
| 2098 | 2101 | if self.def_collector.def_map.block.is_none() |
| 2099 | 2102 | && self.def_collector.is_proc_macro |
| 2100 | 2103 | && self.module_id == self.def_collector.def_map.root |
| ... | ... | @@ -2105,9 +2108,14 @@ impl ModCollector<'_, '_> { |
| 2105 | 2108 | InFile::new(self.file_id(), id), |
| 2106 | 2109 | fn_id, |
| 2107 | 2110 | ); |
| 2108 | } | |
| 2109 | 2111 | |
| 2110 | update_def(self.def_collector, fn_id.into(), &it.name, vis, false); | |
| 2112 | // A proc macro is implemented as a function, but it's treated as a macro, not a function. | |
| 2113 | // You cannot call it like a function, for example, except in its defining crate. | |
| 2114 | // So we keep the function definition, but remove it from the scope, leaving only the macro. | |
| 2115 | self.def_collector.def_map[module_id] | |
| 2116 | .scope | |
| 2117 | .remove_from_value_ns(&it.name, fn_id.into()); | |
| 2118 | } | |
| 2111 | 2119 | } |
| 2112 | 2120 | ModItemId::Struct(id) => { |
| 2113 | 2121 | let it = &self.item_tree[id]; |
src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs+2-4| ... | ... | @@ -1068,10 +1068,8 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream { |
| 1068 | 1068 | - AnotherTrait : macro# |
| 1069 | 1069 | - DummyTrait : macro# |
| 1070 | 1070 | - TokenStream : type value |
| 1071 | - attribute_macro : value macro# | |
| 1072 | - derive_macro : value | |
| 1073 | - derive_macro_2 : value | |
| 1074 | - function_like_macro : value macro! | |
| 1071 | - attribute_macro : macro# | |
| 1072 | - function_like_macro : macro! | |
| 1075 | 1073 | "#]], |
| 1076 | 1074 | ); |
| 1077 | 1075 | } |
src/tools/rust-analyzer/crates/hir-def/src/resolver.rs+36-37| ... | ... | @@ -32,7 +32,7 @@ use crate::{ |
| 32 | 32 | BindingId, ExprId, LabelId, |
| 33 | 33 | generics::{GenericParams, TypeOrConstParamData}, |
| 34 | 34 | }, |
| 35 | item_scope::{BUILTIN_SCOPE, BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, ItemScope}, | |
| 35 | item_scope::{BUILTIN_SCOPE, BuiltinShadowMode, ImportOrExternCrate, ItemScope}, | |
| 36 | 36 | lang_item::LangItemTarget, |
| 37 | 37 | nameres::{DefMap, LocalDefMap, MacroSubNs, ResolvePathResultPrefixInfo, block_def_map}, |
| 38 | 38 | per_ns::PerNs, |
| ... | ... | @@ -111,8 +111,8 @@ pub enum TypeNs { |
| 111 | 111 | |
| 112 | 112 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| 113 | 113 | pub enum ResolveValueResult { |
| 114 | ValueNs(ValueNs, Option<ImportOrGlob>), | |
| 115 | Partial(TypeNs, usize, Option<ImportOrExternCrate>), | |
| 114 | ValueNs(ValueNs), | |
| 115 | Partial(TypeNs, usize), | |
| 116 | 116 | } |
| 117 | 117 | |
| 118 | 118 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
| ... | ... | @@ -332,20 +332,17 @@ impl<'db> Resolver<'db> { |
| 332 | 332 | Path::Normal(it) => &it.mod_path, |
| 333 | 333 | Path::LangItem(l, None) => { |
| 334 | 334 | return Some(( |
| 335 | ResolveValueResult::ValueNs( | |
| 336 | match *l { | |
| 337 | LangItemTarget::FunctionId(it) => ValueNs::FunctionId(it), | |
| 338 | LangItemTarget::StaticId(it) => ValueNs::StaticId(it), | |
| 339 | LangItemTarget::StructId(it) => ValueNs::StructId(it), | |
| 340 | LangItemTarget::EnumVariantId(it) => ValueNs::EnumVariantId(it), | |
| 341 | LangItemTarget::UnionId(_) | |
| 342 | | LangItemTarget::ImplId(_) | |
| 343 | | LangItemTarget::TypeAliasId(_) | |
| 344 | | LangItemTarget::TraitId(_) | |
| 345 | | LangItemTarget::EnumId(_) => return None, | |
| 346 | }, | |
| 347 | None, | |
| 348 | ), | |
| 335 | ResolveValueResult::ValueNs(match *l { | |
| 336 | LangItemTarget::FunctionId(it) => ValueNs::FunctionId(it), | |
| 337 | LangItemTarget::StaticId(it) => ValueNs::StaticId(it), | |
| 338 | LangItemTarget::StructId(it) => ValueNs::StructId(it), | |
| 339 | LangItemTarget::EnumVariantId(it) => ValueNs::EnumVariantId(it), | |
| 340 | LangItemTarget::UnionId(_) | |
| 341 | | LangItemTarget::ImplId(_) | |
| 342 | | LangItemTarget::TypeAliasId(_) | |
| 343 | | LangItemTarget::TraitId(_) | |
| 344 | | LangItemTarget::EnumId(_) => return None, | |
| 345 | }), | |
| 349 | 346 | ResolvePathResultPrefixInfo::default(), |
| 350 | 347 | )); |
| 351 | 348 | } |
| ... | ... | @@ -363,7 +360,7 @@ impl<'db> Resolver<'db> { |
| 363 | 360 | }; |
| 364 | 361 | // Remaining segments start from 0 because lang paths have no segments other than the remaining. |
| 365 | 362 | return Some(( |
| 366 | ResolveValueResult::Partial(type_ns, 0, None), | |
| 363 | ResolveValueResult::Partial(type_ns, 0), | |
| 367 | 364 | ResolvePathResultPrefixInfo::default(), |
| 368 | 365 | )); |
| 369 | 366 | } |
| ... | ... | @@ -388,10 +385,7 @@ impl<'db> Resolver<'db> { |
| 388 | 385 | |
| 389 | 386 | if let Some(e) = entry { |
| 390 | 387 | return Some(( |
| 391 | ResolveValueResult::ValueNs( | |
| 392 | ValueNs::LocalBinding(e.binding()), | |
| 393 | None, | |
| 394 | ), | |
| 388 | ResolveValueResult::ValueNs(ValueNs::LocalBinding(e.binding())), | |
| 395 | 389 | ResolvePathResultPrefixInfo::default(), |
| 396 | 390 | )); |
| 397 | 391 | } |
| ... | ... | @@ -404,14 +398,14 @@ impl<'db> Resolver<'db> { |
| 404 | 398 | && *first_name == sym::Self_ |
| 405 | 399 | { |
| 406 | 400 | return Some(( |
| 407 | ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_), None), | |
| 401 | ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_)), | |
| 408 | 402 | ResolvePathResultPrefixInfo::default(), |
| 409 | 403 | )); |
| 410 | 404 | } |
| 411 | 405 | if let Some(id) = params.find_const_by_name(first_name, *def) { |
| 412 | 406 | let val = ValueNs::GenericParam(id); |
| 413 | 407 | return Some(( |
| 414 | ResolveValueResult::ValueNs(val, None), | |
| 408 | ResolveValueResult::ValueNs(val), | |
| 415 | 409 | ResolvePathResultPrefixInfo::default(), |
| 416 | 410 | )); |
| 417 | 411 | } |
| ... | ... | @@ -431,7 +425,7 @@ impl<'db> Resolver<'db> { |
| 431 | 425 | if let &GenericDefId::ImplId(impl_) = def { |
| 432 | 426 | if *first_name == sym::Self_ { |
| 433 | 427 | return Some(( |
| 434 | ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1, None), | |
| 428 | ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1), | |
| 435 | 429 | ResolvePathResultPrefixInfo::default(), |
| 436 | 430 | )); |
| 437 | 431 | } |
| ... | ... | @@ -440,14 +434,14 @@ impl<'db> Resolver<'db> { |
| 440 | 434 | { |
| 441 | 435 | let ty = TypeNs::AdtSelfType(adt); |
| 442 | 436 | return Some(( |
| 443 | ResolveValueResult::Partial(ty, 1, None), | |
| 437 | ResolveValueResult::Partial(ty, 1), | |
| 444 | 438 | ResolvePathResultPrefixInfo::default(), |
| 445 | 439 | )); |
| 446 | 440 | } |
| 447 | 441 | if let Some(id) = params.find_type_by_name(first_name, *def) { |
| 448 | 442 | let ty = TypeNs::GenericParam(id); |
| 449 | 443 | return Some(( |
| 450 | ResolveValueResult::Partial(ty, 1, None), | |
| 444 | ResolveValueResult::Partial(ty, 1), | |
| 451 | 445 | ResolvePathResultPrefixInfo::default(), |
| 452 | 446 | )); |
| 453 | 447 | } |
| ... | ... | @@ -473,7 +467,7 @@ impl<'db> Resolver<'db> { |
| 473 | 467 | && let Some(builtin) = BuiltinType::by_name(first_name) |
| 474 | 468 | { |
| 475 | 469 | return Some(( |
| 476 | ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None), | |
| 470 | ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1), | |
| 477 | 471 | ResolvePathResultPrefixInfo::default(), |
| 478 | 472 | )); |
| 479 | 473 | } |
| ... | ... | @@ -488,7 +482,7 @@ impl<'db> Resolver<'db> { |
| 488 | 482 | hygiene: HygieneId, |
| 489 | 483 | ) -> Option<ValueNs> { |
| 490 | 484 | match self.resolve_path_in_value_ns(db, path, hygiene)? { |
| 491 | ResolveValueResult::ValueNs(it, _) => Some(it), | |
| 485 | ResolveValueResult::ValueNs(it) => Some(it), | |
| 492 | 486 | ResolveValueResult::Partial(..) => None, |
| 493 | 487 | } |
| 494 | 488 | } |
| ... | ... | @@ -1153,12 +1147,12 @@ impl<'db> ModuleItemMap<'db> { |
| 1153 | 1147 | ); |
| 1154 | 1148 | match unresolved_idx { |
| 1155 | 1149 | None => { |
| 1156 | let (value, import) = to_value_ns(module_def)?; | |
| 1157 | Some((ResolveValueResult::ValueNs(value, import), prefix_info)) | |
| 1150 | let value = to_value_ns(module_def, self.def_map)?; | |
| 1151 | Some((ResolveValueResult::ValueNs(value), prefix_info)) | |
| 1158 | 1152 | } |
| 1159 | 1153 | Some(unresolved_idx) => { |
| 1160 | let def = module_def.take_types_full()?; | |
| 1161 | let ty = match def.def { | |
| 1154 | let def = module_def.take_types()?; | |
| 1155 | let ty = match def { | |
| 1162 | 1156 | ModuleDefId::AdtId(it) => TypeNs::AdtId(it), |
| 1163 | 1157 | ModuleDefId::TraitId(it) => TypeNs::TraitId(it), |
| 1164 | 1158 | ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it), |
| ... | ... | @@ -1171,7 +1165,7 @@ impl<'db> ModuleItemMap<'db> { |
| 1171 | 1165 | | ModuleDefId::MacroId(_) |
| 1172 | 1166 | | ModuleDefId::StaticId(_) => return None, |
| 1173 | 1167 | }; |
| 1174 | Some((ResolveValueResult::Partial(ty, unresolved_idx, def.import), prefix_info)) | |
| 1168 | Some((ResolveValueResult::Partial(ty, unresolved_idx), prefix_info)) | |
| 1175 | 1169 | } |
| 1176 | 1170 | } |
| 1177 | 1171 | } |
| ... | ... | @@ -1194,8 +1188,13 @@ impl<'db> ModuleItemMap<'db> { |
| 1194 | 1188 | } |
| 1195 | 1189 | } |
| 1196 | 1190 | |
| 1197 | fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option<ImportOrGlob>)> { | |
| 1198 | let (def, import) = per_ns.take_values_import()?; | |
| 1191 | fn to_value_ns(per_ns: PerNs, def_map: &DefMap) -> Option<ValueNs> { | |
| 1192 | let def = per_ns.take_values().or_else(|| { | |
| 1193 | let Some(MacroId::ProcMacroId(proc_macro)) = per_ns.take_macros() else { return None }; | |
| 1194 | // If we cannot resolve to value ns, but we can resolve to a proc macro, and this is the crate | |
| 1195 | // defining this proc macro - inside this crate, we should treat the macro as a function. | |
| 1196 | def_map.proc_macro_as_fn(proc_macro).map(ModuleDefId::FunctionId) | |
| 1197 | })?; | |
| 1199 | 1198 | let res = match def { |
| 1200 | 1199 | ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it), |
| 1201 | 1200 | ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it), |
| ... | ... | @@ -1210,7 +1209,7 @@ fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option<ImportOrGlob>)> { |
| 1210 | 1209 | | ModuleDefId::MacroId(_) |
| 1211 | 1210 | | ModuleDefId::ModuleId(_) => return None, |
| 1212 | 1211 | }; |
| 1213 | Some((res, import)) | |
| 1212 | Some(res) | |
| 1214 | 1213 | } |
| 1215 | 1214 | |
| 1216 | 1215 | fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option<ImportOrExternCrate>)> { |
src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs+1-1| ... | ... | @@ -430,7 +430,7 @@ impl<'db> UnsafeVisitor<'db> { |
| 430 | 430 | fn mark_unsafe_path(&mut self, node: ExprOrPatId, path: &Path) { |
| 431 | 431 | let hygiene = self.body.expr_or_pat_path_hygiene(node); |
| 432 | 432 | let value_or_partial = self.resolver.resolve_path_in_value_ns(self.db, path, hygiene); |
| 433 | if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial { | |
| 433 | if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial { | |
| 434 | 434 | let static_data = self.db.static_signature(id); |
| 435 | 435 | if static_data.flags.contains(StaticFlags::MUTABLE) { |
| 436 | 436 | self.on_unsafe_op(node, UnsafetyReason::MutableStatic); |
src/tools/rust-analyzer/crates/hir-ty/src/infer.rs+2-2| ... | ... | @@ -1584,7 +1584,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { |
| 1584 | 1584 | return (self.err_ty(), None); |
| 1585 | 1585 | }; |
| 1586 | 1586 | match res { |
| 1587 | ResolveValueResult::ValueNs(value, _) => match value { | |
| 1587 | ResolveValueResult::ValueNs(value) => match value { | |
| 1588 | 1588 | ValueNs::EnumVariantId(var) => { |
| 1589 | 1589 | let args = path_ctx.substs_from_path(var.into(), true, false); |
| 1590 | 1590 | drop(ctx); |
| ... | ... | @@ -1608,7 +1608,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { |
| 1608 | 1608 | return (self.err_ty(), None); |
| 1609 | 1609 | } |
| 1610 | 1610 | }, |
| 1611 | ResolveValueResult::Partial(typens, unresolved, _) => (typens, Some(unresolved)), | |
| 1611 | ResolveValueResult::Partial(typens, unresolved) => (typens, Some(unresolved)), | |
| 1612 | 1612 | } |
| 1613 | 1613 | } else { |
| 1614 | 1614 | match path_ctx.resolve_path_in_type_ns() { |
src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs+1-1| ... | ... | @@ -751,7 +751,7 @@ impl<'db> InferenceContext<'_, 'db> { |
| 751 | 751 | |
| 752 | 752 | if let Some(lhs_ty) = lhs_ty { |
| 753 | 753 | self.write_pat_ty(target, lhs_ty); |
| 754 | self.infer_expr_coerce(value, &Expectation::has_type(lhs_ty), ExprIsRead::No); | |
| 754 | self.infer_expr_coerce(value, &Expectation::has_type(lhs_ty), ExprIsRead::Yes); | |
| 755 | 755 | } else { |
| 756 | 756 | let rhs_ty = self.infer_expr(value, &Expectation::none(), ExprIsRead::Yes); |
| 757 | 757 | let resolver_guard = |
src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs+1-1| ... | ... | @@ -673,7 +673,7 @@ impl<'db> InferenceContext<'_, 'db> { |
| 673 | 673 | pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool { |
| 674 | 674 | let mut res = false; |
| 675 | 675 | body.walk_pats(pat_id, &mut |pat| { |
| 676 | res |= matches!(body[pat], Pat::Bind { id, .. } if body[id].mode == BindingAnnotation::Ref); | |
| 676 | res |= matches!(body[pat], Pat::Bind { id, .. } if matches!(body[id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)); | |
| 677 | 677 | }); |
| 678 | 678 | res |
| 679 | 679 | } |
src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs+2-2| ... | ... | @@ -164,11 +164,11 @@ impl<'db> InferenceContext<'_, 'db> { |
| 164 | 164 | let value_or_partial = path_ctx.resolve_path_in_value_ns(hygiene)?; |
| 165 | 165 | |
| 166 | 166 | match value_or_partial { |
| 167 | ResolveValueResult::ValueNs(it, _) => { | |
| 167 | ResolveValueResult::ValueNs(it) => { | |
| 168 | 168 | drop_ctx(ctx, no_diagnostics); |
| 169 | 169 | (it, None) |
| 170 | 170 | } |
| 171 | ResolveValueResult::Partial(def, remaining_index, _) => { | |
| 171 | ResolveValueResult::Partial(def, remaining_index) => { | |
| 172 | 172 | // there may be more intermediate segments between the resolved one and |
| 173 | 173 | // the end. Only the last segment needs to be resolved to a value; from |
| 174 | 174 | // the segments before that, we need to get either a type or a trait ref. |
src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs+4-6| ... | ... | @@ -396,12 +396,10 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { |
| 396 | 396 | } |
| 397 | 397 | |
| 398 | 398 | let (mod_segments, enum_segment, resolved_segment_idx) = match res { |
| 399 | ResolveValueResult::Partial(_, unresolved_segment, _) => { | |
| 399 | ResolveValueResult::Partial(_, unresolved_segment) => { | |
| 400 | 400 | (segments.take(unresolved_segment - 1), None, unresolved_segment - 1) |
| 401 | 401 | } |
| 402 | ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _) | |
| 403 | if prefix_info.enum_variant => | |
| 404 | { | |
| 402 | ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_)) if prefix_info.enum_variant => { | |
| 405 | 403 | (segments.strip_last_two(), segments.len().checked_sub(2), segments.len() - 1) |
| 406 | 404 | } |
| 407 | 405 | ResolveValueResult::ValueNs(..) => (segments.strip_last(), None, segments.len() - 1), |
| ... | ... | @@ -431,7 +429,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { |
| 431 | 429 | } |
| 432 | 430 | |
| 433 | 431 | match &res { |
| 434 | ResolveValueResult::ValueNs(resolution, _) => { | |
| 432 | ResolveValueResult::ValueNs(resolution) => { | |
| 435 | 433 | let resolved_segment_idx = self.current_segment_u32(); |
| 436 | 434 | let resolved_segment = self.current_or_prev_segment; |
| 437 | 435 | |
| ... | ... | @@ -469,7 +467,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { |
| 469 | 467 | | ValueNs::ConstId(_) => {} |
| 470 | 468 | } |
| 471 | 469 | } |
| 472 | ResolveValueResult::Partial(resolution, _, _) => { | |
| 470 | ResolveValueResult::Partial(resolution, _) => { | |
| 473 | 471 | if !self.handle_type_ns_resolution(resolution) { |
| 474 | 472 | return None; |
| 475 | 473 | } |
src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs+1-1| ... | ... | @@ -1625,7 +1625,7 @@ impl<'db> Evaluator<'db> { |
| 1625 | 1625 | }; |
| 1626 | 1626 | match target_ty { |
| 1627 | 1627 | rustc_type_ir::FloatTy::F32 => Owned((value as f32).to_le_bytes().to_vec()), |
| 1628 | rustc_type_ir::FloatTy::F64 => Owned((value as f64).to_le_bytes().to_vec()), | |
| 1628 | rustc_type_ir::FloatTy::F64 => Owned(value.to_le_bytes().to_vec()), | |
| 1629 | 1629 | rustc_type_ir::FloatTy::F16 | rustc_type_ir::FloatTy::F128 => { |
| 1630 | 1630 | not_supported!("unstable floating point type f16 and f128"); |
| 1631 | 1631 | } |
src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs+2-2| ... | ... | @@ -1429,7 +1429,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { |
| 1429 | 1429 | .resolve_path_in_value_ns(self.db, c, HygieneId::ROOT) |
| 1430 | 1430 | .ok_or_else(unresolved_name)?; |
| 1431 | 1431 | match pr { |
| 1432 | ResolveValueResult::ValueNs(v, _) => { | |
| 1432 | ResolveValueResult::ValueNs(v) => { | |
| 1433 | 1433 | if let ValueNs::ConstId(c) = v { |
| 1434 | 1434 | self.lower_const_to_operand( |
| 1435 | 1435 | GenericArgs::empty(self.interner()), |
| ... | ... | @@ -1439,7 +1439,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { |
| 1439 | 1439 | not_supported!("bad path in range pattern"); |
| 1440 | 1440 | } |
| 1441 | 1441 | } |
| 1442 | ResolveValueResult::Partial(_, _, _) => { | |
| 1442 | ResolveValueResult::Partial(_, _) => { | |
| 1443 | 1443 | not_supported!("associated constants in range pattern") |
| 1444 | 1444 | } |
| 1445 | 1445 | } |
src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs+2-2| ... | ... | @@ -373,7 +373,7 @@ impl<'db> MirLowerCtx<'_, 'db> { |
| 373 | 373 | |
| 374 | 374 | if let ( |
| 375 | 375 | MatchingMode::Assign, |
| 376 | ResolveValueResult::ValueNs(ValueNs::LocalBinding(binding), _), | |
| 376 | ResolveValueResult::ValueNs(ValueNs::LocalBinding(binding)), | |
| 377 | 377 | ) = (mode, &pr) |
| 378 | 378 | { |
| 379 | 379 | let local = self.binding_local(*binding)?; |
| ... | ... | @@ -398,7 +398,7 @@ impl<'db> MirLowerCtx<'_, 'db> { |
| 398 | 398 | { |
| 399 | 399 | break 'b (c, x.1); |
| 400 | 400 | } |
| 401 | if let ResolveValueResult::ValueNs(ValueNs::ConstId(c), _) = pr { | |
| 401 | if let ResolveValueResult::ValueNs(ValueNs::ConstId(c)) = pr { | |
| 402 | 402 | break 'b (c, GenericArgs::empty(self.interner())); |
| 403 | 403 | } |
| 404 | 404 | not_supported!("path in pattern position that is not const or variant") |
src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs+4-3| ... | ... | @@ -714,9 +714,9 @@ impl<'db> rustc_type_ir::inherent::Predicate<DbInterner<'db>> for Predicate<'db> |
| 714 | 714 | fn allow_normalization(self) -> bool { |
| 715 | 715 | // TODO: this should probably live in rustc_type_ir |
| 716 | 716 | match self.inner().as_ref().skip_binder() { |
| 717 | PredicateKind::Clause(ClauseKind::WellFormed(_)) | |
| 718 | | PredicateKind::AliasRelate(..) | |
| 719 | | PredicateKind::NormalizesTo(..) => false, | |
| 717 | PredicateKind::Clause(ClauseKind::WellFormed(_)) | PredicateKind::AliasRelate(..) => { | |
| 718 | false | |
| 719 | } | |
| 720 | 720 | PredicateKind::Clause(ClauseKind::Trait(_)) |
| 721 | 721 | | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) |
| 722 | 722 | | PredicateKind::Clause(ClauseKind::TypeOutlives(_)) |
| ... | ... | @@ -729,6 +729,7 @@ impl<'db> rustc_type_ir::inherent::Predicate<DbInterner<'db>> for Predicate<'db> |
| 729 | 729 | | PredicateKind::Coerce(_) |
| 730 | 730 | | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_)) |
| 731 | 731 | | PredicateKind::ConstEquate(_, _) |
| 732 | | PredicateKind::NormalizesTo(..) | |
| 732 | 733 | | PredicateKind::Ambiguous => true, |
| 733 | 734 | } |
| 734 | 735 | } |
src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs+42| ... | ... | @@ -760,6 +760,48 @@ fn coerce_ref_binding() -> ! { |
| 760 | 760 | ) |
| 761 | 761 | } |
| 762 | 762 | |
| 763 | #[test] | |
| 764 | fn diverging_place_match_ref_mut() { | |
| 765 | check_infer_with_mismatches( | |
| 766 | r#" | |
| 767 | //- minicore: sized | |
| 768 | fn coerce_ref_mut_binding() -> ! { | |
| 769 | unsafe { | |
| 770 | let x: *mut ! = 0 as _; | |
| 771 | let ref mut _x: () = *x; | |
| 772 | } | |
| 773 | } | |
| 774 | "#, | |
| 775 | expect![[r#" | |
| 776 | 33..120 '{ ... } }': ! | |
| 777 | 39..118 'unsafe... }': ! | |
| 778 | 60..61 'x': *mut ! | |
| 779 | 72..73 '0': i32 | |
| 780 | 72..78 '0 as _': *mut ! | |
| 781 | 92..102 'ref mut _x': &'? mut () | |
| 782 | 109..111 '*x': ! | |
| 783 | 110..111 'x': *mut ! | |
| 784 | 109..111: expected (), got ! | |
| 785 | "#]], | |
| 786 | ) | |
| 787 | } | |
| 788 | ||
| 789 | #[test] | |
| 790 | fn assign_never_place_no_mismatch() { | |
| 791 | check_no_mismatches( | |
| 792 | r#" | |
| 793 | //- minicore: sized | |
| 794 | fn foo() { | |
| 795 | unsafe { | |
| 796 | let p: *mut ! = 0 as _; | |
| 797 | let mut x: () = (); | |
| 798 | x = *p; | |
| 799 | } | |
| 800 | } | |
| 801 | "#, | |
| 802 | ); | |
| 803 | } | |
| 804 | ||
| 763 | 805 | #[test] |
| 764 | 806 | fn never_place_isnt_diverging() { |
| 765 | 807 | check_infer_with_mismatches( |
src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs+83-1| ... | ... | @@ -2363,7 +2363,6 @@ fn test() { |
| 2363 | 2363 | } |
| 2364 | 2364 | "#, |
| 2365 | 2365 | expect![[r#" |
| 2366 | 46..49 'Foo': Foo<N> | |
| 2367 | 2366 | 93..97 'self': Foo<N> |
| 2368 | 2367 | 108..125 '{ ... }': usize |
| 2369 | 2368 | 118..119 'N': usize |
| ... | ... | @@ -2688,3 +2687,86 @@ pub trait FilterT<F: FilterT<F, V = Self::V> = Self> { |
| 2688 | 2687 | "#, |
| 2689 | 2688 | ); |
| 2690 | 2689 | } |
| 2690 | ||
| 2691 | #[test] | |
| 2692 | fn regression_21605() { | |
| 2693 | check_infer( | |
| 2694 | r#" | |
| 2695 | //- minicore: fn, coerce_unsized, dispatch_from_dyn, iterator, iterators | |
| 2696 | pub struct Filter<'a, 'b, T> | |
| 2697 | where | |
| 2698 | T: 'b, | |
| 2699 | 'a: 'b, | |
| 2700 | { | |
| 2701 | filter_fn: dyn Fn(&'a T) -> bool, | |
| 2702 | t: Option<T>, | |
| 2703 | b: &'b (), | |
| 2704 | } | |
| 2705 | ||
| 2706 | impl<'a, 'b, T> Filter<'a, 'b, T> | |
| 2707 | where | |
| 2708 | T: 'b, | |
| 2709 | 'a: 'b, | |
| 2710 | { | |
| 2711 | pub fn new(filter_fn: dyn Fn(&T) -> bool) -> Self { | |
| 2712 | Self { | |
| 2713 | filter_fn: filter_fn, | |
| 2714 | t: None, | |
| 2715 | b: &(), | |
| 2716 | } | |
| 2717 | } | |
| 2718 | } | |
| 2719 | ||
| 2720 | pub trait FilterExt<T> { | |
| 2721 | type Output; | |
| 2722 | fn filter(&self, filter: &Filter<T>) -> Self::Output; | |
| 2723 | } | |
| 2724 | ||
| 2725 | impl<const N: usize, T> FilterExt<T> for [T; N] | |
| 2726 | where | |
| 2727 | T: IntoIterator, | |
| 2728 | { | |
| 2729 | type Output = T; | |
| 2730 | fn filter(&self, filter: &Filter<T>) -> Self::Output { | |
| 2731 | let _ = self.into_iter().filter(filter.filter_fn); | |
| 2732 | loop {} | |
| 2733 | } | |
| 2734 | } | |
| 2735 | "#, | |
| 2736 | expect![[r#" | |
| 2737 | 214..223 'filter_fn': dyn Fn(&'? T) -> bool + 'static | |
| 2738 | 253..360 '{ ... }': Filter<'a, 'b, T> | |
| 2739 | 263..354 'Self {... }': Filter<'a, 'b, T> | |
| 2740 | 293..302 'filter_fn': dyn Fn(&'? T) -> bool + 'static | |
| 2741 | 319..323 'None': Option<T> | |
| 2742 | 340..343 '&()': &'? () | |
| 2743 | 341..343 '()': () | |
| 2744 | 421..425 'self': &'? Self | |
| 2745 | 427..433 'filter': &'? Filter<'?, '?, T> | |
| 2746 | 580..584 'self': &'? [T; N] | |
| 2747 | 586..592 'filter': &'? Filter<'?, '?, T> | |
| 2748 | 622..704 '{ ... }': T | |
| 2749 | 636..637 '_': Filter<Iter<'?, T>, dyn Fn(&'? T) -> bool + '?> | |
| 2750 | 640..644 'self': &'? [T; N] | |
| 2751 | 640..656 'self.i...iter()': Iter<'?, T> | |
| 2752 | 640..681 'self.i...er_fn)': Filter<Iter<'?, T>, dyn Fn(&'? T) -> bool + '?> | |
| 2753 | 664..670 'filter': &'? Filter<'?, '?, T> | |
| 2754 | 664..680 'filter...ter_fn': dyn Fn(&'? T) -> bool + 'static | |
| 2755 | 691..698 'loop {}': ! | |
| 2756 | 696..698 '{}': () | |
| 2757 | "#]], | |
| 2758 | ); | |
| 2759 | } | |
| 2760 | ||
| 2761 | #[test] | |
| 2762 | fn extern_fns_cannot_have_param_patterns() { | |
| 2763 | check_no_mismatches( | |
| 2764 | r#" | |
| 2765 | pub(crate) struct Builder<'a>(&'a ()); | |
| 2766 | ||
| 2767 | unsafe extern "C" { | |
| 2768 | pub(crate) fn foo<'a>(Builder: &Builder<'a>); | |
| 2769 | } | |
| 2770 | "#, | |
| 2771 | ); | |
| 2772 | } |
src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs+35| ... | ... | @@ -4074,3 +4074,38 @@ static S: &[u8; 158] = include_bytes!("/foo/bar/baz.txt"); |
| 4074 | 4074 | "#, |
| 4075 | 4075 | ); |
| 4076 | 4076 | } |
| 4077 | ||
| 4078 | #[test] | |
| 4079 | fn proc_macros_are_functions_inside_defining_crate_and_macros_outside() { | |
| 4080 | check_types( | |
| 4081 | r#" | |
| 4082 | //- /pm.rs crate:pm | |
| 4083 | #![crate_type = "proc-macro"] | |
| 4084 | ||
| 4085 | #[proc_macro_attribute] | |
| 4086 | pub fn proc_macro() {} | |
| 4087 | ||
| 4088 | fn foo() { | |
| 4089 | proc_macro; | |
| 4090 | // ^^^^^^^^^^ fn proc_macro() | |
| 4091 | } | |
| 4092 | ||
| 4093 | mod bar { | |
| 4094 | use super::proc_macro; | |
| 4095 | ||
| 4096 | fn baz() { | |
| 4097 | super::proc_macro; | |
| 4098 | // ^^^^^^^^^^^^^^^^^ fn proc_macro() | |
| 4099 | proc_macro; | |
| 4100 | // ^^^^^^^^^^ fn proc_macro() | |
| 4101 | } | |
| 4102 | } | |
| 4103 | ||
| 4104 | //- /lib.rs crate:lib deps:pm | |
| 4105 | fn foo() { | |
| 4106 | pm::proc_macro; | |
| 4107 | // ^^^^^^^^^^^^^^ {unknown} | |
| 4108 | } | |
| 4109 | "#, | |
| 4110 | ); | |
| 4111 | } |
src/tools/rust-analyzer/crates/hir/src/display.rs+11-2| ... | ... | @@ -172,8 +172,13 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re |
| 172 | 172 | |
| 173 | 173 | write_generic_params(GenericDefId::FunctionId(func_id), f)?; |
| 174 | 174 | |
| 175 | let too_long_param = data.params.len() > 4; | |
| 175 | 176 | f.write_char('(')?; |
| 176 | 177 | |
| 178 | if too_long_param { | |
| 179 | f.write_str("\n ")?; | |
| 180 | } | |
| 181 | ||
| 177 | 182 | let mut first = true; |
| 178 | 183 | let mut skip_self = 0; |
| 179 | 184 | if let Some(self_param) = func.self_param(db) { |
| ... | ... | @@ -182,11 +187,12 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re |
| 182 | 187 | skip_self = 1; |
| 183 | 188 | } |
| 184 | 189 | |
| 190 | let comma = if too_long_param { ",\n " } else { ", " }; | |
| 185 | 191 | // FIXME: Use resolved `param.ty` once we no longer discard lifetimes |
| 186 | 192 | let body = db.body(func_id.into()); |
| 187 | 193 | for (type_ref, param) in data.params.iter().zip(func.assoc_fn_params(db)).skip(skip_self) { |
| 188 | 194 | if !first { |
| 189 | f.write_str(", ")?; | |
| 195 | f.write_str(comma)?; | |
| 190 | 196 | } else { |
| 191 | 197 | first = false; |
| 192 | 198 | } |
| ... | ... | @@ -201,11 +207,14 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re |
| 201 | 207 | |
| 202 | 208 | if data.is_varargs() { |
| 203 | 209 | if !first { |
| 204 | f.write_str(", ")?; | |
| 210 | f.write_str(comma)?; | |
| 205 | 211 | } |
| 206 | 212 | f.write_str("...")?; |
| 207 | 213 | } |
| 208 | 214 | |
| 215 | if too_long_param { | |
| 216 | f.write_char('\n')?; | |
| 217 | } | |
| 209 | 218 | f.write_char(')')?; |
| 210 | 219 | |
| 211 | 220 | // `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns. |
src/tools/rust-analyzer/crates/hir/src/lib.rs+34-5| ... | ... | @@ -2244,6 +2244,39 @@ impl DefWithBody { |
| 2244 | 2244 | acc.push(diag.into()) |
| 2245 | 2245 | } |
| 2246 | 2246 | } |
| 2247 | ||
| 2248 | /// Returns an iterator over the inferred types of all expressions in this body. | |
| 2249 | pub fn expression_types<'db>( | |
| 2250 | self, | |
| 2251 | db: &'db dyn HirDatabase, | |
| 2252 | ) -> impl Iterator<Item = Type<'db>> { | |
| 2253 | self.id().into_iter().flat_map(move |def_id| { | |
| 2254 | let infer = InferenceResult::for_body(db, def_id); | |
| 2255 | let resolver = def_id.resolver(db); | |
| 2256 | ||
| 2257 | infer.expression_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) | |
| 2258 | }) | |
| 2259 | } | |
| 2260 | ||
| 2261 | /// Returns an iterator over the inferred types of all patterns in this body. | |
| 2262 | pub fn pattern_types<'db>(self, db: &'db dyn HirDatabase) -> impl Iterator<Item = Type<'db>> { | |
| 2263 | self.id().into_iter().flat_map(move |def_id| { | |
| 2264 | let infer = InferenceResult::for_body(db, def_id); | |
| 2265 | let resolver = def_id.resolver(db); | |
| 2266 | ||
| 2267 | infer.pattern_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) | |
| 2268 | }) | |
| 2269 | } | |
| 2270 | ||
| 2271 | /// Returns an iterator over the inferred types of all bindings in this body. | |
| 2272 | pub fn binding_types<'db>(self, db: &'db dyn HirDatabase) -> impl Iterator<Item = Type<'db>> { | |
| 2273 | self.id().into_iter().flat_map(move |def_id| { | |
| 2274 | let infer = InferenceResult::for_body(db, def_id); | |
| 2275 | let resolver = def_id.resolver(db); | |
| 2276 | ||
| 2277 | infer.binding_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) | |
| 2278 | }) | |
| 2279 | } | |
| 2247 | 2280 | } |
| 2248 | 2281 | |
| 2249 | 2282 | fn expr_store_diagnostics<'db>( |
| ... | ... | @@ -6068,11 +6101,7 @@ impl<'db> Type<'db> { |
| 6068 | 6101 | |
| 6069 | 6102 | match name { |
| 6070 | 6103 | Some(name) => { |
| 6071 | match ctx.probe_for_name( | |
| 6072 | method_resolution::Mode::MethodCall, | |
| 6073 | name.clone(), | |
| 6074 | self_ty, | |
| 6075 | ) { | |
| 6104 | match ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), self_ty) { | |
| 6076 | 6105 | Ok(candidate) |
| 6077 | 6106 | | Err(method_resolution::MethodError::PrivateMatch(candidate)) => { |
| 6078 | 6107 | let id = candidate.item.into(); |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs+32-1| ... | ... | @@ -352,7 +352,7 @@ mod tests { |
| 352 | 352 | let config = TEST_CONFIG; |
| 353 | 353 | let ctx = AssistContext::new(sema, &config, frange); |
| 354 | 354 | let mut acc = Assists::new(&ctx, AssistResolveStrategy::All); |
| 355 | auto_import(&mut acc, &ctx); | |
| 355 | hir::attach_db(&db, || auto_import(&mut acc, &ctx)); | |
| 356 | 356 | let assists = acc.finish(); |
| 357 | 357 | |
| 358 | 358 | let labels = assists.iter().map(|assist| assist.label.to_string()).collect::<Vec<_>>(); |
| ... | ... | @@ -1897,4 +1897,35 @@ fn foo(_: S) {} |
| 1897 | 1897 | "#, |
| 1898 | 1898 | ); |
| 1899 | 1899 | } |
| 1900 | ||
| 1901 | #[test] | |
| 1902 | fn with_after_segments() { | |
| 1903 | let before = r#" | |
| 1904 | mod foo { | |
| 1905 | pub mod wanted { | |
| 1906 | pub fn abc() {} | |
| 1907 | } | |
| 1908 | } | |
| 1909 | ||
| 1910 | mod bar { | |
| 1911 | pub mod wanted {} | |
| 1912 | } | |
| 1913 | ||
| 1914 | mod baz { | |
| 1915 | pub fn wanted() {} | |
| 1916 | } | |
| 1917 | ||
| 1918 | mod quux { | |
| 1919 | pub struct wanted; | |
| 1920 | } | |
| 1921 | impl quux::wanted { | |
| 1922 | fn abc() {} | |
| 1923 | } | |
| 1924 | ||
| 1925 | fn f() { | |
| 1926 | wanted$0::abc; | |
| 1927 | } | |
| 1928 | "#; | |
| 1929 | check_auto_import_order(before, &["Import `foo::wanted`", "Import `quux::wanted`"]); | |
| 1930 | } | |
| 1900 | 1931 | } |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs+1-1| ... | ... | @@ -2,7 +2,7 @@ use crate::assist_context::{AssistContext, Assists}; |
| 2 | 2 | use ide_db::{LineIndexDatabase, assists::AssistId, defs::Definition}; |
| 3 | 3 | use syntax::{ |
| 4 | 4 | AstNode, |
| 5 | ast::{self, HasName, edit_in_place::Indent}, | |
| 5 | ast::{self, HasName, edit::AstNodeEdit}, | |
| 6 | 6 | }; |
| 7 | 7 | |
| 8 | 8 | // Assist: bind_unused_param |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs+3-3| ... | ... | @@ -11,9 +11,10 @@ use ide_db::{ |
| 11 | 11 | source_change::SourceChangeBuilder, |
| 12 | 12 | }; |
| 13 | 13 | use itertools::Itertools; |
| 14 | use syntax::ast::edit::AstNodeEdit; | |
| 14 | 15 | use syntax::{ |
| 15 | 16 | AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, |
| 16 | ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, make}, | |
| 17 | ast::{self, HasName, edit::IndentLevel, make}, | |
| 17 | 18 | }; |
| 18 | 19 | |
| 19 | 20 | use crate::{ |
| ... | ... | @@ -479,10 +480,9 @@ fn add_enum_def( |
| 479 | 480 | ctx.sema.scope(name.syntax()).map(|scope| scope.module()) |
| 480 | 481 | }) |
| 481 | 482 | .any(|module| module.nearest_non_block_module(ctx.db()) != *target_module); |
| 482 | let enum_def = make_bool_enum(make_enum_pub); | |
| 483 | 483 | |
| 484 | 484 | let indent = IndentLevel::from_node(&insert_before); |
| 485 | enum_def.reindent_to(indent); | |
| 485 | let enum_def = make_bool_enum(make_enum_pub).reset_indent().indent(indent); | |
| 486 | 486 | |
| 487 | 487 | edit.insert( |
| 488 | 488 | insert_before.text_range().start(), |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs+3-3| ... | ... | @@ -1,7 +1,6 @@ |
| 1 | 1 | use syntax::T; |
| 2 | 2 | use syntax::ast::RangeItem; |
| 3 | use syntax::ast::edit::IndentLevel; | |
| 4 | use syntax::ast::edit_in_place::Indent; | |
| 3 | use syntax::ast::edit::AstNodeEdit; | |
| 5 | 4 | use syntax::ast::syntax_factory::SyntaxFactory; |
| 6 | 5 | use syntax::ast::{self, AstNode, HasName, LetStmt, Pat}; |
| 7 | 6 | |
| ... | ... | @@ -93,7 +92,8 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' |
| 93 | 92 | ); |
| 94 | 93 | let else_arm = make.match_arm(make.wildcard_pat().into(), None, else_expr); |
| 95 | 94 | let match_ = make.expr_match(init, make.match_arm_list([binding_arm, else_arm])); |
| 96 | match_.reindent_to(IndentLevel::from_node(let_stmt.syntax())); | |
| 95 | let match_ = match_.reset_indent(); | |
| 96 | let match_ = match_.indent(let_stmt.indent_level()); | |
| 97 | 97 | |
| 98 | 98 | if bindings.is_empty() { |
| 99 | 99 | editor.replace(let_stmt.syntax(), match_.syntax()); |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs+82-45| ... | ... | @@ -5,15 +5,20 @@ use ide_db::{ |
| 5 | 5 | assists::AssistId, |
| 6 | 6 | defs::Definition, |
| 7 | 7 | helpers::mod_path_to_ast, |
| 8 | imports::insert_use::{ImportScope, insert_use}, | |
| 8 | imports::insert_use::{ImportScope, insert_use_with_editor}, | |
| 9 | 9 | search::{FileReference, UsageSearchResult}, |
| 10 | 10 | source_change::SourceChangeBuilder, |
| 11 | 11 | syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, |
| 12 | 12 | }; |
| 13 | 13 | use syntax::{ |
| 14 | 14 | AstNode, SyntaxNode, |
| 15 | ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, make}, | |
| 16 | match_ast, ted, | |
| 15 | ast::{ | |
| 16 | self, HasName, | |
| 17 | edit::{AstNodeEdit, IndentLevel}, | |
| 18 | syntax_factory::SyntaxFactory, | |
| 19 | }, | |
| 20 | match_ast, | |
| 21 | syntax_editor::SyntaxEditor, | |
| 17 | 22 | }; |
| 18 | 23 | |
| 19 | 24 | use crate::assist_context::{AssistContext, Assists}; |
| ... | ... | @@ -67,14 +72,15 @@ pub(crate) fn convert_tuple_return_type_to_struct( |
| 67 | 72 | "Convert tuple return type to tuple struct", |
| 68 | 73 | target, |
| 69 | 74 | move |edit| { |
| 70 | let ret_type = edit.make_mut(ret_type); | |
| 71 | let fn_ = edit.make_mut(fn_); | |
| 75 | let mut syntax_editor = edit.make_editor(ret_type.syntax()); | |
| 76 | let syntax_factory = SyntaxFactory::with_mappings(); | |
| 72 | 77 | |
| 73 | 78 | let usages = Definition::Function(fn_def).usages(&ctx.sema).all(); |
| 74 | 79 | let struct_name = format!("{}Result", stdx::to_camel_case(&fn_name.to_string())); |
| 75 | 80 | let parent = fn_.syntax().ancestors().find_map(<Either<ast::Impl, ast::Trait>>::cast); |
| 76 | 81 | add_tuple_struct_def( |
| 77 | 82 | edit, |
| 83 | &syntax_factory, | |
| 78 | 84 | ctx, |
| 79 | 85 | &usages, |
| 80 | 86 | parent.as_ref().map(|it| it.syntax()).unwrap_or(fn_.syntax()), |
| ... | ... | @@ -83,15 +89,23 @@ pub(crate) fn convert_tuple_return_type_to_struct( |
| 83 | 89 | &target_module, |
| 84 | 90 | ); |
| 85 | 91 | |
| 86 | ted::replace( | |
| 92 | syntax_editor.replace( | |
| 87 | 93 | ret_type.syntax(), |
| 88 | make::ret_type(make::ty(&struct_name)).syntax().clone_for_update(), | |
| 94 | syntax_factory.ret_type(syntax_factory.ty(&struct_name)).syntax(), | |
| 89 | 95 | ); |
| 90 | 96 | |
| 91 | 97 | if let Some(fn_body) = fn_.body() { |
| 92 | replace_body_return_values(ast::Expr::BlockExpr(fn_body), &struct_name); | |
| 98 | replace_body_return_values( | |
| 99 | &mut syntax_editor, | |
| 100 | &syntax_factory, | |
| 101 | ast::Expr::BlockExpr(fn_body), | |
| 102 | &struct_name, | |
| 103 | ); | |
| 93 | 104 | } |
| 94 | 105 | |
| 106 | syntax_editor.add_mappings(syntax_factory.finish_with_mappings()); | |
| 107 | edit.add_file_edits(ctx.vfs_file_id(), syntax_editor); | |
| 108 | ||
| 95 | 109 | replace_usages(edit, ctx, &usages, &struct_name, &target_module); |
| 96 | 110 | }, |
| 97 | 111 | ) |
| ... | ... | @@ -106,24 +120,37 @@ fn replace_usages( |
| 106 | 120 | target_module: &hir::Module, |
| 107 | 121 | ) { |
| 108 | 122 | for (file_id, references) in usages.iter() { |
| 109 | edit.edit_file(file_id.file_id(ctx.db())); | |
| 123 | let Some(first_ref) = references.first() else { continue }; | |
| 124 | ||
| 125 | let mut editor = edit.make_editor(first_ref.name.syntax().as_node().unwrap()); | |
| 126 | let syntax_factory = SyntaxFactory::with_mappings(); | |
| 110 | 127 | |
| 111 | let refs_with_imports = | |
| 112 | augment_references_with_imports(edit, ctx, references, struct_name, target_module); | |
| 128 | let refs_with_imports = augment_references_with_imports( | |
| 129 | &syntax_factory, | |
| 130 | ctx, | |
| 131 | references, | |
| 132 | struct_name, | |
| 133 | target_module, | |
| 134 | ); | |
| 113 | 135 | |
| 114 | 136 | refs_with_imports.into_iter().rev().for_each(|(name, import_data)| { |
| 115 | 137 | if let Some(fn_) = name.syntax().parent().and_then(ast::Fn::cast) { |
| 116 | 138 | cov_mark::hit!(replace_trait_impl_fns); |
| 117 | 139 | |
| 118 | 140 | if let Some(ret_type) = fn_.ret_type() { |
| 119 | ted::replace( | |
| 141 | editor.replace( | |
| 120 | 142 | ret_type.syntax(), |
| 121 | make::ret_type(make::ty(struct_name)).syntax().clone_for_update(), | |
| 143 | syntax_factory.ret_type(syntax_factory.ty(struct_name)).syntax(), | |
| 122 | 144 | ); |
| 123 | 145 | } |
| 124 | 146 | |
| 125 | 147 | if let Some(fn_body) = fn_.body() { |
| 126 | replace_body_return_values(ast::Expr::BlockExpr(fn_body), struct_name); | |
| 148 | replace_body_return_values( | |
| 149 | &mut editor, | |
| 150 | &syntax_factory, | |
| 151 | ast::Expr::BlockExpr(fn_body), | |
| 152 | struct_name, | |
| 153 | ); | |
| 127 | 154 | } |
| 128 | 155 | } else { |
| 129 | 156 | // replace tuple patterns |
| ... | ... | @@ -143,22 +170,30 @@ fn replace_usages( |
| 143 | 170 | _ => None, |
| 144 | 171 | }); |
| 145 | 172 | for tuple_pat in tuple_pats { |
| 146 | ted::replace( | |
| 173 | editor.replace( | |
| 147 | 174 | tuple_pat.syntax(), |
| 148 | make::tuple_struct_pat( | |
| 149 | make::path_from_text(struct_name), | |
| 150 | tuple_pat.fields(), | |
| 151 | ) | |
| 152 | .clone_for_update() | |
| 153 | .syntax(), | |
| 175 | syntax_factory | |
| 176 | .tuple_struct_pat( | |
| 177 | syntax_factory.path_from_text(struct_name), | |
| 178 | tuple_pat.fields(), | |
| 179 | ) | |
| 180 | .syntax(), | |
| 154 | 181 | ); |
| 155 | 182 | } |
| 156 | 183 | } |
| 157 | // add imports across modules where needed | |
| 158 | 184 | if let Some((import_scope, path)) = import_data { |
| 159 | insert_use(&import_scope, path, &ctx.config.insert_use); | |
| 185 | insert_use_with_editor( | |
| 186 | &import_scope, | |
| 187 | path, | |
| 188 | &ctx.config.insert_use, | |
| 189 | &mut editor, | |
| 190 | &syntax_factory, | |
| 191 | ); | |
| 160 | 192 | } |
| 161 | }) | |
| 193 | }); | |
| 194 | ||
| 195 | editor.add_mappings(syntax_factory.finish_with_mappings()); | |
| 196 | edit.add_file_edits(file_id.file_id(ctx.db()), editor); | |
| 162 | 197 | } |
| 163 | 198 | } |
| 164 | 199 | |
| ... | ... | @@ -176,7 +211,7 @@ fn node_to_pats(node: SyntaxNode) -> Option<Vec<ast::Pat>> { |
| 176 | 211 | } |
| 177 | 212 | |
| 178 | 213 | fn augment_references_with_imports( |
| 179 | edit: &mut SourceChangeBuilder, | |
| 214 | syntax_factory: &SyntaxFactory, | |
| 180 | 215 | ctx: &AssistContext<'_>, |
| 181 | 216 | references: &[FileReference], |
| 182 | 217 | struct_name: &str, |
| ... | ... | @@ -191,8 +226,6 @@ fn augment_references_with_imports( |
| 191 | 226 | ctx.sema.scope(name.syntax()).map(|scope| (name, scope.module())) |
| 192 | 227 | }) |
| 193 | 228 | .map(|(name, ref_module)| { |
| 194 | let new_name = edit.make_mut(name); | |
| 195 | ||
| 196 | 229 | // if the referenced module is not the same as the target one and has not been seen before, add an import |
| 197 | 230 | let import_data = if ref_module.nearest_non_block_module(ctx.db()) != *target_module |
| 198 | 231 | && !visited_modules.contains(&ref_module) |
| ... | ... | @@ -201,8 +234,7 @@ fn augment_references_with_imports( |
| 201 | 234 | |
| 202 | 235 | let cfg = |
| 203 | 236 | ctx.config.find_path_config(ctx.sema.is_nightly(ref_module.krate(ctx.sema.db))); |
| 204 | let import_scope = | |
| 205 | ImportScope::find_insert_use_container(new_name.syntax(), &ctx.sema); | |
| 237 | let import_scope = ImportScope::find_insert_use_container(name.syntax(), &ctx.sema); | |
| 206 | 238 | let path = ref_module |
| 207 | 239 | .find_use_path( |
| 208 | 240 | ctx.sema.db, |
| ... | ... | @@ -211,12 +243,12 @@ fn augment_references_with_imports( |
| 211 | 243 | cfg, |
| 212 | 244 | ) |
| 213 | 245 | .map(|mod_path| { |
| 214 | make::path_concat( | |
| 246 | syntax_factory.path_concat( | |
| 215 | 247 | mod_path_to_ast( |
| 216 | 248 | &mod_path, |
| 217 | 249 | target_module.krate(ctx.db()).edition(ctx.db()), |
| 218 | 250 | ), |
| 219 | make::path_from_text(struct_name), | |
| 251 | syntax_factory.path_from_text(struct_name), | |
| 220 | 252 | ) |
| 221 | 253 | }); |
| 222 | 254 | |
| ... | ... | @@ -225,7 +257,7 @@ fn augment_references_with_imports( |
| 225 | 257 | None |
| 226 | 258 | }; |
| 227 | 259 | |
| 228 | (new_name, import_data) | |
| 260 | (name, import_data) | |
| 229 | 261 | }) |
| 230 | 262 | .collect() |
| 231 | 263 | } |
| ... | ... | @@ -233,6 +265,7 @@ fn augment_references_with_imports( |
| 233 | 265 | // Adds the definition of the tuple struct before the parent function. |
| 234 | 266 | fn add_tuple_struct_def( |
| 235 | 267 | edit: &mut SourceChangeBuilder, |
| 268 | syntax_factory: &SyntaxFactory, | |
| 236 | 269 | ctx: &AssistContext<'_>, |
| 237 | 270 | usages: &UsageSearchResult, |
| 238 | 271 | parent: &SyntaxNode, |
| ... | ... | @@ -248,22 +281,27 @@ fn add_tuple_struct_def( |
| 248 | 281 | ctx.sema.scope(name.syntax()).map(|scope| scope.module()) |
| 249 | 282 | }) |
| 250 | 283 | .any(|module| module.nearest_non_block_module(ctx.db()) != *target_module); |
| 251 | let visibility = if make_struct_pub { Some(make::visibility_pub()) } else { None }; | |
| 284 | let visibility = if make_struct_pub { Some(syntax_factory.visibility_pub()) } else { None }; | |
| 252 | 285 | |
| 253 | let field_list = ast::FieldList::TupleFieldList(make::tuple_field_list( | |
| 254 | tuple_ty.fields().map(|ty| make::tuple_field(visibility.clone(), ty)), | |
| 286 | let field_list = ast::FieldList::TupleFieldList(syntax_factory.tuple_field_list( | |
| 287 | tuple_ty.fields().map(|ty| syntax_factory.tuple_field(visibility.clone(), ty)), | |
| 255 | 288 | )); |
| 256 | let struct_name = make::name(struct_name); | |
| 257 | let struct_def = make::struct_(visibility, struct_name, None, field_list).clone_for_update(); | |
| 289 | let struct_name = syntax_factory.name(struct_name); | |
| 290 | let struct_def = syntax_factory.struct_(visibility, struct_name, None, field_list); | |
| 258 | 291 | |
| 259 | 292 | let indent = IndentLevel::from_node(parent); |
| 260 | struct_def.reindent_to(indent); | |
| 293 | let struct_def = struct_def.indent(indent); | |
| 261 | 294 | |
| 262 | 295 | edit.insert(parent.text_range().start(), format!("{struct_def}\n\n{indent}")); |
| 263 | 296 | } |
| 264 | 297 | |
| 265 | 298 | /// Replaces each returned tuple in `body` with the constructor of the tuple struct named `struct_name`. |
| 266 | fn replace_body_return_values(body: ast::Expr, struct_name: &str) { | |
| 299 | fn replace_body_return_values( | |
| 300 | syntax_editor: &mut SyntaxEditor, | |
| 301 | syntax_factory: &SyntaxFactory, | |
| 302 | body: ast::Expr, | |
| 303 | struct_name: &str, | |
| 304 | ) { | |
| 267 | 305 | let mut exprs_to_wrap = Vec::new(); |
| 268 | 306 | |
| 269 | 307 | let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e); |
| ... | ... | @@ -278,12 +316,11 @@ fn replace_body_return_values(body: ast::Expr, struct_name: &str) { |
| 278 | 316 | |
| 279 | 317 | for ret_expr in exprs_to_wrap { |
| 280 | 318 | if let ast::Expr::TupleExpr(tuple_expr) = &ret_expr { |
| 281 | let struct_constructor = make::expr_call( | |
| 282 | make::expr_path(make::ext::ident_path(struct_name)), | |
| 283 | make::arg_list(tuple_expr.fields()), | |
| 284 | ) | |
| 285 | .clone_for_update(); | |
| 286 | ted::replace(ret_expr.syntax(), struct_constructor.syntax()); | |
| 319 | let struct_constructor = syntax_factory.expr_call( | |
| 320 | syntax_factory.expr_path(syntax_factory.ident_path(struct_name)), | |
| 321 | syntax_factory.arg_list(tuple_expr.fields()), | |
| 322 | ); | |
| 323 | syntax_editor.replace(ret_expr.syntax(), struct_constructor.syntax()); | |
| 287 | 324 | } |
| 288 | 325 | } |
| 289 | 326 | } |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs+52-33| ... | ... | @@ -8,8 +8,8 @@ use ide_db::{ |
| 8 | 8 | use itertools::Itertools; |
| 9 | 9 | use syntax::{ |
| 10 | 10 | T, |
| 11 | ast::{self, AstNode, FieldExpr, HasName, IdentPat, make}, | |
| 12 | ted, | |
| 11 | ast::{self, AstNode, FieldExpr, HasName, IdentPat, syntax_factory::SyntaxFactory}, | |
| 12 | syntax_editor::{Position, SyntaxEditor}, | |
| 13 | 13 | }; |
| 14 | 14 | |
| 15 | 15 | use crate::{ |
| ... | ... | @@ -89,13 +89,20 @@ fn destructure_tuple_edit_impl( |
| 89 | 89 | data: &TupleData, |
| 90 | 90 | in_sub_pattern: bool, |
| 91 | 91 | ) { |
| 92 | let assignment_edit = edit_tuple_assignment(ctx, edit, data, in_sub_pattern); | |
| 93 | let current_file_usages_edit = edit_tuple_usages(data, edit, ctx, in_sub_pattern); | |
| 92 | let mut syntax_editor = edit.make_editor(data.ident_pat.syntax()); | |
| 93 | let syntax_factory = SyntaxFactory::with_mappings(); | |
| 94 | 94 | |
| 95 | assignment_edit.apply(); | |
| 95 | let assignment_edit = | |
| 96 | edit_tuple_assignment(ctx, edit, &mut syntax_editor, &syntax_factory, data, in_sub_pattern); | |
| 97 | let current_file_usages_edit = edit_tuple_usages(data, ctx, &syntax_factory, in_sub_pattern); | |
| 98 | ||
| 99 | assignment_edit.apply(&mut syntax_editor, &syntax_factory); | |
| 96 | 100 | if let Some(usages_edit) = current_file_usages_edit { |
| 97 | usages_edit.into_iter().for_each(|usage_edit| usage_edit.apply(edit)) | |
| 101 | usages_edit.into_iter().for_each(|usage_edit| usage_edit.apply(edit, &mut syntax_editor)) | |
| 98 | 102 | } |
| 103 | ||
| 104 | syntax_editor.add_mappings(syntax_factory.finish_with_mappings()); | |
| 105 | edit.add_file_edits(ctx.vfs_file_id(), syntax_editor); | |
| 99 | 106 | } |
| 100 | 107 | |
| 101 | 108 | fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleData> { |
| ... | ... | @@ -165,11 +172,11 @@ struct TupleData { |
| 165 | 172 | fn edit_tuple_assignment( |
| 166 | 173 | ctx: &AssistContext<'_>, |
| 167 | 174 | edit: &mut SourceChangeBuilder, |
| 175 | editor: &mut SyntaxEditor, | |
| 176 | make: &SyntaxFactory, | |
| 168 | 177 | data: &TupleData, |
| 169 | 178 | in_sub_pattern: bool, |
| 170 | 179 | ) -> AssignmentEdit { |
| 171 | let ident_pat = edit.make_mut(data.ident_pat.clone()); | |
| 172 | ||
| 173 | 180 | let tuple_pat = { |
| 174 | 181 | let original = &data.ident_pat; |
| 175 | 182 | let is_ref = original.ref_token().is_some(); |
| ... | ... | @@ -177,10 +184,11 @@ fn edit_tuple_assignment( |
| 177 | 184 | let fields = data |
| 178 | 185 | .field_names |
| 179 | 186 | .iter() |
| 180 | .map(|name| ast::Pat::from(make::ident_pat(is_ref, is_mut, make::name(name)))); | |
| 181 | make::tuple_pat(fields).clone_for_update() | |
| 187 | .map(|name| ast::Pat::from(make.ident_pat(is_ref, is_mut, make.name(name)))); | |
| 188 | make.tuple_pat(fields) | |
| 182 | 189 | }; |
| 183 | let is_shorthand_field = ident_pat | |
| 190 | let is_shorthand_field = data | |
| 191 | .ident_pat | |
| 184 | 192 | .name() |
| 185 | 193 | .as_ref() |
| 186 | 194 | .and_then(ast::RecordPatField::for_field_name) |
| ... | ... | @@ -189,14 +197,20 @@ fn edit_tuple_assignment( |
| 189 | 197 | if let Some(cap) = ctx.config.snippet_cap { |
| 190 | 198 | // place cursor on first tuple name |
| 191 | 199 | if let Some(ast::Pat::IdentPat(first_pat)) = tuple_pat.fields().next() { |
| 192 | edit.add_tabstop_before( | |
| 193 | cap, | |
| 194 | first_pat.name().expect("first ident pattern should have a name"), | |
| 195 | ) | |
| 200 | let annotation = edit.make_tabstop_before(cap); | |
| 201 | editor.add_annotation( | |
| 202 | first_pat.name().expect("first ident pattern should have a name").syntax(), | |
| 203 | annotation, | |
| 204 | ); | |
| 196 | 205 | } |
| 197 | 206 | } |
| 198 | 207 | |
| 199 | AssignmentEdit { ident_pat, tuple_pat, in_sub_pattern, is_shorthand_field } | |
| 208 | AssignmentEdit { | |
| 209 | ident_pat: data.ident_pat.clone(), | |
| 210 | tuple_pat, | |
| 211 | in_sub_pattern, | |
| 212 | is_shorthand_field, | |
| 213 | } | |
| 200 | 214 | } |
| 201 | 215 | struct AssignmentEdit { |
| 202 | 216 | ident_pat: ast::IdentPat, |
| ... | ... | @@ -206,23 +220,30 @@ struct AssignmentEdit { |
| 206 | 220 | } |
| 207 | 221 | |
| 208 | 222 | impl AssignmentEdit { |
| 209 | fn apply(self) { | |
| 223 | fn apply(self, syntax_editor: &mut SyntaxEditor, syntax_mapping: &SyntaxFactory) { | |
| 210 | 224 | // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)` |
| 211 | 225 | if self.in_sub_pattern { |
| 212 | self.ident_pat.set_pat(Some(self.tuple_pat.into())) | |
| 226 | self.ident_pat.set_pat_with_editor( | |
| 227 | Some(self.tuple_pat.into()), | |
| 228 | syntax_editor, | |
| 229 | syntax_mapping, | |
| 230 | ) | |
| 213 | 231 | } else if self.is_shorthand_field { |
| 214 | ted::insert(ted::Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax()); | |
| 215 | ted::insert_raw(ted::Position::after(self.ident_pat.syntax()), make::token(T![:])); | |
| 232 | syntax_editor.insert(Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax()); | |
| 233 | syntax_editor | |
| 234 | .insert(Position::after(self.ident_pat.syntax()), syntax_mapping.whitespace(" ")); | |
| 235 | syntax_editor | |
| 236 | .insert(Position::after(self.ident_pat.syntax()), syntax_mapping.token(T![:])); | |
| 216 | 237 | } else { |
| 217 | ted::replace(self.ident_pat.syntax(), self.tuple_pat.syntax()) | |
| 238 | syntax_editor.replace(self.ident_pat.syntax(), self.tuple_pat.syntax()) | |
| 218 | 239 | } |
| 219 | 240 | } |
| 220 | 241 | } |
| 221 | 242 | |
| 222 | 243 | fn edit_tuple_usages( |
| 223 | 244 | data: &TupleData, |
| 224 | edit: &mut SourceChangeBuilder, | |
| 225 | 245 | ctx: &AssistContext<'_>, |
| 246 | make: &SyntaxFactory, | |
| 226 | 247 | in_sub_pattern: bool, |
| 227 | 248 | ) -> Option<Vec<EditTupleUsage>> { |
| 228 | 249 | // We need to collect edits first before actually applying them |
| ... | ... | @@ -238,20 +259,20 @@ fn edit_tuple_usages( |
| 238 | 259 | .as_ref()? |
| 239 | 260 | .as_slice() |
| 240 | 261 | .iter() |
| 241 | .filter_map(|r| edit_tuple_usage(ctx, edit, r, data, in_sub_pattern)) | |
| 262 | .filter_map(|r| edit_tuple_usage(ctx, make, r, data, in_sub_pattern)) | |
| 242 | 263 | .collect_vec(); |
| 243 | 264 | |
| 244 | 265 | Some(edits) |
| 245 | 266 | } |
| 246 | 267 | fn edit_tuple_usage( |
| 247 | 268 | ctx: &AssistContext<'_>, |
| 248 | builder: &mut SourceChangeBuilder, | |
| 269 | make: &SyntaxFactory, | |
| 249 | 270 | usage: &FileReference, |
| 250 | 271 | data: &TupleData, |
| 251 | 272 | in_sub_pattern: bool, |
| 252 | 273 | ) -> Option<EditTupleUsage> { |
| 253 | 274 | match detect_tuple_index(usage, data) { |
| 254 | Some(index) => Some(edit_tuple_field_usage(ctx, builder, data, index)), | |
| 275 | Some(index) => Some(edit_tuple_field_usage(ctx, make, data, index)), | |
| 255 | 276 | None if in_sub_pattern => { |
| 256 | 277 | cov_mark::hit!(destructure_tuple_call_with_subpattern); |
| 257 | 278 | None |
| ... | ... | @@ -262,20 +283,18 @@ fn edit_tuple_usage( |
| 262 | 283 | |
| 263 | 284 | fn edit_tuple_field_usage( |
| 264 | 285 | ctx: &AssistContext<'_>, |
| 265 | builder: &mut SourceChangeBuilder, | |
| 286 | make: &SyntaxFactory, | |
| 266 | 287 | data: &TupleData, |
| 267 | 288 | index: TupleIndex, |
| 268 | 289 | ) -> EditTupleUsage { |
| 269 | 290 | let field_name = &data.field_names[index.index]; |
| 270 | let field_name = make::expr_path(make::ext::ident_path(field_name)); | |
| 291 | let field_name = make.expr_path(make.ident_path(field_name)); | |
| 271 | 292 | |
| 272 | 293 | if data.ref_type.is_some() { |
| 273 | 294 | let (replace_expr, ref_data) = determine_ref_and_parens(ctx, &index.field_expr); |
| 274 | let replace_expr = builder.make_mut(replace_expr); | |
| 275 | EditTupleUsage::ReplaceExpr(replace_expr, ref_data.wrap_expr(field_name)) | |
| 295 | EditTupleUsage::ReplaceExpr(replace_expr, ref_data.wrap_expr_with_factory(field_name, make)) | |
| 276 | 296 | } else { |
| 277 | let field_expr = builder.make_mut(index.field_expr); | |
| 278 | EditTupleUsage::ReplaceExpr(field_expr.into(), field_name) | |
| 297 | EditTupleUsage::ReplaceExpr(index.field_expr.into(), field_name) | |
| 279 | 298 | } |
| 280 | 299 | } |
| 281 | 300 | enum EditTupleUsage { |
| ... | ... | @@ -291,14 +310,14 @@ enum EditTupleUsage { |
| 291 | 310 | } |
| 292 | 311 | |
| 293 | 312 | impl EditTupleUsage { |
| 294 | fn apply(self, edit: &mut SourceChangeBuilder) { | |
| 313 | fn apply(self, edit: &mut SourceChangeBuilder, syntax_editor: &mut SyntaxEditor) { | |
| 295 | 314 | match self { |
| 296 | 315 | EditTupleUsage::NoIndex(range) => { |
| 297 | 316 | edit.insert(range.start(), "/*"); |
| 298 | 317 | edit.insert(range.end(), "*/"); |
| 299 | 318 | } |
| 300 | 319 | EditTupleUsage::ReplaceExpr(target_expr, replace_with) => { |
| 301 | ted::replace(target_expr.syntax(), replace_with.clone_for_update().syntax()) | |
| 320 | syntax_editor.replace(target_expr.syntax(), replace_with.syntax()) | |
| 302 | 321 | } |
| 303 | 322 | } |
| 304 | 323 | } |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs+100-61| ... | ... | @@ -2,13 +2,16 @@ use ide_db::{famous_defs::FamousDefs, source_change::SourceChangeBuilder}; |
| 2 | 2 | use stdx::{format_to, to_lower_snake_case}; |
| 3 | 3 | use syntax::{ |
| 4 | 4 | TextRange, |
| 5 | ast::{self, AstNode, HasName, HasVisibility, edit_in_place::Indent, make}, | |
| 6 | ted, | |
| 5 | ast::{ | |
| 6 | self, AstNode, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, | |
| 7 | syntax_factory::SyntaxFactory, | |
| 8 | }, | |
| 9 | syntax_editor::Position, | |
| 7 | 10 | }; |
| 8 | 11 | |
| 9 | 12 | use crate::{ |
| 10 | 13 | AssistContext, AssistId, Assists, GroupLabel, |
| 11 | utils::{convert_reference_type, find_struct_impl, generate_impl}, | |
| 14 | utils::{convert_reference_type, find_struct_impl}, | |
| 12 | 15 | }; |
| 13 | 16 | |
| 14 | 17 | // Assist: generate_setter |
| ... | ... | @@ -215,12 +218,14 @@ fn generate_getter_from_info( |
| 215 | 218 | ctx: &AssistContext<'_>, |
| 216 | 219 | info: &AssistInfo, |
| 217 | 220 | record_field_info: &RecordFieldInfo, |
| 221 | syntax_factory: &SyntaxFactory, | |
| 218 | 222 | ) -> ast::Fn { |
| 219 | 223 | let (ty, body) = if matches!(info.assist_type, AssistType::MutGet) { |
| 224 | let self_expr = syntax_factory.expr_path(syntax_factory.ident_path("self")); | |
| 220 | 225 | ( |
| 221 | make::ty_ref(record_field_info.field_ty.clone(), true), | |
| 222 | make::expr_ref( | |
| 223 | make::expr_field(make::ext::expr_self(), &record_field_info.field_name.text()), | |
| 226 | syntax_factory.ty_ref(record_field_info.field_ty.clone(), true), | |
| 227 | syntax_factory.expr_ref( | |
| 228 | syntax_factory.expr_field(self_expr, &record_field_info.field_name.text()).into(), | |
| 224 | 229 | true, |
| 225 | 230 | ), |
| 226 | 231 | ) |
| ... | ... | @@ -241,9 +246,14 @@ fn generate_getter_from_info( |
| 241 | 246 | })() |
| 242 | 247 | .unwrap_or_else(|| { |
| 243 | 248 | ( |
| 244 | make::ty_ref(record_field_info.field_ty.clone(), false), | |
| 245 | make::expr_ref( | |
| 246 | make::expr_field(make::ext::expr_self(), &record_field_info.field_name.text()), | |
| 249 | syntax_factory.ty_ref(record_field_info.field_ty.clone(), false), | |
| 250 | syntax_factory.expr_ref( | |
| 251 | syntax_factory | |
| 252 | .expr_field( | |
| 253 | syntax_factory.expr_path(syntax_factory.ident_path("self")), | |
| 254 | &record_field_info.field_name.text(), | |
| 255 | ) | |
| 256 | .into(), | |
| 247 | 257 | false, |
| 248 | 258 | ), |
| 249 | 259 | ) |
| ... | ... | @@ -251,18 +261,18 @@ fn generate_getter_from_info( |
| 251 | 261 | }; |
| 252 | 262 | |
| 253 | 263 | let self_param = if matches!(info.assist_type, AssistType::MutGet) { |
| 254 | make::mut_self_param() | |
| 264 | syntax_factory.mut_self_param() | |
| 255 | 265 | } else { |
| 256 | make::self_param() | |
| 266 | syntax_factory.self_param() | |
| 257 | 267 | }; |
| 258 | 268 | |
| 259 | 269 | let strukt = &info.strukt; |
| 260 | let fn_name = make::name(&record_field_info.fn_name); | |
| 261 | let params = make::param_list(Some(self_param), []); | |
| 262 | let ret_type = Some(make::ret_type(ty)); | |
| 263 | let body = make::block_expr([], Some(body)); | |
| 270 | let fn_name = syntax_factory.name(&record_field_info.fn_name); | |
| 271 | let params = syntax_factory.param_list(Some(self_param), []); | |
| 272 | let ret_type = Some(syntax_factory.ret_type(ty)); | |
| 273 | let body = syntax_factory.block_expr([], Some(body)); | |
| 264 | 274 | |
| 265 | make::fn_( | |
| 275 | syntax_factory.fn_( | |
| 266 | 276 | None, |
| 267 | 277 | strukt.visibility(), |
| 268 | 278 | fn_name, |
| ... | ... | @@ -278,28 +288,35 @@ fn generate_getter_from_info( |
| 278 | 288 | ) |
| 279 | 289 | } |
| 280 | 290 | |
| 281 | fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldInfo) -> ast::Fn { | |
| 291 | fn generate_setter_from_info( | |
| 292 | info: &AssistInfo, | |
| 293 | record_field_info: &RecordFieldInfo, | |
| 294 | syntax_factory: &SyntaxFactory, | |
| 295 | ) -> ast::Fn { | |
| 282 | 296 | let strukt = &info.strukt; |
| 283 | 297 | let field_name = &record_field_info.fn_name; |
| 284 | let fn_name = make::name(&format!("set_{field_name}")); | |
| 298 | let fn_name = syntax_factory.name(&format!("set_{field_name}")); | |
| 285 | 299 | let field_ty = &record_field_info.field_ty; |
| 286 | 300 | |
| 287 | 301 | // Make the param list |
| 288 | 302 | // `(&mut self, $field_name: $field_ty)` |
| 289 | let field_param = | |
| 290 | make::param(make::ident_pat(false, false, make::name(field_name)).into(), field_ty.clone()); | |
| 291 | let params = make::param_list(Some(make::mut_self_param()), [field_param]); | |
| 303 | let field_param = syntax_factory.param( | |
| 304 | syntax_factory.ident_pat(false, false, syntax_factory.name(field_name)).into(), | |
| 305 | field_ty.clone(), | |
| 306 | ); | |
| 307 | let params = syntax_factory.param_list(Some(syntax_factory.mut_self_param()), [field_param]); | |
| 292 | 308 | |
| 293 | 309 | // Make the assignment body |
| 294 | 310 | // `self.$field_name = $field_name` |
| 295 | let self_expr = make::ext::expr_self(); | |
| 296 | let lhs = make::expr_field(self_expr, field_name); | |
| 297 | let rhs = make::expr_path(make::ext::ident_path(field_name)); | |
| 298 | let assign_stmt = make::expr_stmt(make::expr_assignment(lhs, rhs).into()); | |
| 299 | let body = make::block_expr([assign_stmt.into()], None); | |
| 311 | let self_expr = syntax_factory.expr_path(syntax_factory.ident_path("self")); | |
| 312 | let lhs = syntax_factory.expr_field(self_expr, field_name); | |
| 313 | let rhs = syntax_factory.expr_path(syntax_factory.ident_path(field_name)); | |
| 314 | let assign_stmt = | |
| 315 | syntax_factory.expr_stmt(syntax_factory.expr_assignment(lhs.into(), rhs).into()); | |
| 316 | let body = syntax_factory.block_expr([assign_stmt.into()], None); | |
| 300 | 317 | |
| 301 | 318 | // Make the setter fn |
| 302 | make::fn_( | |
| 319 | syntax_factory.fn_( | |
| 303 | 320 | None, |
| 304 | 321 | strukt.visibility(), |
| 305 | 322 | fn_name, |
| ... | ... | @@ -403,47 +420,69 @@ fn build_source_change( |
| 403 | 420 | info_of_record_fields: Vec<RecordFieldInfo>, |
| 404 | 421 | assist_info: AssistInfo, |
| 405 | 422 | ) { |
| 406 | let record_fields_count = info_of_record_fields.len(); | |
| 407 | ||
| 408 | let impl_def = if let Some(impl_def) = &assist_info.impl_def { | |
| 409 | // We have an existing impl to add to | |
| 410 | builder.make_mut(impl_def.clone()) | |
| 411 | } else { | |
| 412 | // Generate a new impl to add the methods to | |
| 413 | let impl_def = generate_impl(&ast::Adt::Struct(assist_info.strukt.clone())); | |
| 414 | ||
| 415 | // Insert it after the adt | |
| 416 | let strukt = builder.make_mut(assist_info.strukt.clone()); | |
| 417 | ||
| 418 | ted::insert_all_raw( | |
| 419 | ted::Position::after(strukt.syntax()), | |
| 420 | vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()], | |
| 421 | ); | |
| 422 | ||
| 423 | impl_def | |
| 424 | }; | |
| 423 | let syntax_factory = SyntaxFactory::without_mappings(); | |
| 425 | 424 | |
| 426 | let assoc_item_list = impl_def.get_or_create_assoc_item_list(); | |
| 425 | let items: Vec<ast::AssocItem> = info_of_record_fields | |
| 426 | .iter() | |
| 427 | .map(|record_field_info| { | |
| 428 | let method = match assist_info.assist_type { | |
| 429 | AssistType::Set => { | |
| 430 | generate_setter_from_info(&assist_info, record_field_info, &syntax_factory) | |
| 431 | } | |
| 432 | _ => { | |
| 433 | generate_getter_from_info(ctx, &assist_info, record_field_info, &syntax_factory) | |
| 434 | } | |
| 435 | }; | |
| 436 | let new_fn = method.clone_for_update(); | |
| 437 | let new_fn = new_fn.indent(1.into()); | |
| 438 | new_fn.into() | |
| 439 | }) | |
| 440 | .collect(); | |
| 427 | 441 | |
| 428 | for (i, record_field_info) in info_of_record_fields.iter().enumerate() { | |
| 429 | // Make the new getter or setter fn | |
| 430 | let new_fn = match assist_info.assist_type { | |
| 431 | AssistType::Set => generate_setter_from_info(&assist_info, record_field_info), | |
| 432 | _ => generate_getter_from_info(ctx, &assist_info, record_field_info), | |
| 433 | } | |
| 434 | .clone_for_update(); | |
| 435 | new_fn.indent(1.into()); | |
| 442 | if let Some(impl_def) = &assist_info.impl_def { | |
| 443 | // We have an existing impl to add to | |
| 444 | let mut editor = builder.make_editor(impl_def.syntax()); | |
| 445 | impl_def.assoc_item_list().unwrap().add_items(&mut editor, items.clone()); | |
| 436 | 446 | |
| 437 | // Insert a tabstop only for last method we generate | |
| 438 | if i == record_fields_count - 1 | |
| 439 | && let Some(cap) = ctx.config.snippet_cap | |
| 440 | && let Some(name) = new_fn.name() | |
| 447 | if let Some(cap) = ctx.config.snippet_cap | |
| 448 | && let Some(ast::AssocItem::Fn(fn_)) = items.last() | |
| 449 | && let Some(name) = fn_.name() | |
| 441 | 450 | { |
| 442 | builder.add_tabstop_before(cap, name); | |
| 451 | let tabstop = builder.make_tabstop_before(cap); | |
| 452 | editor.add_annotation(name.syntax(), tabstop); | |
| 443 | 453 | } |
| 444 | 454 | |
| 445 | assoc_item_list.add_item(new_fn.clone().into()); | |
| 455 | builder.add_file_edits(ctx.vfs_file_id(), editor); | |
| 456 | return; | |
| 446 | 457 | } |
| 458 | let ty_params = assist_info.strukt.generic_param_list(); | |
| 459 | let ty_args = ty_params.as_ref().map(|it| it.to_generic_args()); | |
| 460 | let impl_def = syntax_factory.impl_( | |
| 461 | None, | |
| 462 | ty_params, | |
| 463 | ty_args, | |
| 464 | syntax_factory | |
| 465 | .ty_path(syntax_factory.ident_path(&assist_info.strukt.name().unwrap().to_string())) | |
| 466 | .into(), | |
| 467 | None, | |
| 468 | Some(syntax_factory.assoc_item_list(items)), | |
| 469 | ); | |
| 470 | let mut editor = builder.make_editor(assist_info.strukt.syntax()); | |
| 471 | editor.insert_all( | |
| 472 | Position::after(assist_info.strukt.syntax()), | |
| 473 | vec![syntax_factory.whitespace("\n\n").into(), impl_def.syntax().clone().into()], | |
| 474 | ); | |
| 475 | ||
| 476 | if let Some(cap) = ctx.config.snippet_cap | |
| 477 | && let Some(assoc_list) = impl_def.assoc_item_list() | |
| 478 | && let Some(ast::AssocItem::Fn(fn_)) = assoc_list.assoc_items().last() | |
| 479 | && let Some(name) = fn_.name() | |
| 480 | { | |
| 481 | let tabstop = builder.make_tabstop_before(cap); | |
| 482 | editor.add_annotation(name.syntax().clone(), tabstop); | |
| 483 | } | |
| 484 | ||
| 485 | builder.add_file_edits(ctx.vfs_file_id(), editor); | |
| 447 | 486 | } |
| 448 | 487 | |
| 449 | 488 | #[cfg(test)] |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs+15-6| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | use syntax::{ |
| 2 | ast::{self, AstNode, HasGenericParams, HasName, edit_in_place::Indent, make}, | |
| 2 | ast::{self, AstNode, HasGenericParams, HasName, edit::AstNodeEdit, make}, | |
| 3 | 3 | syntax_editor::{Position, SyntaxEditor}, |
| 4 | 4 | }; |
| 5 | 5 | |
| ... | ... | @@ -8,10 +8,14 @@ use crate::{ |
| 8 | 8 | utils::{self, DefaultMethods, IgnoreAssocItems}, |
| 9 | 9 | }; |
| 10 | 10 | |
| 11 | fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &impl Indent) { | |
| 11 | fn insert_impl( | |
| 12 | editor: &mut SyntaxEditor, | |
| 13 | impl_: &ast::Impl, | |
| 14 | nominal: &impl AstNodeEdit, | |
| 15 | ) -> ast::Impl { | |
| 12 | 16 | let indent = nominal.indent_level(); |
| 13 | 17 | |
| 14 | impl_.indent(indent); | |
| 18 | let impl_ = impl_.indent(indent); | |
| 15 | 19 | editor.insert_all( |
| 16 | 20 | Position::after(nominal.syntax()), |
| 17 | 21 | vec![ |
| ... | ... | @@ -20,6 +24,8 @@ fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &impl Inde |
| 20 | 24 | impl_.syntax().clone().into(), |
| 21 | 25 | ], |
| 22 | 26 | ); |
| 27 | ||
| 28 | impl_ | |
| 23 | 29 | } |
| 24 | 30 | |
| 25 | 31 | // Assist: generate_impl |
| ... | ... | @@ -57,6 +63,8 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio |
| 57 | 63 | let impl_ = utils::generate_impl(&nominal); |
| 58 | 64 | |
| 59 | 65 | let mut editor = edit.make_editor(nominal.syntax()); |
| 66 | ||
| 67 | let impl_ = insert_impl(&mut editor, &impl_, &nominal); | |
| 60 | 68 | // Add a tabstop after the left curly brace |
| 61 | 69 | if let Some(cap) = ctx.config.snippet_cap |
| 62 | 70 | && let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) |
| ... | ... | @@ -65,7 +73,6 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio |
| 65 | 73 | editor.add_annotation(l_curly, tabstop); |
| 66 | 74 | } |
| 67 | 75 | |
| 68 | insert_impl(&mut editor, &impl_, &nominal); | |
| 69 | 76 | edit.add_file_edits(ctx.vfs_file_id(), editor); |
| 70 | 77 | }, |
| 71 | 78 | ) |
| ... | ... | @@ -106,6 +113,8 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> |
| 106 | 113 | let impl_ = utils::generate_trait_impl_intransitive(&nominal, make::ty_placeholder()); |
| 107 | 114 | |
| 108 | 115 | let mut editor = edit.make_editor(nominal.syntax()); |
| 116 | ||
| 117 | let impl_ = insert_impl(&mut editor, &impl_, &nominal); | |
| 109 | 118 | // Make the trait type a placeholder snippet |
| 110 | 119 | if let Some(cap) = ctx.config.snippet_cap { |
| 111 | 120 | if let Some(trait_) = impl_.trait_() { |
| ... | ... | @@ -119,7 +128,6 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> |
| 119 | 128 | } |
| 120 | 129 | } |
| 121 | 130 | |
| 122 | insert_impl(&mut editor, &impl_, &nominal); | |
| 123 | 131 | edit.add_file_edits(ctx.vfs_file_id(), editor); |
| 124 | 132 | }, |
| 125 | 133 | ) |
| ... | ... | @@ -206,6 +214,8 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> |
| 206 | 214 | make_impl_(Some(assoc_item_list)) |
| 207 | 215 | }; |
| 208 | 216 | |
| 217 | let impl_ = insert_impl(&mut editor, &impl_, &trait_); | |
| 218 | ||
| 209 | 219 | if let Some(cap) = ctx.config.snippet_cap { |
| 210 | 220 | if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) { |
| 211 | 221 | for generic in generics.generic_args() { |
| ... | ... | @@ -232,7 +242,6 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> |
| 232 | 242 | } |
| 233 | 243 | } |
| 234 | 244 | |
| 235 | insert_impl(&mut editor, &impl_, &trait_); | |
| 236 | 245 | edit.add_file_edits(ctx.vfs_file_id(), editor); |
| 237 | 246 | }, |
| 238 | 247 | ) |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs+3-3| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait}; |
| 2 | 2 | use syntax::{ |
| 3 | 3 | AstNode, SyntaxElement, SyntaxNode, T, |
| 4 | ast::{self, edit::AstNodeEdit, edit_in_place::Indent, syntax_factory::SyntaxFactory}, | |
| 4 | ast::{self, edit::AstNodeEdit, syntax_factory::SyntaxFactory}, | |
| 5 | 5 | syntax_editor::{Element, Position, SyntaxEditor}, |
| 6 | 6 | }; |
| 7 | 7 | |
| ... | ... | @@ -46,7 +46,7 @@ use crate::{AssistContext, AssistId, Assists}; |
| 46 | 46 | // ``` |
| 47 | 47 | pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { |
| 48 | 48 | let impl_def = ctx.find_node_at_offset::<ast::Impl>()?; |
| 49 | let indent = Indent::indent_level(&impl_def); | |
| 49 | let indent = impl_def.indent_level(); | |
| 50 | 50 | |
| 51 | 51 | let ast::Type::PathType(path) = impl_def.trait_()? else { |
| 52 | 52 | return None; |
| ... | ... | @@ -78,7 +78,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> |
| 78 | 78 | |
| 79 | 79 | let new_impl = ast::Impl::cast(new_root.clone()).unwrap(); |
| 80 | 80 | |
| 81 | Indent::indent(&new_impl, indent); | |
| 81 | let new_impl = new_impl.indent(indent); | |
| 82 | 82 | |
| 83 | 83 | let mut editor = edit.make_editor(impl_def.syntax()); |
| 84 | 84 | editor.insert_all( |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs+6-7| ... | ... | @@ -3,7 +3,7 @@ use ide_db::{ |
| 3 | 3 | use_trivial_constructor::use_trivial_constructor, |
| 4 | 4 | }; |
| 5 | 5 | use syntax::{ |
| 6 | ast::{self, AstNode, HasName, HasVisibility, StructKind, edit_in_place::Indent, make}, | |
| 6 | ast::{self, AstNode, HasName, HasVisibility, StructKind, edit::AstNodeEdit, make}, | |
| 7 | 7 | syntax_editor::Position, |
| 8 | 8 | }; |
| 9 | 9 | |
| ... | ... | @@ -150,14 +150,14 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option |
| 150 | 150 | false, |
| 151 | 151 | false, |
| 152 | 152 | ) |
| 153 | .clone_for_update(); | |
| 154 | fn_.indent(1.into()); | |
| 153 | .clone_for_update() | |
| 154 | .indent(1.into()); | |
| 155 | 155 | |
| 156 | 156 | let mut editor = builder.make_editor(strukt.syntax()); |
| 157 | 157 | |
| 158 | 158 | // Get the node for set annotation |
| 159 | 159 | let contain_fn = if let Some(impl_def) = impl_def { |
| 160 | fn_.indent(impl_def.indent_level()); | |
| 160 | let fn_ = fn_.indent(impl_def.indent_level()); | |
| 161 | 161 | |
| 162 | 162 | if let Some(l_curly) = impl_def.assoc_item_list().and_then(|list| list.l_curly_token()) |
| 163 | 163 | { |
| ... | ... | @@ -182,9 +182,8 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option |
| 182 | 182 | let indent_level = strukt.indent_level(); |
| 183 | 183 | let body = vec![ast::AssocItem::Fn(fn_)]; |
| 184 | 184 | let list = make::assoc_item_list(Some(body)); |
| 185 | let impl_def = generate_impl_with_item(&ast::Adt::Struct(strukt.clone()), Some(list)); | |
| 186 | ||
| 187 | impl_def.indent(strukt.indent_level()); | |
| 185 | let impl_def = generate_impl_with_item(&ast::Adt::Struct(strukt.clone()), Some(list)) | |
| 186 | .indent(strukt.indent_level()); | |
| 188 | 187 | |
| 189 | 188 | // Insert it after the adt |
| 190 | 189 | editor.insert_all( |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs+1-1| ... | ... | @@ -2,7 +2,7 @@ use crate::assist_context::{AssistContext, Assists}; |
| 2 | 2 | use ide_db::assists::AssistId; |
| 3 | 3 | use syntax::{ |
| 4 | 4 | AstNode, SyntaxKind, T, |
| 5 | ast::{self, HasGenericParams, HasName, HasVisibility, edit_in_place::Indent, make}, | |
| 5 | ast::{self, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, make}, | |
| 6 | 6 | syntax_editor::{Position, SyntaxEditor}, |
| 7 | 7 | }; |
| 8 | 8 |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs+52| ... | ... | @@ -403,6 +403,12 @@ fn inline( |
| 403 | 403 | .find(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW) |
| 404 | 404 | { |
| 405 | 405 | let replace_with = t.clone_subtree().syntax().clone_for_update(); |
| 406 | if !is_in_type_path(&self_tok) | |
| 407 | && let Some(ty) = ast::Type::cast(replace_with.clone()) | |
| 408 | && let Some(generic_arg_list) = ty.generic_arg_list() | |
| 409 | { | |
| 410 | ted::remove(generic_arg_list.syntax()); | |
| 411 | } | |
| 406 | 412 | ted::replace(self_tok, replace_with); |
| 407 | 413 | } |
| 408 | 414 | } |
| ... | ... | @@ -588,6 +594,17 @@ fn inline( |
| 588 | 594 | } |
| 589 | 595 | } |
| 590 | 596 | |
| 597 | fn is_in_type_path(self_tok: &syntax::SyntaxToken) -> bool { | |
| 598 | self_tok | |
| 599 | .parent_ancestors() | |
| 600 | .skip_while(|it| !ast::Path::can_cast(it.kind())) | |
| 601 | .map_while(ast::Path::cast) | |
| 602 | .last() | |
| 603 | .and_then(|it| it.syntax().parent()) | |
| 604 | .and_then(ast::PathType::cast) | |
| 605 | .is_some() | |
| 606 | } | |
| 607 | ||
| 591 | 608 | fn path_expr_as_record_field(usage: &PathExpr) -> Option<ast::RecordExprField> { |
| 592 | 609 | let path = usage.path()?; |
| 593 | 610 | let name_ref = path.as_single_name_ref()?; |
| ... | ... | @@ -1694,6 +1711,41 @@ fn main() { |
| 1694 | 1711 | ) |
| 1695 | 1712 | } |
| 1696 | 1713 | |
| 1714 | #[test] | |
| 1715 | fn inline_trait_method_call_with_lifetimes() { | |
| 1716 | check_assist( | |
| 1717 | inline_call, | |
| 1718 | r#" | |
| 1719 | trait Trait { | |
| 1720 | fn f() -> Self; | |
| 1721 | } | |
| 1722 | struct Foo<'a>(&'a ()); | |
| 1723 | impl<'a> Trait for Foo<'a> { | |
| 1724 | fn f() -> Self { Self(&()) } | |
| 1725 | } | |
| 1726 | impl Foo<'_> { | |
| 1727 | fn new() -> Self { | |
| 1728 | Self::$0f() | |
| 1729 | } | |
| 1730 | } | |
| 1731 | "#, | |
| 1732 | r#" | |
| 1733 | trait Trait { | |
| 1734 | fn f() -> Self; | |
| 1735 | } | |
| 1736 | struct Foo<'a>(&'a ()); | |
| 1737 | impl<'a> Trait for Foo<'a> { | |
| 1738 | fn f() -> Self { Self(&()) } | |
| 1739 | } | |
| 1740 | impl Foo<'_> { | |
| 1741 | fn new() -> Self { | |
| 1742 | Foo(&()) | |
| 1743 | } | |
| 1744 | } | |
| 1745 | "#, | |
| 1746 | ) | |
| 1747 | } | |
| 1748 | ||
| 1697 | 1749 | #[test] |
| 1698 | 1750 | fn method_by_reborrow() { |
| 1699 | 1751 | check_assist( |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs+140-69| ... | ... | @@ -1,11 +1,12 @@ |
| 1 | use ide_db::FxHashSet; | |
| 1 | use ide_db::{FileId, FxHashSet}; | |
| 2 | 2 | use syntax::{ |
| 3 | AstNode, TextRange, | |
| 4 | ast::{self, HasGenericParams, edit_in_place::GenericParamsOwnerEdit, make}, | |
| 5 | ted::{self, Position}, | |
| 3 | AstNode, SmolStr, T, TextRange, ToSmolStr, | |
| 4 | ast::{self, HasGenericParams, HasName, syntax_factory::SyntaxFactory}, | |
| 5 | format_smolstr, | |
| 6 | syntax_editor::{Element, Position, SyntaxEditor}, | |
| 6 | 7 | }; |
| 7 | 8 | |
| 8 | use crate::{AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder}; | |
| 9 | use crate::{AssistContext, AssistId, Assists}; | |
| 9 | 10 | |
| 10 | 11 | static ASSIST_NAME: &str = "introduce_named_lifetime"; |
| 11 | 12 | static ASSIST_LABEL: &str = "Introduce named lifetime"; |
| ... | ... | @@ -38,100 +39,108 @@ pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext<'_ |
| 38 | 39 | // FIXME: should also add support for the case fun(f: &Foo) -> &$0Foo |
| 39 | 40 | let lifetime = |
| 40 | 41 | ctx.find_node_at_offset::<ast::Lifetime>().filter(|lifetime| lifetime.text() == "'_")?; |
| 42 | let file_id = ctx.vfs_file_id(); | |
| 41 | 43 | let lifetime_loc = lifetime.lifetime_ident_token()?.text_range(); |
| 42 | 44 | |
| 43 | 45 | if let Some(fn_def) = lifetime.syntax().ancestors().find_map(ast::Fn::cast) { |
| 44 | generate_fn_def_assist(acc, fn_def, lifetime_loc, lifetime) | |
| 46 | generate_fn_def_assist(acc, fn_def, lifetime_loc, lifetime, file_id) | |
| 45 | 47 | } else if let Some(impl_def) = lifetime.syntax().ancestors().find_map(ast::Impl::cast) { |
| 46 | generate_impl_def_assist(acc, impl_def, lifetime_loc, lifetime) | |
| 48 | generate_impl_def_assist(acc, impl_def, lifetime_loc, lifetime, file_id) | |
| 47 | 49 | } else { |
| 48 | 50 | None |
| 49 | 51 | } |
| 50 | 52 | } |
| 51 | 53 | |
| 52 | /// Generate the assist for the fn def case | |
| 54 | /// Given a type parameter list, generate a unique lifetime parameter name | |
| 55 | /// which is not in the list | |
| 56 | fn generate_unique_lifetime_param_name( | |
| 57 | existing_params: Option<ast::GenericParamList>, | |
| 58 | ) -> Option<SmolStr> { | |
| 59 | let used_lifetime_param: FxHashSet<SmolStr> = existing_params | |
| 60 | .iter() | |
| 61 | .flat_map(|params| params.lifetime_params()) | |
| 62 | .map(|p| p.syntax().text().to_smolstr()) | |
| 63 | .collect(); | |
| 64 | ('a'..='z').map(|c| format_smolstr!("'{c}")).find(|lt| !used_lifetime_param.contains(lt)) | |
| 65 | } | |
| 66 | ||
| 53 | 67 | fn generate_fn_def_assist( |
| 54 | 68 | acc: &mut Assists, |
| 55 | 69 | fn_def: ast::Fn, |
| 56 | 70 | lifetime_loc: TextRange, |
| 57 | 71 | lifetime: ast::Lifetime, |
| 72 | file_id: FileId, | |
| 58 | 73 | ) -> Option<()> { |
| 59 | let param_list: ast::ParamList = fn_def.param_list()?; | |
| 60 | let new_lifetime_param = generate_unique_lifetime_param_name(fn_def.generic_param_list())?; | |
| 74 | let param_list = fn_def.param_list()?; | |
| 75 | let new_lifetime_name = generate_unique_lifetime_param_name(fn_def.generic_param_list())?; | |
| 61 | 76 | let self_param = |
| 62 | // use the self if it's a reference and has no explicit lifetime | |
| 63 | 77 | param_list.self_param().filter(|p| p.lifetime().is_none() && p.amp_token().is_some()); |
| 64 | // compute the location which implicitly has the same lifetime as the anonymous lifetime | |
| 78 | ||
| 65 | 79 | let loc_needing_lifetime = if let Some(self_param) = self_param { |
| 66 | // if we have a self reference, use that | |
| 67 | 80 | Some(NeedsLifetime::SelfParam(self_param)) |
| 68 | 81 | } else { |
| 69 | // otherwise, if there's a single reference parameter without a named lifetime, use that | |
| 70 | let fn_params_without_lifetime: Vec<_> = param_list | |
| 82 | let unnamed_refs: Vec<_> = param_list | |
| 71 | 83 | .params() |
| 72 | 84 | .filter_map(|param| match param.ty() { |
| 73 | Some(ast::Type::RefType(ascribed_type)) if ascribed_type.lifetime().is_none() => { | |
| 74 | Some(NeedsLifetime::RefType(ascribed_type)) | |
| 85 | Some(ast::Type::RefType(ref_type)) if ref_type.lifetime().is_none() => { | |
| 86 | Some(NeedsLifetime::RefType(ref_type)) | |
| 75 | 87 | } |
| 76 | 88 | _ => None, |
| 77 | 89 | }) |
| 78 | 90 | .collect(); |
| 79 | match fn_params_without_lifetime.len() { | |
| 80 | 1 => Some(fn_params_without_lifetime.into_iter().next()?), | |
| 91 | ||
| 92 | match unnamed_refs.len() { | |
| 93 | 1 => Some(unnamed_refs.into_iter().next()?), | |
| 81 | 94 | 0 => None, |
| 82 | // multiple unnamed is invalid. assist is not applicable | |
| 83 | 95 | _ => return None, |
| 84 | 96 | } |
| 85 | 97 | }; |
| 86 | acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| { | |
| 87 | let fn_def = builder.make_mut(fn_def); | |
| 88 | let lifetime = builder.make_mut(lifetime); | |
| 89 | let loc_needing_lifetime = | |
| 90 | loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position()); | |
| 91 | ||
| 92 | fn_def.get_or_create_generic_param_list().add_generic_param( | |
| 93 | make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(), | |
| 94 | ); | |
| 95 | ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax()); | |
| 96 | if let Some(position) = loc_needing_lifetime { | |
| 97 | ted::insert(position, new_lifetime_param.clone_for_update().syntax()); | |
| 98 | ||
| 99 | acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |edit| { | |
| 100 | let root = fn_def.syntax().ancestors().last().unwrap().clone(); | |
| 101 | let mut editor = SyntaxEditor::new(root); | |
| 102 | let factory = SyntaxFactory::with_mappings(); | |
| 103 | ||
| 104 | if let Some(generic_list) = fn_def.generic_param_list() { | |
| 105 | insert_lifetime_param(&mut editor, &factory, &generic_list, &new_lifetime_name); | |
| 106 | } else { | |
| 107 | insert_new_generic_param_list_fn(&mut editor, &factory, &fn_def, &new_lifetime_name); | |
| 98 | 108 | } |
| 109 | ||
| 110 | editor.replace(lifetime.syntax(), factory.lifetime(&new_lifetime_name).syntax()); | |
| 111 | ||
| 112 | if let Some(pos) = loc_needing_lifetime.and_then(|l| l.to_position()) { | |
| 113 | editor.insert_all( | |
| 114 | pos, | |
| 115 | vec![ | |
| 116 | factory.lifetime(&new_lifetime_name).syntax().clone().into(), | |
| 117 | factory.whitespace(" ").into(), | |
| 118 | ], | |
| 119 | ); | |
| 120 | } | |
| 121 | ||
| 122 | edit.add_file_edits(file_id, editor); | |
| 99 | 123 | }) |
| 100 | 124 | } |
| 101 | 125 | |
| 102 | /// Generate the assist for the impl def case | |
| 103 | fn generate_impl_def_assist( | |
| 104 | acc: &mut Assists, | |
| 105 | impl_def: ast::Impl, | |
| 106 | lifetime_loc: TextRange, | |
| 107 | lifetime: ast::Lifetime, | |
| 126 | fn insert_new_generic_param_list_fn( | |
| 127 | editor: &mut SyntaxEditor, | |
| 128 | factory: &SyntaxFactory, | |
| 129 | fn_def: &ast::Fn, | |
| 130 | lifetime_name: &str, | |
| 108 | 131 | ) -> Option<()> { |
| 109 | let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; | |
| 110 | acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| { | |
| 111 | let impl_def = builder.make_mut(impl_def); | |
| 112 | let lifetime = builder.make_mut(lifetime); | |
| 132 | let name = fn_def.name()?; | |
| 113 | 133 | |
| 114 | impl_def.get_or_create_generic_param_list().add_generic_param( | |
| 115 | make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(), | |
| 116 | ); | |
| 117 | ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax()); | |
| 118 | }) | |
| 119 | } | |
| 134 | editor.insert_all( | |
| 135 | Position::after(name.syntax()), | |
| 136 | vec![ | |
| 137 | factory.token(T![<]).syntax_element(), | |
| 138 | factory.lifetime(lifetime_name).syntax().syntax_element(), | |
| 139 | factory.token(T![>]).syntax_element(), | |
| 140 | ], | |
| 141 | ); | |
| 120 | 142 | |
| 121 | /// Given a type parameter list, generate a unique lifetime parameter name | |
| 122 | /// which is not in the list | |
| 123 | fn generate_unique_lifetime_param_name( | |
| 124 | existing_type_param_list: Option<ast::GenericParamList>, | |
| 125 | ) -> Option<ast::Lifetime> { | |
| 126 | match existing_type_param_list { | |
| 127 | Some(type_params) => { | |
| 128 | let used_lifetime_params: FxHashSet<_> = | |
| 129 | type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect(); | |
| 130 | ('a'..='z').map(|it| format!("'{it}")).find(|it| !used_lifetime_params.contains(it)) | |
| 131 | } | |
| 132 | None => Some("'a".to_owned()), | |
| 133 | } | |
| 134 | .map(|it| make::lifetime(&it)) | |
| 143 | Some(()) | |
| 135 | 144 | } |
| 136 | 145 | |
| 137 | 146 | enum NeedsLifetime { |
| ... | ... | @@ -140,13 +149,6 @@ enum NeedsLifetime { |
| 140 | 149 | } |
| 141 | 150 | |
| 142 | 151 | impl NeedsLifetime { |
| 143 | fn make_mut(self, builder: &mut SourceChangeBuilder) -> Self { | |
| 144 | match self { | |
| 145 | Self::SelfParam(it) => Self::SelfParam(builder.make_mut(it)), | |
| 146 | Self::RefType(it) => Self::RefType(builder.make_mut(it)), | |
| 147 | } | |
| 148 | } | |
| 149 | ||
| 150 | 152 | fn to_position(self) -> Option<Position> { |
| 151 | 153 | match self { |
| 152 | 154 | Self::SelfParam(it) => Some(Position::after(it.amp_token()?)), |
| ... | ... | @@ -155,6 +157,75 @@ impl NeedsLifetime { |
| 155 | 157 | } |
| 156 | 158 | } |
| 157 | 159 | |
| 160 | fn generate_impl_def_assist( | |
| 161 | acc: &mut Assists, | |
| 162 | impl_def: ast::Impl, | |
| 163 | lifetime_loc: TextRange, | |
| 164 | lifetime: ast::Lifetime, | |
| 165 | file_id: FileId, | |
| 166 | ) -> Option<()> { | |
| 167 | let new_lifetime_name = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; | |
| 168 | ||
| 169 | acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |edit| { | |
| 170 | let root = impl_def.syntax().ancestors().last().unwrap().clone(); | |
| 171 | let mut editor = SyntaxEditor::new(root); | |
| 172 | let factory = SyntaxFactory::without_mappings(); | |
| 173 | ||
| 174 | if let Some(generic_list) = impl_def.generic_param_list() { | |
| 175 | insert_lifetime_param(&mut editor, &factory, &generic_list, &new_lifetime_name); | |
| 176 | } else { | |
| 177 | insert_new_generic_param_list_imp(&mut editor, &factory, &impl_def, &new_lifetime_name); | |
| 178 | } | |
| 179 | ||
| 180 | editor.replace(lifetime.syntax(), factory.lifetime(&new_lifetime_name).syntax()); | |
| 181 | ||
| 182 | edit.add_file_edits(file_id, editor); | |
| 183 | }) | |
| 184 | } | |
| 185 | ||
| 186 | fn insert_new_generic_param_list_imp( | |
| 187 | editor: &mut SyntaxEditor, | |
| 188 | factory: &SyntaxFactory, | |
| 189 | impl_: &ast::Impl, | |
| 190 | lifetime_name: &str, | |
| 191 | ) -> Option<()> { | |
| 192 | let impl_kw = impl_.impl_token()?; | |
| 193 | ||
| 194 | editor.insert_all( | |
| 195 | Position::after(impl_kw), | |
| 196 | vec![ | |
| 197 | factory.token(T![<]).syntax_element(), | |
| 198 | factory.lifetime(lifetime_name).syntax().syntax_element(), | |
| 199 | factory.token(T![>]).syntax_element(), | |
| 200 | ], | |
| 201 | ); | |
| 202 | ||
| 203 | Some(()) | |
| 204 | } | |
| 205 | ||
| 206 | fn insert_lifetime_param( | |
| 207 | editor: &mut SyntaxEditor, | |
| 208 | factory: &SyntaxFactory, | |
| 209 | generic_list: &ast::GenericParamList, | |
| 210 | lifetime_name: &str, | |
| 211 | ) -> Option<()> { | |
| 212 | let r_angle = generic_list.r_angle_token()?; | |
| 213 | let needs_comma = generic_list.generic_params().next().is_some(); | |
| 214 | ||
| 215 | let mut elements = Vec::new(); | |
| 216 | ||
| 217 | if needs_comma { | |
| 218 | elements.push(factory.token(T![,]).syntax_element()); | |
| 219 | elements.push(factory.whitespace(" ").syntax_element()); | |
| 220 | } | |
| 221 | ||
| 222 | let lifetime = factory.lifetime(lifetime_name); | |
| 223 | elements.push(lifetime.syntax().clone().into()); | |
| 224 | ||
| 225 | editor.insert_all(Position::before(r_angle), elements); | |
| 226 | Some(()) | |
| 227 | } | |
| 228 | ||
| 158 | 229 | #[cfg(test)] |
| 159 | 230 | mod tests { |
| 160 | 231 | use super::*; |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs+6-2| ... | ... | @@ -2,7 +2,10 @@ use hir::{AsAssocItem, AssocItemContainer, FileRange, HasSource}; |
| 2 | 2 | use ide_db::{assists::AssistId, defs::Definition, search::SearchScope}; |
| 3 | 3 | use syntax::{ |
| 4 | 4 | SyntaxKind, |
| 5 | ast::{self, AstNode, edit::IndentLevel, edit_in_place::Indent}, | |
| 5 | ast::{ | |
| 6 | self, AstNode, | |
| 7 | edit::{AstNodeEdit, IndentLevel}, | |
| 8 | }, | |
| 6 | 9 | }; |
| 7 | 10 | |
| 8 | 11 | use crate::assist_context::{AssistContext, Assists}; |
| ... | ... | @@ -136,7 +139,8 @@ pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> |
| 136 | 139 | let indent = IndentLevel::from_node(parent_fn.syntax()); |
| 137 | 140 | |
| 138 | 141 | let const_ = const_.clone_for_update(); |
| 139 | const_.reindent_to(indent); | |
| 142 | let const_ = const_.reset_indent(); | |
| 143 | let const_ = const_.indent(indent); | |
| 140 | 144 | builder.insert(insert_offset, format!("\n{indent}{const_}{fixup}")); |
| 141 | 145 | }, |
| 142 | 146 | ) |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs+23| ... | ... | @@ -1307,6 +1307,29 @@ impl<T: Clone> Clone for Foo<T> { |
| 1307 | 1307 | ) |
| 1308 | 1308 | } |
| 1309 | 1309 | |
| 1310 | #[test] | |
| 1311 | fn add_custom_impl_clone_generic_tuple_struct_with_associated() { | |
| 1312 | check_assist( | |
| 1313 | replace_derive_with_manual_impl, | |
| 1314 | r#" | |
| 1315 | //- minicore: clone, derive, deref | |
| 1316 | #[derive(Clo$0ne)] | |
| 1317 | struct Foo<T: core::ops::Deref>(T::Target); | |
| 1318 | "#, | |
| 1319 | r#" | |
| 1320 | struct Foo<T: core::ops::Deref>(T::Target); | |
| 1321 | ||
| 1322 | impl<T: core::ops::Deref + Clone> Clone for Foo<T> | |
| 1323 | where T::Target: Clone | |
| 1324 | { | |
| 1325 | $0fn clone(&self) -> Self { | |
| 1326 | Self(self.0.clone()) | |
| 1327 | } | |
| 1328 | } | |
| 1329 | "#, | |
| 1330 | ) | |
| 1331 | } | |
| 1332 | ||
| 1310 | 1333 | #[test] |
| 1311 | 1334 | fn test_ignore_derive_macro_without_input() { |
| 1312 | 1335 | check_assist_not_applicable( |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs+32-30| ... | ... | @@ -3,7 +3,11 @@ use std::iter::successors; |
| 3 | 3 | use ide_db::{RootDatabase, defs::NameClass, ty_filter::TryEnum}; |
| 4 | 4 | use syntax::{ |
| 5 | 5 | AstNode, Edition, SyntaxKind, T, TextRange, |
| 6 | ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory}, | |
| 6 | ast::{ | |
| 7 | self, HasName, | |
| 8 | edit::{AstNodeEdit, IndentLevel}, | |
| 9 | syntax_factory::SyntaxFactory, | |
| 10 | }, | |
| 7 | 11 | syntax_editor::SyntaxEditor, |
| 8 | 12 | }; |
| 9 | 13 | |
| ... | ... | @@ -54,8 +58,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' |
| 54 | 58 | ast::ElseBranch::IfExpr(expr) => Some(expr), |
| 55 | 59 | ast::ElseBranch::Block(block) => { |
| 56 | 60 | let block = unwrap_trivial_block(block).clone_for_update(); |
| 57 | block.reindent_to(IndentLevel(1)); | |
| 58 | else_block = Some(block); | |
| 61 | else_block = Some(block.reset_indent().indent(IndentLevel(1))); | |
| 59 | 62 | None |
| 60 | 63 | } |
| 61 | 64 | }); |
| ... | ... | @@ -82,12 +85,13 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' |
| 82 | 85 | (Some(pat), guard) |
| 83 | 86 | } |
| 84 | 87 | }; |
| 85 | if let Some(guard) = &guard { | |
| 86 | guard.dedent(indent); | |
| 87 | guard.indent(IndentLevel(1)); | |
| 88 | } | |
| 89 | let body = if_expr.then_branch()?.clone_for_update(); | |
| 90 | body.indent(IndentLevel(1)); | |
| 88 | let guard = if let Some(guard) = &guard { | |
| 89 | Some(guard.dedent(indent).indent(IndentLevel(1))) | |
| 90 | } else { | |
| 91 | guard | |
| 92 | }; | |
| 93 | ||
| 94 | let body = if_expr.then_branch()?.clone_for_update().indent(IndentLevel(1)); | |
| 91 | 95 | cond_bodies.push((cond, guard, body)); |
| 92 | 96 | } |
| 93 | 97 | |
| ... | ... | @@ -109,7 +113,8 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' |
| 109 | 113 | let else_arm = make_else_arm(ctx, &make, else_block, &cond_bodies); |
| 110 | 114 | let make_match_arm = |
| 111 | 115 | |(pat, guard, body): (_, Option<ast::Expr>, ast::BlockExpr)| { |
| 112 | body.reindent_to(IndentLevel::single()); | |
| 116 | // Dedent from original position, then indent for match arm | |
| 117 | let body = body.dedent(indent).indent(IndentLevel::single()); | |
| 113 | 118 | let body = unwrap_trivial_block(body); |
| 114 | 119 | match (pat, guard.map(|it| make.match_guard(it))) { |
| 115 | 120 | (Some(pat), guard) => make.match_arm(pat, guard, body), |
| ... | ... | @@ -122,8 +127,8 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' |
| 122 | 127 | } |
| 123 | 128 | }; |
| 124 | 129 | let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]); |
| 125 | let match_expr = make.expr_match(scrutinee_to_be_expr, make.match_arm_list(arms)); | |
| 126 | match_expr.indent(indent); | |
| 130 | let match_expr = | |
| 131 | make.expr_match(scrutinee_to_be_expr, make.match_arm_list(arms)).indent(indent); | |
| 127 | 132 | match_expr.into() |
| 128 | 133 | }; |
| 129 | 134 | |
| ... | ... | @@ -131,10 +136,9 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' |
| 131 | 136 | if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind())); |
| 132 | 137 | let expr = if has_preceding_if_expr { |
| 133 | 138 | // make sure we replace the `else if let ...` with a block so we don't end up with `else expr` |
| 134 | match_expr.dedent(indent); | |
| 135 | match_expr.indent(IndentLevel(1)); | |
| 136 | let block_expr = make.block_expr([], Some(match_expr)); | |
| 137 | block_expr.indent(indent); | |
| 139 | let block_expr = make | |
| 140 | .block_expr([], Some(match_expr.dedent(indent).indent(IndentLevel(1)))) | |
| 141 | .indent(indent); | |
| 138 | 142 | block_expr.into() |
| 139 | 143 | } else { |
| 140 | 144 | match_expr |
| ... | ... | @@ -267,10 +271,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' |
| 267 | 271 | // wrap them in another BlockExpr. |
| 268 | 272 | match expr { |
| 269 | 273 | ast::Expr::BlockExpr(block) if block.modifier().is_none() => block, |
| 270 | expr => { | |
| 271 | expr.indent(IndentLevel(1)); | |
| 272 | make.block_expr([], Some(expr)) | |
| 273 | } | |
| 274 | expr => make.block_expr([], Some(expr.indent(IndentLevel(1)))), | |
| 274 | 275 | } |
| 275 | 276 | }; |
| 276 | 277 | |
| ... | ... | @@ -292,18 +293,19 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' |
| 292 | 293 | } else { |
| 293 | 294 | condition |
| 294 | 295 | }; |
| 295 | let then_expr = then_expr.clone_for_update(); | |
| 296 | let else_expr = else_expr.clone_for_update(); | |
| 297 | then_expr.reindent_to(IndentLevel::single()); | |
| 298 | else_expr.reindent_to(IndentLevel::single()); | |
| 296 | let then_expr = | |
| 297 | then_expr.clone_for_update().reset_indent().indent(IndentLevel::single()); | |
| 298 | let else_expr = | |
| 299 | else_expr.clone_for_update().reset_indent().indent(IndentLevel::single()); | |
| 299 | 300 | let then_block = make_block_expr(then_expr); |
| 300 | 301 | let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) }; |
| 301 | let if_let_expr = make.expr_if( | |
| 302 | condition, | |
| 303 | then_block, | |
| 304 | else_expr.map(make_block_expr).map(ast::ElseBranch::Block), | |
| 305 | ); | |
| 306 | if_let_expr.indent(IndentLevel::from_node(match_expr.syntax())); | |
| 302 | let if_let_expr = make | |
| 303 | .expr_if( | |
| 304 | condition, | |
| 305 | then_block, | |
| 306 | else_expr.map(make_block_expr).map(ast::ElseBranch::Block), | |
| 307 | ) | |
| 308 | .indent(IndentLevel::from_node(match_expr.syntax())); | |
| 307 | 309 | |
| 308 | 310 | let mut editor = builder.make_editor(match_expr.syntax()); |
| 309 | 311 | editor.replace(match_expr.syntax(), if_let_expr.syntax()); |
src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs+6-2| ... | ... | @@ -1,7 +1,11 @@ |
| 1 | 1 | use ide_db::ty_filter::TryEnum; |
| 2 | 2 | use syntax::{ |
| 3 | 3 | AstNode, T, |
| 4 | ast::{self, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory}, | |
| 4 | ast::{ | |
| 5 | self, | |
| 6 | edit::{AstNodeEdit, IndentLevel}, | |
| 7 | syntax_factory::SyntaxFactory, | |
| 8 | }, | |
| 5 | 9 | }; |
| 6 | 10 | |
| 7 | 11 | use crate::{AssistContext, AssistId, Assists}; |
| ... | ... | @@ -64,7 +68,7 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_> |
| 64 | 68 | if let_expr_needs_paren(&init) { make.expr_paren(init).into() } else { init }; |
| 65 | 69 | |
| 66 | 70 | let block = make.block_expr([], None); |
| 67 | block.indent(IndentLevel::from_node(let_stmt.syntax())); | |
| 71 | let block = block.indent(IndentLevel::from_node(let_stmt.syntax())); | |
| 68 | 72 | let if_expr = make.expr_if( |
| 69 | 73 | make.expr_let(pat, init_expr).into(), |
| 70 | 74 | block, |
src/tools/rust-analyzer/crates/ide-assists/src/utils.rs+52-1| ... | ... | @@ -14,6 +14,7 @@ use ide_db::{ |
| 14 | 14 | path_transform::PathTransform, |
| 15 | 15 | syntax_helpers::{node_ext::preorder_expr, prettify_macro_expansion}, |
| 16 | 16 | }; |
| 17 | use itertools::Itertools; | |
| 17 | 18 | use stdx::format_to; |
| 18 | 19 | use syntax::{ |
| 19 | 20 | AstNode, AstToken, Direction, NodeOrToken, SourceFile, |
| ... | ... | @@ -765,6 +766,11 @@ fn generate_impl_inner( |
| 765 | 766 | }); |
| 766 | 767 | let generic_args = |
| 767 | 768 | generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update()); |
| 769 | let adt_assoc_bounds = trait_ | |
| 770 | .as_ref() | |
| 771 | .zip(generic_params.as_ref()) | |
| 772 | .and_then(|(trait_, params)| generic_param_associated_bounds(adt, trait_, params)); | |
| 773 | ||
| 768 | 774 | let ty = make::ty_path(make::ext::ident_path(&adt.name().unwrap().text())); |
| 769 | 775 | |
| 770 | 776 | let cfg_attrs = |
| ... | ... | @@ -780,7 +786,7 @@ fn generate_impl_inner( |
| 780 | 786 | false, |
| 781 | 787 | trait_, |
| 782 | 788 | ty, |
| 783 | None, | |
| 789 | adt_assoc_bounds, | |
| 784 | 790 | adt.where_clause(), |
| 785 | 791 | body, |
| 786 | 792 | ), |
| ... | ... | @@ -789,6 +795,51 @@ fn generate_impl_inner( |
| 789 | 795 | .clone_for_update() |
| 790 | 796 | } |
| 791 | 797 | |
| 798 | fn generic_param_associated_bounds( | |
| 799 | adt: &ast::Adt, | |
| 800 | trait_: &ast::Type, | |
| 801 | generic_params: &ast::GenericParamList, | |
| 802 | ) -> Option<ast::WhereClause> { | |
| 803 | let in_type_params = |name: &ast::NameRef| { | |
| 804 | generic_params | |
| 805 | .generic_params() | |
| 806 | .filter_map(|param| match param { | |
| 807 | ast::GenericParam::TypeParam(type_param) => type_param.name(), | |
| 808 | _ => None, | |
| 809 | }) | |
| 810 | .any(|param| param.text() == name.text()) | |
| 811 | }; | |
| 812 | let adt_body = match adt { | |
| 813 | ast::Adt::Enum(e) => e.variant_list().map(|it| it.syntax().clone()), | |
| 814 | ast::Adt::Struct(s) => s.field_list().map(|it| it.syntax().clone()), | |
| 815 | ast::Adt::Union(u) => u.record_field_list().map(|it| it.syntax().clone()), | |
| 816 | }; | |
| 817 | let mut trait_where_clause = adt_body | |
| 818 | .into_iter() | |
| 819 | .flat_map(|it| it.descendants()) | |
| 820 | .filter_map(ast::Path::cast) | |
| 821 | .filter_map(|path| { | |
| 822 | let qualifier = path.qualifier()?.as_single_segment()?; | |
| 823 | let qualifier = qualifier | |
| 824 | .name_ref() | |
| 825 | .or_else(|| match qualifier.type_anchor()?.ty()? { | |
| 826 | ast::Type::PathType(path_type) => path_type.path()?.as_single_name_ref(), | |
| 827 | _ => None, | |
| 828 | }) | |
| 829 | .filter(in_type_params)?; | |
| 830 | Some((qualifier, path.segment()?.name_ref()?)) | |
| 831 | }) | |
| 832 | .map(|(qualifier, assoc_name)| { | |
| 833 | let segments = [qualifier, assoc_name].map(make::path_segment); | |
| 834 | let path = make::path_from_segments(segments, false); | |
| 835 | let bounds = Some(make::type_bound(trait_.clone())); | |
| 836 | make::where_pred(either::Either::Right(make::ty_path(path)), bounds) | |
| 837 | }) | |
| 838 | .unique_by(|it| it.syntax().to_string()) | |
| 839 | .peekable(); | |
| 840 | trait_where_clause.peek().is_some().then(|| make::where_clause(trait_where_clause)) | |
| 841 | } | |
| 842 | ||
| 792 | 843 | pub(crate) fn add_method_to_adt( |
| 793 | 844 | builder: &mut SourceChangeBuilder, |
| 794 | 845 | adt: &ast::Adt, |
src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs+17-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | //! based on the parent of the existing expression. |
| 6 | 6 | use syntax::{ |
| 7 | 7 | AstNode, T, |
| 8 | ast::{self, FieldExpr, MethodCallExpr, make}, | |
| 8 | ast::{self, FieldExpr, MethodCallExpr, make, syntax_factory::SyntaxFactory}, | |
| 9 | 9 | }; |
| 10 | 10 | |
| 11 | 11 | use crate::AssistContext; |
| ... | ... | @@ -130,4 +130,20 @@ impl RefData { |
| 130 | 130 | |
| 131 | 131 | expr |
| 132 | 132 | } |
| 133 | ||
| 134 | pub(crate) fn wrap_expr_with_factory( | |
| 135 | &self, | |
| 136 | mut expr: ast::Expr, | |
| 137 | syntax_factory: &SyntaxFactory, | |
| 138 | ) -> ast::Expr { | |
| 139 | if self.needs_deref { | |
| 140 | expr = syntax_factory.expr_prefix(T![*], expr).into(); | |
| 141 | } | |
| 142 | ||
| 143 | if self.needs_parentheses { | |
| 144 | expr = syntax_factory.expr_paren(expr).into(); | |
| 145 | } | |
| 146 | ||
| 147 | expr | |
| 148 | } | |
| 133 | 149 | } |
src/tools/rust-analyzer/crates/ide-completion/src/context.rs+4-1| ... | ... | @@ -821,7 +821,10 @@ impl<'db> CompletionContext<'db> { |
| 821 | 821 | CompleteSemicolon::DoNotComplete |
| 822 | 822 | } else if let Some(term_node) = |
| 823 | 823 | sema.token_ancestors_with_macros(token.clone()).find(|node| { |
| 824 | matches!(node.kind(), BLOCK_EXPR | MATCH_ARM | CLOSURE_EXPR | ARG_LIST | PAREN_EXPR) | |
| 824 | matches!( | |
| 825 | node.kind(), | |
| 826 | BLOCK_EXPR | MATCH_ARM | CLOSURE_EXPR | ARG_LIST | PAREN_EXPR | ARRAY_EXPR | |
| 827 | ) | |
| 825 | 828 | }) |
| 826 | 829 | { |
| 827 | 830 | let next_token = iter::successors(token.next_token(), |it| it.next_token()) |
src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs+19| ... | ... | @@ -905,6 +905,25 @@ fn baz(_: impl FnOnce()) {} |
| 905 | 905 | fn bar() { |
| 906 | 906 | baz(foo()$0); |
| 907 | 907 | } |
| 908 | "#, | |
| 909 | ); | |
| 910 | } | |
| 911 | ||
| 912 | #[test] | |
| 913 | fn no_semicolon_in_array() { | |
| 914 | check_edit( | |
| 915 | r#"foo"#, | |
| 916 | r#" | |
| 917 | fn foo() {} | |
| 918 | fn bar() { | |
| 919 | let _ = [fo$0]; | |
| 920 | } | |
| 921 | "#, | |
| 922 | r#" | |
| 923 | fn foo() {} | |
| 924 | fn bar() { | |
| 925 | let _ = [foo()$0]; | |
| 926 | } | |
| 908 | 927 | "#, |
| 909 | 928 | ); |
| 910 | 929 | } |
src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs+83-5| ... | ... | @@ -9,7 +9,7 @@ use hir::{ |
| 9 | 9 | }; |
| 10 | 10 | use itertools::Itertools; |
| 11 | 11 | use rustc_hash::{FxHashMap, FxHashSet}; |
| 12 | use smallvec::SmallVec; | |
| 12 | use smallvec::{SmallVec, smallvec}; | |
| 13 | 13 | use syntax::{ |
| 14 | 14 | AstNode, SyntaxNode, |
| 15 | 15 | ast::{self, HasName, make}, |
| ... | ... | @@ -68,6 +68,8 @@ pub struct PathImportCandidate { |
| 68 | 68 | pub qualifier: Vec<Name>, |
| 69 | 69 | /// The name the item (struct, trait, enum, etc.) should have. |
| 70 | 70 | pub name: NameToImport, |
| 71 | /// Potentially more segments that should resolve in the candidate. | |
| 72 | pub after: Vec<Name>, | |
| 71 | 73 | } |
| 72 | 74 | |
| 73 | 75 | /// A name that will be used during item lookups. |
| ... | ... | @@ -376,7 +378,7 @@ fn path_applicable_imports( |
| 376 | 378 | ) -> FxIndexSet<LocatedImport> { |
| 377 | 379 | let _p = tracing::info_span!("ImportAssets::path_applicable_imports").entered(); |
| 378 | 380 | |
| 379 | match &*path_candidate.qualifier { | |
| 381 | let mut result = match &*path_candidate.qualifier { | |
| 380 | 382 | [] => { |
| 381 | 383 | items_locator::items_with_name( |
| 382 | 384 | db, |
| ... | ... | @@ -433,6 +435,75 @@ fn path_applicable_imports( |
| 433 | 435 | }) |
| 434 | 436 | .take(DEFAULT_QUERY_SEARCH_LIMIT) |
| 435 | 437 | .collect(), |
| 438 | }; | |
| 439 | ||
| 440 | filter_candidates_by_after_path(db, scope, path_candidate, &mut result); | |
| 441 | ||
| 442 | result | |
| 443 | } | |
| 444 | ||
| 445 | fn filter_candidates_by_after_path( | |
| 446 | db: &RootDatabase, | |
| 447 | scope: &SemanticsScope<'_>, | |
| 448 | path_candidate: &PathImportCandidate, | |
| 449 | imports: &mut FxIndexSet<LocatedImport>, | |
| 450 | ) { | |
| 451 | if imports.len() <= 1 { | |
| 452 | // Short-circuit, as even if it doesn't match fully we want it. | |
| 453 | return; | |
| 454 | } | |
| 455 | ||
| 456 | let Some((last_after, after_except_last)) = path_candidate.after.split_last() else { | |
| 457 | return; | |
| 458 | }; | |
| 459 | ||
| 460 | let original_imports = imports.clone(); | |
| 461 | ||
| 462 | let traits_in_scope = scope.visible_traits(); | |
| 463 | imports.retain(|import| { | |
| 464 | let items = if after_except_last.is_empty() { | |
| 465 | smallvec![import.original_item] | |
| 466 | } else { | |
| 467 | let ItemInNs::Types(ModuleDef::Module(item)) = import.original_item else { | |
| 468 | return false; | |
| 469 | }; | |
| 470 | // FIXME: This doesn't consider visibilities. | |
| 471 | item.resolve_mod_path(db, after_except_last.iter().cloned()) | |
| 472 | .into_iter() | |
| 473 | .flatten() | |
| 474 | .collect::<SmallVec<[_; 3]>>() | |
| 475 | }; | |
| 476 | items.into_iter().any(|item| { | |
| 477 | let has_last_method = |ty: hir::Type<'_>| { | |
| 478 | ty.iterate_path_candidates(db, scope, &traits_in_scope, Some(last_after), |_| { | |
| 479 | Some(()) | |
| 480 | }) | |
| 481 | .is_some() | |
| 482 | }; | |
| 483 | // FIXME: A trait can have an assoc type that has a function/const, that's two segments before last. | |
| 484 | match item { | |
| 485 | // A module? Can we resolve one more segment? | |
| 486 | ItemInNs::Types(ModuleDef::Module(module)) => module | |
| 487 | .resolve_mod_path(db, [last_after.clone()]) | |
| 488 | .is_some_and(|mut it| it.any(|_| true)), | |
| 489 | // And ADT/Type Alias? That might be a method. | |
| 490 | ItemInNs::Types(ModuleDef::Adt(it)) => has_last_method(it.ty(db)), | |
| 491 | ItemInNs::Types(ModuleDef::BuiltinType(it)) => has_last_method(it.ty(db)), | |
| 492 | ItemInNs::Types(ModuleDef::TypeAlias(it)) => has_last_method(it.ty(db)), | |
| 493 | // A trait? Might have an associated item. | |
| 494 | ItemInNs::Types(ModuleDef::Trait(it)) => it | |
| 495 | .items(db) | |
| 496 | .into_iter() | |
| 497 | .any(|assoc_item| assoc_item.name(db) == Some(last_after.clone())), | |
| 498 | // Other items? can't resolve one more segment. | |
| 499 | _ => false, | |
| 500 | } | |
| 501 | }) | |
| 502 | }); | |
| 503 | ||
| 504 | if imports.is_empty() { | |
| 505 | // Better one half-match than zero full matches. | |
| 506 | *imports = original_imports; | |
| 436 | 507 | } |
| 437 | 508 | } |
| 438 | 509 | |
| ... | ... | @@ -759,10 +830,14 @@ impl<'db> ImportCandidate<'db> { |
| 759 | 830 | if sema.resolve_path(path).is_some() { |
| 760 | 831 | return None; |
| 761 | 832 | } |
| 833 | let after = std::iter::successors(path.parent_path(), |it| it.parent_path()) | |
| 834 | .map(|seg| seg.segment()?.name_ref().map(|name| Name::new_root(&name.text()))) | |
| 835 | .collect::<Option<_>>()?; | |
| 762 | 836 | path_import_candidate( |
| 763 | 837 | sema, |
| 764 | 838 | path.qualifier(), |
| 765 | 839 | NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string()), |
| 840 | after, | |
| 766 | 841 | ) |
| 767 | 842 | } |
| 768 | 843 | |
| ... | ... | @@ -777,6 +852,7 @@ impl<'db> ImportCandidate<'db> { |
| 777 | 852 | Some(ImportCandidate::Path(PathImportCandidate { |
| 778 | 853 | qualifier: vec![], |
| 779 | 854 | name: NameToImport::exact_case_sensitive(name.to_string()), |
| 855 | after: vec![], | |
| 780 | 856 | })) |
| 781 | 857 | } |
| 782 | 858 | |
| ... | ... | @@ -785,7 +861,8 @@ impl<'db> ImportCandidate<'db> { |
| 785 | 861 | fuzzy_name: String, |
| 786 | 862 | sema: &Semantics<'db, RootDatabase>, |
| 787 | 863 | ) -> Option<Self> { |
| 788 | path_import_candidate(sema, qualifier, NameToImport::fuzzy(fuzzy_name)) | |
| 864 | // Assume a fuzzy match does not want the segments after. Because... I guess why not? | |
| 865 | path_import_candidate(sema, qualifier, NameToImport::fuzzy(fuzzy_name), Vec::new()) | |
| 789 | 866 | } |
| 790 | 867 | } |
| 791 | 868 | |
| ... | ... | @@ -793,6 +870,7 @@ fn path_import_candidate<'db>( |
| 793 | 870 | sema: &Semantics<'db, RootDatabase>, |
| 794 | 871 | qualifier: Option<ast::Path>, |
| 795 | 872 | name: NameToImport, |
| 873 | after: Vec<Name>, | |
| 796 | 874 | ) -> Option<ImportCandidate<'db>> { |
| 797 | 875 | Some(match qualifier { |
| 798 | 876 | Some(qualifier) => match sema.resolve_path(&qualifier) { |
| ... | ... | @@ -802,7 +880,7 @@ fn path_import_candidate<'db>( |
| 802 | 880 | .segments() |
| 803 | 881 | .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text()))) |
| 804 | 882 | .collect::<Option<Vec<_>>>()?; |
| 805 | ImportCandidate::Path(PathImportCandidate { qualifier, name }) | |
| 883 | ImportCandidate::Path(PathImportCandidate { qualifier, name, after }) | |
| 806 | 884 | } else { |
| 807 | 885 | return None; |
| 808 | 886 | } |
| ... | ... | @@ -826,7 +904,7 @@ fn path_import_candidate<'db>( |
| 826 | 904 | } |
| 827 | 905 | Some(_) => return None, |
| 828 | 906 | }, |
| 829 | None => ImportCandidate::Path(PathImportCandidate { qualifier: vec![], name }), | |
| 907 | None => ImportCandidate::Path(PathImportCandidate { qualifier: vec![], name, after }), | |
| 830 | 908 | }) |
| 831 | 909 | } |
| 832 | 910 |
src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs+199-1| ... | ... | @@ -9,8 +9,9 @@ use syntax::{ |
| 9 | 9 | Direction, NodeOrToken, SyntaxKind, SyntaxNode, algo, |
| 10 | 10 | ast::{ |
| 11 | 11 | self, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind, |
| 12 | edit_in_place::Removable, make, | |
| 12 | edit_in_place::Removable, make, syntax_factory::SyntaxFactory, | |
| 13 | 13 | }, |
| 14 | syntax_editor::{Position, SyntaxEditor}, | |
| 14 | 15 | ted, |
| 15 | 16 | }; |
| 16 | 17 | |
| ... | ... | @@ -146,6 +147,17 @@ pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) { |
| 146 | 147 | insert_use_with_alias_option(scope, path, cfg, None); |
| 147 | 148 | } |
| 148 | 149 | |
| 150 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. | |
| 151 | pub fn insert_use_with_editor( | |
| 152 | scope: &ImportScope, | |
| 153 | path: ast::Path, | |
| 154 | cfg: &InsertUseConfig, | |
| 155 | syntax_editor: &mut SyntaxEditor, | |
| 156 | syntax_factory: &SyntaxFactory, | |
| 157 | ) { | |
| 158 | insert_use_with_alias_option_with_editor(scope, path, cfg, None, syntax_editor, syntax_factory); | |
| 159 | } | |
| 160 | ||
| 149 | 161 | pub fn insert_use_as_alias( |
| 150 | 162 | scope: &ImportScope, |
| 151 | 163 | path: ast::Path, |
| ... | ... | @@ -229,6 +241,71 @@ fn insert_use_with_alias_option( |
| 229 | 241 | insert_use_(scope, use_item, cfg.group); |
| 230 | 242 | } |
| 231 | 243 | |
| 244 | fn insert_use_with_alias_option_with_editor( | |
| 245 | scope: &ImportScope, | |
| 246 | path: ast::Path, | |
| 247 | cfg: &InsertUseConfig, | |
| 248 | alias: Option<ast::Rename>, | |
| 249 | syntax_editor: &mut SyntaxEditor, | |
| 250 | syntax_factory: &SyntaxFactory, | |
| 251 | ) { | |
| 252 | let _p = tracing::info_span!("insert_use_with_alias_option").entered(); | |
| 253 | let mut mb = match cfg.granularity { | |
| 254 | ImportGranularity::Crate => Some(MergeBehavior::Crate), | |
| 255 | ImportGranularity::Module => Some(MergeBehavior::Module), | |
| 256 | ImportGranularity::One => Some(MergeBehavior::One), | |
| 257 | ImportGranularity::Item => None, | |
| 258 | }; | |
| 259 | if !cfg.enforce_granularity { | |
| 260 | let file_granularity = guess_granularity_from_scope(scope); | |
| 261 | mb = match file_granularity { | |
| 262 | ImportGranularityGuess::Unknown => mb, | |
| 263 | ImportGranularityGuess::Item => None, | |
| 264 | ImportGranularityGuess::Module => Some(MergeBehavior::Module), | |
| 265 | // We use the user's setting to infer if this is module or item. | |
| 266 | ImportGranularityGuess::ModuleOrItem => match mb { | |
| 267 | Some(MergeBehavior::Module) | None => mb, | |
| 268 | // There isn't really a way to decide between module or item here, so we just pick one. | |
| 269 | // FIXME: Maybe it is possible to infer based on semantic analysis? | |
| 270 | Some(MergeBehavior::One | MergeBehavior::Crate) => Some(MergeBehavior::Module), | |
| 271 | }, | |
| 272 | ImportGranularityGuess::Crate => Some(MergeBehavior::Crate), | |
| 273 | ImportGranularityGuess::CrateOrModule => match mb { | |
| 274 | Some(MergeBehavior::Crate | MergeBehavior::Module) => mb, | |
| 275 | Some(MergeBehavior::One) | None => Some(MergeBehavior::Crate), | |
| 276 | }, | |
| 277 | ImportGranularityGuess::One => Some(MergeBehavior::One), | |
| 278 | }; | |
| 279 | } | |
| 280 | ||
| 281 | let use_tree = syntax_factory.use_tree(path, None, alias, false); | |
| 282 | if mb == Some(MergeBehavior::One) && use_tree.path().is_some() { | |
| 283 | use_tree.wrap_in_tree_list(); | |
| 284 | } | |
| 285 | let use_item = make::use_(None, None, use_tree).clone_for_update(); | |
| 286 | for attr in | |
| 287 | scope.required_cfgs.iter().map(|attr| attr.syntax().clone_subtree().clone_for_update()) | |
| 288 | { | |
| 289 | syntax_editor.insert(Position::first_child_of(use_item.syntax()), attr); | |
| 290 | } | |
| 291 | ||
| 292 | // merge into existing imports if possible | |
| 293 | if let Some(mb) = mb { | |
| 294 | let filter = |it: &_| !(cfg.skip_glob_imports && ast::Use::is_simple_glob(it)); | |
| 295 | for existing_use in | |
| 296 | scope.as_syntax_node().children().filter_map(ast::Use::cast).filter(filter) | |
| 297 | { | |
| 298 | if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { | |
| 299 | syntax_editor.replace(existing_use.syntax(), merged.syntax()); | |
| 300 | return; | |
| 301 | } | |
| 302 | } | |
| 303 | } | |
| 304 | // either we weren't allowed to merge or there is no import that fits the merge conditions | |
| 305 | // so look for the place we have to insert to | |
| 306 | insert_use_with_editor_(scope, use_item, cfg.group, syntax_editor, syntax_factory); | |
| 307 | } | |
| 308 | ||
| 232 | 309 | pub fn ast_to_remove_for_path_in_use_stmt(path: &ast::Path) -> Option<Box<dyn Removable>> { |
| 233 | 310 | // FIXME: improve this |
| 234 | 311 | if path.parent_path().is_some() { |
| ... | ... | @@ -500,6 +577,127 @@ fn insert_use_(scope: &ImportScope, use_item: ast::Use, group_imports: bool) { |
| 500 | 577 | } |
| 501 | 578 | } |
| 502 | 579 | |
| 580 | fn insert_use_with_editor_( | |
| 581 | scope: &ImportScope, | |
| 582 | use_item: ast::Use, | |
| 583 | group_imports: bool, | |
| 584 | syntax_editor: &mut SyntaxEditor, | |
| 585 | syntax_factory: &SyntaxFactory, | |
| 586 | ) { | |
| 587 | let scope_syntax = scope.as_syntax_node(); | |
| 588 | let insert_use_tree = | |
| 589 | use_item.use_tree().expect("`use_item` should have a use tree for `insert_path`"); | |
| 590 | let group = ImportGroup::new(&insert_use_tree); | |
| 591 | let path_node_iter = scope_syntax | |
| 592 | .children() | |
| 593 | .filter_map(|node| ast::Use::cast(node.clone()).zip(Some(node))) | |
| 594 | .flat_map(|(use_, node)| { | |
| 595 | let tree = use_.use_tree()?; | |
| 596 | Some((tree, node)) | |
| 597 | }); | |
| 598 | ||
| 599 | if group_imports { | |
| 600 | // Iterator that discards anything that's not in the required grouping | |
| 601 | // This implementation allows the user to rearrange their import groups as this only takes the first group that fits | |
| 602 | let group_iter = path_node_iter | |
| 603 | .clone() | |
| 604 | .skip_while(|(use_tree, ..)| ImportGroup::new(use_tree) != group) | |
| 605 | .take_while(|(use_tree, ..)| ImportGroup::new(use_tree) == group); | |
| 606 | ||
| 607 | // track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place | |
| 608 | let mut last = None; | |
| 609 | // find the element that would come directly after our new import | |
| 610 | let post_insert: Option<(_, SyntaxNode)> = group_iter | |
| 611 | .inspect(|(.., node)| last = Some(node.clone())) | |
| 612 | .find(|(use_tree, _)| use_tree_cmp(&insert_use_tree, use_tree) != Ordering::Greater); | |
| 613 | ||
| 614 | if let Some((.., node)) = post_insert { | |
| 615 | cov_mark::hit!(insert_group); | |
| 616 | // insert our import before that element | |
| 617 | return syntax_editor.insert(Position::before(node), use_item.syntax()); | |
| 618 | } | |
| 619 | if let Some(node) = last { | |
| 620 | cov_mark::hit!(insert_group_last); | |
| 621 | // there is no element after our new import, so append it to the end of the group | |
| 622 | return syntax_editor.insert(Position::after(node), use_item.syntax()); | |
| 623 | } | |
| 624 | ||
| 625 | // the group we were looking for actually doesn't exist, so insert | |
| 626 | ||
| 627 | let mut last = None; | |
| 628 | // find the group that comes after where we want to insert | |
| 629 | let post_group = path_node_iter | |
| 630 | .inspect(|(.., node)| last = Some(node.clone())) | |
| 631 | .find(|(use_tree, ..)| ImportGroup::new(use_tree) > group); | |
| 632 | if let Some((.., node)) = post_group { | |
| 633 | cov_mark::hit!(insert_group_new_group); | |
| 634 | syntax_editor.insert(Position::before(&node), use_item.syntax()); | |
| 635 | if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) { | |
| 636 | syntax_editor.insert(Position::after(node), syntax_factory.whitespace("\n")); | |
| 637 | } | |
| 638 | return; | |
| 639 | } | |
| 640 | // there is no such group, so append after the last one | |
| 641 | if let Some(node) = last { | |
| 642 | cov_mark::hit!(insert_group_no_group); | |
| 643 | syntax_editor.insert(Position::after(&node), use_item.syntax()); | |
| 644 | syntax_editor.insert(Position::after(node), syntax_factory.whitespace("\n")); | |
| 645 | return; | |
| 646 | } | |
| 647 | } else { | |
| 648 | // There exists a group, so append to the end of it | |
| 649 | if let Some((_, node)) = path_node_iter.last() { | |
| 650 | cov_mark::hit!(insert_no_grouping_last); | |
| 651 | syntax_editor.insert(Position::after(node), use_item.syntax()); | |
| 652 | return; | |
| 653 | } | |
| 654 | } | |
| 655 | ||
| 656 | let l_curly = match &scope.kind { | |
| 657 | ImportScopeKind::File(_) => None, | |
| 658 | // don't insert the imports before the item list/block expr's opening curly brace | |
| 659 | ImportScopeKind::Module(item_list) => item_list.l_curly_token(), | |
| 660 | // don't insert the imports before the item list's opening curly brace | |
| 661 | ImportScopeKind::Block(block) => block.l_curly_token(), | |
| 662 | }; | |
| 663 | // there are no imports in this file at all | |
| 664 | // so put the import after all inner module attributes and possible license header comments | |
| 665 | if let Some(last_inner_element) = scope_syntax | |
| 666 | .children_with_tokens() | |
| 667 | // skip the curly brace | |
| 668 | .skip(l_curly.is_some() as usize) | |
| 669 | .take_while(|child| match child { | |
| 670 | NodeOrToken::Node(node) => is_inner_attribute(node.clone()), | |
| 671 | NodeOrToken::Token(token) => { | |
| 672 | [SyntaxKind::WHITESPACE, SyntaxKind::COMMENT, SyntaxKind::SHEBANG] | |
| 673 | .contains(&token.kind()) | |
| 674 | } | |
| 675 | }) | |
| 676 | .filter(|child| child.as_token().is_none_or(|t| t.kind() != SyntaxKind::WHITESPACE)) | |
| 677 | .last() | |
| 678 | { | |
| 679 | cov_mark::hit!(insert_empty_inner_attr); | |
| 680 | syntax_editor.insert(Position::after(&last_inner_element), use_item.syntax()); | |
| 681 | syntax_editor.insert(Position::after(last_inner_element), syntax_factory.whitespace("\n")); | |
| 682 | } else { | |
| 683 | match l_curly { | |
| 684 | Some(b) => { | |
| 685 | cov_mark::hit!(insert_empty_module); | |
| 686 | syntax_editor.insert(Position::after(&b), syntax_factory.whitespace("\n")); | |
| 687 | syntax_editor.insert(Position::after(&b), use_item.syntax()); | |
| 688 | } | |
| 689 | None => { | |
| 690 | cov_mark::hit!(insert_empty_file); | |
| 691 | syntax_editor.insert( | |
| 692 | Position::first_child_of(scope_syntax), | |
| 693 | syntax_factory.whitespace("\n\n"), | |
| 694 | ); | |
| 695 | syntax_editor.insert(Position::first_child_of(scope_syntax), use_item.syntax()); | |
| 696 | } | |
| 697 | } | |
| 698 | } | |
| 699 | } | |
| 700 | ||
| 503 | 701 | fn is_inner_attribute(node: SyntaxNode) -> bool { |
| 504 | 702 | ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner) |
| 505 | 703 | } |
src/tools/rust-analyzer/crates/ide/src/hover/tests.rs+93| ... | ... | @@ -9719,6 +9719,99 @@ fn test_hover_function_with_pat_param() { |
| 9719 | 9719 | ); |
| 9720 | 9720 | } |
| 9721 | 9721 | |
| 9722 | #[test] | |
| 9723 | fn test_hover_function_with_too_long_param() { | |
| 9724 | check( | |
| 9725 | r#" | |
| 9726 | fn fn_$0( | |
| 9727 | attrs: impl IntoIterator<Item = ast::Attr>, | |
| 9728 | visibility: Option<ast::Visibility>, | |
| 9729 | fn_name: ast::Name, | |
| 9730 | type_params: Option<ast::GenericParamList>, | |
| 9731 | where_clause: Option<ast::WhereClause>, | |
| 9732 | params: ast::ParamList, | |
| 9733 | body: ast::BlockExpr, | |
| 9734 | ret_type: Option<ast::RetType>, | |
| 9735 | is_async: bool, | |
| 9736 | is_const: bool, | |
| 9737 | is_unsafe: bool, | |
| 9738 | is_gen: bool, | |
| 9739 | ) -> ast::Fn {} | |
| 9740 | "#, | |
| 9741 | expect![[r#" | |
| 9742 | *fn_* | |
| 9743 | ||
| 9744 | ```rust | |
| 9745 | ra_test_fixture | |
| 9746 | ``` | |
| 9747 | ||
| 9748 | ```rust | |
| 9749 | fn fn_( | |
| 9750 | attrs: impl IntoIterator<Item = ast::Attr>, | |
| 9751 | visibility: Option<ast::Visibility>, | |
| 9752 | fn_name: ast::Name, | |
| 9753 | type_params: Option<ast::GenericParamList>, | |
| 9754 | where_clause: Option<ast::WhereClause>, | |
| 9755 | params: ast::ParamList, | |
| 9756 | body: ast::BlockExpr, | |
| 9757 | ret_type: Option<ast::RetType>, | |
| 9758 | is_async: bool, | |
| 9759 | is_const: bool, | |
| 9760 | is_unsafe: bool, | |
| 9761 | is_gen: bool | |
| 9762 | ) -> ast::Fn | |
| 9763 | ``` | |
| 9764 | "#]], | |
| 9765 | ); | |
| 9766 | ||
| 9767 | check( | |
| 9768 | r#" | |
| 9769 | fn fn_$0( | |
| 9770 | &self, | |
| 9771 | attrs: impl IntoIterator<Item = ast::Attr>, | |
| 9772 | visibility: Option<ast::Visibility>, | |
| 9773 | fn_name: ast::Name, | |
| 9774 | type_params: Option<ast::GenericParamList>, | |
| 9775 | where_clause: Option<ast::WhereClause>, | |
| 9776 | params: ast::ParamList, | |
| 9777 | body: ast::BlockExpr, | |
| 9778 | ret_type: Option<ast::RetType>, | |
| 9779 | is_async: bool, | |
| 9780 | is_const: bool, | |
| 9781 | is_unsafe: bool, | |
| 9782 | is_gen: bool, | |
| 9783 | ... | |
| 9784 | ) -> ast::Fn {} | |
| 9785 | "#, | |
| 9786 | expect![[r#" | |
| 9787 | *fn_* | |
| 9788 | ||
| 9789 | ```rust | |
| 9790 | ra_test_fixture | |
| 9791 | ``` | |
| 9792 | ||
| 9793 | ```rust | |
| 9794 | fn fn_( | |
| 9795 | &self, | |
| 9796 | attrs: impl IntoIterator<Item = ast::Attr>, | |
| 9797 | visibility: Option<ast::Visibility>, | |
| 9798 | fn_name: ast::Name, | |
| 9799 | type_params: Option<ast::GenericParamList>, | |
| 9800 | where_clause: Option<ast::WhereClause>, | |
| 9801 | params: ast::ParamList, | |
| 9802 | body: ast::BlockExpr, | |
| 9803 | ret_type: Option<ast::RetType>, | |
| 9804 | is_async: bool, | |
| 9805 | is_const: bool, | |
| 9806 | is_unsafe: bool, | |
| 9807 | is_gen: bool, | |
| 9808 | ... | |
| 9809 | ) -> ast::Fn | |
| 9810 | ``` | |
| 9811 | "#]], | |
| 9812 | ); | |
| 9813 | } | |
| 9814 | ||
| 9722 | 9815 | #[test] |
| 9723 | 9816 | fn hover_path_inside_block_scope() { |
| 9724 | 9817 | check( |
src/tools/rust-analyzer/crates/ide/src/lib.rs+1-5| ... | ... | @@ -67,7 +67,7 @@ use ide_db::{ |
| 67 | 67 | FxHashMap, FxIndexSet, LineIndexDatabase, |
| 68 | 68 | base_db::{ |
| 69 | 69 | CrateOrigin, CrateWorkspaceData, Env, FileSet, RootQueryDb, SourceDatabase, VfsPath, |
| 70 | salsa::{CancellationToken, Cancelled, Database}, | |
| 70 | salsa::{Cancelled, Database}, | |
| 71 | 71 | }, |
| 72 | 72 | prime_caches, symbol_index, |
| 73 | 73 | }; |
| ... | ... | @@ -947,10 +947,6 @@ impl Analysis { |
| 947 | 947 | // We use `attach_db_allow_change()` and not `attach_db()` because fixture injection can change the database. |
| 948 | 948 | hir::attach_db_allow_change(&self.db, || Cancelled::catch(|| f(&self.db))) |
| 949 | 949 | } |
| 950 | ||
| 951 | pub fn cancellation_token(&self) -> CancellationToken { | |
| 952 | self.db.cancellation_token() | |
| 953 | } | |
| 954 | 950 | } |
| 955 | 951 | |
| 956 | 952 | #[test] |
src/tools/rust-analyzer/crates/ide/src/references.rs+2| ... | ... | @@ -2073,6 +2073,7 @@ fn func() {} |
| 2073 | 2073 | expect![[r#" |
| 2074 | 2074 | identity Attribute FileId(1) 1..107 32..40 |
| 2075 | 2075 | |
| 2076 | FileId(0) 17..25 import | |
| 2076 | 2077 | FileId(0) 43..51 |
| 2077 | 2078 | "#]], |
| 2078 | 2079 | ); |
| ... | ... | @@ -2103,6 +2104,7 @@ mirror$0! {} |
| 2103 | 2104 | expect![[r#" |
| 2104 | 2105 | mirror ProcMacro FileId(1) 1..77 22..28 |
| 2105 | 2106 | |
| 2107 | FileId(0) 17..23 import | |
| 2106 | 2108 | FileId(0) 26..32 |
| 2107 | 2109 | "#]], |
| 2108 | 2110 | ) |
src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html+2-1| ... | ... | @@ -41,7 +41,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd |
| 41 | 41 | .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } |
| 42 | 42 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } |
| 43 | 43 | </style> |
| 44 | <pre><code><span class="keyword">use</span> <span class="crate_root library">proc_macros</span><span class="operator">::</span><span class="brace">{</span><span class="function library">mirror</span><span class="comma">,</span> <span class="function library">identity</span><span class="comma">,</span> <span class="derive library">DeriveIdentity</span><span class="brace">}</span><span class="semicolon">;</span> | |
| 44 | <pre><code><span class="keyword">use</span> <span class="crate_root library">proc_macros</span><span class="operator">::</span><span class="brace">{</span><span class="proc_macro library">mirror</span><span class="comma">,</span> <span class="attribute library">identity</span><span class="comma">,</span> <span class="derive library">DeriveIdentity</span><span class="brace">}</span><span class="semicolon">;</span> | |
| 45 | <span class="keyword">use</span> <span class="crate_root library">pm</span><span class="operator">::</span><span class="attribute library">proc_macro</span><span class="semicolon">;</span> | |
| 45 | 46 | |
| 46 | 47 | <span class="proc_macro library">mirror</span><span class="macro_bang">!</span> <span class="brace">{</span> |
| 47 | 48 | <span class="brace macro proc_macro">{</span> |
src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs+7-1| ... | ... | @@ -55,8 +55,9 @@ fn macros() { |
| 55 | 55 | r#" |
| 56 | 56 | //- proc_macros: mirror, identity, derive_identity |
| 57 | 57 | //- minicore: fmt, include, concat |
| 58 | //- /lib.rs crate:lib | |
| 58 | //- /lib.rs crate:lib deps:pm | |
| 59 | 59 | use proc_macros::{mirror, identity, DeriveIdentity}; |
| 60 | use pm::proc_macro; | |
| 60 | 61 | |
| 61 | 62 | mirror! { |
| 62 | 63 | { |
| ... | ... | @@ -126,6 +127,11 @@ fn main() { |
| 126 | 127 | //- /foo/foo.rs crate:foo |
| 127 | 128 | mod foo {} |
| 128 | 129 | use self::foo as bar; |
| 130 | //- /pm.rs crate:pm | |
| 131 | #![crate_type = "proc-macro"] | |
| 132 | ||
| 133 | #[proc_macro_attribute] | |
| 134 | pub fn proc_macro() {} | |
| 129 | 135 | "#, |
| 130 | 136 | expect_file!["./test_data/highlight_macros.html"], |
| 131 | 137 | false, |
src/tools/rust-analyzer/crates/load-cargo/src/lib.rs+2-5| ... | ... | @@ -26,10 +26,7 @@ use ide_db::{ |
| 26 | 26 | use itertools::Itertools; |
| 27 | 27 | use proc_macro_api::{ |
| 28 | 28 | MacroDylib, ProcMacroClient, |
| 29 | bidirectional_protocol::{ | |
| 30 | msg::{SubRequest, SubResponse}, | |
| 31 | reject_subrequests, | |
| 32 | }, | |
| 29 | bidirectional_protocol::msg::{SubRequest, SubResponse}, | |
| 33 | 30 | }; |
| 34 | 31 | use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace}; |
| 35 | 32 | use span::{Span, SpanAnchor, SyntaxContext}; |
| ... | ... | @@ -446,7 +443,7 @@ pub fn load_proc_macro( |
| 446 | 443 | ) -> ProcMacroLoadResult { |
| 447 | 444 | let res: Result<Vec<_>, _> = (|| { |
| 448 | 445 | let dylib = MacroDylib::new(path.to_path_buf()); |
| 449 | let vec = server.load_dylib(dylib, Some(&reject_subrequests)).map_err(|e| { | |
| 446 | let vec = server.load_dylib(dylib).map_err(|e| { | |
| 450 | 447 | ProcMacroLoadingError::ProcMacroSrvError(format!("{e}").into_boxed_str()) |
| 451 | 448 | })?; |
| 452 | 449 | if vec.is_empty() { |
src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml+1| ... | ... | @@ -31,6 +31,7 @@ span = { path = "../span", version = "0.0.0", default-features = false} |
| 31 | 31 | intern.workspace = true |
| 32 | 32 | postcard.workspace = true |
| 33 | 33 | semver.workspace = true |
| 34 | rayon.workspace = true | |
| 34 | 35 | |
| 35 | 36 | [features] |
| 36 | 37 | sysroot-abi = ["proc-macro-srv", "proc-macro-srv/sysroot-abi"] |
src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs+2-6| ... | ... | @@ -198,12 +198,8 @@ impl ProcMacroClient { |
| 198 | 198 | } |
| 199 | 199 | |
| 200 | 200 | /// Loads a proc-macro dylib into the server process returning a list of `ProcMacro`s loaded. |
| 201 | pub fn load_dylib( | |
| 202 | &self, | |
| 203 | dylib: MacroDylib, | |
| 204 | callback: Option<SubCallback<'_>>, | |
| 205 | ) -> Result<Vec<ProcMacro>, ServerError> { | |
| 206 | self.pool.load_dylib(&dylib, callback) | |
| 201 | pub fn load_dylib(&self, dylib: MacroDylib) -> Result<Vec<ProcMacro>, ServerError> { | |
| 202 | self.pool.load_dylib(&dylib) | |
| 207 | 203 | } |
| 208 | 204 | |
| 209 | 205 | /// Checks if the proc-macro server has exited. |
src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs+13-15| ... | ... | @@ -1,10 +1,9 @@ |
| 1 | 1 | //! A pool of proc-macro server processes |
| 2 | 2 | use std::sync::Arc; |
| 3 | 3 | |
| 4 | use crate::{ | |
| 5 | MacroDylib, ProcMacro, ServerError, bidirectional_protocol::SubCallback, | |
| 6 | process::ProcMacroServerProcess, | |
| 7 | }; | |
| 4 | use rayon::iter::{IntoParallelIterator, ParallelIterator}; | |
| 5 | ||
| 6 | use crate::{MacroDylib, ProcMacro, ServerError, process::ProcMacroServerProcess}; | |
| 8 | 7 | |
| 9 | 8 | #[derive(Debug, Clone)] |
| 10 | 9 | pub(crate) struct ProcMacroServerPool { |
| ... | ... | @@ -50,11 +49,7 @@ impl ProcMacroServerPool { |
| 50 | 49 | }) |
| 51 | 50 | } |
| 52 | 51 | |
| 53 | pub(crate) fn load_dylib( | |
| 54 | &self, | |
| 55 | dylib: &MacroDylib, | |
| 56 | callback: Option<SubCallback<'_>>, | |
| 57 | ) -> Result<Vec<ProcMacro>, ServerError> { | |
| 52 | pub(crate) fn load_dylib(&self, dylib: &MacroDylib) -> Result<Vec<ProcMacro>, ServerError> { | |
| 58 | 53 | let _span = tracing::info_span!("ProcMacroServer::load_dylib").entered(); |
| 59 | 54 | |
| 60 | 55 | let dylib_path = Arc::new(dylib.path.clone()); |
| ... | ... | @@ -64,14 +59,17 @@ impl ProcMacroServerPool { |
| 64 | 59 | let (first, rest) = self.workers.split_first().expect("worker pool must not be empty"); |
| 65 | 60 | |
| 66 | 61 | let macros = first |
| 67 | .find_proc_macros(&dylib.path, callback)? | |
| 62 | .find_proc_macros(&dylib.path)? | |
| 68 | 63 | .map_err(|e| ServerError { message: e, io: None })?; |
| 69 | 64 | |
| 70 | for worker in rest { | |
| 71 | worker | |
| 72 | .find_proc_macros(&dylib.path, callback)? | |
| 73 | .map_err(|e| ServerError { message: e, io: None })?; | |
| 74 | } | |
| 65 | rest.into_par_iter() | |
| 66 | .map(|worker| { | |
| 67 | worker | |
| 68 | .find_proc_macros(&dylib.path)? | |
| 69 | .map(|_| ()) | |
| 70 | .map_err(|e| ServerError { message: e, io: None }) | |
| 71 | }) | |
| 72 | .collect::<Result<(), _>>()?; | |
| 75 | 73 | |
| 76 | 74 | Ok(macros |
| 77 | 75 | .into_iter() |
src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs+12-4| ... | ... | @@ -18,7 +18,11 @@ use stdx::JodChild; |
| 18 | 18 | |
| 19 | 19 | use crate::{ |
| 20 | 20 | ProcMacro, ProcMacroKind, ProtocolFormat, ServerError, |
| 21 | bidirectional_protocol::{self, SubCallback, msg::BidirectionalMessage, reject_subrequests}, | |
| 21 | bidirectional_protocol::{ | |
| 22 | self, SubCallback, | |
| 23 | msg::{BidirectionalMessage, SubResponse}, | |
| 24 | reject_subrequests, | |
| 25 | }, | |
| 22 | 26 | legacy_protocol::{self, SpanMode}, |
| 23 | 27 | version, |
| 24 | 28 | }; |
| ... | ... | @@ -207,14 +211,18 @@ impl ProcMacroServerProcess { |
| 207 | 211 | pub(crate) fn find_proc_macros( |
| 208 | 212 | &self, |
| 209 | 213 | dylib_path: &AbsPath, |
| 210 | callback: Option<SubCallback<'_>>, | |
| 211 | 214 | ) -> Result<Result<Vec<(String, ProcMacroKind)>, String>, ServerError> { |
| 212 | 215 | match self.protocol { |
| 213 | 216 | Protocol::LegacyJson { .. } => legacy_protocol::find_proc_macros(self, dylib_path), |
| 214 | 217 | |
| 215 | 218 | Protocol::BidirectionalPostcardPrototype { .. } => { |
| 216 | let cb = callback.expect("callback required for bidirectional protocol"); | |
| 217 | bidirectional_protocol::find_proc_macros(self, dylib_path, cb) | |
| 219 | bidirectional_protocol::find_proc_macros(self, dylib_path, &|_| { | |
| 220 | Ok(SubResponse::Cancel { | |
| 221 | reason: String::from( | |
| 222 | "Server should not do a sub request when loading proc-macros", | |
| 223 | ), | |
| 224 | }) | |
| 225 | }) | |
| 218 | 226 | } |
| 219 | 227 | } |
| 220 | 228 | } |
src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs+30-58| ... | ... | @@ -741,70 +741,42 @@ impl FlycheckActor { |
| 741 | 741 | flycheck_id = self.id, |
| 742 | 742 | message = diagnostic.message, |
| 743 | 743 | package_id = package_id.as_ref().map(|it| it.as_str()), |
| 744 | scope = ?self.scope, | |
| 745 | 744 | "diagnostic received" |
| 746 | 745 | ); |
| 747 | ||
| 748 | match &self.scope { | |
| 749 | FlycheckScope::Workspace => { | |
| 750 | if self.diagnostics_received == DiagnosticsReceived::NotYet { | |
| 751 | self.send(FlycheckMessage::ClearDiagnostics { | |
| 752 | id: self.id, | |
| 753 | kind: ClearDiagnosticsKind::All(ClearScope::Workspace), | |
| 754 | }); | |
| 755 | ||
| 756 | self.diagnostics_received = | |
| 757 | DiagnosticsReceived::AtLeastOneAndClearedWorkspace; | |
| 758 | } | |
| 759 | ||
| 760 | if let Some(package_id) = package_id { | |
| 761 | tracing::warn!( | |
| 762 | "Ignoring package label {:?} and applying diagnostics to the whole workspace", | |
| 763 | package_id | |
| 764 | ); | |
| 765 | } | |
| 766 | ||
| 767 | self.send(FlycheckMessage::AddDiagnostic { | |
| 768 | id: self.id, | |
| 769 | generation: self.generation, | |
| 770 | package_id: None, | |
| 771 | workspace_root: self.root.clone(), | |
| 772 | diagnostic, | |
| 773 | }); | |
| 774 | } | |
| 775 | FlycheckScope::Package { package: flycheck_package, .. } => { | |
| 776 | if self.diagnostics_received == DiagnosticsReceived::NotYet { | |
| 777 | self.diagnostics_received = DiagnosticsReceived::AtLeastOne; | |
| 778 | } | |
| 779 | ||
| 780 | // If the package has been set in the diagnostic JSON, respect that. Otherwise, use the | |
| 781 | // package that the current flycheck is scoped to. This is useful when a project is | |
| 782 | // directly using rustc for its checks (e.g. custom check commands in rust-project.json). | |
| 783 | let package_id = package_id.unwrap_or(flycheck_package.clone()); | |
| 784 | ||
| 785 | if self.diagnostics_cleared_for.insert(package_id.clone()) { | |
| 786 | tracing::trace!( | |
| 787 | flycheck_id = self.id, | |
| 788 | package_id = package_id.as_str(), | |
| 789 | "clearing diagnostics" | |
| 790 | ); | |
| 791 | self.send(FlycheckMessage::ClearDiagnostics { | |
| 792 | id: self.id, | |
| 793 | kind: ClearDiagnosticsKind::All(ClearScope::Package( | |
| 794 | package_id.clone(), | |
| 795 | )), | |
| 796 | }); | |
| 797 | } | |
| 798 | ||
| 799 | self.send(FlycheckMessage::AddDiagnostic { | |
| 746 | if self.diagnostics_received == DiagnosticsReceived::NotYet { | |
| 747 | self.diagnostics_received = DiagnosticsReceived::AtLeastOne; | |
| 748 | } | |
| 749 | if let Some(package_id) = &package_id { | |
| 750 | if self.diagnostics_cleared_for.insert(package_id.clone()) { | |
| 751 | tracing::trace!( | |
| 752 | flycheck_id = self.id, | |
| 753 | package_id = package_id.as_str(), | |
| 754 | "clearing diagnostics" | |
| 755 | ); | |
| 756 | self.send(FlycheckMessage::ClearDiagnostics { | |
| 800 | 757 | id: self.id, |
| 801 | generation: self.generation, | |
| 802 | package_id: Some(package_id), | |
| 803 | workspace_root: self.root.clone(), | |
| 804 | diagnostic, | |
| 758 | kind: ClearDiagnosticsKind::All(ClearScope::Package( | |
| 759 | package_id.clone(), | |
| 760 | )), | |
| 805 | 761 | }); |
| 806 | 762 | } |
| 763 | } else if self.diagnostics_received | |
| 764 | != DiagnosticsReceived::AtLeastOneAndClearedWorkspace | |
| 765 | { | |
| 766 | self.diagnostics_received = | |
| 767 | DiagnosticsReceived::AtLeastOneAndClearedWorkspace; | |
| 768 | self.send(FlycheckMessage::ClearDiagnostics { | |
| 769 | id: self.id, | |
| 770 | kind: ClearDiagnosticsKind::All(ClearScope::Workspace), | |
| 771 | }); | |
| 807 | 772 | } |
| 773 | self.send(FlycheckMessage::AddDiagnostic { | |
| 774 | id: self.id, | |
| 775 | generation: self.generation, | |
| 776 | package_id, | |
| 777 | workspace_root: self.root.clone(), | |
| 778 | diagnostic, | |
| 779 | }); | |
| 808 | 780 | } |
| 809 | 781 | }, |
| 810 | 782 | } |
src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs+1-7| ... | ... | @@ -14,7 +14,7 @@ use hir::ChangeWithProcMacros; |
| 14 | 14 | use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId}; |
| 15 | 15 | use ide_db::{ |
| 16 | 16 | MiniCore, |
| 17 | base_db::{Crate, ProcMacroPaths, SourceDatabase, salsa::CancellationToken, salsa::Revision}, | |
| 17 | base_db::{Crate, ProcMacroPaths, SourceDatabase, salsa::Revision}, | |
| 18 | 18 | }; |
| 19 | 19 | use itertools::Itertools; |
| 20 | 20 | use load_cargo::SourceRootConfig; |
| ... | ... | @@ -88,7 +88,6 @@ pub(crate) struct GlobalState { |
| 88 | 88 | pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, |
| 89 | 89 | pub(crate) fmt_pool: Handle<TaskPool<Task>, Receiver<Task>>, |
| 90 | 90 | pub(crate) cancellation_pool: thread::Pool, |
| 91 | pub(crate) cancellation_tokens: FxHashMap<lsp_server::RequestId, CancellationToken>, | |
| 92 | 91 | |
| 93 | 92 | pub(crate) config: Arc<Config>, |
| 94 | 93 | pub(crate) config_errors: Option<ConfigErrors>, |
| ... | ... | @@ -266,7 +265,6 @@ impl GlobalState { |
| 266 | 265 | task_pool, |
| 267 | 266 | fmt_pool, |
| 268 | 267 | cancellation_pool, |
| 269 | cancellation_tokens: Default::default(), | |
| 270 | 268 | loader, |
| 271 | 269 | config: Arc::new(config.clone()), |
| 272 | 270 | analysis_host, |
| ... | ... | @@ -619,7 +617,6 @@ impl GlobalState { |
| 619 | 617 | } |
| 620 | 618 | |
| 621 | 619 | pub(crate) fn respond(&mut self, response: lsp_server::Response) { |
| 622 | self.cancellation_tokens.remove(&response.id); | |
| 623 | 620 | if let Some((method, start)) = self.req_queue.incoming.complete(&response.id) { |
| 624 | 621 | if let Some(err) = &response.error |
| 625 | 622 | && err.message.starts_with("server panicked") |
| ... | ... | @@ -634,9 +631,6 @@ impl GlobalState { |
| 634 | 631 | } |
| 635 | 632 | |
| 636 | 633 | pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) { |
| 637 | if let Some(token) = self.cancellation_tokens.remove(&request_id) { | |
| 638 | token.cancel(); | |
| 639 | } | |
| 640 | 634 | if let Some(response) = self.req_queue.incoming.cancel(request_id) { |
| 641 | 635 | self.send(response.into()); |
| 642 | 636 | } |
src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs+1-16| ... | ... | @@ -253,9 +253,6 @@ impl RequestDispatcher<'_> { |
| 253 | 253 | tracing::debug!(?params); |
| 254 | 254 | |
| 255 | 255 | let world = self.global_state.snapshot(); |
| 256 | self.global_state | |
| 257 | .cancellation_tokens | |
| 258 | .insert(req.id.clone(), world.analysis.cancellation_token()); | |
| 259 | 256 | if RUSTFMT { |
| 260 | 257 | &mut self.global_state.fmt_pool.handle |
| 261 | 258 | } else { |
| ... | ... | @@ -268,19 +265,7 @@ impl RequestDispatcher<'_> { |
| 268 | 265 | }); |
| 269 | 266 | match thread_result_to_response::<R>(req.id.clone(), result) { |
| 270 | 267 | Ok(response) => Task::Response(response), |
| 271 | Err(HandlerCancelledError::Inner( | |
| 272 | Cancelled::PendingWrite | Cancelled::PropagatedPanic, | |
| 273 | )) if ALLOW_RETRYING => Task::Retry(req), | |
| 274 | // Note: Technically the return value here does not matter as we have already responded to the client with this error. | |
| 275 | Err(HandlerCancelledError::Inner(Cancelled::Local)) => Task::Response(Response { | |
| 276 | id: req.id, | |
| 277 | result: None, | |
| 278 | error: Some(ResponseError { | |
| 279 | code: lsp_server::ErrorCode::RequestCanceled as i32, | |
| 280 | message: "canceled by client".to_owned(), | |
| 281 | data: None, | |
| 282 | }), | |
| 283 | }), | |
| 268 | Err(_cancelled) if ALLOW_RETRYING => Task::Retry(req), | |
| 284 | 269 | Err(_cancelled) => { |
| 285 | 270 | let error = on_cancelled(); |
| 286 | 271 | Task::Response(Response { id: req.id, result: None, error: Some(error) }) |
src/tools/rust-analyzer/crates/span/src/ast_id.rs-2| ... | ... | @@ -88,7 +88,6 @@ impl fmt::Debug for ErasedFileAstId { |
| 88 | 88 | Module, |
| 89 | 89 | Static, |
| 90 | 90 | Trait, |
| 91 | TraitAlias, | |
| 92 | 91 | Variant, |
| 93 | 92 | Const, |
| 94 | 93 | Fn, |
| ... | ... | @@ -129,7 +128,6 @@ enum ErasedFileAstIdKind { |
| 129 | 128 | Module, |
| 130 | 129 | Static, |
| 131 | 130 | Trait, |
| 132 | TraitAlias, | |
| 133 | 131 | // Until here associated with `ErasedHasNameFileAstId`. |
| 134 | 132 | // The following are associated with `ErasedAssocItemFileAstId`. |
| 135 | 133 | Variant, |
src/tools/rust-analyzer/crates/span/src/hygiene.rs+26-25| ... | ... | @@ -81,24 +81,25 @@ const _: () = { |
| 81 | 81 | #[derive(Hash)] |
| 82 | 82 | struct StructKey<'db, T0, T1, T2, T3>(T0, T1, T2, T3, std::marker::PhantomData<&'db ()>); |
| 83 | 83 | |
| 84 | impl<'db, T0, T1, T2, T3> zalsa_::HashEqLike<StructKey<'db, T0, T1, T2, T3>> for SyntaxContextData | |
| 84 | impl<'db, T0, T1, T2, T3> zalsa_::interned::HashEqLike<StructKey<'db, T0, T1, T2, T3>> | |
| 85 | for SyntaxContextData | |
| 85 | 86 | where |
| 86 | Option<MacroCallId>: zalsa_::HashEqLike<T0>, | |
| 87 | Transparency: zalsa_::HashEqLike<T1>, | |
| 88 | Edition: zalsa_::HashEqLike<T2>, | |
| 89 | SyntaxContext: zalsa_::HashEqLike<T3>, | |
| 87 | Option<MacroCallId>: zalsa_::interned::HashEqLike<T0>, | |
| 88 | Transparency: zalsa_::interned::HashEqLike<T1>, | |
| 89 | Edition: zalsa_::interned::HashEqLike<T2>, | |
| 90 | SyntaxContext: zalsa_::interned::HashEqLike<T3>, | |
| 90 | 91 | { |
| 91 | 92 | fn hash<H: std::hash::Hasher>(&self, h: &mut H) { |
| 92 | zalsa_::HashEqLike::<T0>::hash(&self.outer_expn, &mut *h); | |
| 93 | zalsa_::HashEqLike::<T1>::hash(&self.outer_transparency, &mut *h); | |
| 94 | zalsa_::HashEqLike::<T2>::hash(&self.edition, &mut *h); | |
| 95 | zalsa_::HashEqLike::<T3>::hash(&self.parent, &mut *h); | |
| 93 | zalsa_::interned::HashEqLike::<T0>::hash(&self.outer_expn, &mut *h); | |
| 94 | zalsa_::interned::HashEqLike::<T1>::hash(&self.outer_transparency, &mut *h); | |
| 95 | zalsa_::interned::HashEqLike::<T2>::hash(&self.edition, &mut *h); | |
| 96 | zalsa_::interned::HashEqLike::<T3>::hash(&self.parent, &mut *h); | |
| 96 | 97 | } |
| 97 | 98 | fn eq(&self, data: &StructKey<'db, T0, T1, T2, T3>) -> bool { |
| 98 | zalsa_::HashEqLike::<T0>::eq(&self.outer_expn, &data.0) | |
| 99 | && zalsa_::HashEqLike::<T1>::eq(&self.outer_transparency, &data.1) | |
| 100 | && zalsa_::HashEqLike::<T2>::eq(&self.edition, &data.2) | |
| 101 | && zalsa_::HashEqLike::<T3>::eq(&self.parent, &data.3) | |
| 99 | zalsa_::interned::HashEqLike::<T0>::eq(&self.outer_expn, &data.0) | |
| 100 | && zalsa_::interned::HashEqLike::<T1>::eq(&self.outer_transparency, &data.1) | |
| 101 | && zalsa_::interned::HashEqLike::<T2>::eq(&self.edition, &data.2) | |
| 102 | && zalsa_::interned::HashEqLike::<T3>::eq(&self.parent, &data.3) | |
| 102 | 103 | } |
| 103 | 104 | } |
| 104 | 105 | impl zalsa_struct_::Configuration for SyntaxContext { |
| ... | ... | @@ -202,10 +203,10 @@ const _: () = { |
| 202 | 203 | impl<'db> SyntaxContext { |
| 203 | 204 | pub fn new< |
| 204 | 205 | Db, |
| 205 | T0: zalsa_::Lookup<Option<MacroCallId>> + std::hash::Hash, | |
| 206 | T1: zalsa_::Lookup<Transparency> + std::hash::Hash, | |
| 207 | T2: zalsa_::Lookup<Edition> + std::hash::Hash, | |
| 208 | T3: zalsa_::Lookup<SyntaxContext> + std::hash::Hash, | |
| 206 | T0: zalsa_::interned::Lookup<Option<MacroCallId>> + std::hash::Hash, | |
| 207 | T1: zalsa_::interned::Lookup<Transparency> + std::hash::Hash, | |
| 208 | T2: zalsa_::interned::Lookup<Edition> + std::hash::Hash, | |
| 209 | T3: zalsa_::interned::Lookup<SyntaxContext> + std::hash::Hash, | |
| 209 | 210 | >( |
| 210 | 211 | db: &'db Db, |
| 211 | 212 | outer_expn: T0, |
| ... | ... | @@ -217,10 +218,10 @@ const _: () = { |
| 217 | 218 | ) -> Self |
| 218 | 219 | where |
| 219 | 220 | Db: ?Sized + salsa::Database, |
| 220 | Option<MacroCallId>: zalsa_::HashEqLike<T0>, | |
| 221 | Transparency: zalsa_::HashEqLike<T1>, | |
| 222 | Edition: zalsa_::HashEqLike<T2>, | |
| 223 | SyntaxContext: zalsa_::HashEqLike<T3>, | |
| 221 | Option<MacroCallId>: zalsa_::interned::HashEqLike<T0>, | |
| 222 | Transparency: zalsa_::interned::HashEqLike<T1>, | |
| 223 | Edition: zalsa_::interned::HashEqLike<T2>, | |
| 224 | SyntaxContext: zalsa_::interned::HashEqLike<T3>, | |
| 224 | 225 | { |
| 225 | 226 | let (zalsa, zalsa_local) = db.zalsas(); |
| 226 | 227 | |
| ... | ... | @@ -235,10 +236,10 @@ const _: () = { |
| 235 | 236 | std::marker::PhantomData, |
| 236 | 237 | ), |
| 237 | 238 | |id, data| SyntaxContextData { |
| 238 | outer_expn: zalsa_::Lookup::into_owned(data.0), | |
| 239 | outer_transparency: zalsa_::Lookup::into_owned(data.1), | |
| 240 | edition: zalsa_::Lookup::into_owned(data.2), | |
| 241 | parent: zalsa_::Lookup::into_owned(data.3), | |
| 239 | outer_expn: zalsa_::interned::Lookup::into_owned(data.0), | |
| 240 | outer_transparency: zalsa_::interned::Lookup::into_owned(data.1), | |
| 241 | edition: zalsa_::interned::Lookup::into_owned(data.2), | |
| 242 | parent: zalsa_::interned::Lookup::into_owned(data.3), | |
| 242 | 243 | opaque: opaque(zalsa_::FromId::from_id(id)), |
| 243 | 244 | opaque_and_semiopaque: opaque_and_semiopaque(zalsa_::FromId::from_id(id)), |
| 244 | 245 | }, |
src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs+112-61| ... | ... | @@ -9,8 +9,9 @@ use crate::{ |
| 9 | 9 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, |
| 10 | 10 | SyntaxNode, SyntaxToken, |
| 11 | 11 | algo::{self, neighbor}, |
| 12 | ast::{self, HasGenericParams, edit::IndentLevel, make}, | |
| 13 | ted::{self, Position}, | |
| 12 | ast::{self, HasGenericParams, edit::IndentLevel, make, syntax_factory::SyntaxFactory}, | |
| 13 | syntax_editor::{Position, SyntaxEditor}, | |
| 14 | ted, | |
| 14 | 15 | }; |
| 15 | 16 | |
| 16 | 17 | use super::{GenericParam, HasName}; |
| ... | ... | @@ -26,13 +27,13 @@ impl GenericParamsOwnerEdit for ast::Fn { |
| 26 | 27 | Some(it) => it, |
| 27 | 28 | None => { |
| 28 | 29 | let position = if let Some(name) = self.name() { |
| 29 | Position::after(name.syntax) | |
| 30 | ted::Position::after(name.syntax) | |
| 30 | 31 | } else if let Some(fn_token) = self.fn_token() { |
| 31 | Position::after(fn_token) | |
| 32 | ted::Position::after(fn_token) | |
| 32 | 33 | } else if let Some(param_list) = self.param_list() { |
| 33 | Position::before(param_list.syntax) | |
| 34 | ted::Position::before(param_list.syntax) | |
| 34 | 35 | } else { |
| 35 | Position::last_child_of(self.syntax()) | |
| 36 | ted::Position::last_child_of(self.syntax()) | |
| 36 | 37 | }; |
| 37 | 38 | create_generic_param_list(position) |
| 38 | 39 | } |
| ... | ... | @@ -42,11 +43,11 @@ impl GenericParamsOwnerEdit for ast::Fn { |
| 42 | 43 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
| 43 | 44 | if self.where_clause().is_none() { |
| 44 | 45 | let position = if let Some(ty) = self.ret_type() { |
| 45 | Position::after(ty.syntax()) | |
| 46 | ted::Position::after(ty.syntax()) | |
| 46 | 47 | } else if let Some(param_list) = self.param_list() { |
| 47 | Position::after(param_list.syntax()) | |
| 48 | ted::Position::after(param_list.syntax()) | |
| 48 | 49 | } else { |
| 49 | Position::last_child_of(self.syntax()) | |
| 50 | ted::Position::last_child_of(self.syntax()) | |
| 50 | 51 | }; |
| 51 | 52 | create_where_clause(position); |
| 52 | 53 | } |
| ... | ... | @@ -60,8 +61,8 @@ impl GenericParamsOwnerEdit for ast::Impl { |
| 60 | 61 | Some(it) => it, |
| 61 | 62 | None => { |
| 62 | 63 | let position = match self.impl_token() { |
| 63 | Some(imp_token) => Position::after(imp_token), | |
| 64 | None => Position::last_child_of(self.syntax()), | |
| 64 | Some(imp_token) => ted::Position::after(imp_token), | |
| 65 | None => ted::Position::last_child_of(self.syntax()), | |
| 65 | 66 | }; |
| 66 | 67 | create_generic_param_list(position) |
| 67 | 68 | } |
| ... | ... | @@ -71,8 +72,8 @@ impl GenericParamsOwnerEdit for ast::Impl { |
| 71 | 72 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
| 72 | 73 | if self.where_clause().is_none() { |
| 73 | 74 | let position = match self.assoc_item_list() { |
| 74 | Some(items) => Position::before(items.syntax()), | |
| 75 | None => Position::last_child_of(self.syntax()), | |
| 75 | Some(items) => ted::Position::before(items.syntax()), | |
| 76 | None => ted::Position::last_child_of(self.syntax()), | |
| 76 | 77 | }; |
| 77 | 78 | create_where_clause(position); |
| 78 | 79 | } |
| ... | ... | @@ -86,11 +87,11 @@ impl GenericParamsOwnerEdit for ast::Trait { |
| 86 | 87 | Some(it) => it, |
| 87 | 88 | None => { |
| 88 | 89 | let position = if let Some(name) = self.name() { |
| 89 | Position::after(name.syntax) | |
| 90 | ted::Position::after(name.syntax) | |
| 90 | 91 | } else if let Some(trait_token) = self.trait_token() { |
| 91 | Position::after(trait_token) | |
| 92 | ted::Position::after(trait_token) | |
| 92 | 93 | } else { |
| 93 | Position::last_child_of(self.syntax()) | |
| 94 | ted::Position::last_child_of(self.syntax()) | |
| 94 | 95 | }; |
| 95 | 96 | create_generic_param_list(position) |
| 96 | 97 | } |
| ... | ... | @@ -100,9 +101,9 @@ impl GenericParamsOwnerEdit for ast::Trait { |
| 100 | 101 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
| 101 | 102 | if self.where_clause().is_none() { |
| 102 | 103 | let position = match (self.assoc_item_list(), self.semicolon_token()) { |
| 103 | (Some(items), _) => Position::before(items.syntax()), | |
| 104 | (_, Some(tok)) => Position::before(tok), | |
| 105 | (None, None) => Position::last_child_of(self.syntax()), | |
| 104 | (Some(items), _) => ted::Position::before(items.syntax()), | |
| 105 | (_, Some(tok)) => ted::Position::before(tok), | |
| 106 | (None, None) => ted::Position::last_child_of(self.syntax()), | |
| 106 | 107 | }; |
| 107 | 108 | create_where_clause(position); |
| 108 | 109 | } |
| ... | ... | @@ -116,11 +117,11 @@ impl GenericParamsOwnerEdit for ast::TypeAlias { |
| 116 | 117 | Some(it) => it, |
| 117 | 118 | None => { |
| 118 | 119 | let position = if let Some(name) = self.name() { |
| 119 | Position::after(name.syntax) | |
| 120 | ted::Position::after(name.syntax) | |
| 120 | 121 | } else if let Some(trait_token) = self.type_token() { |
| 121 | Position::after(trait_token) | |
| 122 | ted::Position::after(trait_token) | |
| 122 | 123 | } else { |
| 123 | Position::last_child_of(self.syntax()) | |
| 124 | ted::Position::last_child_of(self.syntax()) | |
| 124 | 125 | }; |
| 125 | 126 | create_generic_param_list(position) |
| 126 | 127 | } |
| ... | ... | @@ -130,10 +131,10 @@ impl GenericParamsOwnerEdit for ast::TypeAlias { |
| 130 | 131 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
| 131 | 132 | if self.where_clause().is_none() { |
| 132 | 133 | let position = match self.eq_token() { |
| 133 | Some(tok) => Position::before(tok), | |
| 134 | Some(tok) => ted::Position::before(tok), | |
| 134 | 135 | None => match self.semicolon_token() { |
| 135 | Some(tok) => Position::before(tok), | |
| 136 | None => Position::last_child_of(self.syntax()), | |
| 136 | Some(tok) => ted::Position::before(tok), | |
| 137 | None => ted::Position::last_child_of(self.syntax()), | |
| 137 | 138 | }, |
| 138 | 139 | }; |
| 139 | 140 | create_where_clause(position); |
| ... | ... | @@ -148,11 +149,11 @@ impl GenericParamsOwnerEdit for ast::Struct { |
| 148 | 149 | Some(it) => it, |
| 149 | 150 | None => { |
| 150 | 151 | let position = if let Some(name) = self.name() { |
| 151 | Position::after(name.syntax) | |
| 152 | ted::Position::after(name.syntax) | |
| 152 | 153 | } else if let Some(struct_token) = self.struct_token() { |
| 153 | Position::after(struct_token) | |
| 154 | ted::Position::after(struct_token) | |
| 154 | 155 | } else { |
| 155 | Position::last_child_of(self.syntax()) | |
| 156 | ted::Position::last_child_of(self.syntax()) | |
| 156 | 157 | }; |
| 157 | 158 | create_generic_param_list(position) |
| 158 | 159 | } |
| ... | ... | @@ -166,13 +167,13 @@ impl GenericParamsOwnerEdit for ast::Struct { |
| 166 | 167 | ast::FieldList::TupleFieldList(it) => Some(it), |
| 167 | 168 | }); |
| 168 | 169 | let position = if let Some(tfl) = tfl { |
| 169 | Position::after(tfl.syntax()) | |
| 170 | ted::Position::after(tfl.syntax()) | |
| 170 | 171 | } else if let Some(gpl) = self.generic_param_list() { |
| 171 | Position::after(gpl.syntax()) | |
| 172 | ted::Position::after(gpl.syntax()) | |
| 172 | 173 | } else if let Some(name) = self.name() { |
| 173 | Position::after(name.syntax()) | |
| 174 | ted::Position::after(name.syntax()) | |
| 174 | 175 | } else { |
| 175 | Position::last_child_of(self.syntax()) | |
| 176 | ted::Position::last_child_of(self.syntax()) | |
| 176 | 177 | }; |
| 177 | 178 | create_where_clause(position); |
| 178 | 179 | } |
| ... | ... | @@ -186,11 +187,11 @@ impl GenericParamsOwnerEdit for ast::Enum { |
| 186 | 187 | Some(it) => it, |
| 187 | 188 | None => { |
| 188 | 189 | let position = if let Some(name) = self.name() { |
| 189 | Position::after(name.syntax) | |
| 190 | ted::Position::after(name.syntax) | |
| 190 | 191 | } else if let Some(enum_token) = self.enum_token() { |
| 191 | Position::after(enum_token) | |
| 192 | ted::Position::after(enum_token) | |
| 192 | 193 | } else { |
| 193 | Position::last_child_of(self.syntax()) | |
| 194 | ted::Position::last_child_of(self.syntax()) | |
| 194 | 195 | }; |
| 195 | 196 | create_generic_param_list(position) |
| 196 | 197 | } |
| ... | ... | @@ -200,11 +201,11 @@ impl GenericParamsOwnerEdit for ast::Enum { |
| 200 | 201 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
| 201 | 202 | if self.where_clause().is_none() { |
| 202 | 203 | let position = if let Some(gpl) = self.generic_param_list() { |
| 203 | Position::after(gpl.syntax()) | |
| 204 | ted::Position::after(gpl.syntax()) | |
| 204 | 205 | } else if let Some(name) = self.name() { |
| 205 | Position::after(name.syntax()) | |
| 206 | ted::Position::after(name.syntax()) | |
| 206 | 207 | } else { |
| 207 | Position::last_child_of(self.syntax()) | |
| 208 | ted::Position::last_child_of(self.syntax()) | |
| 208 | 209 | }; |
| 209 | 210 | create_where_clause(position); |
| 210 | 211 | } |
| ... | ... | @@ -212,12 +213,12 @@ impl GenericParamsOwnerEdit for ast::Enum { |
| 212 | 213 | } |
| 213 | 214 | } |
| 214 | 215 | |
| 215 | fn create_where_clause(position: Position) { | |
| 216 | fn create_where_clause(position: ted::Position) { | |
| 216 | 217 | let where_clause = make::where_clause(empty()).clone_for_update(); |
| 217 | 218 | ted::insert(position, where_clause.syntax()); |
| 218 | 219 | } |
| 219 | 220 | |
| 220 | fn create_generic_param_list(position: Position) -> ast::GenericParamList { | |
| 221 | fn create_generic_param_list(position: ted::Position) -> ast::GenericParamList { | |
| 221 | 222 | let gpl = make::generic_param_list(empty()).clone_for_update(); |
| 222 | 223 | ted::insert_raw(position, gpl.syntax()); |
| 223 | 224 | gpl |
| ... | ... | @@ -253,7 +254,7 @@ impl ast::GenericParamList { |
| 253 | 254 | pub fn add_generic_param(&self, generic_param: ast::GenericParam) { |
| 254 | 255 | match self.generic_params().last() { |
| 255 | 256 | Some(last_param) => { |
| 256 | let position = Position::after(last_param.syntax()); | |
| 257 | let position = ted::Position::after(last_param.syntax()); | |
| 257 | 258 | let elements = vec![ |
| 258 | 259 | make::token(T![,]).into(), |
| 259 | 260 | make::tokens::single_space().into(), |
| ... | ... | @@ -262,7 +263,7 @@ impl ast::GenericParamList { |
| 262 | 263 | ted::insert_all(position, elements); |
| 263 | 264 | } |
| 264 | 265 | None => { |
| 265 | let after_l_angle = Position::after(self.l_angle_token().unwrap()); | |
| 266 | let after_l_angle = ted::Position::after(self.l_angle_token().unwrap()); | |
| 266 | 267 | ted::insert(after_l_angle, generic_param.syntax()); |
| 267 | 268 | } |
| 268 | 269 | } |
| ... | ... | @@ -412,7 +413,7 @@ impl ast::UseTree { |
| 412 | 413 | match self.use_tree_list() { |
| 413 | 414 | Some(it) => it, |
| 414 | 415 | None => { |
| 415 | let position = Position::last_child_of(self.syntax()); | |
| 416 | let position = ted::Position::last_child_of(self.syntax()); | |
| 416 | 417 | let use_tree_list = make::use_tree_list(empty()).clone_for_update(); |
| 417 | 418 | let mut elements = Vec::with_capacity(2); |
| 418 | 419 | if self.coloncolon_token().is_none() { |
| ... | ... | @@ -458,7 +459,7 @@ impl ast::UseTree { |
| 458 | 459 | // Next, transform 'suffix' use tree into 'prefix::{suffix}' |
| 459 | 460 | let subtree = self.clone_subtree().clone_for_update(); |
| 460 | 461 | ted::remove_all_iter(self.syntax().children_with_tokens()); |
| 461 | ted::insert(Position::first_child_of(self.syntax()), prefix.syntax()); | |
| 462 | ted::insert(ted::Position::first_child_of(self.syntax()), prefix.syntax()); | |
| 462 | 463 | self.get_or_create_use_tree_list().add_use_tree(subtree); |
| 463 | 464 | |
| 464 | 465 | fn split_path_prefix(prefix: &ast::Path) -> Option<()> { |
| ... | ... | @@ -507,7 +508,7 @@ impl ast::UseTreeList { |
| 507 | 508 | pub fn add_use_tree(&self, use_tree: ast::UseTree) { |
| 508 | 509 | let (position, elements) = match self.use_trees().last() { |
| 509 | 510 | Some(last_tree) => ( |
| 510 | Position::after(last_tree.syntax()), | |
| 511 | ted::Position::after(last_tree.syntax()), | |
| 511 | 512 | vec![ |
| 512 | 513 | make::token(T![,]).into(), |
| 513 | 514 | make::tokens::single_space().into(), |
| ... | ... | @@ -516,8 +517,8 @@ impl ast::UseTreeList { |
| 516 | 517 | ), |
| 517 | 518 | None => { |
| 518 | 519 | let position = match self.l_curly_token() { |
| 519 | Some(l_curly) => Position::after(l_curly), | |
| 520 | None => Position::last_child_of(self.syntax()), | |
| 520 | Some(l_curly) => ted::Position::after(l_curly), | |
| 521 | None => ted::Position::last_child_of(self.syntax()), | |
| 521 | 522 | }; |
| 522 | 523 | (position, vec![use_tree.syntax.into()]) |
| 523 | 524 | } |
| ... | ... | @@ -582,15 +583,15 @@ impl ast::AssocItemList { |
| 582 | 583 | let (indent, position, whitespace) = match self.assoc_items().last() { |
| 583 | 584 | Some(last_item) => ( |
| 584 | 585 | IndentLevel::from_node(last_item.syntax()), |
| 585 | Position::after(last_item.syntax()), | |
| 586 | ted::Position::after(last_item.syntax()), | |
| 586 | 587 | "\n\n", |
| 587 | 588 | ), |
| 588 | 589 | None => match self.l_curly_token() { |
| 589 | 590 | Some(l_curly) => { |
| 590 | 591 | normalize_ws_between_braces(self.syntax()); |
| 591 | (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n") | |
| 592 | (IndentLevel::from_token(&l_curly) + 1, ted::Position::after(&l_curly), "\n") | |
| 592 | 593 | } |
| 593 | None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), | |
| 594 | None => (IndentLevel::single(), ted::Position::last_child_of(self.syntax()), "\n"), | |
| 594 | 595 | }, |
| 595 | 596 | }; |
| 596 | 597 | let elements: Vec<SyntaxElement> = vec![ |
| ... | ... | @@ -618,17 +619,17 @@ impl ast::RecordExprFieldList { |
| 618 | 619 | let position = match self.fields().last() { |
| 619 | 620 | Some(last_field) => { |
| 620 | 621 | let comma = get_or_insert_comma_after(last_field.syntax()); |
| 621 | Position::after(comma) | |
| 622 | ted::Position::after(comma) | |
| 622 | 623 | } |
| 623 | 624 | None => match self.l_curly_token() { |
| 624 | Some(it) => Position::after(it), | |
| 625 | None => Position::last_child_of(self.syntax()), | |
| 625 | Some(it) => ted::Position::after(it), | |
| 626 | None => ted::Position::last_child_of(self.syntax()), | |
| 626 | 627 | }, |
| 627 | 628 | }; |
| 628 | 629 | |
| 629 | 630 | ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]); |
| 630 | 631 | if is_multiline { |
| 631 | ted::insert(Position::after(field.syntax()), ast::make::token(T![,])); | |
| 632 | ted::insert(ted::Position::after(field.syntax()), ast::make::token(T![,])); | |
| 632 | 633 | } |
| 633 | 634 | } |
| 634 | 635 | } |
| ... | ... | @@ -656,7 +657,7 @@ impl ast::RecordExprField { |
| 656 | 657 | ast::make::tokens::single_space().into(), |
| 657 | 658 | expr.syntax().clone().into(), |
| 658 | 659 | ]; |
| 659 | ted::insert_all_raw(Position::last_child_of(self.syntax()), children); | |
| 660 | ted::insert_all_raw(ted::Position::last_child_of(self.syntax()), children); | |
| 660 | 661 | } |
| 661 | 662 | } |
| 662 | 663 | } |
| ... | ... | @@ -679,17 +680,17 @@ impl ast::RecordPatFieldList { |
| 679 | 680 | Some(last_field) => { |
| 680 | 681 | let syntax = last_field.syntax(); |
| 681 | 682 | let comma = get_or_insert_comma_after(syntax); |
| 682 | Position::after(comma) | |
| 683 | ted::Position::after(comma) | |
| 683 | 684 | } |
| 684 | 685 | None => match self.l_curly_token() { |
| 685 | Some(it) => Position::after(it), | |
| 686 | None => Position::last_child_of(self.syntax()), | |
| 686 | Some(it) => ted::Position::after(it), | |
| 687 | None => ted::Position::last_child_of(self.syntax()), | |
| 687 | 688 | }, |
| 688 | 689 | }; |
| 689 | 690 | |
| 690 | 691 | ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]); |
| 691 | 692 | if is_multiline { |
| 692 | ted::insert(Position::after(field.syntax()), ast::make::token(T![,])); | |
| 693 | ted::insert(ted::Position::after(field.syntax()), ast::make::token(T![,])); | |
| 693 | 694 | } |
| 694 | 695 | } |
| 695 | 696 | } |
| ... | ... | @@ -703,7 +704,7 @@ fn get_or_insert_comma_after(syntax: &SyntaxNode) -> SyntaxToken { |
| 703 | 704 | Some(it) => it, |
| 704 | 705 | None => { |
| 705 | 706 | let comma = ast::make::token(T![,]); |
| 706 | ted::insert(Position::after(syntax), &comma); | |
| 707 | ted::insert(ted::Position::after(syntax), &comma); | |
| 707 | 708 | comma |
| 708 | 709 | } |
| 709 | 710 | } |
| ... | ... | @@ -728,7 +729,7 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { |
| 728 | 729 | } |
| 729 | 730 | } |
| 730 | 731 | Some(ws) if ws.kind() == T!['}'] => { |
| 731 | ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{indent}"))); | |
| 732 | ted::insert(ted::Position::after(l), make::tokens::whitespace(&format!("\n{indent}"))); | |
| 732 | 733 | } |
| 733 | 734 | _ => (), |
| 734 | 735 | } |
| ... | ... | @@ -780,6 +781,56 @@ impl ast::IdentPat { |
| 780 | 781 | } |
| 781 | 782 | } |
| 782 | 783 | } |
| 784 | ||
| 785 | pub fn set_pat_with_editor( | |
| 786 | &self, | |
| 787 | pat: Option<ast::Pat>, | |
| 788 | syntax_editor: &mut SyntaxEditor, | |
| 789 | syntax_factory: &SyntaxFactory, | |
| 790 | ) { | |
| 791 | match pat { | |
| 792 | None => { | |
| 793 | if let Some(at_token) = self.at_token() { | |
| 794 | // Remove `@ Pat` | |
| 795 | let start = at_token.clone().into(); | |
| 796 | let end = self | |
| 797 | .pat() | |
| 798 | .map(|it| it.syntax().clone().into()) | |
| 799 | .unwrap_or_else(|| at_token.into()); | |
| 800 | syntax_editor.delete_all(start..=end); | |
| 801 | ||
| 802 | // Remove any trailing ws | |
| 803 | if let Some(last) = | |
| 804 | self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) | |
| 805 | { | |
| 806 | last.detach(); | |
| 807 | } | |
| 808 | } | |
| 809 | } | |
| 810 | Some(pat) => { | |
| 811 | if let Some(old_pat) = self.pat() { | |
| 812 | // Replace existing pattern | |
| 813 | syntax_editor.replace(old_pat.syntax(), pat.syntax()) | |
| 814 | } else if let Some(at_token) = self.at_token() { | |
| 815 | // Have an `@` token but not a pattern yet | |
| 816 | syntax_editor.insert(Position::after(at_token), pat.syntax()); | |
| 817 | } else { | |
| 818 | // Don't have an `@`, should have a name | |
| 819 | let name = self.name().unwrap(); | |
| 820 | ||
| 821 | syntax_editor.insert_all( | |
| 822 | Position::after(name.syntax()), | |
| 823 | vec![ | |
| 824 | syntax_factory.whitespace(" ").into(), | |
| 825 | syntax_factory.token(T![@]).into(), | |
| 826 | syntax_factory.whitespace(" ").into(), | |
| 827 | pat.syntax().clone().into(), | |
| 828 | ], | |
| 829 | ) | |
| 830 | } | |
| 831 | } | |
| 832 | } | |
| 833 | } | |
| 783 | 834 | } |
| 784 | 835 | |
| 785 | 836 | pub trait HasVisibilityEdit: ast::HasVisibility { |
src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs+77| ... | ... | @@ -75,6 +75,24 @@ impl SyntaxFactory { |
| 75 | 75 | make::path_from_text(text).clone_for_update() |
| 76 | 76 | } |
| 77 | 77 | |
| 78 | pub fn path_concat(&self, first: ast::Path, second: ast::Path) -> ast::Path { | |
| 79 | make::path_concat(first, second).clone_for_update() | |
| 80 | } | |
| 81 | ||
| 82 | pub fn visibility_pub(&self) -> ast::Visibility { | |
| 83 | make::visibility_pub() | |
| 84 | } | |
| 85 | ||
| 86 | pub fn struct_( | |
| 87 | &self, | |
| 88 | visibility: Option<ast::Visibility>, | |
| 89 | strukt_name: ast::Name, | |
| 90 | generic_param_list: Option<ast::GenericParamList>, | |
| 91 | field_list: ast::FieldList, | |
| 92 | ) -> ast::Struct { | |
| 93 | make::struct_(visibility, strukt_name, generic_param_list, field_list).clone_for_update() | |
| 94 | } | |
| 95 | ||
| 78 | 96 | pub fn expr_field(&self, receiver: ast::Expr, field: &str) -> ast::FieldExpr { |
| 79 | 97 | let ast::Expr::FieldExpr(ast) = |
| 80 | 98 | make::expr_field(receiver.clone(), field).clone_for_update() |
| ... | ... | @@ -1590,6 +1608,65 @@ impl SyntaxFactory { |
| 1590 | 1608 | ast |
| 1591 | 1609 | } |
| 1592 | 1610 | |
| 1611 | pub fn self_param(&self) -> ast::SelfParam { | |
| 1612 | let ast = make::self_param().clone_for_update(); | |
| 1613 | ||
| 1614 | if let Some(mut mapping) = self.mappings() { | |
| 1615 | let builder = SyntaxMappingBuilder::new(ast.syntax().clone()); | |
| 1616 | builder.finish(&mut mapping); | |
| 1617 | } | |
| 1618 | ||
| 1619 | ast | |
| 1620 | } | |
| 1621 | ||
| 1622 | pub fn impl_( | |
| 1623 | &self, | |
| 1624 | attrs: impl IntoIterator<Item = ast::Attr>, | |
| 1625 | generic_params: Option<ast::GenericParamList>, | |
| 1626 | generic_args: Option<ast::GenericArgList>, | |
| 1627 | path_type: ast::Type, | |
| 1628 | where_clause: Option<ast::WhereClause>, | |
| 1629 | body: Option<ast::AssocItemList>, | |
| 1630 | ) -> ast::Impl { | |
| 1631 | let (attrs, attrs_input) = iterator_input(attrs); | |
| 1632 | let ast = make::impl_( | |
| 1633 | attrs, | |
| 1634 | generic_params.clone(), | |
| 1635 | generic_args.clone(), | |
| 1636 | path_type.clone(), | |
| 1637 | where_clause.clone(), | |
| 1638 | body.clone(), | |
| 1639 | ) | |
| 1640 | .clone_for_update(); | |
| 1641 | ||
| 1642 | if let Some(mut mapping) = self.mappings() { | |
| 1643 | let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); | |
| 1644 | builder.map_children(attrs_input, ast.attrs().map(|attr| attr.syntax().clone())); | |
| 1645 | if let Some(generic_params) = generic_params { | |
| 1646 | builder.map_node( | |
| 1647 | generic_params.syntax().clone(), | |
| 1648 | ast.generic_param_list().unwrap().syntax().clone(), | |
| 1649 | ); | |
| 1650 | } | |
| 1651 | builder.map_node(path_type.syntax().clone(), ast.self_ty().unwrap().syntax().clone()); | |
| 1652 | if let Some(where_clause) = where_clause { | |
| 1653 | builder.map_node( | |
| 1654 | where_clause.syntax().clone(), | |
| 1655 | ast.where_clause().unwrap().syntax().clone(), | |
| 1656 | ); | |
| 1657 | } | |
| 1658 | if let Some(body) = body { | |
| 1659 | builder.map_node( | |
| 1660 | body.syntax().clone(), | |
| 1661 | ast.assoc_item_list().unwrap().syntax().clone(), | |
| 1662 | ); | |
| 1663 | } | |
| 1664 | builder.finish(&mut mapping); | |
| 1665 | } | |
| 1666 | ||
| 1667 | ast | |
| 1668 | } | |
| 1669 | ||
| 1593 | 1670 | pub fn ret_type(&self, ty: ast::Type) -> ast::RetType { |
| 1594 | 1671 | let ast = make::ret_type(ty.clone()).clone_for_update(); |
| 1595 | 1672 |
src/tools/rust-analyzer/crates/test-utils/src/minicore.rs+23-1| ... | ... | @@ -1689,6 +1689,21 @@ pub mod iter { |
| 1689 | 1689 | } |
| 1690 | 1690 | } |
| 1691 | 1691 | |
| 1692 | pub struct Filter<I, P> { | |
| 1693 | iter: I, | |
| 1694 | predicate: P, | |
| 1695 | } | |
| 1696 | impl<I: Iterator, P> Iterator for Filter<I, P> | |
| 1697 | where | |
| 1698 | P: FnMut(&I::Item) -> bool, | |
| 1699 | { | |
| 1700 | type Item = I::Item; | |
| 1701 | ||
| 1702 | fn next(&mut self) -> Option<I::Item> { | |
| 1703 | loop {} | |
| 1704 | } | |
| 1705 | } | |
| 1706 | ||
| 1692 | 1707 | pub struct FilterMap<I, F> { |
| 1693 | 1708 | iter: I, |
| 1694 | 1709 | f: F, |
| ... | ... | @@ -1705,7 +1720,7 @@ pub mod iter { |
| 1705 | 1720 | } |
| 1706 | 1721 | } |
| 1707 | 1722 | } |
| 1708 | pub use self::adapters::{FilterMap, Take}; | |
| 1723 | pub use self::adapters::{Filter, FilterMap, Take}; | |
| 1709 | 1724 | |
| 1710 | 1725 | mod sources { |
| 1711 | 1726 | mod repeat { |
| ... | ... | @@ -1756,6 +1771,13 @@ pub mod iter { |
| 1756 | 1771 | { |
| 1757 | 1772 | loop {} |
| 1758 | 1773 | } |
| 1774 | fn filter<P>(self, predicate: P) -> crate::iter::Filter<Self, P> | |
| 1775 | where | |
| 1776 | Self: Sized, | |
| 1777 | P: FnMut(&Self::Item) -> bool, | |
| 1778 | { | |
| 1779 | loop {} | |
| 1780 | } | |
| 1759 | 1781 | fn filter_map<B, F>(self, _f: F) -> crate::iter::FilterMap<Self, F> |
| 1760 | 1782 | where |
| 1761 | 1783 | Self: Sized, |
src/tools/rust-analyzer/crates/tt/src/storage.rs+1-1| ... | ... | @@ -488,7 +488,7 @@ impl TopSubtree { |
| 488 | 488 | unreachable!() |
| 489 | 489 | }; |
| 490 | 490 | *open_span = S::new(span.open.range, 0); |
| 491 | *close_span = S::new(span.close.range, 0); | |
| 491 | *close_span = S::new(span.close.range, 1); | |
| 492 | 492 | } |
| 493 | 493 | dispatch! { |
| 494 | 494 | match &mut self.repr => tt => do_it(tt, span) |
src/tools/rust-analyzer/editors/code/package-lock.json+3-9| ... | ... | @@ -1486,7 +1486,6 @@ |
| 1486 | 1486 | "integrity": "sha512-4gbs64bnbSzu4FpgMiQ1A+D+urxkoJk/kqlDJ2W//5SygaEiAP2B4GoS7TEdxgwol2el03gckFV9lJ4QOMiiHg==", |
| 1487 | 1487 | "dev": true, |
| 1488 | 1488 | "license": "MIT", |
| 1489 | "peer": true, | |
| 1490 | 1489 | "dependencies": { |
| 1491 | 1490 | "@typescript-eslint/scope-manager": "8.25.0", |
| 1492 | 1491 | "@typescript-eslint/types": "8.25.0", |
| ... | ... | @@ -1870,7 +1869,6 @@ |
| 1870 | 1869 | "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", |
| 1871 | 1870 | "dev": true, |
| 1872 | 1871 | "license": "MIT", |
| 1873 | "peer": true, | |
| 1874 | 1872 | "bin": { |
| 1875 | 1873 | "acorn": "bin/acorn" |
| 1876 | 1874 | }, |
| ... | ... | @@ -2840,7 +2838,6 @@ |
| 2840 | 2838 | "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", |
| 2841 | 2839 | "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", |
| 2842 | 2840 | "license": "ISC", |
| 2843 | "peer": true, | |
| 2844 | 2841 | "engines": { |
| 2845 | 2842 | "node": ">=12" |
| 2846 | 2843 | } |
| ... | ... | @@ -3322,7 +3319,6 @@ |
| 3322 | 3319 | "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==", |
| 3323 | 3320 | "dev": true, |
| 3324 | 3321 | "license": "MIT", |
| 3325 | "peer": true, | |
| 3326 | 3322 | "dependencies": { |
| 3327 | 3323 | "@eslint-community/eslint-utils": "^4.2.0", |
| 3328 | 3324 | "@eslint-community/regexpp": "^4.12.1", |
| ... | ... | @@ -4410,7 +4406,6 @@ |
| 4410 | 4406 | "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", |
| 4411 | 4407 | "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", |
| 4412 | 4408 | "license": "MIT", |
| 4413 | "peer": true, | |
| 4414 | 4409 | "bin": { |
| 4415 | 4410 | "jiti": "lib/jiti-cli.mjs" |
| 4416 | 4411 | } |
| ... | ... | @@ -5584,9 +5579,9 @@ |
| 5584 | 5579 | } |
| 5585 | 5580 | }, |
| 5586 | 5581 | "node_modules/qs": { |
| 5587 | "version": "6.14.1", | |
| 5588 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", | |
| 5589 | "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", | |
| 5582 | "version": "6.14.2", | |
| 5583 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", | |
| 5584 | "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", | |
| 5590 | 5585 | "dev": true, |
| 5591 | 5586 | "license": "BSD-3-Clause", |
| 5592 | 5587 | "dependencies": { |
| ... | ... | @@ -6678,7 +6673,6 @@ |
| 6678 | 6673 | "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", |
| 6679 | 6674 | "dev": true, |
| 6680 | 6675 | "license": "Apache-2.0", |
| 6681 | "peer": true, | |
| 6682 | 6676 | "bin": { |
| 6683 | 6677 | "tsc": "bin/tsc", |
| 6684 | 6678 | "tsserver": "bin/tsserver" |
src/tools/rust-analyzer/lib/smol_str/src/borsh.rs+3-2| ... | ... | @@ -29,8 +29,9 @@ impl BorshDeserialize for SmolStr { |
| 29 | 29 | })) |
| 30 | 30 | } else { |
| 31 | 31 | // u8::vec_from_reader always returns Some on success in current implementation |
| 32 | let vec = u8::vec_from_reader(len, reader)? | |
| 33 | .ok_or_else(|| Error::other("u8::vec_from_reader unexpectedly returned None"))?; | |
| 32 | let vec = u8::vec_from_reader(len, reader)?.ok_or_else(|| { | |
| 33 | Error::new(ErrorKind::Other, "u8::vec_from_reader unexpectedly returned None") | |
| 34 | })?; | |
| 34 | 35 | Ok(SmolStr::from(String::from_utf8(vec).map_err(|err| { |
| 35 | 36 | let msg = err.to_string(); |
| 36 | 37 | Error::new(ErrorKind::InvalidData, msg) |
src/tools/rust-analyzer/lib/smol_str/tests/test.rs+1-1| ... | ... | @@ -393,7 +393,7 @@ mod test_str_ext { |
| 393 | 393 | } |
| 394 | 394 | } |
| 395 | 395 | |
| 396 | #[cfg(feature = "borsh")] | |
| 396 | #[cfg(all(feature = "borsh", feature = "std"))] | |
| 397 | 397 | mod borsh_tests { |
| 398 | 398 | use borsh::BorshDeserialize; |
| 399 | 399 | use smol_str::{SmolStr, ToSmolStr}; |
src/tools/rust-analyzer/rust-version+1-1| ... | ... | @@ -1 +1 @@ |
| 1 | ba284f468cd2cda48420251efc991758ec13d450 | |
| 1 | 139651428df86cf88443295542c12ea617cbb587 |
src/tools/rustfmt/src/items.rs+6-6| ... | ... | @@ -319,12 +319,13 @@ impl<'a> FnSig<'a> { |
| 319 | 319 | method_sig: &'a ast::FnSig, |
| 320 | 320 | generics: &'a ast::Generics, |
| 321 | 321 | visibility: &'a ast::Visibility, |
| 322 | defaultness: ast::Defaultness, | |
| 322 | 323 | ) -> FnSig<'a> { |
| 323 | 324 | FnSig { |
| 324 | 325 | safety: method_sig.header.safety, |
| 325 | 326 | coroutine_kind: Cow::Borrowed(&method_sig.header.coroutine_kind), |
| 326 | 327 | constness: method_sig.header.constness, |
| 327 | defaultness: ast::Defaultness::Final, | |
| 328 | defaultness, | |
| 328 | 329 | ext: method_sig.header.ext, |
| 329 | 330 | decl: &*method_sig.decl, |
| 330 | 331 | generics, |
| ... | ... | @@ -339,9 +340,7 @@ impl<'a> FnSig<'a> { |
| 339 | 340 | ) -> FnSig<'a> { |
| 340 | 341 | match *fn_kind { |
| 341 | 342 | visit::FnKind::Fn(visit::FnCtxt::Assoc(..), vis, ast::Fn { sig, generics, .. }) => { |
| 342 | let mut fn_sig = FnSig::from_method_sig(sig, generics, vis); | |
| 343 | fn_sig.defaultness = defaultness; | |
| 344 | fn_sig | |
| 343 | FnSig::from_method_sig(sig, generics, vis, defaultness) | |
| 345 | 344 | } |
| 346 | 345 | visit::FnKind::Fn(_, vis, ast::Fn { sig, generics, .. }) => FnSig { |
| 347 | 346 | decl, |
| ... | ... | @@ -459,6 +458,7 @@ impl<'a> FmtVisitor<'a> { |
| 459 | 458 | sig: &ast::FnSig, |
| 460 | 459 | vis: &ast::Visibility, |
| 461 | 460 | generics: &ast::Generics, |
| 461 | defaultness: ast::Defaultness, | |
| 462 | 462 | span: Span, |
| 463 | 463 | ) -> RewriteResult { |
| 464 | 464 | // Drop semicolon or it will be interpreted as comment. |
| ... | ... | @@ -469,7 +469,7 @@ impl<'a> FmtVisitor<'a> { |
| 469 | 469 | &context, |
| 470 | 470 | indent, |
| 471 | 471 | ident, |
| 472 | &FnSig::from_method_sig(sig, generics, vis), | |
| 472 | &FnSig::from_method_sig(sig, generics, vis, defaultness), | |
| 473 | 473 | span, |
| 474 | 474 | FnBraceStyle::None, |
| 475 | 475 | )?; |
| ... | ... | @@ -3495,7 +3495,7 @@ impl Rewrite for ast::ForeignItem { |
| 3495 | 3495 | context, |
| 3496 | 3496 | shape.indent, |
| 3497 | 3497 | ident, |
| 3498 | &FnSig::from_method_sig(sig, generics, &self.vis), | |
| 3498 | &FnSig::from_method_sig(sig, generics, &self.vis, defaultness), | |
| 3499 | 3499 | span, |
| 3500 | 3500 | FnBraceStyle::None, |
| 3501 | 3501 | ) |
src/tools/rustfmt/src/utils.rs+2-1| ... | ... | @@ -102,8 +102,9 @@ pub(crate) fn format_constness_right(constness: ast::Const) -> &'static str { |
| 102 | 102 | #[inline] |
| 103 | 103 | pub(crate) fn format_defaultness(defaultness: ast::Defaultness) -> &'static str { |
| 104 | 104 | match defaultness { |
| 105 | ast::Defaultness::Implicit => "", | |
| 105 | 106 | ast::Defaultness::Default(..) => "default ", |
| 106 | ast::Defaultness::Final => "", | |
| 107 | ast::Defaultness::Final(..) => "final ", | |
| 107 | 108 | } |
| 108 | 109 | } |
| 109 | 110 |
src/tools/rustfmt/src/visitor.rs+18-2| ... | ... | @@ -583,7 +583,15 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { |
| 583 | 583 | } else { |
| 584 | 584 | let indent = self.block_indent; |
| 585 | 585 | let rewrite = self |
| 586 | .rewrite_required_fn(indent, ident, sig, &item.vis, generics, item.span) | |
| 586 | .rewrite_required_fn( | |
| 587 | indent, | |
| 588 | ident, | |
| 589 | sig, | |
| 590 | &item.vis, | |
| 591 | generics, | |
| 592 | defaultness, | |
| 593 | item.span, | |
| 594 | ) | |
| 587 | 595 | .ok(); |
| 588 | 596 | self.push_rewrite(item.span, rewrite); |
| 589 | 597 | } |
| ... | ... | @@ -686,7 +694,15 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { |
| 686 | 694 | } else { |
| 687 | 695 | let indent = self.block_indent; |
| 688 | 696 | let rewrite = self |
| 689 | .rewrite_required_fn(indent, fn_kind.ident, sig, &ai.vis, generics, ai.span) | |
| 697 | .rewrite_required_fn( | |
| 698 | indent, | |
| 699 | fn_kind.ident, | |
| 700 | sig, | |
| 701 | &ai.vis, | |
| 702 | generics, | |
| 703 | defaultness, | |
| 704 | ai.span, | |
| 705 | ) | |
| 690 | 706 | .ok(); |
| 691 | 707 | self.push_rewrite(ai.span, rewrite); |
| 692 | 708 | } |
src/tools/rustfmt/tests/target/final-kw.rs created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | trait Foo { | |
| 2 | final fn final_() {} | |
| 3 | ||
| 4 | fn not_final() {} | |
| 5 | } |
tests/run-make/short-ice/rmake.rs+3-3| ... | ... | @@ -31,10 +31,10 @@ fn main() { |
| 31 | 31 | let output_bt_full = &concat_stderr_stdout(&rustc_bt_full); |
| 32 | 32 | |
| 33 | 33 | // Count how many lines of output mention symbols or paths in |
| 34 | // `rustc_query_system` or `rustc_query_impl`, which are the kinds of | |
| 34 | // `rustc_query_impl`, which are the kinds of | |
| 35 | 35 | // stack frames we want to be omitting in short backtraces. |
| 36 | let rustc_query_count_short = count_lines_with(output_bt_short, "rustc_query_"); | |
| 37 | let rustc_query_count_full = count_lines_with(output_bt_full, "rustc_query_"); | |
| 36 | let rustc_query_count_short = count_lines_with(output_bt_short, "rustc_query_impl"); | |
| 37 | let rustc_query_count_full = count_lines_with(output_bt_full, "rustc_query_impl"); | |
| 38 | 38 | |
| 39 | 39 | // Dump both outputs in full to make debugging easier, especially on CI. |
| 40 | 40 | // Use `--no-capture --force-rerun` to view output even when the test is passing. |
tests/rustdoc-html/final-trait-method.rs created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | #![feature(final_associated_functions)] | |
| 2 | ||
| 3 | //@ has final_trait_method/trait.Item.html | |
| 4 | pub trait Item { | |
| 5 | //@ has - '//*[@id="method.foo"]' 'final fn foo()' | |
| 6 | //@ !has - '//*[@id="method.foo"]' 'default fn foo()' | |
| 7 | final fn foo() {} | |
| 8 | ||
| 9 | //@ has - '//*[@id="method.bar"]' 'fn bar()' | |
| 10 | //@ !has - '//*[@id="method.bar"]' 'default fn bar()' | |
| 11 | //@ !has - '//*[@id="method.bar"]' 'final fn bar()' | |
| 12 | fn bar() {} | |
| 13 | } | |
| 14 | ||
| 15 | //@ has final_trait_method/struct.Foo.html | |
| 16 | pub struct Foo; | |
| 17 | impl Item for Foo { | |
| 18 | //@ has - '//*[@id="method.bar"]' 'fn bar()' | |
| 19 | //@ !has - '//*[@id="method.bar"]' 'final fn bar()' | |
| 20 | //@ !has - '//*[@id="method.bar"]' 'default fn bar()' | |
| 21 | fn bar() {} | |
| 22 | } |
tests/ui-fulldeps/hash-stable-is-unstable.rs+1-1| ... | ... | @@ -7,7 +7,7 @@ extern crate rustc_macros; |
| 7 | 7 | //~^ ERROR use of unstable library feature `rustc_private` |
| 8 | 8 | //~| NOTE: see issue #27812 <https://github.com/rust-lang/rust/issues/27812> for more information |
| 9 | 9 | //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date |
| 10 | extern crate rustc_query_system; | |
| 10 | extern crate rustc_middle; | |
| 11 | 11 | //~^ ERROR use of unstable library feature `rustc_private` |
| 12 | 12 | //~| NOTE: see issue #27812 <https://github.com/rust-lang/rust/issues/27812> for more information |
| 13 | 13 | //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date |
tests/ui-fulldeps/hash-stable-is-unstable.stderr+2-2| ... | ... | @@ -21,8 +21,8 @@ LL | extern crate rustc_macros; |
| 21 | 21 | error[E0658]: use of unstable library feature `rustc_private`: this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? |
| 22 | 22 | --> $DIR/hash-stable-is-unstable.rs:10:1 |
| 23 | 23 | | |
| 24 | LL | extern crate rustc_query_system; | |
| 25 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 24 | LL | extern crate rustc_middle; | |
| 25 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 26 | 26 | | |
| 27 | 27 | = note: see issue #27812 <https://github.com/rust-lang/rust/issues/27812> for more information |
| 28 | 28 | = help: add `#![feature(rustc_private)]` to the crate attributes to enable |
tests/ui/coroutine/gen_fn.none.stderr+2-2| ... | ... | @@ -1,8 +1,8 @@ |
| 1 | error: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` | |
| 1 | error: expected one of `#`, `async`, `const`, `default`, `extern`, `final`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` | |
| 2 | 2 | --> $DIR/gen_fn.rs:4:1 |
| 3 | 3 | | |
| 4 | 4 | LL | gen fn foo() {} |
| 5 | | ^^^ expected one of 10 possible tokens | |
| 5 | | ^^^ expected one of 11 possible tokens | |
| 6 | 6 | |
| 7 | 7 | error: aborting due to 1 previous error |
| 8 | 8 |
tests/ui/coroutine/gen_fn.rs+1-1| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | //@[e2024] edition: 2024 |
| 3 | 3 | |
| 4 | 4 | gen fn foo() {} |
| 5 | //[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` | |
| 5 | //[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `final`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` | |
| 6 | 6 | //[e2024]~^^ ERROR: gen blocks are experimental |
| 7 | 7 | |
| 8 | 8 | fn main() {} |
tests/ui/feature-gates/feature-gate-final-associated-functions.rs created+6| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | trait Foo { | |
| 2 | final fn bar() {} | |
| 3 | //~^ ERROR `final` on trait functions is experimental | |
| 4 | } | |
| 5 | ||
| 6 | fn main() {} |
tests/ui/feature-gates/feature-gate-final-associated-functions.stderr created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | error[E0658]: `final` on trait functions is experimental | |
| 2 | --> $DIR/feature-gate-final-associated-functions.rs:2:5 | |
| 3 | | | |
| 4 | LL | final fn bar() {} | |
| 5 | | ^^^^^ | |
| 6 | | | |
| 7 | = note: see issue #1 <https://github.com/rust-lang/rust/issues/1> for more information | |
| 8 | = help: add `#![feature(final_associated_functions)]` to the crate attributes to enable | |
| 9 | = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date | |
| 10 | ||
| 11 | error: aborting due to 1 previous error | |
| 12 | ||
| 13 | For more information about this error, try `rustc --explain E0658`. |
tests/ui/methods/call_method_unknown_pointee.rs+23-10| ... | ... | @@ -3,26 +3,39 @@ |
| 3 | 3 | // tests that the pointee type of a raw pointer must be known to call methods on it |
| 4 | 4 | // see also: `tests/ui/editions/edition-raw-pointer-method-2018.rs` |
| 5 | 5 | |
| 6 | fn main() { | |
| 7 | let val = 1_u32; | |
| 8 | let ptr = &val as *const u32; | |
| 6 | fn a() { | |
| 7 | let ptr = &1u32 as *const u32; | |
| 9 | 8 | unsafe { |
| 10 | 9 | let _a: i32 = (ptr as *const _).read(); |
| 11 | 10 | //~^ ERROR type annotations needed |
| 11 | } | |
| 12 | } | |
| 13 | ||
| 14 | fn b() { | |
| 15 | let ptr = &1u32 as *const u32; | |
| 16 | unsafe { | |
| 12 | 17 | let b = ptr as *const _; |
| 13 | 18 | //~^ ERROR type annotations needed |
| 14 | 19 | let _b: u8 = b.read(); |
| 15 | let _c = (ptr as *const u8).read(); // we know the type here | |
| 16 | 20 | } |
| 21 | } | |
| 22 | ||
| 17 | 23 | |
| 18 | let mut val = 2_u32; | |
| 19 | let ptr = &mut val as *mut u32; | |
| 24 | fn c() { | |
| 25 | let ptr = &mut 2u32 as *mut u32; | |
| 20 | 26 | unsafe { |
| 21 | let _a: i32 = (ptr as *mut _).read(); | |
| 27 | let _c: i32 = (ptr as *mut _).read(); | |
| 22 | 28 | //~^ ERROR type annotations needed |
| 23 | let b = ptr as *mut _; | |
| 29 | } | |
| 30 | } | |
| 31 | ||
| 32 | fn d() { | |
| 33 | let ptr = &mut 2u32 as *mut u32; | |
| 34 | unsafe { | |
| 35 | let d = ptr as *mut _; | |
| 24 | 36 | //~^ ERROR type annotations needed |
| 25 | b.write(10); | |
| 26 | (ptr as *mut i32).write(1000); // we know the type here | |
| 37 | let _d: u8 = d.read(); | |
| 27 | 38 | } |
| 28 | 39 | } |
| 40 | ||
| 41 | fn main() {} |
tests/ui/methods/call_method_unknown_pointee.stderr+10-10| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | error[E0282]: type annotations needed |
| 2 | --> $DIR/call_method_unknown_pointee.rs:10:23 | |
| 2 | --> $DIR/call_method_unknown_pointee.rs:9:23 | |
| 3 | 3 | | |
| 4 | 4 | LL | let _a: i32 = (ptr as *const _).read(); |
| 5 | 5 | | ^^^^^^^^^^^^^^^^^ ---- cannot call a method on a raw pointer with an unknown pointee type |
| ... | ... | @@ -7,7 +7,7 @@ LL | let _a: i32 = (ptr as *const _).read(); |
| 7 | 7 | | cannot infer type |
| 8 | 8 | |
| 9 | 9 | error[E0282]: type annotations needed for `*const _` |
| 10 | --> $DIR/call_method_unknown_pointee.rs:12:13 | |
| 10 | --> $DIR/call_method_unknown_pointee.rs:17:13 | |
| 11 | 11 | | |
| 12 | 12 | LL | let b = ptr as *const _; |
| 13 | 13 | | ^ |
| ... | ... | @@ -21,25 +21,25 @@ LL | let b: *const _ = ptr as *const _; |
| 21 | 21 | | ++++++++++ |
| 22 | 22 | |
| 23 | 23 | error[E0282]: type annotations needed |
| 24 | --> $DIR/call_method_unknown_pointee.rs:21:23 | |
| 24 | --> $DIR/call_method_unknown_pointee.rs:27:23 | |
| 25 | 25 | | |
| 26 | LL | let _a: i32 = (ptr as *mut _).read(); | |
| 26 | LL | let _c: i32 = (ptr as *mut _).read(); | |
| 27 | 27 | | ^^^^^^^^^^^^^^^ ---- cannot call a method on a raw pointer with an unknown pointee type |
| 28 | 28 | | | |
| 29 | 29 | | cannot infer type |
| 30 | 30 | |
| 31 | 31 | error[E0282]: type annotations needed for `*mut _` |
| 32 | --> $DIR/call_method_unknown_pointee.rs:23:13 | |
| 32 | --> $DIR/call_method_unknown_pointee.rs:35:13 | |
| 33 | 33 | | |
| 34 | LL | let b = ptr as *mut _; | |
| 34 | LL | let d = ptr as *mut _; | |
| 35 | 35 | | ^ |
| 36 | 36 | LL | |
| 37 | LL | b.write(10); | |
| 38 | | ----- cannot call a method on a raw pointer with an unknown pointee type | |
| 37 | LL | let _d: u8 = d.read(); | |
| 38 | | ---- cannot call a method on a raw pointer with an unknown pointee type | |
| 39 | 39 | | |
| 40 | help: consider giving `b` an explicit type, where the placeholders `_` are specified | |
| 40 | help: consider giving `d` an explicit type, where the placeholders `_` are specified | |
| 41 | 41 | | |
| 42 | LL | let b: *mut _ = ptr as *mut _; | |
| 42 | LL | let d: *mut _ = ptr as *mut _; | |
| 43 | 43 | | ++++++++ |
| 44 | 44 | |
| 45 | 45 | error: aborting due to 4 previous errors |
tests/ui/methods/call_method_unknown_referent.rs+10-6| ... | ... | @@ -14,20 +14,22 @@ impl<T> SmartPtr<T> { |
| 14 | 14 | fn foo(&self) {} |
| 15 | 15 | } |
| 16 | 16 | |
| 17 | fn main() { | |
| 18 | let val = 1_u32; | |
| 19 | let ptr = &val; | |
| 17 | fn a() { | |
| 18 | let ptr = &1u32; | |
| 20 | 19 | let _a: i32 = (ptr as &_).read(); |
| 21 | 20 | //~^ ERROR type annotations needed |
| 21 | } | |
| 22 | 22 | |
| 23 | fn b() { | |
| 23 | 24 | // Same again, but with a smart pointer type |
| 24 | let val2 = 1_u32; | |
| 25 | let rc = std::rc::Rc::new(val2); | |
| 25 | let rc = std::rc::Rc::new(1u32); | |
| 26 | 26 | let _b = (rc as std::rc::Rc<_>).read(); |
| 27 | 27 | //~^ ERROR type annotations needed |
| 28 | } | |
| 28 | 29 | |
| 30 | fn c() { | |
| 29 | 31 | // Same again, but with a smart pointer type |
| 30 | let ptr = SmartPtr(val); | |
| 32 | let ptr = SmartPtr(1u32); | |
| 31 | 33 | |
| 32 | 34 | // We can call unambiguous outer-type methods on this |
| 33 | 35 | (ptr as SmartPtr<_>).foo(); |
| ... | ... | @@ -46,3 +48,5 @@ fn main() { |
| 46 | 48 | let _c = (ptr as SmartPtr<_>).read(); |
| 47 | 49 | //~^ ERROR no method named `read` found for struct `SmartPtr<T>` |
| 48 | 50 | } |
| 51 | ||
| 52 | fn main() {} |
tests/ui/methods/call_method_unknown_referent.stderr+2-2| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | error[E0282]: type annotations needed |
| 2 | --> $DIR/call_method_unknown_referent.rs:20:19 | |
| 2 | --> $DIR/call_method_unknown_referent.rs:19:19 | |
| 3 | 3 | | |
| 4 | 4 | LL | let _a: i32 = (ptr as &_).read(); |
| 5 | 5 | | ^^^^^^^^^^^ cannot infer type |
| ... | ... | @@ -11,7 +11,7 @@ LL | let _b = (rc as std::rc::Rc<_>).read(); |
| 11 | 11 | | ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type |
| 12 | 12 | |
| 13 | 13 | error[E0599]: no method named `read` found for struct `SmartPtr<T>` in the current scope |
| 14 | --> $DIR/call_method_unknown_referent.rs:46:35 | |
| 14 | --> $DIR/call_method_unknown_referent.rs:48:35 | |
| 15 | 15 | | |
| 16 | 16 | LL | struct SmartPtr<T>(T); |
| 17 | 17 | | ------------------ method `read` not found for this struct |
tests/ui/parser/duplicate-visibility.rs+2-2| ... | ... | @@ -2,8 +2,8 @@ fn main() {} |
| 2 | 2 | |
| 3 | 3 | extern "C" { //~ NOTE while parsing this item list starting here |
| 4 | 4 | pub pub fn foo(); |
| 5 | //~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` | |
| 6 | //~| NOTE expected one of 9 possible tokens | |
| 5 | //~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `final`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` | |
| 6 | //~| NOTE expected one of 10 possible tokens | |
| 7 | 7 | //~| HELP there is already a visibility modifier, remove one |
| 8 | 8 | //~| NOTE explicit visibility first seen here |
| 9 | 9 | } //~ NOTE the item list ends here |
tests/ui/parser/duplicate-visibility.stderr+2-2| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | error: expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` | |
| 1 | error: expected one of `(`, `async`, `const`, `default`, `extern`, `final`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` | |
| 2 | 2 | --> $DIR/duplicate-visibility.rs:4:9 |
| 3 | 3 | | |
| 4 | 4 | LL | extern "C" { |
| ... | ... | @@ -6,7 +6,7 @@ LL | extern "C" { |
| 6 | 6 | LL | pub pub fn foo(); |
| 7 | 7 | | ^^^ |
| 8 | 8 | | | |
| 9 | | expected one of 9 possible tokens | |
| 9 | | expected one of 10 possible tokens | |
| 10 | 10 | | help: there is already a visibility modifier, remove one |
| 11 | 11 | ... |
| 12 | 12 | LL | } |
tests/ui/parser/misspelled-keywords/pub-fn.stderr+2-2| ... | ... | @@ -1,8 +1,8 @@ |
| 1 | error: expected one of `#`, `async`, `auto`, `const`, `default`, `enum`, `extern`, `fn`, `gen`, `impl`, `macro_rules`, `macro`, `mod`, `pub`, `safe`, `static`, `struct`, `trait`, `type`, `unsafe`, or `use`, found `puB` | |
| 1 | error: expected one of `#`, `async`, `auto`, `const`, `default`, `enum`, `extern`, `final`, `fn`, `gen`, `impl`, `macro_rules`, `macro`, `mod`, `pub`, `safe`, `static`, `struct`, `trait`, `type`, `unsafe`, or `use`, found `puB` | |
| 2 | 2 | --> $DIR/pub-fn.rs:1:1 |
| 3 | 3 | | |
| 4 | 4 | LL | puB fn code() {} |
| 5 | | ^^^ expected one of 21 possible tokens | |
| 5 | | ^^^ expected one of 22 possible tokens | |
| 6 | 6 | | |
| 7 | 7 | help: write keyword `pub` in lowercase |
| 8 | 8 | | |
tests/ui/proc-macro/quote/not-repeatable.rs-1| ... | ... | @@ -10,5 +10,4 @@ fn main() { |
| 10 | 10 | let ip = Ipv4Addr; |
| 11 | 11 | let _ = quote! { $($ip)* }; |
| 12 | 12 | //~^ ERROR the method `quote_into_iter` exists for struct `Ipv4Addr`, but its trait bounds were not satisfied |
| 13 | //~| ERROR type annotations needed | |
| 14 | 13 | } |
tests/ui/proc-macro/quote/not-repeatable.stderr+2-9| ... | ... | @@ -20,13 +20,6 @@ note: the traits `Iterator` and `ToTokens` must be implemented |
| 20 | 20 | --> $SRC_DIR/proc_macro/src/to_tokens.rs:LL:COL |
| 21 | 21 | --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL |
| 22 | 22 | |
| 23 | error[E0282]: type annotations needed | |
| 24 | --> $DIR/not-repeatable.rs:11:25 | |
| 25 | | | |
| 26 | LL | let _ = quote! { $($ip)* }; | |
| 27 | | ^^ cannot infer type | |
| 28 | ||
| 29 | error: aborting due to 2 previous errors | |
| 23 | error: aborting due to 1 previous error | |
| 30 | 24 | |
| 31 | Some errors have detailed explanations: E0282, E0599. | |
| 32 | For more information about an error, try `rustc --explain E0282`. | |
| 25 | For more information about this error, try `rustc --explain E0599`. |
tests/ui/traits/auxiliary/force_unstable.rs created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | //@ edition: 2024 | |
| 2 | //@ compile-flags: -Zforce-unstable-if-unmarked | |
| 3 | ||
| 4 | // Auxiliary crate that uses `-Zforce-unstable-if-unmarked` to export an | |
| 5 | // "unstable" trait. | |
| 6 | ||
| 7 | pub trait ForeignTrait {} |
tests/ui/traits/final/dyn-compat.rs created+40| ... | ... | @@ -0,0 +1,40 @@ |
| 1 | //@ run-pass | |
| 2 | ||
| 3 | #![feature(final_associated_functions)] | |
| 4 | ||
| 5 | trait FinalNoReceiver { | |
| 6 | final fn no_receiver() {} | |
| 7 | } | |
| 8 | ||
| 9 | trait FinalGeneric { | |
| 10 | final fn generic<T>(&self, _value: T) {} | |
| 11 | } | |
| 12 | ||
| 13 | trait FinalSelfParam { | |
| 14 | final fn self_param(&self, _other: &Self) {} | |
| 15 | } | |
| 16 | ||
| 17 | trait FinalSelfReturn { | |
| 18 | final fn self_return(&self) -> &Self { | |
| 19 | self | |
| 20 | } | |
| 21 | } | |
| 22 | ||
| 23 | struct S; | |
| 24 | ||
| 25 | impl FinalNoReceiver for S {} | |
| 26 | impl FinalGeneric for S {} | |
| 27 | impl FinalSelfParam for S {} | |
| 28 | impl FinalSelfReturn for S {} | |
| 29 | ||
| 30 | fn main() { | |
| 31 | let s = S; | |
| 32 | <S as FinalNoReceiver>::no_receiver(); | |
| 33 | let obj_generic: &dyn FinalGeneric = &s; | |
| 34 | let obj_param: &dyn FinalSelfParam = &s; | |
| 35 | let obj_return: &dyn FinalSelfReturn = &s; | |
| 36 | obj_generic.generic(1u8); | |
| 37 | obj_param.self_param(obj_param); | |
| 38 | let _ = obj_return.self_return(); | |
| 39 | let _: &dyn FinalNoReceiver = &s; | |
| 40 | } |
tests/ui/traits/final/final-kw.gated.stderr created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | error[E0658]: `final` on trait functions is experimental | |
| 2 | --> $DIR/final-kw.rs:5:5 | |
| 3 | | | |
| 4 | LL | final fn foo() {} | |
| 5 | | ^^^^^ | |
| 6 | | | |
| 7 | = note: see issue #1 <https://github.com/rust-lang/rust/issues/1> for more information | |
| 8 | = help: add `#![feature(final_associated_functions)]` to the crate attributes to enable | |
| 9 | = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date | |
| 10 | ||
| 11 | error: aborting due to 1 previous error | |
| 12 | ||
| 13 | For more information about this error, try `rustc --explain E0658`. |
tests/ui/traits/final/final-kw.rs created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | //@ revisions: ungated gated | |
| 2 | ||
| 3 | #[cfg(ungated)] | |
| 4 | trait Trait { | |
| 5 | final fn foo() {} | |
| 6 | //~^ ERROR `final` on trait functions is experimental | |
| 7 | } | |
| 8 | ||
| 9 | fn main() {} |
tests/ui/traits/final/final-kw.ungated.stderr created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | error[E0658]: `final` on trait functions is experimental | |
| 2 | --> $DIR/final-kw.rs:5:5 | |
| 3 | | | |
| 4 | LL | final fn foo() {} | |
| 5 | | ^^^^^ | |
| 6 | | | |
| 7 | = note: see issue #1 <https://github.com/rust-lang/rust/issues/1> for more information | |
| 8 | = help: add `#![feature(final_associated_functions)]` to the crate attributes to enable | |
| 9 | = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date | |
| 10 | ||
| 11 | error: aborting due to 1 previous error | |
| 12 | ||
| 13 | For more information about this error, try `rustc --explain E0658`. |
tests/ui/traits/final/final-must-have-body.rs created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | #![feature(final_associated_functions)] | |
| 2 | ||
| 3 | trait Foo { | |
| 4 | final fn method(); | |
| 5 | //~^ ERROR `final` is only allowed on associated functions if they have a body | |
| 6 | } | |
| 7 | ||
| 8 | fn main() {} |
tests/ui/traits/final/final-must-have-body.stderr created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | error: `final` is only allowed on associated functions if they have a body | |
| 2 | --> $DIR/final-must-have-body.rs:4:5 | |
| 3 | | | |
| 4 | LL | final fn method(); | |
| 5 | | -----^^^^^^^^^^^^^ | |
| 6 | | | | |
| 7 | | `final` because of this | |
| 8 | ||
| 9 | error: aborting due to 1 previous error | |
| 10 |
tests/ui/traits/final/overriding.rs created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | #![feature(final_associated_functions)] | |
| 2 | ||
| 3 | trait Foo { | |
| 4 | final fn method() {} | |
| 5 | } | |
| 6 | ||
| 7 | impl Foo for () { | |
| 8 | fn method() {} | |
| 9 | //~^ ERROR cannot override `method` because it already has a `final` definition in the trait | |
| 10 | } | |
| 11 | ||
| 12 | fn main() {} |
tests/ui/traits/final/overriding.stderr created+14| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | error: cannot override `method` because it already has a `final` definition in the trait | |
| 2 | --> $DIR/overriding.rs:8:5 | |
| 3 | | | |
| 4 | LL | fn method() {} | |
| 5 | | ^^^^^^^^^^^ | |
| 6 | | | |
| 7 | note: `method` is marked final here | |
| 8 | --> $DIR/overriding.rs:4:5 | |
| 9 | | | |
| 10 | LL | final fn method() {} | |
| 11 | | ^^^^^^^^^^^^^^^^^ | |
| 12 | ||
| 13 | error: aborting due to 1 previous error | |
| 14 |
tests/ui/traits/final/positions.rs created+72| ... | ... | @@ -0,0 +1,72 @@ |
| 1 | #![feature(final_associated_functions)] | |
| 2 | ||
| 3 | // Just for exercising the syntax positions | |
| 4 | #![feature(associated_type_defaults, extern_types, inherent_associated_types)] | |
| 5 | #![allow(incomplete_features)] | |
| 6 | ||
| 7 | final struct Foo {} | |
| 8 | //~^ ERROR a struct cannot be `final` | |
| 9 | ||
| 10 | final trait Trait { | |
| 11 | //~^ ERROR a trait cannot be `final` | |
| 12 | ||
| 13 | final fn method() {} | |
| 14 | // OK! | |
| 15 | ||
| 16 | final type Foo = (); | |
| 17 | //~^ ERROR `final` is only allowed on associated functions in traits | |
| 18 | ||
| 19 | final const FOO: usize = 1; | |
| 20 | //~^ ERROR `final` is only allowed on associated functions in traits | |
| 21 | } | |
| 22 | ||
| 23 | final impl Foo { | |
| 24 | final fn method() {} | |
| 25 | //~^ ERROR `final` is only allowed on associated functions in traits | |
| 26 | ||
| 27 | final type Foo = (); | |
| 28 | //~^ ERROR `final` is only allowed on associated functions in traits | |
| 29 | ||
| 30 | final const FOO: usize = 1; | |
| 31 | //~^ ERROR `final` is only allowed on associated functions in traits | |
| 32 | } | |
| 33 | ||
| 34 | final impl Trait for Foo { | |
| 35 | final fn method() {} | |
| 36 | //~^ ERROR `final` is only allowed on associated functions in traits | |
| 37 | //~^^ ERROR cannot override `method` because it already has a `final` definition in the trait | |
| 38 | ||
| 39 | final type Foo = (); | |
| 40 | //~^ ERROR `final` is only allowed on associated functions in traits | |
| 41 | //~^^ ERROR cannot override `Foo` because it already has a `final` definition in the trait | |
| 42 | ||
| 43 | final const FOO: usize = 1; | |
| 44 | //~^ ERROR `final` is only allowed on associated functions in traits | |
| 45 | //~^^ ERROR cannot override `FOO` because it already has a `final` definition in the trait | |
| 46 | } | |
| 47 | ||
| 48 | ||
| 49 | final fn foo() {} | |
| 50 | //~^ ERROR `final` is only allowed on associated functions in traits | |
| 51 | ||
| 52 | final type FooTy = (); | |
| 53 | //~^ ERROR `final` is only allowed on associated functions in traits | |
| 54 | ||
| 55 | final const FOO: usize = 0; | |
| 56 | //~^ ERROR `final` is only allowed on associated functions in traits | |
| 57 | ||
| 58 | final unsafe extern "C" { | |
| 59 | //~^ ERROR an extern block cannot be `final` | |
| 60 | ||
| 61 | final fn foo_extern(); | |
| 62 | //~^ ERROR `final` is only allowed on associated functions in traits | |
| 63 | ||
| 64 | final type FooExtern; | |
| 65 | //~^ ERROR `final` is only allowed on associated functions in traits | |
| 66 | ||
| 67 | final static FOO_EXTERN: usize = 0; | |
| 68 | //~^ ERROR a static item cannot be `final` | |
| 69 | //~| ERROR incorrect `static` inside `extern` block | |
| 70 | } | |
| 71 | ||
| 72 | fn main() {} |
tests/ui/traits/final/positions.stderr created+187| ... | ... | @@ -0,0 +1,187 @@ |
| 1 | error: a struct cannot be `final` | |
| 2 | --> $DIR/positions.rs:7:1 | |
| 3 | | | |
| 4 | LL | final struct Foo {} | |
| 5 | | ^^^^^ `final` because of this | |
| 6 | | | |
| 7 | = note: only associated functions in traits can be `final` | |
| 8 | ||
| 9 | error: a trait cannot be `final` | |
| 10 | --> $DIR/positions.rs:10:1 | |
| 11 | | | |
| 12 | LL | final trait Trait { | |
| 13 | | ^^^^^ `final` because of this | |
| 14 | | | |
| 15 | = note: only associated functions in traits can be `final` | |
| 16 | ||
| 17 | error: a static item cannot be `final` | |
| 18 | --> $DIR/positions.rs:67:5 | |
| 19 | | | |
| 20 | LL | final static FOO_EXTERN: usize = 0; | |
| 21 | | ^^^^^ `final` because of this | |
| 22 | | | |
| 23 | = note: only associated functions in traits can be `final` | |
| 24 | ||
| 25 | error: an extern block cannot be `final` | |
| 26 | --> $DIR/positions.rs:58:1 | |
| 27 | | | |
| 28 | LL | final unsafe extern "C" { | |
| 29 | | ^^^^^ `final` because of this | |
| 30 | | | |
| 31 | = note: only associated functions in traits can be `final` | |
| 32 | ||
| 33 | error: `final` is only allowed on associated functions in traits | |
| 34 | --> $DIR/positions.rs:16:5 | |
| 35 | | | |
| 36 | LL | final type Foo = (); | |
| 37 | | -----^^^^^^^^^^^^^^^ | |
| 38 | | | | |
| 39 | | `final` because of this | |
| 40 | ||
| 41 | error: `final` is only allowed on associated functions in traits | |
| 42 | --> $DIR/positions.rs:19:5 | |
| 43 | | | |
| 44 | LL | final const FOO: usize = 1; | |
| 45 | | -----^^^^^^^^^^^^^^^^^^^^^^ | |
| 46 | | | | |
| 47 | | `final` because of this | |
| 48 | ||
| 49 | error: `final` is only allowed on associated functions in traits | |
| 50 | --> $DIR/positions.rs:24:5 | |
| 51 | | | |
| 52 | LL | final fn method() {} | |
| 53 | | -----^^^^^^^^^^^^ | |
| 54 | | | | |
| 55 | | `final` because of this | |
| 56 | ||
| 57 | error: `final` is only allowed on associated functions in traits | |
| 58 | --> $DIR/positions.rs:27:5 | |
| 59 | | | |
| 60 | LL | final type Foo = (); | |
| 61 | | -----^^^^^^^^^^^^^^^ | |
| 62 | | | | |
| 63 | | `final` because of this | |
| 64 | ||
| 65 | error: `final` is only allowed on associated functions in traits | |
| 66 | --> $DIR/positions.rs:30:5 | |
| 67 | | | |
| 68 | LL | final const FOO: usize = 1; | |
| 69 | | -----^^^^^^^^^^^^^^^^^^^^^^ | |
| 70 | | | | |
| 71 | | `final` because of this | |
| 72 | ||
| 73 | error: `final` is only allowed on associated functions in traits | |
| 74 | --> $DIR/positions.rs:35:5 | |
| 75 | | | |
| 76 | LL | final fn method() {} | |
| 77 | | -----^^^^^^^^^^^^ | |
| 78 | | | | |
| 79 | | `final` because of this | |
| 80 | ||
| 81 | error: `final` is only allowed on associated functions in traits | |
| 82 | --> $DIR/positions.rs:39:5 | |
| 83 | | | |
| 84 | LL | final type Foo = (); | |
| 85 | | -----^^^^^^^^^^^^^^^ | |
| 86 | | | | |
| 87 | | `final` because of this | |
| 88 | ||
| 89 | error: `final` is only allowed on associated functions in traits | |
| 90 | --> $DIR/positions.rs:43:5 | |
| 91 | | | |
| 92 | LL | final const FOO: usize = 1; | |
| 93 | | -----^^^^^^^^^^^^^^^^^^^^^^ | |
| 94 | | | | |
| 95 | | `final` because of this | |
| 96 | ||
| 97 | error: `final` is only allowed on associated functions in traits | |
| 98 | --> $DIR/positions.rs:49:1 | |
| 99 | | | |
| 100 | LL | final fn foo() {} | |
| 101 | | -----^^^^^^^^^ | |
| 102 | | | | |
| 103 | | `final` because of this | |
| 104 | ||
| 105 | error: `final` is only allowed on associated functions in traits | |
| 106 | --> $DIR/positions.rs:52:1 | |
| 107 | | | |
| 108 | LL | final type FooTy = (); | |
| 109 | | -----^^^^^^^^^^^^^^^^^ | |
| 110 | | | | |
| 111 | | `final` because of this | |
| 112 | ||
| 113 | error: `final` is only allowed on associated functions in traits | |
| 114 | --> $DIR/positions.rs:55:1 | |
| 115 | | | |
| 116 | LL | final const FOO: usize = 0; | |
| 117 | | -----^^^^^^^^^^^^^^^^^^^^^^ | |
| 118 | | | | |
| 119 | | `final` because of this | |
| 120 | ||
| 121 | error: `final` is only allowed on associated functions in traits | |
| 122 | --> $DIR/positions.rs:61:5 | |
| 123 | | | |
| 124 | LL | final fn foo_extern(); | |
| 125 | | -----^^^^^^^^^^^^^^^^^ | |
| 126 | | | | |
| 127 | | `final` because of this | |
| 128 | ||
| 129 | error: `final` is only allowed on associated functions in traits | |
| 130 | --> $DIR/positions.rs:64:5 | |
| 131 | | | |
| 132 | LL | final type FooExtern; | |
| 133 | | -----^^^^^^^^^^^^^^^^ | |
| 134 | | | | |
| 135 | | `final` because of this | |
| 136 | ||
| 137 | error: incorrect `static` inside `extern` block | |
| 138 | --> $DIR/positions.rs:67:18 | |
| 139 | | | |
| 140 | LL | final unsafe extern "C" { | |
| 141 | | ----------------------- `extern` blocks define existing foreign statics and statics inside of them cannot have a body | |
| 142 | ... | |
| 143 | LL | final static FOO_EXTERN: usize = 0; | |
| 144 | | ^^^^^^^^^^ - the invalid body | |
| 145 | | | | |
| 146 | | cannot have a body | |
| 147 | | | |
| 148 | = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html | |
| 149 | ||
| 150 | error: cannot override `method` because it already has a `final` definition in the trait | |
| 151 | --> $DIR/positions.rs:35:5 | |
| 152 | | | |
| 153 | LL | final fn method() {} | |
| 154 | | ^^^^^^^^^^^^^^^^^ | |
| 155 | | | |
| 156 | note: `method` is marked final here | |
| 157 | --> $DIR/positions.rs:13:5 | |
| 158 | | | |
| 159 | LL | final fn method() {} | |
| 160 | | ^^^^^^^^^^^^^^^^^ | |
| 161 | ||
| 162 | error: cannot override `Foo` because it already has a `final` definition in the trait | |
| 163 | --> $DIR/positions.rs:39:5 | |
| 164 | | | |
| 165 | LL | final type Foo = (); | |
| 166 | | ^^^^^^^^^^^^^^ | |
| 167 | | | |
| 168 | note: `Foo` is marked final here | |
| 169 | --> $DIR/positions.rs:16:5 | |
| 170 | | | |
| 171 | LL | final type Foo = (); | |
| 172 | | ^^^^^^^^^^^^^^ | |
| 173 | ||
| 174 | error: cannot override `FOO` because it already has a `final` definition in the trait | |
| 175 | --> $DIR/positions.rs:43:5 | |
| 176 | | | |
| 177 | LL | final const FOO: usize = 1; | |
| 178 | | ^^^^^^^^^^^^^^^^^^^^^^ | |
| 179 | | | |
| 180 | note: `FOO` is marked final here | |
| 181 | --> $DIR/positions.rs:19:5 | |
| 182 | | | |
| 183 | LL | final const FOO: usize = 1; | |
| 184 | | ^^^^^^^^^^^^^^^^^^^^^^ | |
| 185 | ||
| 186 | error: aborting due to 21 previous errors | |
| 187 |
tests/ui/traits/final/works.rs created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | //@ check-pass | |
| 2 | ||
| 3 | #![feature(final_associated_functions)] | |
| 4 | ||
| 5 | trait Foo { | |
| 6 | final fn bar(&self) {} | |
| 7 | } | |
| 8 | ||
| 9 | impl Foo for () {} | |
| 10 | ||
| 11 | fn main() { | |
| 12 | ().bar(); | |
| 13 | } |
tests/ui/traits/next-solver-ice.rs created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | //@compile-flags: -Znext-solver=globally | |
| 2 | //@check-fail | |
| 3 | ||
| 4 | fn check<T: Iterator>() { | |
| 5 | <f32 as From<<T as Iterator>::Item>>::from; | |
| 6 | //~^ ERROR the trait bound `f32: From<<T as Iterator>::Item>` is not satisfied | |
| 7 | } | |
| 8 | ||
| 9 | fn main() {} |
tests/ui/traits/next-solver-ice.stderr created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | error[E0277]: the trait bound `f32: From<<T as Iterator>::Item>` is not satisfied | |
| 2 | --> $DIR/next-solver-ice.rs:5:6 | |
| 3 | | | |
| 4 | LL | <f32 as From<<T as Iterator>::Item>>::from; | |
| 5 | | ^^^ the nightly-only, unstable trait `ZeroablePrimitive` is not implemented for `f32` | |
| 6 | | | |
| 7 | = help: the following other types implement trait `ZeroablePrimitive`: | |
| 8 | i128 | |
| 9 | i16 | |
| 10 | i32 | |
| 11 | i64 | |
| 12 | i8 | |
| 13 | isize | |
| 14 | u128 | |
| 15 | u16 | |
| 16 | and 4 others | |
| 17 | = note: required for `f32` to implement `From<<T as Iterator>::Item>` | |
| 18 | ||
| 19 | error: aborting due to 1 previous error | |
| 20 | ||
| 21 | For more information about this error, try `rustc --explain E0277`. |
tests/ui/traits/nightly-only-unstable.force.stderr created+36| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | error[E0277]: the trait bound `(): LocalTrait` is not satisfied | |
| 2 | --> $DIR/nightly-only-unstable.rs:25:21 | |
| 3 | | | |
| 4 | LL | use_local_trait(()); | |
| 5 | | --------------- ^^ the trait `LocalTrait` is not implemented for `()` | |
| 6 | | | | |
| 7 | | required by a bound introduced by this call | |
| 8 | | | |
| 9 | help: this trait has no implementations, consider adding one | |
| 10 | --> $DIR/nightly-only-unstable.rs:14:1 | |
| 11 | | | |
| 12 | LL | trait LocalTrait {} | |
| 13 | | ^^^^^^^^^^^^^^^^ | |
| 14 | note: required by a bound in `use_local_trait` | |
| 15 | --> $DIR/nightly-only-unstable.rs:16:28 | |
| 16 | | | |
| 17 | LL | fn use_local_trait(_: impl LocalTrait) {} | |
| 18 | | ^^^^^^^^^^ required by this bound in `use_local_trait` | |
| 19 | ||
| 20 | error[E0277]: the trait bound `(): ForeignTrait` is not satisfied | |
| 21 | --> $DIR/nightly-only-unstable.rs:31:23 | |
| 22 | | | |
| 23 | LL | use_foreign_trait(()); | |
| 24 | | ----------------- ^^ the trait `ForeignTrait` is not implemented for `()` | |
| 25 | | | | |
| 26 | | required by a bound introduced by this call | |
| 27 | | | |
| 28 | note: required by a bound in `use_foreign_trait` | |
| 29 | --> $DIR/nightly-only-unstable.rs:20:30 | |
| 30 | | | |
| 31 | LL | fn use_foreign_trait(_: impl force_unstable::ForeignTrait) {} | |
| 32 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `use_foreign_trait` | |
| 33 | ||
| 34 | error: aborting due to 2 previous errors | |
| 35 | ||
| 36 | For more information about this error, try `rustc --explain E0277`. |
tests/ui/traits/nightly-only-unstable.normal.stderr created+36| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | error[E0277]: the trait bound `(): LocalTrait` is not satisfied | |
| 2 | --> $DIR/nightly-only-unstable.rs:25:21 | |
| 3 | | | |
| 4 | LL | use_local_trait(()); | |
| 5 | | --------------- ^^ the trait `LocalTrait` is not implemented for `()` | |
| 6 | | | | |
| 7 | | required by a bound introduced by this call | |
| 8 | | | |
| 9 | help: this trait has no implementations, consider adding one | |
| 10 | --> $DIR/nightly-only-unstable.rs:14:1 | |
| 11 | | | |
| 12 | LL | trait LocalTrait {} | |
| 13 | | ^^^^^^^^^^^^^^^^ | |
| 14 | note: required by a bound in `use_local_trait` | |
| 15 | --> $DIR/nightly-only-unstable.rs:16:28 | |
| 16 | | | |
| 17 | LL | fn use_local_trait(_: impl LocalTrait) {} | |
| 18 | | ^^^^^^^^^^ required by this bound in `use_local_trait` | |
| 19 | ||
| 20 | error[E0277]: the trait bound `(): ForeignTrait` is not satisfied | |
| 21 | --> $DIR/nightly-only-unstable.rs:31:23 | |
| 22 | | | |
| 23 | LL | use_foreign_trait(()); | |
| 24 | | ----------------- ^^ the nightly-only, unstable trait `ForeignTrait` is not implemented for `()` | |
| 25 | | | | |
| 26 | | required by a bound introduced by this call | |
| 27 | | | |
| 28 | note: required by a bound in `use_foreign_trait` | |
| 29 | --> $DIR/nightly-only-unstable.rs:20:30 | |
| 30 | | | |
| 31 | LL | fn use_foreign_trait(_: impl force_unstable::ForeignTrait) {} | |
| 32 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `use_foreign_trait` | |
| 33 | ||
| 34 | error: aborting due to 2 previous errors | |
| 35 | ||
| 36 | For more information about this error, try `rustc --explain E0277`. |
tests/ui/traits/nightly-only-unstable.rs created+36| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | //@ revisions: normal force | |
| 2 | //@ edition: 2024 | |
| 3 | //@ aux-crate: force_unstable=force_unstable.rs | |
| 4 | //@[force] compile-flags: -Zforce-unstable-if-unmarked | |
| 5 | ||
| 6 | #![feature(rustc_private)] | |
| 7 | ||
| 8 | // Regression test for <https://github.com/rust-lang/rust/issues/152692>. | |
| 9 | // | |
| 10 | // When building a crate with `-Zforce-unstable-if-unmarked` (e.g. the compiler or stdlib), | |
| 11 | // it's unhelpful to mention that a not-implemented trait is unstable, because that will | |
| 12 | // be true of every local and foreign trait that isn't explicitly marked stable. | |
| 13 | ||
| 14 | trait LocalTrait {} | |
| 15 | ||
| 16 | fn use_local_trait(_: impl LocalTrait) {} | |
| 17 | //~^ NOTE required by a bound in `use_local_trait` | |
| 18 | //~| NOTE required by this bound in `use_local_trait` | |
| 19 | ||
| 20 | fn use_foreign_trait(_: impl force_unstable::ForeignTrait) {} | |
| 21 | //~^ NOTE required by a bound in `use_foreign_trait` | |
| 22 | //~| NOTE required by this bound in `use_foreign_trait` | |
| 23 | ||
| 24 | fn main() { | |
| 25 | use_local_trait(()); | |
| 26 | //~^ ERROR the trait bound `(): LocalTrait` is not satisfied | |
| 27 | //[normal]~| NOTE the trait `LocalTrait` is not implemented for `()` | |
| 28 | //[force]~| NOTE the trait `LocalTrait` is not implemented for `()` | |
| 29 | //~| NOTE required by a bound introduced by this call | |
| 30 | ||
| 31 | use_foreign_trait(()); | |
| 32 | //~^ ERROR the trait bound `(): ForeignTrait` is not satisfied | |
| 33 | //[normal]~| NOTE the nightly-only, unstable trait `ForeignTrait` is not implemented for `()` | |
| 34 | //[force]~| NOTE the trait `ForeignTrait` is not implemented for `()` | |
| 35 | //~| NOTE required by a bound introduced by this call | |
| 36 | } |
tests/ui/typeck/issue-13853.rs+1-1| ... | ... | @@ -25,7 +25,7 @@ impl Node for Stuff { |
| 25 | 25 | |
| 26 | 26 | fn iterate<N: Node, G: Graph<N>>(graph: &G) { |
| 27 | 27 | for node in graph.iter() { //~ ERROR no method named `iter` found |
| 28 | node.zomg(); //~ ERROR type annotations needed | |
| 28 | node.zomg(); | |
| 29 | 29 | } |
| 30 | 30 | } |
| 31 | 31 |
tests/ui/typeck/issue-13853.stderr+3-9| ... | ... | @@ -17,12 +17,6 @@ error[E0599]: no method named `iter` found for reference `&G` in the current sco |
| 17 | 17 | LL | for node in graph.iter() { |
| 18 | 18 | | ^^^^ method not found in `&G` |
| 19 | 19 | |
| 20 | error[E0282]: type annotations needed | |
| 21 | --> $DIR/issue-13853.rs:28:9 | |
| 22 | | | |
| 23 | LL | node.zomg(); | |
| 24 | | ^^^^ cannot infer type | |
| 25 | ||
| 26 | 20 | error[E0308]: mismatched types |
| 27 | 21 | --> $DIR/issue-13853.rs:37:13 |
| 28 | 22 | | |
| ... | ... | @@ -43,7 +37,7 @@ help: consider borrowing here |
| 43 | 37 | LL | iterate(&graph); |
| 44 | 38 | | + |
| 45 | 39 | |
| 46 | error: aborting due to 4 previous errors | |
| 40 | error: aborting due to 3 previous errors | |
| 47 | 41 | |
| 48 | Some errors have detailed explanations: E0282, E0308, E0599. | |
| 49 | For more information about an error, try `rustc --explain E0282`. | |
| 42 | Some errors have detailed explanations: E0308, E0599. | |
| 43 | For more information about an error, try `rustc --explain E0308`. |
triagebot.toml+2-3| ... | ... | @@ -540,7 +540,6 @@ trigger_files = [ |
| 540 | 540 | |
| 541 | 541 | [autolabel."A-query-system"] |
| 542 | 542 | trigger_files = [ |
| 543 | "compiler/rustc_query_system", | |
| 544 | 543 | "compiler/rustc_query_impl", |
| 545 | 544 | "compiler/rustc_macros/src/query.rs" |
| 546 | 545 | ] |
| ... | ... | @@ -1557,8 +1556,10 @@ dep-bumps = [ |
| 1557 | 1556 | "/compiler/rustc_codegen_llvm/src/debuginfo" = ["compiler", "debuginfo"] |
| 1558 | 1557 | "/compiler/rustc_codegen_ssa" = ["compiler", "codegen"] |
| 1559 | 1558 | "/compiler/rustc_middle/src/dep_graph" = ["compiler", "incremental", "query-system"] |
| 1559 | "/compiler/rustc_middle/src/ich" = ["compiler", "incremental", "query-system"] | |
| 1560 | 1560 | "/compiler/rustc_middle/src/mir" = ["compiler", "mir"] |
| 1561 | 1561 | "/compiler/rustc_middle/src/traits" = ["compiler", "types"] |
| 1562 | "/compiler/rustc_middle/src/query" = ["compiler", "query-system"] | |
| 1562 | 1563 | "/compiler/rustc_middle/src/ty" = ["compiler", "types"] |
| 1563 | 1564 | "/compiler/rustc_const_eval/src/interpret" = ["compiler", "mir"] |
| 1564 | 1565 | "/compiler/rustc_mir_build/src/builder" = ["compiler", "mir"] |
| ... | ... | @@ -1567,8 +1568,6 @@ dep-bumps = [ |
| 1567 | 1568 | "/compiler/rustc_parse" = ["compiler", "parser"] |
| 1568 | 1569 | "/compiler/rustc_parse/src/lexer" = ["compiler", "lexer"] |
| 1569 | 1570 | "/compiler/rustc_query_impl" = ["compiler", "query-system"] |
| 1570 | "/compiler/rustc_query_system" = ["compiler", "query-system"] | |
| 1571 | "/compiler/rustc_query_system/src/ich" = ["compiler", "incremental", "query-system"] | |
| 1572 | 1571 | "/compiler/rustc_trait_selection" = ["compiler", "types"] |
| 1573 | 1572 | "/compiler/rustc_traits" = ["compiler", "types"] |
| 1574 | 1573 | "/compiler/rustc_type_ir" = ["compiler", "types"] |